New Sample - react-multimedia-gallery - SPFx (#899)
* update docs * Update README.md * Update README.md * Support to IE11 * fine tunning, added Fade on Images * use CSS transitions * clean extra comments
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-calendar",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
@ -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,32 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
||||
# 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,13 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"version": "1.8.2",
|
||||
"libraryName": "react-image-gallery",
|
||||
"libraryId": "cff1f832-50fb-4a72-bb4e-1393121dc4ad",
|
||||
"environment": "spo",
|
||||
"packageManager": "npm",
|
||||
"framework": "react",
|
||||
"isCreatingSolution": true,
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
# React Image Gallery
|
||||
|
||||
## Summary
|
||||
|
||||
This web part show images and videos in responsive grid, on click it show images and videos in a carousel view.
|
||||
|
||||
It uses Microsoft Graph API to get thumbnails and image/video url and use PnPjs to load files from library the images/videos are loading in lazy mode, progressively.
|
||||
|
||||
|
||||
##
|
||||
![callendar](/samples/react-multimedia-gallery/assets/MultimediaGallery.gif)
|
||||
|
||||
|
||||
## Web Part - Screenshots
|
||||
|
||||
![gallery](/samples/react-multimedia-gallery/assets/Annotation2.jpg)
|
||||
|
||||
![gallery](/samples/react-multimedia-gallery/assets/Annotation0.jpg)
|
||||
|
||||
![gallery](/samples/react-multimedia-gallery/assets/Annotation1.jpg)
|
||||
|
||||
|
||||
![gallery](/samples/react-multimedia-gallery/assets/Screenshot1.png)
|
||||
|
||||
|
||||
![gallery](/samples/react-multimedia-gallery/assets/Screenshot2.png)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
![drop](https://img.shields.io/badge/version-1.8.2-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Online](https:/dev.office.com/sharepoint)
|
||||
* [Microsoft Teams](https://products.office.com/en-US/microsoft-teams/group-chat-software)
|
||||
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
|
||||
|
||||
|
||||
## WebPart Properties
|
||||
|
||||
Property |Type|Required| comments
|
||||
--------------------|----|--------|----------
|
||||
Site Url of library | Text| yes|
|
||||
Library| Choice/Dropdown | yes| this is filled with all libraries
|
||||
number images to load | number| yes | number between 1 and 200
|
||||
|
||||
|
||||
|
||||
## Solution
|
||||
The web part Use PnPjs library, Microsoft Graph API, Office-ui-fabric-react components, react-slick Compoment
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
Multimedia Gallery Web Part|João Mendes
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0.0|June 24, 2019|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 repository
|
||||
- in the command line run:
|
||||
- `npm install`
|
||||
- `gulp build`
|
||||
- `gulp bundle --ship`
|
||||
- `gulp package-solution --ship`
|
||||
- `Add to AppCatalog and deploy`
|
||||
|
||||
|
||||
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-multimedia-gallery" />
|
||||
|
After Width: | Height: | Size: 377 KiB |
After Width: | Height: | Size: 254 KiB |
After Width: | Height: | Size: 378 KiB |
After Width: | Height: | Size: 40 MiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 1.4 MiB |
|
@ -0,0 +1,78 @@
|
|||
resources:
|
||||
- repo: self
|
||||
trigger:
|
||||
- master
|
||||
- develop
|
||||
queue:
|
||||
name: Hosted VS2017
|
||||
demands:
|
||||
- npm
|
||||
- node.js
|
||||
|
||||
steps:
|
||||
#install node 8.x
|
||||
- task: NodeTool@0
|
||||
displayName: 'Use Node 8.x'
|
||||
inputs:
|
||||
versionSpec: 8.x
|
||||
checkLatest: true
|
||||
|
||||
#install nodejs modules with npm
|
||||
- task: Npm@1
|
||||
displayName: 'npm install'
|
||||
inputs:
|
||||
workingDir: '$(Build.SourcesDirectory)'
|
||||
verbose: false
|
||||
|
||||
#start unit tests
|
||||
- task: Npm@1
|
||||
displayName: 'npm test'
|
||||
inputs:
|
||||
command: custom
|
||||
customCommand: 'test'
|
||||
workingDir: '$(Build.SourcesDirectory)'
|
||||
verbose: false
|
||||
|
||||
# Publish Test Results to Azure Pipelines/TFS
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFiles: 'temp/test/junit/junit.xml'
|
||||
searchFolder: '$(Build.SourcesDirectory)'
|
||||
|
||||
# publish coverage test results
|
||||
- task: PublishCodeCoverageResults@1
|
||||
displayName: 'Publish Code Coverage Results $(Build.SourcesDirectory)/temp/test/cobertura-coverage.xml'
|
||||
inputs:
|
||||
codeCoverageTool: Cobertura
|
||||
summaryFileLocation: '$(Build.SourcesDirectory)/temp/test/cobertura-coverage.xml'
|
||||
reportDirectory: '$(Build.SourcesDirectory)/temp/test/'
|
||||
|
||||
#bundle code with gulp
|
||||
- task: Gulp@0
|
||||
displayName: 'gulp bundle'
|
||||
inputs:
|
||||
gulpFile: '$(Build.SourcesDirectory)/gulpfile.js'
|
||||
targets: bundle
|
||||
arguments: '--ship'
|
||||
continueOnError: true
|
||||
|
||||
#package solution with gulp
|
||||
- task: Gulp@0
|
||||
displayName: 'gulp package-solution'
|
||||
inputs:
|
||||
gulpFile: '$(Build.SourcesDirectory)/gulpfile.js'
|
||||
targets: 'package-solution'
|
||||
arguments: '--ship'
|
||||
|
||||
#copy files to artifact repository
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy Files to: $(build.artifactstagingdirectory)/drop'
|
||||
inputs:
|
||||
Contents: '**\*.sppkg'
|
||||
TargetFolder: '$(build.artifactstagingdirectory)/drop'
|
||||
|
||||
#publish artifacts
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Artifact: drop'
|
||||
inputs:
|
||||
PathtoPublish: '$(build.artifactstagingdirectory)/drop'
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"image-gallery-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/imageGallery/ImageGalleryWebPart.js",
|
||||
"manifest": "./src/webparts/imageGallery/ImageGalleryWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"ImageGalleryWebPartStrings": "lib/webparts/imageGallery/loc/{locale}.js",
|
||||
"ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js",
|
||||
"PropertyControlStrings": "node_modules/@pnp/spfx-property-controls/lib/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "react-image-gallery",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"preset":"@voitanos/jest-preset-spfx-react16","rootDir":"../src","coverageReporters":["text","json","lcov","text-summary","cobertura"],"reporters":["default","jest-junit"]}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "react-image-gallery-client-side-solution",
|
||||
"id": "cff1f832-50fb-4a72-bb4e-1393121dc4ad",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true,
|
||||
"isDomainIsolated": false
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/react-image-gallery.sppkg"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
|
||||
"port": 4321,
|
||||
"https": true,
|
||||
"initialPage": "https://localhost:5432/workbench",
|
||||
"api": {
|
||||
"port": 5432,
|
||||
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
'use strict';
|
||||
|
||||
// check if gulp dist was called
|
||||
if (process.argv.indexOf('dist') !== -1) {
|
||||
// add ship options to command call
|
||||
process.argv.push('--ship');
|
||||
}
|
||||
|
||||
const path = require('path');
|
||||
const gulp = require('gulp');
|
||||
const build = require('@microsoft/sp-build-web');
|
||||
const gulpSequence = require('gulp-sequence');
|
||||
|
||||
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||
|
||||
// Create clean distrubution package
|
||||
gulp.task('dist', gulpSequence('clean', 'bundle', 'package-solution'));
|
||||
// Create clean development package
|
||||
gulp.task('dev', gulpSequence('clean', 'bundle', 'package-solution'));
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Custom Framework Specific gulp tasks
|
||||
*/
|
||||
|
||||
|
||||
build.initialize(gulp);
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"name": "react-image-gallery",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"preversion": "node ./tools/pre-version.js",
|
||||
"postversion": "gulp dist",
|
||||
"test": "./node_modules/.bin/jest --config ./config/jest.config.json",
|
||||
"test:watch": "./node_modules/.bin/jest --config ./config/jest.config.json --watchAll"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "1.8.2",
|
||||
"@microsoft/sp-lodash-subset": "1.8.2",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.8.2",
|
||||
"@microsoft/sp-property-pane": "1.8.2",
|
||||
"@microsoft/sp-webpart-base": "1.8.2",
|
||||
"@pnp/pnpjs": "^1.3.2",
|
||||
"@pnp/polyfill-ie11": "^1.0.1",
|
||||
"@pnp/spfx-controls-react": "1.13.1",
|
||||
"@pnp/spfx-property-controls": "1.14.1",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/jquery": "^3.3.29",
|
||||
"@types/react": "16.7.22",
|
||||
"@types/react-dom": "16.8.0",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"@uifabric/fluent-theme": "^0.16.9",
|
||||
"jquery": "^3.4.1",
|
||||
"lightbox-react": "^0.3.7",
|
||||
"office-ui-fabric-react": "^6.182.0",
|
||||
"react": "16.7.0",
|
||||
"react-dom": "16.7.0",
|
||||
"react-grid-gallery": "^0.5.4",
|
||||
"react-responsive-carousel": "^3.1.49",
|
||||
"react-slick": "^0.24.0",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"video-react": "^0.13.9"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "16.7.22"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/rush-stack-compiler-3.3": "~0.2.x",
|
||||
"@microsoft/sp-build-web": "1.8.2",
|
||||
"@microsoft/sp-module-interfaces": "1.8.2",
|
||||
"@microsoft/sp-tslint-rules": "1.8.2",
|
||||
"@microsoft/sp-webpart-workbench": "1.8.2",
|
||||
"@types/chai": "3.4.34",
|
||||
"@types/mocha": "2.2.38",
|
||||
"@types/react": "^16.7.22",
|
||||
"@voitanos/jest-preset-spfx-react16": "^1.1.0",
|
||||
"ajv": "~5.2.2",
|
||||
"gulp": "~3.9.1",
|
||||
"gulp-sequence": "1.0.0",
|
||||
"jest": "^23.6.0",
|
||||
"jest-junit": "^6.3.0",
|
||||
"typescript": "~3.3.x"
|
||||
},
|
||||
"jest-junit": {
|
||||
"output": "temp/test/junit/junit.xml",
|
||||
"usePathForSuiteName": "true"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,4 @@
|
|||
export interface IList {
|
||||
ID: string;
|
||||
Title: string;
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// João Mendes
|
||||
// March 2019
|
||||
|
||||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||
import { sp, Fields, Web, SearchResults, Field, PermissionKind, RegionalSettings, PagedItemCollection } from '@pnp/sp';
|
||||
import { graph, } from "@pnp/graph";
|
||||
import { SPHttpClient, SPHttpClientResponse, ISPHttpClientOptions, HttpClient, MSGraphClient } from '@microsoft/sp-http';
|
||||
import * as $ from 'jquery';
|
||||
|
||||
import { registerDefaultFontFaces } from "@uifabric/styling";
|
||||
import * as moment from 'moment';
|
||||
import { SiteUser } from "@pnp/sp/src/siteusers";
|
||||
import { dateAdd } from "@pnp/common";
|
||||
import { escape, update } from '@microsoft/sp-lodash-subset';
|
||||
|
||||
|
||||
// Class Services
|
||||
export default class spservices {
|
||||
|
||||
private graphClient: MSGraphClient = null;
|
||||
|
||||
constructor(private context: WebPartContext) {
|
||||
// Setuo Context to PnPjs and MSGraph
|
||||
sp.setup({
|
||||
spfxContext: this.context
|
||||
});
|
||||
|
||||
graph.setup({
|
||||
spfxContext: this.context
|
||||
});
|
||||
// Init
|
||||
this.onInit();
|
||||
}
|
||||
// OnInit Function
|
||||
private async onInit() {
|
||||
}
|
||||
|
||||
public async getSiteLists(siteUrl: string) {
|
||||
|
||||
let results: any[] = [];
|
||||
|
||||
if (!siteUrl) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const web = new Web(siteUrl);
|
||||
results = await web.lists
|
||||
.select("Title", "ID")
|
||||
.filter('BaseTemplate eq 101 or BaseTemplate eq 109')
|
||||
.usingCaching()
|
||||
.get();
|
||||
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
public async getImages(siteUrl: string, listId: string, numberImages: number): Promise<any[]> {
|
||||
let results: any[] = [];
|
||||
try {
|
||||
const web = new Web(siteUrl);
|
||||
results = await web.lists
|
||||
.getById(listId).items
|
||||
.select('Title','File_x0020_Type', 'FileSystemObjectType','File/Name', 'File/ServerRelativeUrl', 'File/Title', 'File/Id', 'File/TimeLastModified')
|
||||
.top(numberImages)
|
||||
.expand('File')
|
||||
.filter((`File_x0020_Type eq 'jpg' or File_x0020_Type eq 'png' or File_x0020_Type eq 'jpeg' or File_x0020_Type eq 'gif' or File_x0020_Type eq 'mp4'`))
|
||||
.orderBy('Modified', false)
|
||||
.usingCaching()
|
||||
.get();
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public async getImagesNextPage(results: PagedItemCollection<any[]>): Promise<PagedItemCollection<any[]>> {
|
||||
return results.getNext();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "b698e561-a0d5-4e5f-b1c2-c57c4f43753d",
|
||||
"alias": "ImageGalleryWebPart",
|
||||
"componentType": "WebPart",
|
||||
// The "*" signifies that the version should be taken from the package.json
|
||||
"version": "*",
|
||||
"manifestVersion": 2,
|
||||
// If true, the component can only be installed on sites where Custom Script is allowed.
|
||||
// Components that allow authors to embed arbitrary script code should set this to true.
|
||||
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||
"requiresCustomScript": false,
|
||||
"supportedHosts": [
|
||||
"SharePointWebPart", "TeamsTab", "SharePointFullPage"
|
||||
],
|
||||
"preconfiguredEntries": [
|
||||
{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||
"group": {
|
||||
"default": "SPFx Web Parts"
|
||||
},
|
||||
"title": {
|
||||
"default": "Image Gallery"
|
||||
},
|
||||
"description": {
|
||||
"default": "Image Gallery"
|
||||
},
|
||||
"officeFabricIconFontName": "PhotoCollection",
|
||||
"properties": {
|
||||
"title": "Image Gallery",
|
||||
"siteUrl": "",
|
||||
"list": "",
|
||||
"numberImages": 20
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
import '@pnp/polyfill-ie11';
|
||||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
import {
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField,
|
||||
PropertyPaneDropdown,
|
||||
IPropertyPaneDropdownOption,
|
||||
PropertyPaneLabel
|
||||
} from '@microsoft/sp-property-pane';
|
||||
|
||||
import * as strings from 'ImageGalleryWebPartStrings';
|
||||
import ImageGallery from './components/ImageGallery/ImageGallery';
|
||||
import { ImageGalleryProps } from './components/ImageGallery/ImageGalleryProps';
|
||||
import spservices from '../../services/spservices';
|
||||
import { PropertyFieldNumber } from '@pnp/spfx-property-controls/lib/PropertyFieldNumber';
|
||||
|
||||
export interface IImageGalleryWebPartProps {
|
||||
title: string;
|
||||
siteUrl: string;
|
||||
list: string;
|
||||
errorMessage: string;
|
||||
numberImages: number;
|
||||
|
||||
}
|
||||
|
||||
export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGalleryWebPartProps> {
|
||||
private lists: IPropertyPaneDropdownOption[] = [];
|
||||
private listsDropdownDisabled: boolean = true;
|
||||
private spService: spservices = null;
|
||||
private errorMessage: string;
|
||||
|
||||
// onInit
|
||||
public async onInit(): Promise<void> {
|
||||
|
||||
this.spService = new spservices(this.context);
|
||||
this.properties.siteUrl = this.properties.siteUrl ? this.properties.siteUrl : this.context.pageContext.site.absoluteUrl;
|
||||
|
||||
if (this.properties.siteUrl && !this.properties.list) {
|
||||
const _lists = await this.loadLists();
|
||||
if ( _lists.length > 0 ){
|
||||
this.lists = _lists;
|
||||
this.properties.list = this.lists[0].key.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
||||
protected async onPropertyPaneConfigurationStart() {
|
||||
|
||||
try {
|
||||
if (this.properties.siteUrl) {
|
||||
const _lists = await this.loadLists();
|
||||
this.lists = _lists;
|
||||
this.listsDropdownDisabled = false;
|
||||
// await this.loadFields(this.properties.siteUrl);
|
||||
this.context.propertyPane.refresh();
|
||||
|
||||
} else {
|
||||
this.lists = [];
|
||||
this.properties.list = '';
|
||||
this.listsDropdownDisabled = false;
|
||||
this.context.propertyPane.refresh();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private async loadLists(): Promise<IPropertyPaneDropdownOption[]> {
|
||||
const _lists: IPropertyPaneDropdownOption[] = [];
|
||||
try {
|
||||
const results = await this.spService.getSiteLists(this.properties.siteUrl);
|
||||
for (const list of results) {
|
||||
_lists.push({ key: list.Id, text: list.Title });
|
||||
}
|
||||
// push new item value
|
||||
} catch (error) {
|
||||
this.errorMessage = `${ escape(error.message.toString())} - please check if site url if valid.` ;
|
||||
this.context.propertyPane.refresh();
|
||||
}
|
||||
return _lists;
|
||||
}
|
||||
|
||||
private onSiteUrlGetErrorMessage(value: string) {
|
||||
let returnValue: string = '';
|
||||
if (value) {
|
||||
returnValue = '';
|
||||
} else {
|
||||
const previousList: string = this.properties.list;
|
||||
const previousSiteUrl: string = this.properties.siteUrl;
|
||||
// reset selected item
|
||||
this.properties.list = undefined;
|
||||
this.properties.siteUrl = undefined;
|
||||
this.lists = [];
|
||||
this.listsDropdownDisabled = true;
|
||||
this.onPropertyPaneFieldChanged('list', previousList, this.properties.list);
|
||||
this.onPropertyPaneFieldChanged('siteUrl', previousSiteUrl, this.properties.siteUrl);
|
||||
this.context.propertyPane.refresh();
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
protected async onPropertyPaneFieldChanged(propertyPath: string, oldValue: string, newValue: string) {
|
||||
try {
|
||||
// reset any error
|
||||
this.properties.errorMessage = undefined;
|
||||
this.errorMessage = undefined;
|
||||
this.context.propertyPane.refresh();
|
||||
|
||||
if (propertyPath === 'siteUrl' && newValue) {
|
||||
super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
|
||||
const _oldValue = this.properties.list;
|
||||
this.onPropertyPaneFieldChanged('list', _oldValue, this.properties.list);
|
||||
this.context.propertyPane.refresh();
|
||||
const _lists = await this.loadLists();
|
||||
this.lists = _lists;
|
||||
this.listsDropdownDisabled = false;
|
||||
this.properties.list = this.lists.length > 0 ? this.lists[0].key.toString() : undefined;
|
||||
this.context.propertyPane.refresh();
|
||||
this.render();
|
||||
}
|
||||
else {
|
||||
super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
|
||||
}
|
||||
} catch (error) {
|
||||
this.errorMessage = `${error.message} - please check if site url if valid.` ;
|
||||
this.context.propertyPane.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<ImageGalleryProps > = React.createElement(
|
||||
ImageGallery,
|
||||
{
|
||||
title: this.properties.title,
|
||||
siteUrl: this.properties.siteUrl,
|
||||
list: this.properties.list,
|
||||
numberImages: this.properties.numberImages,
|
||||
context: this.context,
|
||||
displayMode: this.displayMode,
|
||||
updateProperty: (value: string) => {
|
||||
this.properties.title = value;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected onDispose(): void {
|
||||
ReactDom.unmountComponentAtNode(this.domElement);
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('title', {
|
||||
label: strings.TitleLabel,
|
||||
value: this.properties.title,
|
||||
}),
|
||||
PropertyPaneTextField('siteUrl', {
|
||||
label: strings.SiteUrlFieldLabel,
|
||||
onGetErrorMessage: this.onSiteUrlGetErrorMessage.bind(this),
|
||||
value: this.context.pageContext.site.absoluteUrl,
|
||||
deferredValidationTime: 1200,
|
||||
}),
|
||||
PropertyPaneDropdown('list', {
|
||||
label: strings.ListFieldLabel,
|
||||
options: this.lists,
|
||||
disabled: this.listsDropdownDisabled,
|
||||
}),
|
||||
PropertyPaneLabel('errorMessage', {
|
||||
text: this.errorMessage,
|
||||
}),
|
||||
PropertyFieldNumber("numberImages", {
|
||||
key: "numberValue",
|
||||
label: "Number of images to load",
|
||||
description: "Number between 01 and 200",
|
||||
value: this.properties.numberImages,
|
||||
maxValue: 200,
|
||||
minValue: 1,
|
||||
disabled: false
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
export interface IGalleryImages {
|
||||
imageUrl: string;
|
||||
ServerRelativeUrl: string;
|
||||
thumbnail:string;
|
||||
thumbnailWidth: number;
|
||||
thumbnailHeight: number;
|
||||
caption: string;
|
||||
mediaType: string;
|
||||
customOverlay: any;
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||
|
||||
.carousel {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.slideLoading {
|
||||
display:none !important;
|
||||
}
|
||||
.overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background: rgb(0, 0, 0);
|
||||
background: rgba(0, 0, 0, 0.5); /* Black see-through */
|
||||
color: #f1f1f1;
|
||||
width: 100%;
|
||||
transition: .5s ease;
|
||||
opacity:0;
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 250px;
|
||||
float: left;
|
||||
vertical-align: top;
|
||||
margin:3px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.imageGallery {
|
||||
|
||||
|
||||
|
||||
|
||||
.webpartTitle{
|
||||
color: white !important;
|
||||
}
|
||||
.row {
|
||||
@include ms-Grid-row;
|
||||
@include ms-fontColor-white;
|
||||
background-color: $ms-color-themeDark;
|
||||
padding: 20px;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.column {
|
||||
@include ms-Grid-col;
|
||||
@include ms-lg10;
|
||||
@include ms-xl8;
|
||||
@include ms-xlPush2;
|
||||
@include ms-lgPush1;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include ms-font-xl;
|
||||
@include ms-fontColor-white;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.button {
|
||||
// Our button
|
||||
text-decoration: none;
|
||||
height: 32px;
|
||||
|
||||
// Primary Button
|
||||
min-width: 80px;
|
||||
background-color: $ms-color-themePrimary;
|
||||
border-color: $ms-color-themePrimary;
|
||||
color: $ms-color-white;
|
||||
|
||||
// Basic Button
|
||||
outline: transparent;
|
||||
position: relative;
|
||||
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: $ms-font-size-m;
|
||||
font-weight: $ms-font-weight-regular;
|
||||
border-width: 0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
padding: 0 16px;
|
||||
|
||||
|
||||
.label {
|
||||
font-weight: $ms-font-weight-semibold;
|
||||
font-size: $ms-font-size-m;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 0 4px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,422 @@
|
|||
import * as React from 'react';
|
||||
import styles from './ImageGallery.module.scss';
|
||||
import { ImageGalleryProps } from './ImageGalleryProps';
|
||||
import { ImageGalleryState } from './ImageGalleryState';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
import spservices from '../../../../services/spservices';
|
||||
import Gallery from 'react-grid-gallery';
|
||||
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
|
||||
import RenderImage from '../RenderImage/RenderImage';
|
||||
import {
|
||||
Spinner,
|
||||
SpinnerSize,
|
||||
MessageBar,
|
||||
MessageBarType,
|
||||
Label,
|
||||
Icon,
|
||||
DocumentCard,
|
||||
DefaultButton,
|
||||
PrimaryButton,
|
||||
ImageFit,
|
||||
Image,
|
||||
Dialog,
|
||||
DialogType,
|
||||
DialogFooter,
|
||||
ActionButton,
|
||||
IButtonProps,
|
||||
IconButton,
|
||||
CommandBarButton,
|
||||
ImageLoadState,
|
||||
Panel, PanelType
|
||||
|
||||
} from 'office-ui-fabric-react';
|
||||
import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder";
|
||||
import { DisplayMode } from '@microsoft/sp-core-library';
|
||||
import { FontSizes, } from '@uifabric/fluent-theme/lib/fluent/FluentType';
|
||||
import { CommunicationColors } from '@uifabric/fluent-theme/lib/fluent/FluentColors';
|
||||
import * as strings from 'ImageGalleryWebPartStrings';
|
||||
import * as microsoftTeams from '@microsoft/teams-js';
|
||||
import "react-responsive-carousel/lib/styles/carousel.min.css";
|
||||
import { Carousel } from 'react-responsive-carousel';
|
||||
import 'video-react/dist/video-react.css'; // import css
|
||||
import { Player, BigPlayButton } from 'video-react';
|
||||
import './carousel.scss';
|
||||
import { IGalleryImages } from './IGalleryImages';
|
||||
import Slider from "react-slick";
|
||||
import "slick-carousel/slick/slick.css";
|
||||
import "slick-carousel/slick/slick-theme.css";
|
||||
import * as $ from 'jquery';
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export
|
||||
* @class ImageGallery
|
||||
* @extends {React.Component<ImageGalleryProps, ImageGalleryState>}
|
||||
*/
|
||||
export default class ImageGallery extends React.Component<ImageGalleryProps, ImageGalleryState> {
|
||||
private spService: spservices = null;
|
||||
private images: any;
|
||||
private galleryImages: IGalleryImages[] = [];
|
||||
private _teamsContext: microsoftTeams.Context;
|
||||
private _teamsTheme: string = '';
|
||||
private _carouselImages: any;
|
||||
private _slider: any = null;
|
||||
|
||||
constructor(props: ImageGalleryProps) {
|
||||
super(props);
|
||||
|
||||
|
||||
this.spService = new spservices(this.props.context);
|
||||
|
||||
if (this.props.context.microsoftTeams) {
|
||||
this.props.context.microsoftTeams.getContext(context => {
|
||||
this._teamsContext = context;
|
||||
console.log('ctt', this._teamsContext.theme);
|
||||
this.setState({ teamsTheme: this._teamsContext.theme });
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
this.state = {
|
||||
images: [],
|
||||
isLoading: false,
|
||||
errorMessage: '',
|
||||
hasError: false,
|
||||
teamsTheme: 'default',
|
||||
isPlaying: true,
|
||||
showLithbox: false,
|
||||
photoIndex: 0,
|
||||
isloadingCarousel: false,
|
||||
carouselImages: [],
|
||||
autoplay: true,
|
||||
};
|
||||
|
||||
this.onPlayResume = this.onPlayResume.bind(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @protected
|
||||
* @returns {Promise<any>}
|
||||
* @memberof ImageGallery
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @private
|
||||
* @memberof ImageGallery
|
||||
*/
|
||||
private async loadPictures() {
|
||||
this.setState({ isLoading: true, hasError: false });
|
||||
const tenantUrl = `https://${location.host}`;
|
||||
try {
|
||||
this.images = await this.spService.getImages(this.props.siteUrl, this.props.list, this.props.numberImages);
|
||||
|
||||
for (const image of this.images) {
|
||||
const pURL = `${tenantUrl}/_api/v2.0/sharePoint:${image.File.ServerRelativeUrl}:/driveItem/thumbnails/0/large/content?preferNoRedirect=true `;
|
||||
const thumbnailUrl = `${tenantUrl}/_api/v2.0/sharePoint:${image.File.ServerRelativeUrl}:/driveItem/thumbnails/0/c240x240/content?preferNoRedirect=true `;
|
||||
|
||||
let mediaType: string = '';
|
||||
switch (image.File_x0020_Type) {
|
||||
case ('jpg' || 'jpeg' || 'png' || 'tiff' || 'gif'):
|
||||
mediaType = 'image';
|
||||
break;
|
||||
case ('mp4'):
|
||||
mediaType = 'video';
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
this.galleryImages.push(
|
||||
{
|
||||
imageUrl: pURL,
|
||||
mediaType: mediaType,
|
||||
ServerRelativeUrl: image.File.ServerRelativeUrl,
|
||||
thumbnail: thumbnailUrl,
|
||||
thumbnailWidth: 240,
|
||||
thumbnailHeight: 180,
|
||||
caption: image.Title ? image.Title : image.File.Name,
|
||||
// thumbnailCaption: image.File.Name,
|
||||
customOverlay:
|
||||
<Label style={{ fontSize: FontSizes.size18, bottom: 0, transition: '.5s ease', textAlign: 'center', width: '100%', position: 'absolute', background: 'rgba(0, 0, 0, 0.5)', color: '#f1f1f1', padding: '10px' }}>
|
||||
{image.Title ? image.Title : image.File.Name}
|
||||
</Label>
|
||||
},
|
||||
);
|
||||
|
||||
// Create Carousel Slides from Images
|
||||
|
||||
this._carouselImages = this.galleryImages.map((GalleryImage, i) => {
|
||||
return (
|
||||
<div className='slideLoading'>
|
||||
<div style={{}} >
|
||||
<Label
|
||||
style={{ fontSize: FontSizes.size18, textAlign: 'center', width: '100%', padding: '5px' }}>
|
||||
<ActionButton
|
||||
data-automation-id="test"
|
||||
iconProps={{ iconName: 'Download', styles: { root: { fontSize: FontSizes.size18, } } }}
|
||||
allowDisabledFocus={true}
|
||||
style={{ padding: '10px', fontSize: FontSizes.size18 }}
|
||||
checked={true}
|
||||
href={`${this.props.context.pageContext.legacyPageContext.siteAbsoluteUrl}/_layouts/download.aspx?SourceUrl=${GalleryImage.ServerRelativeUrl}`}
|
||||
>
|
||||
{GalleryImage.caption}
|
||||
</ActionButton>
|
||||
</Label>
|
||||
</div>
|
||||
{GalleryImage.mediaType == 'video' ?
|
||||
<div >
|
||||
<Player
|
||||
poster={GalleryImage.imageUrl}
|
||||
style={{ width: '100%', height: '640px' }}
|
||||
>
|
||||
<BigPlayButton position="center" />
|
||||
<source src={GalleryImage.ServerRelativeUrl}
|
||||
/>
|
||||
</Player>
|
||||
</div>
|
||||
:
|
||||
<div style={{ maxWidth: '100%' }}>
|
||||
<Image src={GalleryImage.imageUrl}
|
||||
style={{ height: 'auto', overflow: 'hidden', maxHeight: '100%' }}
|
||||
onLoadingStateChange={async (loadState: ImageLoadState) => {
|
||||
console.log('imageload Status ' + i, loadState, GalleryImage.imageUrl);
|
||||
if (loadState == ImageLoadState.loaded) {
|
||||
this.setState({ isloadingCarousel: false });
|
||||
}
|
||||
}}
|
||||
height={'400px'}
|
||||
imageFit={ImageFit.contain}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.setState({ hasError: true, errorMessage: decodeURIComponent(error.message) });
|
||||
}
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
|
||||
await this.loadPictures();
|
||||
this.setState({ images: this.galleryImages, carouselImages: this._carouselImages, isLoading: false, isloadingCarousel: false });
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {ImageGalleryProps} prevProps
|
||||
* @returns
|
||||
* @memberof ImageGallery
|
||||
*/
|
||||
public async componentDidUpdate(prevProps: ImageGalleryProps) {
|
||||
|
||||
if (!this.props.list || !this.props.siteUrl) return;
|
||||
// Get Properties change
|
||||
if (prevProps.list !== this.props.list || prevProps.numberImages !== this.props.numberImages) {
|
||||
this.galleryImages = [];
|
||||
this._carouselImages = [];
|
||||
await this.loadPictures();
|
||||
this.setState({ images: this.galleryImages, carouselImages: this._carouselImages, isLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @private
|
||||
* @memberof ImageGallery
|
||||
*/
|
||||
private onConfigure() {
|
||||
// Context of the web part
|
||||
this.props.context.propertyPane.open();
|
||||
}
|
||||
|
||||
private handleclick = event => {
|
||||
const value = event.currentTarget.attributes.getNamedItem("data-i").value;
|
||||
|
||||
this.setState({ showLithbox: true, photoIndex: Number(value), isloadingCarousel: true });
|
||||
}
|
||||
|
||||
private onNext = () => {
|
||||
this._slider.slickNext();
|
||||
}
|
||||
|
||||
private onPrev = () => {
|
||||
this._slider.slickPrev();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async onPlayResume() {
|
||||
const { isPlaying } = this.state;
|
||||
if (isPlaying) {
|
||||
this._slider.slickPause();
|
||||
} else {
|
||||
this._slider.slickPlay();
|
||||
}
|
||||
this.setState({
|
||||
isPlaying: !isPlaying
|
||||
});
|
||||
}
|
||||
|
||||
private onDialogClose = (ev: React.MouseEvent<HTMLButtonElement>) => {
|
||||
this.setState({ showLithbox: false });
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @returns {React.ReactElement<ImageGalleryProps>}
|
||||
* @memberof ImageGallery
|
||||
*/
|
||||
public render(): React.ReactElement<ImageGalleryProps> {
|
||||
console.log('theme', this.state.teamsTheme);
|
||||
const sliderSettings = {
|
||||
dots: false,
|
||||
infinite: true,
|
||||
speed: 500,
|
||||
slidesToShow: 1,
|
||||
slidesToScroll: 1,
|
||||
lazyLoad: 'progressive',
|
||||
autoplaySpeed: 3000,
|
||||
initialSlide: this.state.photoIndex,
|
||||
arrows: false,
|
||||
draggable: false,
|
||||
adaptiveHeight: true,
|
||||
useCSS: true,
|
||||
useTransform: true,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div>
|
||||
{
|
||||
this.state.teamsTheme == 'dark' ?
|
||||
<Label style={{color:'white', fontSize: FontSizes.size24}}>{this.props.title}</Label>
|
||||
:
|
||||
<WebPartTitle displayMode={this.props.displayMode}
|
||||
title={this.props.title}
|
||||
updateProperty={this.props.updateProperty}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
{
|
||||
(!this.props.list) ?
|
||||
<Placeholder iconName='Edit'
|
||||
iconText={strings.WebpartConfigIconText}
|
||||
description={strings.WebpartConfigDescription}
|
||||
buttonLabel={strings.WebPartConfigButtonLabel}
|
||||
hideButton={this.props.displayMode === DisplayMode.Read}
|
||||
onConfigure={this.onConfigure.bind(this)} />
|
||||
:
|
||||
this.state.hasError ?
|
||||
<MessageBar messageBarType={MessageBarType.error}>
|
||||
{this.state.errorMessage}
|
||||
</MessageBar>
|
||||
:
|
||||
this.state.isLoading ?
|
||||
<Spinner size={SpinnerSize.large} label='loading images...' />
|
||||
:
|
||||
this.state.images.length == 0 ?
|
||||
<div style={{ width: '300px', margin: 'auto' }}>
|
||||
<Icon iconName="PhotoCollection"
|
||||
style={{ fontSize: '250px', color: '#d9d9d9' }} />
|
||||
<Label style={{ width: '250px', margin: 'auto', fontSize: FontSizes.size20 }}>No images in the library</Label>
|
||||
</div>
|
||||
:
|
||||
<div style={{ width: '100%', height: '100%', overflow: 'hidden' }}>
|
||||
{
|
||||
this.state.images.map((item, i) => {
|
||||
let v: boolean = true;
|
||||
return (
|
||||
<div
|
||||
onClick={this.handleclick}
|
||||
data-i={i}
|
||||
id={i.toString()}
|
||||
style={{ width: '230px', display: 'inline-block', verticalAlign: 'top', margin: '2px' }}>
|
||||
<DocumentCard>
|
||||
<RenderImage displayCaption={v} image={item} context={this.props.context} />
|
||||
</DocumentCard>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
<Panel
|
||||
isOpen={this.state.showLithbox}
|
||||
onDismiss={this.onDialogClose}
|
||||
headerText="Images - slides"
|
||||
type={PanelType.custom}
|
||||
customWidth="700px"
|
||||
onRenderFooterContent={() => {
|
||||
return (
|
||||
<div style={{ float: 'right', paddingBottom: '20px' }}>
|
||||
<PrimaryButton text="Close" onClick={this.onDialogClose} />
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
>
|
||||
|
||||
<div style={{ marginBottom: 25, verticalAlign: 'Top', width: '100%' }}>
|
||||
<Slider
|
||||
ref={c => (this._slider = c)}
|
||||
{...sliderSettings}
|
||||
autoplay={this.state.autoplay}
|
||||
onReInit={() => {
|
||||
if (!this.state.isloadingCarousel)
|
||||
$(".slideLoading").removeClass("slideLoading");
|
||||
|
||||
}}
|
||||
>
|
||||
{
|
||||
this.state.carouselImages
|
||||
}
|
||||
</Slider>
|
||||
</div>
|
||||
{
|
||||
!this.state.isloadingCarousel ?
|
||||
<div style={{ textAlign: 'center', width: '100%' }}>
|
||||
<CommandBarButton
|
||||
iconProps={{ iconName: 'TriangleSolidLeft12', styles: { root: { fontSize: FontSizes.size16, padding: '10px', color: CommunicationColors.primary } } }}
|
||||
allowDisabledFocus={true}
|
||||
style={{ fontSize: FontSizes.size16, marginRight: '10px' }}
|
||||
title='Prev'
|
||||
onClick={this.onPrev}>
|
||||
</CommandBarButton>
|
||||
<CommandBarButton
|
||||
iconProps={{ iconName: this.state.isPlaying ? 'Pause' : 'PlayResume', styles: { root: { fontSize: FontSizes.size18, padding: '10px', color: CommunicationColors.primary } } }}
|
||||
allowDisabledFocus={true}
|
||||
style={{ fontSize: FontSizes.size18, marginRight: '10px' }}
|
||||
title={this.state.isPlaying ? 'Pause' : 'Play'}
|
||||
onClick={this.onPlayResume}>
|
||||
</CommandBarButton>
|
||||
<CommandBarButton
|
||||
iconProps={{ iconName: 'TriangleSolidRight12', styles: { root: { fontSize: FontSizes.size16, padding: '10px', color: CommunicationColors.primary } } }}
|
||||
allowDisabledFocus={true}
|
||||
style={{ fontSize: FontSizes.size16 }}
|
||||
title='Next'
|
||||
onClick={this.onNext}>
|
||||
</CommandBarButton>
|
||||
</div>
|
||||
:
|
||||
<Spinner size={SpinnerSize.large} label={'Loading...'} style={{ fontSize: FontSizes.size18, color: CommunicationColors.primary }}></Spinner>
|
||||
}
|
||||
</Panel>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||
import { DisplayMode } from '@microsoft/sp-core-library';
|
||||
export interface ImageGalleryProps {
|
||||
title: string;
|
||||
siteUrl: string;
|
||||
list: string;
|
||||
context: WebPartContext;
|
||||
numberImages: number;
|
||||
updateProperty: (value: string) => void;
|
||||
displayMode: DisplayMode;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
import { IGalleryImages } from './IGalleryImages';
|
||||
export interface ImageGalleryState {
|
||||
images: IGalleryImages[];
|
||||
isLoading: boolean;
|
||||
errorMessage: string;
|
||||
hasError: boolean;
|
||||
teamsTheme: string;
|
||||
showLithbox: boolean;
|
||||
photoIndex: number;
|
||||
isloadingCarousel:boolean;
|
||||
carouselImages: any[];
|
||||
isPlaying:boolean;
|
||||
autoplay: boolean;
|
||||
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
import * as React from 'react';
|
||||
import styles from './ImageGallery.module.scss';
|
||||
import { ImageGalleryProps } from './ImageGalleryProps';
|
||||
import { ImageGalleryState } from './ImageGalleryState';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
import spservices from '../../../../services/spservices';
|
||||
import Gallery from 'react-grid-gallery';
|
||||
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
|
||||
import RenderImage from '../RenderImage/RenderImage';
|
||||
import {
|
||||
Spinner,
|
||||
SpinnerSize,
|
||||
MessageBar,
|
||||
MessageBarType,
|
||||
Label,
|
||||
Icon,
|
||||
DocumentCard,
|
||||
DocumentCardActivity,
|
||||
DocumentCardPreview,
|
||||
DocumentCardTitle,
|
||||
IDocumentCardPreviewProps,
|
||||
ImageFit,
|
||||
Image,
|
||||
} from 'office-ui-fabric-react';
|
||||
import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder";
|
||||
import { DisplayMode } from '@microsoft/sp-core-library';
|
||||
import { FontSizes } from '@uifabric/fluent-theme/lib/fluent/FluentType';
|
||||
import * as strings from 'ImageGalleryWebPartStrings';
|
||||
import * as microsoftTeams from '@microsoft/teams-js';
|
||||
import { Root } from '@pnp/graph';
|
||||
|
||||
import Lightbox from 'lightbox-react';
|
||||
import 'lightbox-react/style.css'; // This only needs to be imported once in your app
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export
|
||||
* @class ImageGallery
|
||||
* @extends {React.Component<ImageGalleryProps, ImageGalleryState>}
|
||||
*/
|
||||
export default class ImageGallery extends React.Component<ImageGalleryProps, ImageGalleryState> {
|
||||
private spService: spservices = null;
|
||||
private images: any;
|
||||
private galleryImages: any[] = [];
|
||||
private _teamsContext: microsoftTeams.Context;
|
||||
private _teamsTheme: string = '';
|
||||
private lithboxMedia: any[] = [];
|
||||
|
||||
constructor(props: ImageGalleryProps) {
|
||||
super(props);
|
||||
|
||||
|
||||
this.spService = new spservices(this.props.context);
|
||||
|
||||
if (this.props.context.microsoftTeams) {
|
||||
this.props.context.microsoftTeams.getContext(context => {
|
||||
this._teamsContext = context;
|
||||
this._teamsTheme = this._teamsContext.theme;
|
||||
console.log('ctt', this._teamsContext);
|
||||
this.setState({ teamsTheme: this._teamsTheme });
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
this.state = {
|
||||
images: [],
|
||||
isLoading: false,
|
||||
errorMessage: '',
|
||||
hasError: false,
|
||||
teamsTheme: this._teamsTheme,
|
||||
onOver: false,
|
||||
showLithbox: false,
|
||||
lithboxMedia: [],
|
||||
photoIndex: 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @protected
|
||||
* @returns {Promise<any>}
|
||||
* @memberof ImageGallery
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @private
|
||||
* @memberof ImageGallery
|
||||
*/
|
||||
private async loadPictures() {
|
||||
this.setState({ isLoading: true, hasError: false });
|
||||
const tenantUrl = `https://${location.host}`;
|
||||
try {
|
||||
this.images = await this.spService.getImages(this.props.siteUrl, this.props.list, this.props.numberImages);
|
||||
// const el = <Label style={{fontSize: FontSizes.size18, bottom:0,transition:'.5s ease', textAlign: 'center', width:'100%', position:'absolute', background: 'rgba(0, 0, 0, 0.5)', color: '#f1f1f1', padding: '10px'}}>Teste</Label>;
|
||||
const el = <Label className={styles.overlay}>Teste</Label>;
|
||||
for (const image of this.images) {
|
||||
|
||||
if (image.FileSystemObjectType == 1) continue; // by pass folder item
|
||||
const pURL = `${tenantUrl}/_api/v2.0/sharePoint:${image.File.ServerRelativeUrl}:/driveItem/thumbnails/0/large/content?preferNoRedirect=true `;
|
||||
const thumbnailUrl = `${tenantUrl}/_api/v2.0/sharePoint:${image.File.ServerRelativeUrl}:/driveItem/thumbnails/0/c240x240/content?preferNoRedirect=true `;
|
||||
this.lithboxMedia.push(pURL);
|
||||
this.galleryImages.push(
|
||||
{
|
||||
src: pURL,
|
||||
thumbnail: thumbnailUrl,
|
||||
thumbnailWidth: 240,
|
||||
thumbnailHeight: 180,
|
||||
caption: image.Title ? image.Title : image.File.Name,
|
||||
// thumbnailCaption: image.File.Name,
|
||||
customOverlay:
|
||||
<Label style={{ fontSize: FontSizes.size18, bottom: 0, transition: '.5s ease', textAlign: 'center', width: '100%', position: 'absolute', background: 'rgba(0, 0, 0, 0.5)', color: '#f1f1f1', padding: '10px' }}>
|
||||
{image.Title ? image.Title : image.File.Name}
|
||||
</Label>
|
||||
|
||||
},
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
this.setState({ hasError: true, errorMessage: decodeURIComponent(error.message) });
|
||||
}
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
|
||||
await this.loadPictures();
|
||||
this.setState({ images: this.galleryImages, lithboxMedia: this.lithboxMedia, isLoading: false });
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {ImageGalleryProps} prevProps
|
||||
* @returns
|
||||
* @memberof ImageGallery
|
||||
*/
|
||||
public async componentDidUpdate(prevProps: ImageGalleryProps) {
|
||||
|
||||
if (!this.props.list || !this.props.siteUrl) return;
|
||||
// Get Properties change
|
||||
if (prevProps.list !== this.props.list || prevProps.numberImages !== this.props.numberImages) {
|
||||
this.galleryImages = [];
|
||||
await this.loadPictures();
|
||||
this.setState({ images: this.galleryImages, isLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @private
|
||||
* @memberof ImageGallery
|
||||
*/
|
||||
private onConfigure() {
|
||||
// Context of the web part
|
||||
this.props.context.propertyPane.open();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @returns {React.ReactElement<ImageGalleryProps>}
|
||||
* @memberof ImageGallery
|
||||
*/
|
||||
public render(): React.ReactElement<ImageGalleryProps> {
|
||||
console.log('th', (this._teamsContext && this._teamsContext.theme == 'dark') ? styles.webpartTitle : '');
|
||||
|
||||
return (
|
||||
<div className={styles.imageGallery}>
|
||||
<div >
|
||||
{
|
||||
(this._teamsContext && this._teamsContext.theme == 'dark') ?
|
||||
<Label className={styles.title}>{this.props.title}</Label>
|
||||
:
|
||||
<WebPartTitle displayMode={this.props.displayMode}
|
||||
title={this.props.title}
|
||||
updateProperty={this.props.updateProperty}
|
||||
className={(this._teamsContext && this._teamsContext.theme == 'dark') && styles.webpartTitle}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
{
|
||||
(!this.props.list) ?
|
||||
<Placeholder iconName='Edit'
|
||||
iconText={strings.WebpartConfigIconText}
|
||||
description={strings.WebpartConfigDescription}
|
||||
buttonLabel={strings.WebPartConfigButtonLabel}
|
||||
hideButton={this.props.displayMode === DisplayMode.Read}
|
||||
onConfigure={this.onConfigure.bind(this)} />
|
||||
:
|
||||
this.state.hasError ?
|
||||
<MessageBar messageBarType={MessageBarType.error}>
|
||||
{this.state.errorMessage}
|
||||
</MessageBar>
|
||||
:
|
||||
this.state.isLoading ?
|
||||
<Spinner size={SpinnerSize.large} label='loading images...' />
|
||||
:
|
||||
this.state.images.length == 0 ?
|
||||
|
||||
<div style={{ width: '300px', margin: 'auto' }}>
|
||||
<Icon iconName="PhotoCollection"
|
||||
style={{ fontSize: '250px', color: '#d9d9d9' }} />
|
||||
<Label style={{ width: '250px', margin: 'auto', fontSize: FontSizes.size20 }}>No images in the library</Label>
|
||||
</div>
|
||||
:
|
||||
<div style={{ width: '100%', height: '100%', overflow: 'hidden' }}>
|
||||
<Gallery images={this.state.images} enableImageSelection={false} rowHeight={180} />
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
.carousel .slide {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.slideLoading {
|
||||
display:none !important;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||
import { DisplayMode } from '@microsoft/sp-core-library';
|
||||
import { IGalleryImages } from '../ImageGallery/IGalleryImages';
|
||||
export interface IImageProps {
|
||||
image: IGalleryImages;
|
||||
context: WebPartContext;
|
||||
displayCaption: boolean;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
export interface IImageState {
|
||||
onHover:boolean;
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background: rgb(0, 0, 0);
|
||||
background: rgba(0, 0, 0, 0.5); /* Black see-through */
|
||||
color: #f1f1f1;
|
||||
width: 100%;
|
||||
transition: .5s ease;
|
||||
opacity:0;
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.search {
|
||||
.container {
|
||||
max-width: 700px;
|
||||
margin: 0px auto;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
.webpartTitle{
|
||||
color: white !important;
|
||||
}
|
||||
.row {
|
||||
@include ms-Grid-row;
|
||||
@include ms-fontColor-white;
|
||||
background-color: $ms-color-themeDark;
|
||||
padding: 20px;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.column {
|
||||
@include ms-Grid-col;
|
||||
@include ms-lg10;
|
||||
@include ms-xl8;
|
||||
@include ms-xlPush2;
|
||||
@include ms-lgPush1;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include ms-font-xl;
|
||||
@include ms-fontColor-white;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.button {
|
||||
// Our button
|
||||
text-decoration: none;
|
||||
height: 32px;
|
||||
|
||||
// Primary Button
|
||||
min-width: 80px;
|
||||
background-color: $ms-color-themePrimary;
|
||||
border-color: $ms-color-themePrimary;
|
||||
color: $ms-color-white;
|
||||
|
||||
// Basic Button
|
||||
outline: transparent;
|
||||
position: relative;
|
||||
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: $ms-font-size-m;
|
||||
font-weight: $ms-font-weight-regular;
|
||||
border-width: 0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
padding: 0 16px;
|
||||
|
||||
.label {
|
||||
font-weight: $ms-font-weight-semibold;
|
||||
font-size: $ms-font-size-m;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 0 4px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import * as React from 'react';
|
||||
import { IImageProps } from './IRenderImageProps';
|
||||
import { IImageState } from './IRenderImageState';
|
||||
|
||||
import {
|
||||
Spinner,
|
||||
SpinnerSize,
|
||||
MessageBar,
|
||||
MessageBarType,
|
||||
Label,
|
||||
Icon,
|
||||
DocumentCard,
|
||||
DocumentCardActivity,
|
||||
DocumentCardPreview,
|
||||
DocumentCardTitle,
|
||||
IDocumentCardPreviewProps,
|
||||
ImageFit,
|
||||
Image
|
||||
|
||||
} from 'office-ui-fabric-react';
|
||||
import { FontSizes } from '@uifabric/fluent-theme/lib/fluent/FluentType';
|
||||
import 'video-react/dist/video-react.css'; // import css
|
||||
import { Player, BigPlayButton } from 'video-react';
|
||||
|
||||
export default class RenderImage extends React.Component<IImageProps, IImageState> {
|
||||
|
||||
constructor(props: IImageProps) {
|
||||
super(props);
|
||||
this.state = { onHover: false };
|
||||
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IImageProps> {
|
||||
return (
|
||||
<div
|
||||
|
||||
>
|
||||
<div
|
||||
onMouseOver={(ev) => {
|
||||
ev.preventDefault();
|
||||
this.setState({ onHover: !this.state.onHover });
|
||||
}}
|
||||
onMouseOut={(ev) => {
|
||||
ev.preventDefault();
|
||||
this.setState({ onHover: !this.state.onHover });
|
||||
}}
|
||||
>
|
||||
<Image imageFit={ImageFit.centerCover} src={this.props.image.thumbnail}
|
||||
width={230}
|
||||
height={180}
|
||||
/>
|
||||
{
|
||||
this.props.image.mediaType == 'video' && (
|
||||
<Icon iconName='MSNVideosSolid'
|
||||
style={{ pointerEvents: "none", display: 'block' , fontSize: FontSizes.size42, textAlign: 'center', width: '50px', height: '50px', position: 'absolute', top: '40%', left: '40%' }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
</div>
|
||||
{
|
||||
this.state.onHover &&
|
||||
<div>
|
||||
<Label
|
||||
style={{ pointerEvents: "none", display: 'block', zIndex: 1000, fontSize: FontSizes.size18, bottom: 0, textAlign: 'center', width: '100%', position: 'absolute', background: 'rgba(0, 0, 0, 0.5)', color: '#f1f1f1', padding: '10px' }}
|
||||
>
|
||||
{this.props.image.caption}
|
||||
</Label>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Please select site and library to show in Image Gallery",
|
||||
"BasicGroupName": "Properties",
|
||||
"ListFieldLabel": "Library",
|
||||
"SiteUrlFieldLabel": 'Site Url',
|
||||
"WebPartConfigButtonLabel": "Configure",
|
||||
"WebpartConfigDescription": "Please configure Image Library ",
|
||||
"WebpartConfigIconText": "Configure your Image Gallery Web Part",
|
||||
"TitleLabel": 'Web Part Title'
|
||||
}
|
||||
});
|
15
samples/react-multimedia-gallery/src/webparts/imageGallery/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
declare interface IImageGalleryWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
SiteUrlFieldLabel: string;
|
||||
ListFieldLabel: string;
|
||||
WebPartConfigButtonLabel: string;
|
||||
WebpartConfigDescription: string;
|
||||
WebpartConfigIconText: string;
|
||||
TitleLabel:string;
|
||||
}
|
||||
|
||||
declare module 'ImageGalleryWebPartStrings' {
|
||||
const strings: IImageGalleryWebPartStrings;
|
||||
export = strings;
|
||||
}
|
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* This script updates the package-solution version analogue to the
|
||||
* the package.json file.
|
||||
*/
|
||||
|
||||
if (process.env.npm_package_version === undefined) {
|
||||
|
||||
throw 'Package version cannot be evaluated';
|
||||
|
||||
}
|
||||
|
||||
// define path to package-solution file
|
||||
const solution = './config/package-solution.json',
|
||||
teams = './teams/manifest.json';
|
||||
|
||||
// require filesystem instanc
|
||||
const fs = require('fs');
|
||||
|
||||
// get next automated package version from process variable
|
||||
const nextPkgVersion = process.env.npm_package_version;
|
||||
|
||||
// make sure next build version match
|
||||
const nextVersion = nextPkgVersion.indexOf('-') === -1 ?
|
||||
nextPkgVersion : nextPkgVersion.split('-')[0];
|
||||
|
||||
// Update version in SPFx package-solution if exists
|
||||
if (fs.existsSync(solution)) {
|
||||
|
||||
// read package-solution file
|
||||
const solutionFileContent = fs.readFileSync(solution, 'UTF-8');
|
||||
// parse file as json
|
||||
const solutionContents = JSON.parse(solutionFileContent);
|
||||
|
||||
// set property of version to next version
|
||||
solutionContents.solution.version = nextVersion + '.0';
|
||||
|
||||
// save file
|
||||
fs.writeFileSync(
|
||||
solution,
|
||||
// convert file back to proper json
|
||||
JSON.stringify(solutionContents, null, 2),
|
||||
'UTF-8');
|
||||
|
||||
}
|
||||
|
||||
// Update version in teams manifest if exists
|
||||
if (fs.existsSync(teams)) {
|
||||
|
||||
// read package-solution file
|
||||
const teamsManifestContent = fs.readFileSync(teams, 'UTF-8');
|
||||
// parse file as json
|
||||
const teamsContent = JSON.parse(teamsManifestContent);
|
||||
|
||||
// set property of version to next version
|
||||
teamsContent.version = nextVersion;
|
||||
|
||||
// save file
|
||||
fs.writeFileSync(
|
||||
teams,
|
||||
// convert file back to proper json
|
||||
JSON.stringify(teamsContent, null, 2),
|
||||
'UTF-8');
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "lib",
|
||||
"inlineSources": false,
|
||||
"strictNullChecks": false,
|
||||
"noUnusedLocals": false,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"rules": {
|
||||
"class-name": false,
|
||||
"export-name": false,
|
||||
"forin": false,
|
||||
"label-position": false,
|
||||
"member-access": true,
|
||||
"no-arg": false,
|
||||
"no-console": false,
|
||||
"no-construct": false,
|
||||
"no-duplicate-variable": true,
|
||||
"no-eval": false,
|
||||
"no-function-expression": true,
|
||||
"no-internal-module": true,
|
||||
"no-shadowed-variable": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-unnecessary-semicolons": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-with-statement": true,
|
||||
"semicolon": true,
|
||||
"trailing-comma": false,
|
||||
"typedef": false,
|
||||
"typedef-whitespace": false,
|
||||
"use-named-parameter": true,
|
||||
"variable-name": false,
|
||||
"whitespace": false
|
||||
}
|
||||
}
|