Added Web Part from Olivier Carpentier around JQuery, Photopile.JS & Office UI Fabric Client-Side Web Part
|
@ -0,0 +1,25 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# we recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -0,0 +1 @@
|
|||
* text=auto
|
|
@ -0,0 +1,35 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Yeoman configuration files
|
||||
.yo-rc.json
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
solution
|
||||
temp
|
||||
*.spapp
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Visual Studio files
|
||||
.ntvs_analysis.dat
|
||||
.vs
|
||||
bin
|
||||
obj
|
||||
|
||||
# Resx Generated Code
|
||||
*.resx.ts
|
||||
|
||||
# Styles Generated Code
|
||||
*.scss.ts
|
|
@ -0,0 +1,14 @@
|
|||
# Folders
|
||||
.vscode
|
||||
coverage
|
||||
node_modules
|
||||
solution
|
||||
src
|
||||
temp
|
||||
|
||||
# Files
|
||||
*.csproj
|
||||
.git*
|
||||
.yo-rc.json
|
||||
gulpfile.js
|
||||
tsconfig.json
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
// The number of spaces a tab is equal to.
|
||||
"editor.tabSize": 2,
|
||||
|
||||
// When enabled, will trim trailing whitespace when you save a file.
|
||||
"files.trimTrailingWhitespace": true,
|
||||
|
||||
// Controls if the editor should automatically close brackets after opening them
|
||||
"editor.autoClosingBrackets": false,
|
||||
|
||||
// Configure glob patterns for excluding files and folders.
|
||||
"search.exclude": {
|
||||
"**/bower_components": true,
|
||||
"**/node_modules": true,
|
||||
"coverage": true,
|
||||
"dist": true,
|
||||
"lib-amd": true,
|
||||
"lib": true,
|
||||
"temp": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "0.1.0",
|
||||
"command": "gulp",
|
||||
"isShellCommand": true,
|
||||
"showOutput": "always",
|
||||
"args": [
|
||||
"--no-color"
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "bundle",
|
||||
"isBuildCommand": true,
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"taskName": "test",
|
||||
"isTestCommand": true,
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"taskName": "serve",
|
||||
"isWatching": true,
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
# JQuery, Photopile.JS & Office UI Fabric Client-Side Web Part
|
||||
|
||||
## Summary
|
||||
|
||||
Sample Web Part illustrating using JQuery and [Photopile.Js](https://github.com/bigbhowell/Photopile-JS)
|
||||
with the SharePoint Framework.
|
||||
|
||||
With it, you can display the photos contained in a SharePoint Pictures Library and it
|
||||
simulates a pile of photos scattered about on a surface. Thumbnail clicks remove photos from the pile,
|
||||
(enlarging them as if being picked up by the user), and once in view a secondary click returns the photo to the pile.
|
||||
|
||||
![Photpile Web Part displayed in SharePoint Workbench](./assets/photopileoverview.gif)
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
|
||||
* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant)
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
jquery-photopile|Olivier Carpentier (@olivierc)
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0|September 9, 2016|Initial release
|
||||
|
||||
## Disclaimer
|
||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||
|
||||
---
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
- clone this repo
|
||||
- in the command line run:
|
||||
- `npm i`
|
||||
- `tsd install`
|
||||
- `gulp serve`
|
||||
|
||||
## Features
|
||||
|
||||
This web part uses React, Office UI Fabric, JQuery, JQuery UI and Photopile.js. This web part is available in English (en-us)
|
||||
and French (fr-fr).
|
||||
|
||||
It is able to:
|
||||
* List Picture Libs contained in the current SharePoint web site
|
||||
* List all the pictures in the selected List
|
||||
* Render the pics as a cool photopile
|
||||
* Personalize the layout thanks to editable settings
|
||||
|
||||
This Web Part illustrates the following concepts on top of the SharePoint Framework:
|
||||
* Include JQuery and external framework in your solution
|
||||
* Implement rich web part properties panel with controls like DropDown, Sliders, Toggle, etc.
|
||||
* Load dynamic data from SharePoint as web part properties
|
||||
* Load dynamic data from SharePoint REST Services, as lists or items
|
||||
* Implement mock system to test your solution in the local workbench or on a SharePoint site
|
||||
* Include Office UI Fabric controls in your project
|
||||
* Render content with React
|
||||
* Etc.
|
After Width: | Height: | Size: 534 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 620 KiB |
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"entries": [
|
||||
{
|
||||
"entry": "./lib/webparts/photopileWebPart/PhotopileWebPartWebPart.js",
|
||||
"manifest": "./src/webparts/photopileWebPart/PhotopileWebPartWebPart.manifest.json",
|
||||
"outputPath": "./dist/photopile-web-part.bundle.js"
|
||||
}
|
||||
],
|
||||
"externals": {
|
||||
"@microsoft/sp-client-base": "node_modules/@microsoft/sp-client-base/dist/sp-client-base.js",
|
||||
"@microsoft/sp-client-preview": "node_modules/@microsoft/sp-client-preview/dist/sp-client-preview.js",
|
||||
"@microsoft/sp-lodash-subset": "node_modules/@microsoft/sp-lodash-subset/dist/sp-lodash-subset.js",
|
||||
"office-ui-fabric-react": "node_modules/office-ui-fabric-react/dist/office-ui-fabric-react.js",
|
||||
"react": "node_modules/react/dist/react.min.js",
|
||||
"react-dom": "node_modules/react-dom/dist/react-dom.min.js",
|
||||
"react-dom/server": "node_modules/react-dom/dist/react-dom-server.min.js",
|
||||
"jquery": {
|
||||
"path": "node_modules/jquery/dist/jquery.js",
|
||||
"globalName": "$"
|
||||
},
|
||||
"jqueryui": {
|
||||
"path": "node_modules/jqueryui/jquery-ui.js",
|
||||
"globalName": "jqueryui",
|
||||
"globalDependencies": ["jquery"]
|
||||
},
|
||||
"photopileModule": {
|
||||
"path": "src/webparts/photopileWebPart/js/photopile.js",
|
||||
"globalName": "photopileModule",
|
||||
"globalDependencies": ["jquery", "jqueryui"]
|
||||
}
|
||||
},
|
||||
"localizedResources": {
|
||||
"mystrings": "webparts/photopileWebPart/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "photopilewebpart",
|
||||
"container": "photopile-web-part",
|
||||
"accessKey": ""
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"solution": {
|
||||
"name": "Photopile WebPart",
|
||||
"id": "22c50f5a-8015-406e-be1f-8cfcffc2ec2b",
|
||||
"version": "1.0.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "photopile-web-part.spapp"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"port": 4321,
|
||||
"initialPage": "http://localhost:5432/workbench",
|
||||
"api": {
|
||||
"port": 5432,
|
||||
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"textMatch": ["src/**/*.template.html"]
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"lintConfig": {
|
||||
"rules": {
|
||||
"comment-format": false,
|
||||
"curly": false,
|
||||
"indent": false,
|
||||
"interface-name": false,
|
||||
"max-line-length": false,
|
||||
"member-ordering": false,
|
||||
"no-any": false,
|
||||
"no-bitwise": false,
|
||||
"no-consecutive-blank-lines": false,
|
||||
"no-constant-condition": false,
|
||||
"no-debugger": false,
|
||||
"no-empty": false,
|
||||
"no-null-keyword": false,
|
||||
"no-string-literal": false,
|
||||
"no-trailing-whitespace": false,
|
||||
"no-var-keyword": false,
|
||||
"one-line": false,
|
||||
"quotemark": false,
|
||||
"radix": false,
|
||||
"triple-equals": false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"cdnBasePath": "//photopilewebpart.blob.core.windows.net/photopile-web-part/"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
const build = require('@microsoft/sp-build-web');
|
||||
|
||||
build.initialize(gulp);
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "photopile-web-part",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-client-base": "~0.1.12",
|
||||
"@microsoft/sp-client-preview": "~0.1.12",
|
||||
"jquery": "^3.1.0",
|
||||
"jqueryui": "^1.11.1",
|
||||
"office-ui-fabric-react": "0.36.0",
|
||||
"react": "0.14.8",
|
||||
"react-dom": "0.14.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "~0.4.32",
|
||||
"@microsoft/sp-module-interfaces": "~0.1.8",
|
||||
"@microsoft/sp-webpart-workbench": "~0.1.12",
|
||||
"gulp": "~3.9.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp nuke",
|
||||
"test": "gulp test"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="GulpToVs" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{22c50f5a-8015-406e-be1f-8cfcffc2ec2b}</ProjectGuid>
|
||||
<ProjectHome />
|
||||
<ProjectView>ProjectFiles</ProjectView>
|
||||
<StartupFile>node_modules\gulp\bin\gulp.js</StartupFile>
|
||||
<WorkingDirectory>.</WorkingDirectory>
|
||||
<OutputPath>.</OutputPath>
|
||||
<ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
|
||||
<TypeScriptSourceMap>true</TypeScriptSourceMap>
|
||||
<TypeScriptModuleKind>CommonJS</TypeScriptModuleKind>
|
||||
<EnableTypeScript>false</EnableTypeScript>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
<ScriptArguments>serve</ScriptArguments>
|
||||
<StartWebBrowser>True</StartWebBrowser>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'" />
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'" />
|
||||
<Target Name="GulpToVs">
|
||||
<Message Text="Running gulp2vs.js" Importance="normal" />
|
||||
<Exec Command="CMD.EXE /c node $(MSBuildThisFileDirectory)\node_modules\@microsoft\npmx\lib\gulp2vs.js" />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<Content Include="*.js" />
|
||||
<Content Include="*.json" />
|
||||
<Content Include="*.md" />
|
||||
<Content Include="config\**\*.json" />
|
||||
<Content Include="docs\*.md" />
|
||||
<Content Include="sharepoint\feature_xml\**\*.*" />
|
||||
<Content Include="src\**\*.html" />
|
||||
<Content Include="src\**\*.js" />
|
||||
<Content Include="src\**\*.json" />
|
||||
<Content Include="src\**\*.less" />
|
||||
<Content Include="src\**\*.resx" />
|
||||
<Content Include="src\**\*.scss" />
|
||||
<Content Include="src\**\*.ts" />
|
||||
<Content Include="src\**\*.tsx" />
|
||||
<Content Include="typings\**\*.ts" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<!--Do not delete the following Import Project. While this appears to do nothing it is a marker for setting TypeScript properties before our import that depends on them.-->
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="False" />
|
||||
<Import Project="$(VSToolsPath)\Node.js Tools\Microsoft.NodejsTools.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>0</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:48022/</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>True</UseCustomServer>
|
||||
<CustomServerUrl>http://localhost:1337</CustomServerUrl>
|
||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}" User="">
|
||||
<WebProjectProperties>
|
||||
<StartPageUrl>
|
||||
</StartPageUrl>
|
||||
<StartAction>CurrentPage</StartAction>
|
||||
<AspNetDebugging>True</AspNetDebugging>
|
||||
<SilverlightDebugging>False</SilverlightDebugging>
|
||||
<NativeDebugging>False</NativeDebugging>
|
||||
<SQLDebugging>False</SQLDebugging>
|
||||
<ExternalProgram>
|
||||
</ExternalProgram>
|
||||
<StartExternalURL>
|
||||
</StartExternalURL>
|
||||
<StartCmdLineArguments>
|
||||
</StartCmdLineArguments>
|
||||
<StartWorkingDirectory>
|
||||
</StartWorkingDirectory>
|
||||
<EnableENC>False</EnableENC>
|
||||
<AlwaysStartWebServerOnDebug>False</AlwaysStartWebServerOnDebug>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
|
@ -0,0 +1,5 @@
|
|||
var context = require.context('.', true, /.+\.test\.js?$/);
|
||||
|
||||
context.keys().forEach(context);
|
||||
|
||||
module.exports = context;
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* @file
|
||||
* Photopile Web Part properties interface definition
|
||||
*
|
||||
* Author: Olivier Carpentier
|
||||
*/
|
||||
import { IPhotopileWebPartWebPartProps } from './IPhotopileWebPartWebPartProps';
|
||||
|
||||
export interface IPhotopileWebPartProps extends IPhotopileWebPartWebPartProps {
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @file
|
||||
* Photopile Web Part properties definition
|
||||
*
|
||||
* Author: Olivier Carpentier
|
||||
*/
|
||||
import { IWebPartContext } from '@microsoft/sp-client-preview';
|
||||
|
||||
export interface IPhotopileWebPartWebPartProps {
|
||||
listName: string;
|
||||
orderBy: string;
|
||||
orderByAsc: string;
|
||||
count: number;
|
||||
numLayers: number;
|
||||
thumbOverlap: number;
|
||||
thumbRotation: number;
|
||||
thumbBorderWidth: number;
|
||||
thumbBorderColor: string;
|
||||
thumbBorderHover: string;
|
||||
draggable: boolean;
|
||||
fadeDuration: number;
|
||||
pickupDuration: number;
|
||||
photoZIndex: number;
|
||||
photoBorder: number;
|
||||
photoBorderColor: string;
|
||||
showInfo: boolean;
|
||||
autoplayGallery: boolean;
|
||||
autoplaySpeed: number;
|
||||
|
||||
context: IWebPartContext;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* @file
|
||||
* SharePoint List & ListItems interface definitions
|
||||
*
|
||||
* Author: Olivier Carpentier
|
||||
*/
|
||||
|
||||
/**
|
||||
* @interface
|
||||
* Defines a collection of SharePoint lists
|
||||
*/
|
||||
export interface ISPLists {
|
||||
value: ISPList[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
* Defines a SharePoint list
|
||||
*/
|
||||
export interface ISPList {
|
||||
Title: string;
|
||||
Id: string;
|
||||
BaseTemplate: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
* Defines a SharePoint list's file
|
||||
*/
|
||||
export interface ISPFile {
|
||||
Name: string;
|
||||
ServerRelativeUrl: string;
|
||||
ThumbnailServerUrl?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
* Defines a collection of SharePoint list items
|
||||
*/
|
||||
export interface ISPListItems {
|
||||
value: ISPListItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
* Defines a SharePoint list item
|
||||
*/
|
||||
export interface ISPListItem {
|
||||
ID: string;
|
||||
Title?: string;
|
||||
Description?: string;
|
||||
File: ISPFile;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* @file
|
||||
* Implement a http client to request mock data to use the
|
||||
* web part with the local workbench
|
||||
*
|
||||
* Author: Olivier Carpentier
|
||||
*/
|
||||
import { ISPList, ISPListItem } from './ISPList';
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Defines a http client to request mock data to use the web part with the local workbench
|
||||
*/
|
||||
export default class MockHttpClient {
|
||||
|
||||
/**
|
||||
* @var
|
||||
* Mock SharePoint list sample
|
||||
*/
|
||||
private static _lists: ISPList[] = [{ Title: 'Mock List', Id: '1', BaseTemplate: '109' }];
|
||||
|
||||
/**
|
||||
* @var
|
||||
* Mock SharePoint list item sample
|
||||
*/
|
||||
private static _items: ISPListItem[] = [
|
||||
{ "ID": "1", "Title": "Pic 1", "Description": "", "File": { "Name": "1.jpg", "ServerRelativeUrl": "/Images/1.jpg" } }
|
||||
];
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Mock get SharePoint list request
|
||||
*/
|
||||
public static getLists(restUrl: string, options?: any): Promise<ISPList[]> {
|
||||
return new Promise<ISPList[]>((resolve) => {
|
||||
resolve(MockHttpClient._lists);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Mock get SharePoint list items request
|
||||
*/
|
||||
public static getListsItems(restUrl: string, options?: any): Promise<ISPListItem[]> {
|
||||
return new Promise<ISPListItem[]>((resolve) => {
|
||||
resolve(MockHttpClient._items);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
.photopileWebPart {
|
||||
|
||||
.workingOnItSpinner {
|
||||
display: block;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
height: 33.33333%;
|
||||
line-height: 1.5em;
|
||||
padding: 20px 20px;
|
||||
}
|
||||
|
||||
.loadingLabel {
|
||||
position: relative;
|
||||
top: -25px;
|
||||
left: 35px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
|
||||
|
||||
"id": "4b1150be-30a9-42a7-9bd4-55d50bbd9473",
|
||||
"componentType": "WebPart",
|
||||
"version": "0.0.1",
|
||||
"manifestVersion": 2,
|
||||
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "4b1150be-30a9-42a7-9bd4-55d50bbd9473",
|
||||
"group": { "default": "Default" },
|
||||
"title": { "default": "Photopile" },
|
||||
"description": { "default": "PhotopileWebPart description" },
|
||||
"officeFabricIconFontName": "PhotoCollection",
|
||||
"properties": {
|
||||
"listName": "",
|
||||
"orderBy": "ID",
|
||||
"orderByAsc": "asc",
|
||||
"count": 100,
|
||||
"numLayers": 5,
|
||||
"thumbOverlap": 50,
|
||||
"thumbRotation": 45,
|
||||
"thumbBorderWidth": 2,
|
||||
"thumbBorderColor": "white",
|
||||
"thumbBorderHover": "#EAEAEA",
|
||||
"draggable": true,
|
||||
"fadeDuration": 200,
|
||||
"pickupDuration": 500,
|
||||
"photoZIndex": 100,
|
||||
"photoBorder": 10,
|
||||
"photoBorderColor": "white",
|
||||
"showInfo": true,
|
||||
"autoplayGallery": false,
|
||||
"autoplaySpeed": 5000
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
/**
|
||||
* @file
|
||||
* Defines the Photopile client side web part
|
||||
*
|
||||
* Author: Olivier Carpentier
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneSettings,
|
||||
IWebPartContext,
|
||||
PropertyPaneTextField,
|
||||
PropertyPaneToggle,
|
||||
PropertyPaneDropdown,
|
||||
IPropertyPaneDropdownOption,
|
||||
PropertyPaneSlider
|
||||
} from '@microsoft/sp-client-preview';
|
||||
|
||||
import * as strings from 'mystrings';
|
||||
import { IPhotopileWebPartProps } from './IPhotopileWebPartProps';
|
||||
import PhotopileWebPart from './components/PhotopileWebPart';
|
||||
import { IPhotopileWebPartWebPartProps } from './IPhotopileWebPartWebPartProps';
|
||||
import { SPPicturesListService } from './SPPicturesListService';
|
||||
import { ISPList } from './ISPList';
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Defines the Photopile client side web part
|
||||
*/
|
||||
export default class PhotopileWebPartWebPart extends BaseClientSideWebPart<IPhotopileWebPartWebPartProps> {
|
||||
|
||||
/**
|
||||
* @var
|
||||
* Stores the list of SharePoint Pictures library found in the current SP web
|
||||
*/
|
||||
private listsDropdownOptions: IPropertyPaneDropdownOption[] = [];
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Web Part constructor
|
||||
*/
|
||||
public constructor(context: IWebPartContext) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Function called when the web part is inialized
|
||||
*/
|
||||
public onInit<T>(): Promise<T> {
|
||||
//Init the PicturesListService to get the picture libs
|
||||
const picturesListService: SPPicturesListService = new SPPicturesListService(this.properties, this.context);
|
||||
//Request the libs
|
||||
picturesListService.getPictureLibs()
|
||||
.then((response) => {
|
||||
//Store the result as list of dropdown options
|
||||
this.listsDropdownOptions = response.value.map((list: ISPList) => {
|
||||
return {
|
||||
key: list.Id,
|
||||
text: list.Title
|
||||
};
|
||||
});
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Renders the web part
|
||||
*/
|
||||
public render(): void {
|
||||
|
||||
//Constructs the react element code to JSX
|
||||
const element: React.ReactElement<IPhotopileWebPartProps> = React.createElement(PhotopileWebPart, {
|
||||
listName: this.properties.listName,
|
||||
orderBy: this.properties.orderBy,
|
||||
orderByAsc: this.properties.orderByAsc,
|
||||
count: this.properties.count,
|
||||
numLayers: this.properties.numLayers,
|
||||
thumbOverlap: this.properties.thumbOverlap,
|
||||
thumbRotation: this.properties.thumbRotation,
|
||||
thumbBorderWidth: this.properties.thumbBorderWidth,
|
||||
thumbBorderColor: this.properties.thumbBorderColor,
|
||||
thumbBorderHover: this.properties.thumbBorderHover,
|
||||
draggable: this.properties.draggable,
|
||||
fadeDuration: this.properties.fadeDuration,
|
||||
pickupDuration: this.properties.pickupDuration,
|
||||
photoZIndex: this.properties.photoZIndex,
|
||||
photoBorder: this.properties.photoBorder,
|
||||
photoBorderColor: this.properties.photoBorderColor,
|
||||
showInfo: this.properties.showInfo,
|
||||
autoplayGallery: this.properties.autoplayGallery,
|
||||
autoplaySpeed: this.properties.autoplaySpeed,
|
||||
context: this.context
|
||||
});
|
||||
|
||||
//Render the dom
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Prevent from changing the pane properties on typing
|
||||
*/
|
||||
protected get disableReactivePropertyChanges(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Gets the web part properties panel settings
|
||||
*/
|
||||
protected get propertyPaneSettings(): IPropertyPaneSettings {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
//Display the web part properties as accordion
|
||||
displayGroupsAsAccordion: true,
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.PictureLibraryGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneDropdown('listName', {
|
||||
label: strings.PictureLibraryFieldLabel,
|
||||
options: this.listsDropdownOptions
|
||||
}),
|
||||
PropertyPaneDropdown('orderBy', {
|
||||
label: strings.OrderByFieldLabel,
|
||||
options: [
|
||||
{ key: 'ID', text: strings.OrderByChoiceLabelId },
|
||||
{ key: 'Title', text: strings.OrderByChoiceLabelTitle },
|
||||
{ key: 'Created', text: strings.OrderByChoiceLabelCreated },
|
||||
{ key: 'Modified', text: strings.OrderByChoiceLabelModified },
|
||||
{ key: 'ImageWidth', text: strings.OrderByChoiceLabelImageWidth },
|
||||
{ key: 'ImageHeight', text: strings.OrderByChoiceLabelImageHeight }
|
||||
]
|
||||
}),
|
||||
PropertyPaneDropdown('orderByAsc', {
|
||||
label: strings.OrderByAscFieldLabel,
|
||||
options: [
|
||||
{ key: 'asc', text: strings.OrderByAscChoiceLabel },
|
||||
{ key: 'desc', text: strings.OrderByDescChoiceLabel }
|
||||
]
|
||||
}),
|
||||
PropertyPaneSlider('count', {
|
||||
label: strings.PictureLibraryCountLabel,
|
||||
min: 1,
|
||||
max: 100,
|
||||
step: 1,
|
||||
showValue: true
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
groupName: strings.ThumbnailsGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneSlider('numLayers', {
|
||||
label: strings.NumLayersFieldLabel,
|
||||
min: 1,
|
||||
max: 20,
|
||||
step: 1,
|
||||
showValue: true
|
||||
}),
|
||||
PropertyPaneSlider('thumbOverlap', {
|
||||
label: strings.ThumbOverlabFieldLabel,
|
||||
min: 1,
|
||||
max: 130,
|
||||
step: 1,
|
||||
showValue: true
|
||||
}),
|
||||
PropertyPaneSlider('thumbRotation', {
|
||||
label: strings.ThumbRotationFieldLabel,
|
||||
min: 0,
|
||||
max: 360,
|
||||
step: 1,
|
||||
showValue: true
|
||||
}),
|
||||
PropertyPaneSlider('thumbBorderWidth', {
|
||||
label: strings.ThumbBorderWidthFieldLabel,
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 1,
|
||||
showValue: true
|
||||
}),
|
||||
PropertyPaneTextField('thumbBorderColor', {
|
||||
label: strings.ThumbBorderColorFieldLabel
|
||||
}),
|
||||
PropertyPaneTextField('thumbBorderHover', {
|
||||
label: strings.ThumbBorderHoverFieldLabel
|
||||
}),
|
||||
PropertyPaneToggle('draggable', {
|
||||
label: strings.DraggableFieldLabel
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
groupName: strings.PhotoContainerGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneSlider('fadeDuration', {
|
||||
label: strings.FadeDurationFieldLabel,
|
||||
min: 0,
|
||||
max: 5000,
|
||||
step: 100,
|
||||
showValue: true
|
||||
}),
|
||||
PropertyPaneSlider('pickupDuration', {
|
||||
label: strings.PickupDurationFieldLabel,
|
||||
min: 0,
|
||||
max: 5000,
|
||||
step: 100,
|
||||
showValue: true
|
||||
}),
|
||||
PropertyPaneSlider('photoZIndex', {
|
||||
label: strings.PhotoZIndexFieldLabel,
|
||||
min: 1,
|
||||
max: 1000,
|
||||
step: 1,
|
||||
showValue: true
|
||||
}),
|
||||
PropertyPaneSlider('photoBorder', {
|
||||
label: strings.PhotoBorderFieldLabel,
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 1,
|
||||
showValue: true
|
||||
}),
|
||||
PropertyPaneTextField('photoBorderColor', {
|
||||
label: strings.PhotoBorderColorFieldLabel
|
||||
}),
|
||||
PropertyPaneToggle('showInfo', {
|
||||
label: strings.ShowInfoFieldLabel
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
groupName: strings.AutoplayGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneToggle('autoplayGallery', {
|
||||
label: strings.AutoplayGalleryFieldLabel
|
||||
}),
|
||||
PropertyPaneSlider('autoplaySpeed', {
|
||||
label: strings.AutoplaySpeedFieldLabel,
|
||||
min: 0,
|
||||
max: 5000,
|
||||
step: 100,
|
||||
showValue: true
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,360 @@
|
|||
/**
|
||||
* @file
|
||||
* Service to get list & list items from current SharePoint site
|
||||
*
|
||||
* Author: Olivier Carpentier
|
||||
*/
|
||||
import { ISPLists, ISPListItems, ISPListItem } from './ISPList';
|
||||
import { IWebPartContext } from '@microsoft/sp-client-preview';
|
||||
import { IPhotopileWebPartProps } from './IPhotopileWebPartProps';
|
||||
import { EnvironmentType } from '@microsoft/sp-client-base';
|
||||
import MockHttpClient from './MockHttpClient';
|
||||
|
||||
/**
|
||||
* @interface
|
||||
* Service interface definition
|
||||
*/
|
||||
export interface ISPPicturesListService {
|
||||
/**
|
||||
* @function
|
||||
* Gets the list of picture libs in the current SharePoint site
|
||||
*/
|
||||
getPictureLibs(): Promise<ISPLists>;
|
||||
/**
|
||||
* @function
|
||||
* Gets the pictures from a SharePoint list
|
||||
*/
|
||||
getPictures(libId: string): Promise<ISPListItems>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Service implementation to get list & list items from current SharePoint site
|
||||
*/
|
||||
export class SPPicturesListService implements ISPPicturesListService {
|
||||
private context: IWebPartContext;
|
||||
private props: IPhotopileWebPartProps;
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Service constructor
|
||||
*/
|
||||
constructor(_props: IPhotopileWebPartProps, pageContext: IWebPartContext){
|
||||
this.props = _props;
|
||||
this.context = pageContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Gets the list of picture libs in the current SharePoint site
|
||||
*/
|
||||
public getPictureLibs(): Promise<ISPLists> {
|
||||
if (this.context.environment.type === EnvironmentType.Local) {
|
||||
//If the running environment is local, load the data from the mock
|
||||
return this.getPictureLibsFromMock();
|
||||
}
|
||||
else {
|
||||
//If the running environment is SharePoint, request the lists REST service
|
||||
//Gets only the list with BaseTemplate = 109 (picture libs)
|
||||
return this.context.httpClient.get(
|
||||
`${this.context.pageContext.web.absoluteUrl}/_api/lists?$select=Title,id,BaseTemplate&$filter=BaseTemplate%20eq%20109`)
|
||||
.then((response: Response) => {
|
||||
return response.json();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Returns 3 fake SharePoint lists for the Mock mode
|
||||
*/
|
||||
private getPictureLibsFromMock(): Promise<ISPLists> {
|
||||
return MockHttpClient.getLists(this.context.pageContext.web.absoluteUrl).then(() => {
|
||||
const listData: ISPLists = {
|
||||
value:
|
||||
[
|
||||
{ Title: 'Mock List One', Id: '1', BaseTemplate: '109' },
|
||||
{ Title: 'Mock List Two', Id: '2', BaseTemplate: '109' },
|
||||
{ Title: 'Mock List Three', Id: '3', BaseTemplate: '109' }
|
||||
]
|
||||
};
|
||||
return listData;
|
||||
}) as Promise<ISPLists>;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Gets the pictures from a SharePoint list
|
||||
*/
|
||||
public getPictures(libId: string): Promise<ISPListItems> {
|
||||
if (this.context.environment.type === EnvironmentType.Local) {
|
||||
//If the running environment is local, load the data from the mock
|
||||
return this.getPicturesFromMock(libId);
|
||||
}
|
||||
else {
|
||||
//If the running environment is SharePoint, request the items REST service
|
||||
//Builds the request to get only some fields, order the items & limit the number of items
|
||||
//TODO: optimize the request to not include folders and get only items
|
||||
var restUrl: string = this.context.pageContext.web.absoluteUrl;
|
||||
restUrl += "/_api/Web/Lists(guid'";
|
||||
restUrl += this.props.listName;
|
||||
restUrl += "')/items?$expand=File&$select=Title,Description,id,File,FileSystemObjectType&$orderby=";
|
||||
restUrl += this.props.orderBy;
|
||||
restUrl += "%20";
|
||||
restUrl += this.props.orderByAsc;
|
||||
restUrl += "&$top=";
|
||||
restUrl += this.props.count;
|
||||
|
||||
//Request the SharePoint web service
|
||||
return this.context.httpClient.get(restUrl).then((response: Response) => {
|
||||
return response.json().then((responseFormated: any) => {
|
||||
var formatedResponse: ISPListItems = { value: []};
|
||||
//Fetchs the Json response to construct the final items list
|
||||
responseFormated.value.map((object: any, i: number) => {
|
||||
//Tests if the result is a file and not a folder
|
||||
if (object['FileSystemObjectType'] == '0') {
|
||||
var spListItem: ISPListItem = {
|
||||
'ID': object["ID"],
|
||||
'Title': object['Title'],
|
||||
'Description': object['Description'],
|
||||
'File': {
|
||||
'Name': object['File']['Name'],
|
||||
'ServerRelativeUrl': object['File']['ServerRelativeUrl']
|
||||
}
|
||||
};
|
||||
//Creates the thumbnail item url from the Picture path
|
||||
spListItem.File.ThumbnailServerUrl = this.getThumbnailUrl(spListItem.File.ServerRelativeUrl, spListItem.File.Name);
|
||||
formatedResponse.value.push(spListItem);
|
||||
}
|
||||
});
|
||||
return formatedResponse;
|
||||
});
|
||||
}) as Promise<ISPListItems>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Gets the thumbnail picture url from the Picture name.
|
||||
* In SharePoint pictures libs, the thumbnail url is formated as for example '/_t/10_jpg.jpg'
|
||||
*/
|
||||
private getThumbnailUrl(pictureUrl: string, pictureName: string): string {
|
||||
if (pictureUrl == null || pictureUrl == '')
|
||||
return '';
|
||||
var thumbUrl: string = '';
|
||||
thumbUrl = pictureUrl.replace(pictureName, '');
|
||||
thumbUrl += "_t/";
|
||||
thumbUrl += pictureName.replace(".", "_");
|
||||
thumbUrl += ".jpg";
|
||||
return thumbUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Gets the pictures list from the mock. This function will return a
|
||||
* different list of pics for the lib 1 & 2, and an empty list for the third.
|
||||
*/
|
||||
private getPicturesFromMock(libId: string): Promise<ISPListItems> {
|
||||
return MockHttpClient.getListsItems(this.context.pageContext.web.absoluteUrl).then(() => {
|
||||
var listData: ISPListItems = { value: []};
|
||||
if (libId == '1') {
|
||||
listData = {
|
||||
value:
|
||||
[
|
||||
{
|
||||
"ID": "1", "Title": "Barton Dam, Ann Arbor, Michigan", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "01.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/01.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/01.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "2", "Title": "Building Atlanta, Georgia", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "02.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/02.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/02.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "3", "Title": "Nice day for a swim", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "03.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/03.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/03.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "4", "Title": "The plants that never die", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "04.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/04.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/04.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "5", "Title": "Downtown Atlanta, Georgia", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "05.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/05.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/05.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "6", "Title": "Atlanta traffic", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "06.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/06.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/06.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "7", "Title": "A pathetic dog", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "07.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/07.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/07.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "8", "Title": "Two happy dogs", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "08.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/08.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/08.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "9", "Title": "Antigua, Guatemala", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "09.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/09.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/09.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "10", "Title": "Iximche, Guatemala", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "10.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/10.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/10.jpg"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
else if (libId == '2') {
|
||||
listData = {
|
||||
value:
|
||||
[
|
||||
{
|
||||
"ID": "11", "Title": "Barton Dam, Ann Arbor, Michigan", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "11.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/11.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/11.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "12", "Title": "Building Atlanta, Georgia", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "12.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/12.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/12.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "13", "Title": "Nice day for a swim", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "13.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/13.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/13.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "14", "Title": "The plants that never die", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "14.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/14.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/14.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "15", "Title": "Downtown Atlanta, Georgia", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "15.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/15.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/15.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "16", "Title": "Atlanta traffic", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "16.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/16.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/16.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "17", "Title": "A pathetic dog", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "17.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/17.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/17.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "18", "Title": "Two happy dogs", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "18.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/18.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/18.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "19", "Title": "Antigua, Guatemala", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "19.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/19.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/19.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "20", "Title": "Iximche, Guatemala", "Description": "",
|
||||
"File":
|
||||
{
|
||||
"Name": "20.jpg",
|
||||
"ServerRelativeUrl": "../src/webparts/photopileWebPart/images/fullsize/20.jpg",
|
||||
"ThumbnailServerUrl": "../src/webparts/photopileWebPart/images/thumbs/20.jpg"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
return listData;
|
||||
}) as Promise<ISPListItems>;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
/**
|
||||
* @file
|
||||
* Photopile Web Part React JSX component.
|
||||
*
|
||||
* Contains JSX code to render the web part with HTML templates.
|
||||
*
|
||||
* Author: Olivier Carpentier
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import { Spinner, SpinnerType } from 'office-ui-fabric-react/lib/Spinner';
|
||||
import { IPhotopileWebPartProps } from '../IPhotopileWebPartProps';
|
||||
import { IWebPartContext } from '@microsoft/sp-client-preview';
|
||||
import * as strings from 'mystrings';
|
||||
import styles from '../PhotopileWebPart.module.scss';
|
||||
import { SPPicturesListService } from '../SPPicturesListService';
|
||||
import { ISPListItem } from '../ISPList';
|
||||
import * as photopile from 'photopileModule';
|
||||
|
||||
require('jquery');
|
||||
require('jqueryui');
|
||||
require('../css/photopile.scss');
|
||||
require('photopileModule');
|
||||
|
||||
/**
|
||||
* @interface
|
||||
* Defines Photopile web part state.
|
||||
*/
|
||||
export interface IPhotopileState {
|
||||
results?: ISPListItem[];
|
||||
loaded: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Defines Photopile web part class.
|
||||
*/
|
||||
export default class PhotopileWebPart extends React.Component<IPhotopileWebPartProps, IPhotopileState> {
|
||||
|
||||
//page context
|
||||
private myPageContext: IWebPartContext;
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Photopile web part contructor.
|
||||
*/
|
||||
constructor(props: IPhotopileWebPartProps, context: IWebPartContext) {
|
||||
super(props, context);
|
||||
//Save the context
|
||||
this.myPageContext = props.context;
|
||||
//Init the component state
|
||||
this.state = {
|
||||
results: [],
|
||||
loaded: false
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @function
|
||||
* JSX Element render method
|
||||
*/
|
||||
public render(): JSX.Element {
|
||||
|
||||
if (this.props.listName == null || this.props.listName == '') {
|
||||
//Display select a list message
|
||||
return (
|
||||
<div className="ms-MessageBar">
|
||||
<div className="ms-MessageBar-content">
|
||||
<div className="ms-MessageBar-icon">
|
||||
<i className="ms-Icon ms-Icon--infoCircle"></i>
|
||||
</div>
|
||||
<div className="ms-MessageBar-text">
|
||||
{strings.ErrorSelectList}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (this.state.loaded == false) {
|
||||
//Display the loading spinner with the Office UI Fabric Spinner control
|
||||
return (
|
||||
<div className={ styles.photopileWebPart }>
|
||||
<div className={ styles.workingOnItSpinner }>
|
||||
<Spinner type={ SpinnerType.normal } />
|
||||
<div className={ styles.loadingLabel }>
|
||||
<label className="ms-Label"> {strings.Loading}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else if (this.state.results.length == 0) {
|
||||
//Display message no items
|
||||
return (
|
||||
<div className="ms-MessageBar ms-MessageBar--error">
|
||||
<div className="ms-MessageBar-content">
|
||||
<div className="ms-MessageBar-icon">
|
||||
<i className="ms-Icon ms-Icon--xCircle"></i>
|
||||
</div>
|
||||
<div className="ms-MessageBar-text">
|
||||
{strings.ErrorNoItems}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else {
|
||||
//Display the items list
|
||||
return (
|
||||
<div className='photopile-wrapper'>
|
||||
<ul className='photopile'>
|
||||
{this.state.results.map((object:ISPListItem, i:number) => {
|
||||
//Select the best Alt text with title, description or file's name
|
||||
var altText: string = object.Title;
|
||||
if (altText == null || altText == '')
|
||||
altText = object.Description;
|
||||
if (altText == null || altText == '')
|
||||
altText = object.File.Name;
|
||||
//Render the item
|
||||
return (
|
||||
<li>
|
||||
<a href={object.File.ServerRelativeUrl}>
|
||||
<img src={object.File.ThumbnailServerUrl} alt={altText} width="133" height="100"/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Function called when the component did mount
|
||||
*/
|
||||
public componentDidMount(): void {
|
||||
if (this.props.listName != null && this.props.listName != '') {
|
||||
//Init the Picture list service
|
||||
const picturesListService: SPPicturesListService = new SPPicturesListService(this.props, this.myPageContext);
|
||||
//Load the list of pictures from the current lib
|
||||
picturesListService.getPictures(this.props.listName)
|
||||
.then((response) => {
|
||||
//Modify the component state with the json result
|
||||
this.setState({ results: response.value, loaded: true});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Function called when the web part properties has changed
|
||||
*/
|
||||
public componentWillReceiveProps(nextProps: IPhotopileWebPartProps): void {
|
||||
//Define the state with empty results
|
||||
this.setState({ results: [], loaded: false});
|
||||
if (nextProps.listName != null && nextProps.listName != '') {
|
||||
//Init the Picture list service
|
||||
const picturesListService: SPPicturesListService = new SPPicturesListService(nextProps, this.myPageContext);
|
||||
//Load the list of pictures from the current lib
|
||||
picturesListService.getPictures(nextProps.listName)
|
||||
.then((response) => {
|
||||
//Modify the component state with the json result
|
||||
this.setState({ results: response.value, loaded: true});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Function called when the component has been rendered (ie HTML code is ready)
|
||||
*/
|
||||
public componentDidUpdate(prevProps: IPhotopileWebPartProps, prevState: IPhotopileState): void {
|
||||
|
||||
if (this.state.loaded) {
|
||||
//Init photopile options
|
||||
photopile.setNumLayers(this.props.numLayers);
|
||||
photopile.setThumbOverlap(this.props.thumbOverlap);
|
||||
photopile.setThumbRotation(this.props.thumbRotation);
|
||||
photopile.setThumbBorderWidth(this.props.thumbBorderWidth);
|
||||
photopile.setThumbBorderColor(this.props.thumbBorderColor);
|
||||
photopile.setThumbBorderHover(this.props.thumbBorderHover);
|
||||
photopile.setDraggable(this.props.draggable);
|
||||
photopile.setFadeDuration(this.props.fadeDuration);
|
||||
photopile.setPickupDuration(this.props.pickupDuration);
|
||||
photopile.setPhotoZIndex(this.props.photoZIndex);
|
||||
photopile.setPhotoBorder(this.props.photoBorder);
|
||||
photopile.setPhotoBorderColor(this.props.photoBorderColor);
|
||||
photopile.setShowInfo(this.props.showInfo);
|
||||
photopile.setAutoplayGallery(this.props.autoplayGallery);
|
||||
photopile.setAutoplaySpeed(this.props.autoplaySpeed);
|
||||
|
||||
//Init photopile
|
||||
photopile.scatter();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
|
||||
/*
|
||||
* Photopile image gallery base styles
|
||||
*
|
||||
* Auth: Brian W. Howell
|
||||
* Date: 25 April 2014
|
||||
*
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* THUMBNAIL SCALING
|
||||
* As window size gets smaller, reduce the maximum thumbnail width.
|
||||
* By doing so we can maintain the photopile effect for all screen resolutions.
|
||||
* It is highly likely that you'll want to customize these values based on
|
||||
* your personal preferences and the size of your thumbnails.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
@media (max-width: 320px) { ul.photopile li a { max-width: 85px; }}
|
||||
@media (min-width: 321px) and (max-width: 568px) { ul.photopile li a { max-width: 100px; }}
|
||||
@media (min-width: 569px) and (max-width: 768px) { ul.photopile li a { max-width: 115px; }}
|
||||
@media (min-width: 769px) and (max-width: 1024px) { ul.photopile li a { max-width: 125px; }}
|
||||
@media (min-width: 1025px) { ul.photopile li a { max-width: 150px; }}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Minimum height of the photopile's container div.
|
||||
* This is a placeholder for the gallery while it loads which reduces
|
||||
* shuffling around of elements before the gallery has rendered.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
div.photopile-wrapper {
|
||||
/*min-height : 500px;*/
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Make sure path to navigation sprite is correct
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
div#photopile-nav-next,
|
||||
div#photopile-nav-next:hover,
|
||||
div#photopile-nav-prev,
|
||||
div#photopile-nav-prev:hover {
|
||||
background-image: url('//photopilewebpart.blob.core.windows.net/photopile-web-part/nav-sprites.png');
|
||||
}
|
||||
|
||||
/*----- end customization -----*/
|
||||
|
||||
/* Prevent FOUC */
|
||||
ul.photopile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Thumbnails */
|
||||
ul.photopile {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
ul.photopile li {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
margin: 2px;
|
||||
padding: 0;
|
||||
-webkit-backface-visibility: hidden;
|
||||
}
|
||||
ul.photopile li a {
|
||||
display: block;
|
||||
padding: 2px;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
border: 1px solid #6F6F6F;
|
||||
box-shadow: 0 0 20px #3D3D3D;
|
||||
}
|
||||
ul.photopile li.photopile-active-thumbnail:hover,
|
||||
ul.photopile li.photopile-active-thumbnail a:hover {
|
||||
cursor: default;
|
||||
}
|
||||
ul.photopile li a img {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 1px solid #6F6F6F;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
-moz-box-sizing:border-box;
|
||||
-webkit-box-sizing:border-box;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
|
||||
/* Photo container */
|
||||
div#photopile-active-image-container {
|
||||
border: 1px solid #6F6F6F;
|
||||
box-shadow: 0 20px 80px black;
|
||||
-moz-box-sizing:border-box;
|
||||
-webkit-box-sizing:border-box;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
div#photopile-active-image-container img {
|
||||
margin: 0 auto;
|
||||
height: auto;
|
||||
}
|
||||
div#photopile-active-image-info {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background: rgba(0,0,0,0.3);
|
||||
}
|
||||
div#photopile-active-image-info p {
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
padding: 3px 8px;
|
||||
}
|
||||
|
||||
/* Navigator */
|
||||
div#photopile-nav-next,
|
||||
div#photopile-nav-prev {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 30px;
|
||||
height: 40px;
|
||||
margin-top: -20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
div#photopile-nav-next {
|
||||
right: 0;
|
||||
margin-right: -35px;
|
||||
background-position: -50px 0;
|
||||
}
|
||||
div#photopile-nav-next:hover {
|
||||
background-position: -50px -50px;
|
||||
}
|
||||
div#photopile-nav-prev {
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: -35px;
|
||||
background-position: 0 0;
|
||||
}
|
||||
div#photopile-nav-prev:hover {
|
||||
background-position: 0 -50px;
|
||||
}
|
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 134 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 164 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 131 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 143 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 177 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.3 KiB |