Update SPFx

This commit is contained in:
AriGunawan 2022-05-23 00:17:10 +07:00
parent 39ea25afc2
commit c2a2ec7ffd
24 changed files with 40269 additions and 9992 deletions

View File

@ -9,6 +9,7 @@ node_modules
# Build generated files # Build generated files
dist dist
lib lib
release
solution solution
temp temp
*.sppkg *.sppkg

View File

@ -0,0 +1,16 @@
!dist
config
gulpfile.js
release
src
temp
tsconfig.json
tslint.json
*.log
.yo-rc.json
.vscode

View File

@ -1,11 +1,16 @@
{ {
"@microsoft/generator-sharepoint": { "@microsoft/generator-sharepoint": {
"plusBeta": false,
"isCreatingSolution": true, "isCreatingSolution": true,
"environment": "spo", "version": "1.14.0",
"version": "1.6.0",
"libraryName": "image-gallery", "libraryName": "image-gallery",
"libraryId": "876cbbe8-6974-4676-9da3-5c190ac8164f", "libraryId": "e55194cd-8581-401c-b4fc-06169480813f",
"environment": "spo",
"packageManager": "npm", "packageManager": "npm",
"solutionName": "image-gallery",
"solutionShortDescription": "image-gallery description",
"skipFeatureDeployment": true,
"isDomainIsolated": false,
"componentType": "webpart" "componentType": "webpart"
} }
} }

View File

@ -1,35 +1,37 @@
# Filterable Image Gallery # Filterable Image Gallery
## Summary ## Summary
This sample describes an SPFx application which implements an image gallery with taxonomy base filtering and typed search. This application also implements pagination. This sample describes an SPFx application which implements an image gallery with taxonomy base filtering and typed search. This application also implements pagination.
![Filterable Image Gallery web part built on the SharePoint Framework using React](./assets/image-gallery.gif) ![Filterable Image Gallery web part built on the SharePoint Framework using React](./assets/image-gallery.gif)
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/version-1.6.0-green.svg) ## Compatibility
![SPFx 1.14](https://img.shields.io/badge/SPFx-1.14-green.svg)
![Node.js v14 | v12](https://img.shields.io/badge/Node.js-v14%20%7C%20v12-green.svg)
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
## Applies to ## Applies to
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview) * [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment) * [Microsoft 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
## Solution ## Solution
Solution|Author(s) Solution|Author(s)
--------|--------- --------|---------
react-image-gallery | Ejaz Hussain react-image-gallery | [Ejaz Hussain](https://github.com/ejazhussain) ([@EjazHussain_](https://twitter.com/EjazHussain_))
react-image-gallery | [Ari Gunawan](https://github.com/AriGunawan) ([@arigunawan3023](https://twitter.com/arigunawan3023))
## Version history ## Version history
Version|Date|Comments Version|Date|Comments
-------|----|-------- -------|----|--------
1.0|March 01, 2019|Initial release 1.0|March 01, 2019|Initial release
1.1|May 23, 2022|Update SPFx version, update README, and fix minor issues
## 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 ## Minimal Path to Awesome
@ -39,12 +41,13 @@ Version|Date|Comments
- `gulp serve` - `gulp serve`
- Create a Department Term set with associated child terms, for example, HR, Information Services, Sales, Marketing - Create a Departments Term set with associated child terms, for example, HR, Information Services, Sales, Marketing
- Create an Image Library and add some sample images - Create an Image Library and add some sample images
- Tag each image with Department Metadata Column - Tag each image with Department Metadata Column
- Also fill in Title field for each image, this is require for typed search functionality - Also fill in Title field for each image, this is require for typed search functionality
## Features ## Features
Here are the main features for this application Here are the main features for this application
- Taxonomy-based filtering - Taxonomy-based filtering
@ -52,4 +55,30 @@ Here are the main features for this application
- Right-side popup panel - Right-side popup panel
- Server-side pagination using REST API - Server-side pagination using REST API
## Help
We do not support samples, but this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues.
If you're having issues building the solution, please run [spfx doctor](https://pnp.github.io/cli-microsoft365/cmd/spfx/spfx-doctor/) from within the solution folder to diagnose incompatibility issues with your environment.
You can try looking at [issues related to this sample](https://github.com/pnp/sp-dev-fx-webparts/issues?q=label%3A%22sample%3A%20react-image-gallery%22) to see if anybody else is having the same issues.
You can also try looking at [discussions related to this sample](https://github.com/pnp/sp-dev-fx-webparts/discussions?discussions_q=react-image-gallery) and see what the community is saying.
If you encounter any issues using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected%2Csample%3A%20react-image-gallery&template=bug-report.yml&sample=react-image-gallery&authors=@arigunawan%20@ejazhussain&title=react-image-gallery%20-%20).
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Aquestion%2Csample%3A%20react-image-gallery&template=question.yml&sample=react-image-gallery&authors=@arigunawan%20@ejazhussain&title=react-image-gallery%20-%20).
Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Aenhancement%2Csample%3A%20react-image-gallery&template=suggestion.yml&sample=react-image-gallery&authors=@arigunawan%20@ejazhussain&title=react-image-gallery%20-%20).
## 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.**
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-image-gallery" /> <img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-image-gallery" />

View File

@ -1,6 +1,6 @@
{ {
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json", "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/", "workingDir": "./release/assets/",
"account": "<!-- STORAGE ACCOUNT NAME -->", "account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "image-gallery", "container": "image-gallery",
"accessKey": "<!-- ACCESS KEY -->" "accessKey": "<!-- ACCESS KEY -->"

View File

@ -3,9 +3,36 @@
"solution": { "solution": {
"name": "image-gallery-client-side-solution", "name": "image-gallery-client-side-solution",
"id": "876cbbe8-6974-4676-9da3-5c190ac8164f", "id": "876cbbe8-6974-4676-9da3-5c190ac8164f",
"version": "1.0.0.0", "version": "1.1.0.0",
"includeClientSideAssets": true, "includeClientSideAssets": true,
"skipFeatureDeployment": true "skipFeatureDeployment": true,
"isDomainIsolated": false,
"developer": {
"name": "",
"websiteUrl": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"mpnId": "Undefined-1.14.0"
},
"metadata": {
"shortDescription": {
"default": "image-gallery description"
},
"longDescription": {
"default": "image-gallery description"
},
"screenshotPaths": [],
"videoUrl": "",
"categories": []
},
"features": [
{
"title": "image-gallery Feature",
"description": "The feature that activates elements of the image-gallery solution.",
"id": "afeca86e-a632-48ce-a9eb-b5a4a3e94d85",
"version": "1.0.0.0"
}
]
}, },
"paths": { "paths": {
"zippedPackage": "solution/image-gallery.sppkg" "zippedPackage": "solution/image-gallery.sppkg"

View File

@ -2,9 +2,5 @@
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json", "$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321, "port": 4321,
"https": true, "https": true,
"initialPage": "https://localhost:5432/workbench", "initialPage": "https://enter-your-SharePoint-site/_layouts/workbench.aspx"
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
} }

View File

@ -1,7 +1,16 @@
'use strict'; 'use strict';
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web'); const build = require('@microsoft/sp-build-web');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`); build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
build.initialize(gulp); var getTasks = build.rig.getTasks;
build.rig.getTasks = function () {
var result = getTasks.call(build.rig);
result.set('serve', result.get('serve-deprecated'));
return result;
};
build.initialize(require('gulp'));

File diff suppressed because it is too large Load Diff

23532
samples/react-image-gallery/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,41 +2,33 @@
"name": "react-image-gallery", "name": "react-image-gallery",
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"engines": { "main": "lib/index.js",
"node": ">=0.10.0"
},
"scripts": { "scripts": {
"build": "gulp bundle", "build": "gulp bundle",
"clean": "gulp clean", "clean": "gulp clean",
"test": "gulp test" "test": "gulp test"
}, },
"dependencies": { "dependencies": {
"@microsoft/sp-core-library": "1.6.0", "@microsoft/sp-core-library": "1.14.0",
"@microsoft/sp-lodash-subset": "1.6.0", "@microsoft/sp-lodash-subset": "1.14.0",
"@microsoft/sp-office-ui-fabric-core": "1.6.0", "@microsoft/sp-office-ui-fabric-core": "1.14.0",
"@microsoft/sp-webpart-base": "1.6.0", "@microsoft/sp-property-pane": "1.14.0",
"@pnp/common": "^1.2.7", "@microsoft/sp-webpart-base": "1.14.0",
"@pnp/graph": "^1.2.7", "@pnp/spfx-controls-react": "^3.7.2",
"@pnp/logging": "^1.2.7", "office-ui-fabric-react": "7.174.1",
"@pnp/odata": "^1.2.7", "react": "16.13.1",
"@pnp/sp": "^1.2.7", "react-dom": "16.13.1",
"@pnp/spfx-controls-react": "1.11.0", "react-js-pagination": "^3.0.3"
"@types/es6-promise": "0.0.33",
"@types/react": "15.6.6",
"@types/react-dom": "15.5.6",
"@types/webpack-env": "1.13.1",
"react": "15.6.2",
"react-dom": "15.6.2",
"react-js-pagination": "^3.0.2"
}, },
"devDependencies": { "devDependencies": {
"@microsoft/sp-build-web": "1.6.0", "@types/react": "16.9.51",
"@microsoft/sp-module-interfaces": "1.6.0", "@types/react-dom": "16.9.8",
"@microsoft/sp-webpart-workbench": "1.6.0", "@microsoft/sp-build-web": "1.14.0",
"tslint-microsoft-contrib": "~5.0.0", "@microsoft/sp-tslint-rules": "1.14.0",
"gulp": "~3.9.1", "@microsoft/sp-module-interfaces": "1.14.0",
"@types/chai": "3.4.34", "@microsoft/rush-stack-compiler-3.9": "0.4.47",
"@types/mocha": "2.2.38", "gulp": "~4.0.2",
"ajv": "~5.2.2" "ajv": "~5.2.2",
"@types/webpack-env": "1.13.1"
} }
} }

View File

@ -1,147 +1,94 @@
import { SPHttpClient, SPHttpClientResponse, ISPHttpClientOptions } from '@microsoft/sp-http'; import { SPHttpClient } from "@microsoft/sp-http";
import { reject } from 'lodash'; import { IListService } from "../Interfaces";
import { IListService, IImage } from '../Interfaces';
import { sp, spODataEntityArray, Item } from "@pnp/sp";
import Constants from '../Common/constants';
import { stringIsNullOrEmpty } from '@pnp/common';
export class ListService implements IListService { export class ListService implements IListService {
private spHttpClient: SPHttpClient;
private spHttpClient: SPHttpClient; constructor(spHttpClient?: SPHttpClient) {
this.spHttpClient = spHttpClient;
}
constructor(spHttpClient?: SPHttpClient) { public async readItems(url: string): Promise<any> {
this.spHttpClient = spHttpClient; try {
const response = await this.spHttpClient.get(
url,
SPHttpClient.configurations.v1,
{
headers: {
Accept: "application/json;odata=nometadata",
"odata-version": "",
},
}
);
const items: any = await response.json();
let result = {};
if (items.value.length) {
result = {
items: items.value,
nextLink: items["odata.nextLink"],
};
} else {
result = null;
}
return result;
} catch (error) {
return error;
} }
public async readItems(url: string): Promise<any> { // return new Promise<any>(async (resolve) => {
try {
const response = await this.spHttpClient.get(url, SPHttpClient.configurations.v1, // this.spHttpClient.get(url, SPHttpClient.configurations.v1,
{ // {
headers: { // headers: {
'Accept': 'application/json;odata=nometadata', // 'Accept': 'application/json;odata=nometadata',
'odata-version': '' // 'odata-version': ''
} // }
}); // }).then((response: SPHttpClientResponse): Promise<{ value: number }> => {
// return response.json();
// }).then((response: { value: number }): void => {
const items: any = await response.json(); // resolve(response.value);
let result = {}; // });
if (items.value.length) {
result = { // });
items: items.value, }
nextLink: items["odata.nextLink"]
}
}
else {
result = null;
}
return result;
public async getListItemsCount(url: string): Promise<any> {
try {
const response = await this.spHttpClient.get(
url,
SPHttpClient.configurations.v1,
{
headers: {
Accept: "application/json;odata=nometadata",
"odata-version": "",
},
} }
catch (error) { );
return error;
}
const result: any = await response.json();
return result.value;
} catch (error) {
return error;
// return new Promise<any>(async (resolve) => {
// this.spHttpClient.get(url, SPHttpClient.configurations.v1,
// {
// headers: {
// 'Accept': 'application/json;odata=nometadata',
// 'odata-version': ''
// }
// }).then((response: SPHttpClientResponse): Promise<{ value: number }> => {
// return response.json();
// }).then((response: { value: number }): void => {
// resolve(response.value);
// });
// });
} }
public async getListItemsCount(url: string): Promise<any> { // return new Promise<any>(async (resolve) => {
try {
const response = await this.spHttpClient.get(url, SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
}
});
const result: any = await response.json();
return result.value;
}
catch (error) {
return error;
}
// return new Promise<any>(async (resolve) => {
// this.spHttpClient.get(url, SPHttpClient.configurations.v1,
// {
// headers: {
// 'Accept': 'application/json;odata=nometadata',
// 'odata-version': ''
// }
// }).then((response: SPHttpClientResponse): Promise<{ value: number }> => {
// return response.json();
// }).then((response: { value: number }): void => {
// resolve(response.value);
// });
// });
}
// this.spHttpClient.get(url, SPHttpClient.configurations.v1,
// {
// headers: {
// 'Accept': 'application/json;odata=nometadata',
// 'odata-version': ''
// }
// }).then((response: SPHttpClientResponse): Promise<{ value: number }> => {
// return response.json();
// }).then((response: { value: number }): void => {
// resolve(response.value);
// });
// });
}
} }

View File

@ -12,15 +12,18 @@
// Components that allow authors to embed arbitrary script code should set this to true. // 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 // https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false, "requiresCustomScript": false,
"supportedHosts": ["SharePointWebPart", "TeamsPersonalApp", "TeamsTab", "SharePointFullPage"],
"supportsThemeVariants": true,
"preconfiguredEntries": [{ "preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other "groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "Other" }, "group": { "default": "Other" },
"title": { "default": "imageGallery" }, "title": { "default": "React Image Gallery" },
"description": { "default": "imageGallery description" }, "description": { "default": "React component that shows image gallery from a list" },
"officeFabricIconFontName": "Page", "officeFabricIconFontName": "PhotoCollection",
"properties": { "properties": {
"description": "imageGallery" "description": "imageGallery",
"pageSize": 5
} }
}] }]
} }

View File

@ -2,17 +2,16 @@ import * as React from 'react';
import * as ReactDom from 'react-dom'; import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library'; import { Version } from '@microsoft/sp-core-library';
import { import {
BaseClientSideWebPart,
IPropertyPaneConfiguration, IPropertyPaneConfiguration,
PropertyPaneTextField, PropertyPaneTextField,
PropertyPaneSlider PropertyPaneSlider
} from '@microsoft/sp-webpart-base'; } from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'ImageGalleryWebPartStrings'; import * as strings from 'ImageGalleryWebPartStrings';
import ImageGallery from './components/ImageGallery'; import ImageGallery from './components/ImageGallery';
import { IImageGalleryProps } from './components/IImageGalleryProps'; import { IImageGalleryProps } from './components/IImageGalleryProps';
import ConfigureWebPart from './components/ConfigureWebPart/ConfigureWebPart'; import ConfigureWebPart from './components/ConfigureWebPart/ConfigureWebPart';
import { sp } from '@pnp/sp';
import { ListService } from '../../Services/ListService'; import { ListService } from '../../Services/ListService';
export interface IImageGalleryWebPartProps { export interface IImageGalleryWebPartProps {
@ -22,17 +21,12 @@ export interface IImageGalleryWebPartProps {
} }
export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGalleryWebPartProps> { export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGalleryWebPartProps> {
private listService: ListService private listService: ListService;
protected async onInit(): Promise<void> { protected async onInit(): Promise<void> {
const _ = await super.onInit(); const _ = await super.onInit();
this.listService = new ListService(this.context.spHttpClient); this.listService = new ListService(this.context.spHttpClient);
sp.setup({
spfxContext: this.context
});
} }
public render(): void { public render(): void {
@ -43,17 +37,15 @@ export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGal
// } // }
// ); // );
let element: any; let element: any;
if (this.properties.imageLibrary && this.properties.pageSize) { if (this.properties.imageLibrary && this.properties.pageSize) {
element = React.createElement<IImageGalleryProps>( element = React.createElement<IImageGalleryProps>(
ImageGallery, ImageGallery,
{ {
listName: this.properties.imageLibrary, listName: this.properties.imageLibrary,
context: this.context, context: this.context,
siteUrl: this.context.pageContext.site.absoluteUrl, siteUrl: this.context.pageContext.web.absoluteUrl,
pageSize: this.properties.pageSize pageSize: this.properties.pageSize
} }
@ -72,7 +64,6 @@ export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGal
); );
} }
ReactDom.render(element, this.domElement); ReactDom.render(element, this.domElement);
} }
@ -83,6 +74,7 @@ export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGal
protected get dataVersion(): Version { protected get dataVersion(): Version {
return Version.parse('1.0'); return Version.parse('1.0');
} }
protected get disableReactivePropertyChanges(): boolean { protected get disableReactivePropertyChanges(): boolean {
return true; return true;
} }
@ -91,7 +83,6 @@ export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGal
return { return {
pages: [ pages: [
{ {
groups: [ groups: [
{ {
groupName: strings.BasicGroupName, groupName: strings.BasicGroupName,

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,21 +1,17 @@
import * as React from 'react'; import * as React from 'react';
import { IWebPartContext } from '@microsoft/sp-webpart-base'; import { WebPartContext } from '@microsoft/sp-webpart-base';
import { MessageBar, MessageBarType } from 'office-ui-fabric-react/lib/MessageBar'; import { MessageBar, MessageBarType } from 'office-ui-fabric-react/lib/MessageBar';
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button'; import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import styles from './ConfigureWebPart.module.scss'; import styles from './ConfigureWebPart.module.scss';
export interface IConfigureWebPartProps { export interface IConfigureWebPartProps {
webPartContext: IWebPartContext; webPartContext: WebPartContext;
title: string; title: string;
description?: string; description?: string;
buttonText?: string; buttonText?: string;
} }
const ConfigureWebPart: React.FunctionComponent<IConfigureWebPartProps> = (props) => {
const ConfigureWebPart: React.SFC<IConfigureWebPartProps> = (props) => {
const { const {
webPartContext, webPartContext,
title, title,

View File

@ -4,10 +4,10 @@ export interface IImageGalleryState {
showPanel: boolean; showPanel: boolean;
selectedImage?: IImage; selectedImage?: IImage;
showLoader: boolean; showLoader: boolean;
itemsNotFoundMessage?: string, itemsNotFoundMessage?: string;
sQuery?: string, sQuery?: string;
dQuery?: string dQuery?: string;
itemsNotFound?: boolean, itemsNotFound?: boolean;
itemCount?: number; itemCount?: number;
pageSize?: number; pageSize?: number;
currentPage?: number; currentPage?: number;

View File

@ -179,4 +179,9 @@
background-color: #fff; background-color: #fff;
border: 1px solid rgba(0, 0, 0, .125); border: 1px solid rgba(0, 0, 0, .125);
} }
}
.filterContainer {
display: flex;
justify-content: space-between;
} }

View File

@ -1,26 +1,19 @@
import * as React from 'react'; import * as React from 'react';
import styles from './ImageGallery.module.scss'; import styles from './ImageGallery.module.scss';
import { IImageGalleryProps } from './IImageGalleryProps'; import { IImageGalleryProps } from './IImageGalleryProps';
import { escape } from '@microsoft/sp-lodash-subset'; import { css } from '@uifabric/utilities/lib';
import { css, classNamesFunction, IStyleFunction } from '@uifabric/utilities/lib';
import { TaxonomyPicker, IPickerTerms } from "@pnp/spfx-controls-react/lib/TaxonomyPicker"; import { TaxonomyPicker, IPickerTerms } from "@pnp/spfx-controls-react/lib/TaxonomyPicker";
import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { TextField } from 'office-ui-fabric-react/lib/TextField';
import { IListService, IImage } from '../../../Interfaces'; import { IListService, IImage } from '../../../Interfaces';
import { ListService } from '../../../Services/ListService'; import { ListService } from '../../../Services/ListService';
import { Panel, PanelType } from 'office-ui-fabric-react/lib/Panel'; import { Panel, PanelType } from 'office-ui-fabric-react/lib/Panel';
import { objectDefinedNotNull, stringIsNullOrEmpty } from '@pnp/common';
import { Label } from 'office-ui-fabric-react/lib/Label';
import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner'; import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner';
import { Icon } from 'office-ui-fabric-react/lib/Icon'; import { Icon } from 'office-ui-fabric-react/lib/Icon';
import { MessageBar, MessageBarType } from 'office-ui-fabric-react/lib/MessageBar'; import { MessageBar, MessageBarType } from 'office-ui-fabric-react/lib/MessageBar';
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http'; import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import { Button } from 'office-ui-fabric-react/lib/Button';
import { IImageGalleryState } from './IImageGalleryState'; import { IImageGalleryState } from './IImageGalleryState';
export default class ImageGallery extends React.Component<IImageGalleryProps, IImageGalleryState> { export default class ImageGallery extends React.Component<IImageGalleryProps, IImageGalleryState> {
private _spService: IListService; private _spService: IListService;
private selectQuery: string[] = []; private selectQuery: string[] = [];
private expandQuery: string[] = []; private expandQuery: string[] = [];
@ -41,10 +34,7 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
pageSize: this.props.pageSize, pageSize: this.props.pageSize,
currentPage: 1, currentPage: 1,
nextLink: "", nextLink: "",
};
}
this._onTaxPickerChange = this._onTaxPickerChange.bind(this); this._onTaxPickerChange = this._onTaxPickerChange.bind(this);
this._onClickNext = this._onClickNext.bind(this); this._onClickNext = this._onClickNext.bind(this);
@ -52,17 +42,12 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
this._onImageClick = this._onImageClick.bind(this); this._onImageClick = this._onImageClick.bind(this);
this._onSearchChange = this._onSearchChange.bind(this); this._onSearchChange = this._onSearchChange.bind(this);
this._spService = new ListService(this.props.context.spHttpClient); this._spService = new ListService(this.props.context.spHttpClient);
} }
public async componentDidMount() { public async componentDidMount() {
//Get Images from the library //Get Images from the library
let value = await this._spService.getListItemsCount(`${this.props.siteUrl}/_api/web/lists/GetByTitle('${this.props.listName}')/ItemCount`); let value = await this._spService.getListItemsCount(`${this.props.siteUrl}/_api/web/lists/GetByTitle('${this.props.listName}')/ItemCount`);
this.setState({ this.setState({
itemCount: value itemCount: value
@ -83,17 +68,16 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
}); });
let response = await this._spService.readItems(url); let response = await this._spService.readItems(url);
if (objectDefinedNotNull(response)) { if (response) {
if (response.nextLink) {
if (objectDefinedNotNull(response.nextLink)) {
this.urlCollection.push(response.nextLink); this.urlCollection.push(response.nextLink);
} }
this.setState({ this.setState({
showLoader: false, showLoader: false,
items: response.items, items: response.items,
status: `Showing items ${(this.state.currentPage - 1) * this.props.pageSize + 1} - ${((this.state.currentPage - 1) * this.props.pageSize) + response.items.length} of ${this.state.itemCount}` status: `Showing items ${(this.state.currentPage - 1) * this.props.pageSize + 1} - ${((this.state.currentPage - 1) * this.props.pageSize) + response.items.length} of ${this.state.itemCount}`
}); });
} }
else { else {
this.setState({ this.setState({
@ -102,12 +86,10 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
status: "", status: "",
itemsNotFound: true itemsNotFound: true
}); });
} }
} }
private async _onClickNext() { private async _onClickNext() {
this.setState({ this.setState({
currentPage: this.state.currentPage + 1, currentPage: this.state.currentPage + 1,
showLoader: true showLoader: true
@ -115,12 +97,13 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
let url = this.urlCollection[this.urlCollection.length - 1]; let url = this.urlCollection[this.urlCollection.length - 1];
this._readItems(url); this._readItems(url);
} }
private async _onClickPrevious() { private async _onClickPrevious() {
let url = ""; let url = "";
if (this.urlCollection.length > 1) { if (this.urlCollection.length > 1) {
this.setState({ this.setState({
currentPage: this.state.currentPage - 1 currentPage: this.state.currentPage - 1
}) });
this.urlCollection.pop(); this.urlCollection.pop();
url = this.urlCollection[this.urlCollection.length - 1]; url = this.urlCollection[this.urlCollection.length - 1];
@ -143,8 +126,6 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
}); });
} }
private buildQueryParams(taxQuery?: string, searchQuery?: string): string { private buildQueryParams(taxQuery?: string, searchQuery?: string): string {
this.selectQuery = []; this.selectQuery = [];
this.expandQuery = []; this.expandQuery = [];
@ -179,33 +160,30 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
return queryParam + selectColumns + filterColumns; return queryParam + selectColumns + filterColumns;
} }
private buildFilterQuery(taxQuery: string, searchQuery: string) { private buildFilterQuery(taxQuery: string, searchQuery: string) {
let result: string = ""; let result: string = "";
if (!stringIsNullOrEmpty(taxQuery) && stringIsNullOrEmpty(searchQuery)) { if (!taxQuery && searchQuery) {
result = `TaxCatchAll/Term eq '${taxQuery}'`; result = `TaxCatchAll/Term eq '${taxQuery}'`;
} }
if (stringIsNullOrEmpty(taxQuery) && !stringIsNullOrEmpty(searchQuery)) { if (taxQuery && !searchQuery) {
result = `startswith(Title,'${searchQuery}')`; result = `startswith(Title,'${searchQuery}')`;
} }
if (!stringIsNullOrEmpty(taxQuery) && !stringIsNullOrEmpty(searchQuery)) { if (!taxQuery && !searchQuery) {
result = `(TaxCatchAll/Term eq '${taxQuery}') and (startswith(Title,'${searchQuery}'))`; result = `(TaxCatchAll/Term eq '${taxQuery}') and (startswith(Title,'${searchQuery}'))`;
} }
if (stringIsNullOrEmpty(taxQuery) && stringIsNullOrEmpty(searchQuery)) {
if (taxQuery && searchQuery) {
result = ""; result = "";
} }
return result; return result;
} }
private async _onTaxPickerChange(terms: IPickerTerms) {
private async _onTaxPickerChange(terms: IPickerTerms) {
this.urlCollection = []; this.urlCollection = [];
let query = ""; let query = "";
@ -225,9 +203,9 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
let url = `${this.props.siteUrl}/_api/web/lists/GetByTitle('${this.props.listName}')/items/${queryParam}`; let url = `${this.props.siteUrl}/_api/web/lists/GetByTitle('${this.props.listName}')/items/${queryParam}`;
this.urlCollection.push(url); this.urlCollection.push(url);
this._readItems(url); this._readItems(url);
} }
private async _onSearchChange(query: any) {
private async _onSearchChange(event: any, query: any) {
this.urlCollection = []; this.urlCollection = [];
this.setState({ this.setState({
sQuery: query sQuery: query
@ -235,12 +213,10 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
let queryParam = this.buildQueryParamsTotalFilteredItems(this.state.dQuery, query); let queryParam = this.buildQueryParamsTotalFilteredItems(this.state.dQuery, query);
let response = await this._spService.readItems(`${this.props.siteUrl}/_api/web/lists/GetByTitle('${this.props.listName}')/items/${queryParam}`); let response = await this._spService.readItems(`${this.props.siteUrl}/_api/web/lists/GetByTitle('${this.props.listName}')/items/${queryParam}`);
if (objectDefinedNotNull(response)) { if (response) {
this.setState({ this.setState({
itemCount: response.items.length itemCount: response.items.length
}); });
} }
else { else {
this.setState({ this.setState({
@ -248,12 +224,12 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
}); });
} }
queryParam = this.buildQueryParams(this.state.dQuery, query); queryParam = this.buildQueryParams(this.state.dQuery, query);
let url = `${this.props.siteUrl}/_api/web/lists/GetByTitle('${this.props.listName}')/items/${queryParam}`; let url = `${this.props.siteUrl}/_api/web/lists/GetByTitle('${this.props.listName}')/items/${queryParam}`;
this.urlCollection.push(url); this.urlCollection.push(url);
this._readItems(url); this._readItems(url);
} }
private _onImageClick(selectedImage: any): void { private _onImageClick(selectedImage: any): void {
this.setState({ this.setState({
selectedImage, selectedImage,
@ -263,7 +239,6 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
} }
public render(): React.ReactElement<IImageGalleryProps> { public render(): React.ReactElement<IImageGalleryProps> {
const spinnerStyles = props => ({ const spinnerStyles = props => ({
circle: [ circle: [
{ {
@ -279,13 +254,11 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
] ]
}); });
let result = []; let result = [];
let tagList; let tagList;
if (this.state.items.length) { if (this.state.items.length) {
result = this.state.items.map((item, index) => { result = this.state.items.map((item, index) => {
return ( return (
<div key={index} className={css(styles.column, styles.mslg3)} onClick={() => this._onImageClick(item)}> <div key={index} className={css(styles.column, styles.mslg3)} onClick={() => this._onImageClick(item)}>
@ -295,27 +268,27 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
<figcaption>{item.Title}</figcaption> <figcaption>{item.Title}</figcaption>
</div> </div>
</div> </div>
) );
}); });
} }
if (objectDefinedNotNull(this.state.selectedImage.Department)) { if (this.state.selectedImage.Department) {
tagList = this.state.selectedImage.Department.map((tag: any, index) => { tagList = this.state.selectedImage.Department.map((tag: any, index) => {
return <li className={styles.listGroupItem} key={index}> <Icon iconName="Tag" className={styles.msIconTag} /> {tag.Label}</li>; return <li className={styles.listGroupItem} key={index}> <Icon iconName="Tag" className={styles.msIconTag} /> {tag.Label}</li>;
}); });
} }
return ( return (
<div className={styles.imageGallery}> <div className={styles.imageGallery}>
<div className={styles.container} dir="ltr"> <div className={styles.container} dir="ltr">
<div className={css(styles.row, styles.header)}> <div className={css(styles.row, styles.header)}>
<div className={css(styles.column, styles.mslg12, styles.pageTitle)}> <div className={css(styles.column, styles.mslg12, styles.pageTitle)}>
<h1>Image Gallery <small> Filterable</small></h1></div> <h1>Image Gallery <small> Filterable</small></h1></div>
</div> </div>
<div className={css(styles.row, styles.filters)}> <div className={css(styles.row, styles.filters)}>
<div className={css(styles.column, styles.mslg12, styles.panel)}> <div className={css(styles.column, styles.mslg12, styles.panel)}>
<div className={styles.panelBody}> <div className={styles.filterContainer}>
<div className={css(styles.column, styles.mslg3, styles.filter)}> <div>
<TaxonomyPicker <TaxonomyPicker
allowMultipleSelections={false} allowMultipleSelections={false}
termsetNameOrID="Departments" termsetNameOrID="Departments"
@ -325,10 +298,9 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
onChange={this._onTaxPickerChange} onChange={this._onTaxPickerChange}
isTermSetSelectable={false} isTermSetSelectable={false}
/> />
</div> </div>
<div className={css(styles.column, styles.mslg3, "ms-u-lgPush6", styles.searchBox)}> <div>
<TextField label="Search" className={styles.searchBoxInputField} placeholder="Enter search term" onChanged={this._onSearchChange} /> <TextField label="Search" className={styles.searchBoxInputField} placeholder="Enter search term" onChange={this._onSearchChange} />
</div> </div>
</div> </div>
</div> </div>
@ -336,15 +308,12 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
<div className={css(styles.row)}> <div className={css(styles.row)}>
<div className={css(styles.column, styles.mslg12, styles.panel)}> <div className={css(styles.column, styles.mslg12, styles.panel)}>
<div className={styles.panelBody}> <div className={styles.panelBody}>
{ {
this.state.showLoader this.state.showLoader
? <Spinner size={SpinnerSize.large} label="loading..." className={css(styles.loader)} getStyles={spinnerStyles} /> ? <Spinner size={SpinnerSize.large} label="loading..." className={css(styles.loader)} styles={spinnerStyles} />
: "" : ""
} }
<div className={css(styles.row, styles.mainContent)}> <div className={css(styles.row, styles.mainContent)}>
{result.length > 0 ? result : ""} {result.length > 0 ? result : ""}
{!result.length && this.state.itemsNotFound ? <MessageBar {!result.length && this.state.itemsNotFound ? <MessageBar
messageBarType={MessageBarType.warning} messageBarType={MessageBarType.warning}
@ -375,7 +344,6 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
</div> </div>
</div> </div>
</Panel> </Panel>
</div> </div>
</div> </div>
</div> </div>
@ -386,10 +354,10 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
<div className={styles.status}>{this.state.status}</div> <div className={styles.status}>{this.state.status}</div>
<ul className={styles.pager}> <ul className={styles.pager}>
<li> <li>
<Button disabled={((this.state.currentPage - 1) * this.props.pageSize + 1) <= 1} onClick={this._onClickPrevious}>Previous</Button> <PrimaryButton disabled={((this.state.currentPage - 1) * this.props.pageSize + 1) <= 1} onClick={this._onClickPrevious}>Previous</PrimaryButton>
</li> </li>
<li> <li>
<Button disabled={((this.state.currentPage - 1) * this.props.pageSize) + this.state.items.length >= this.state.itemCount} onClick={this._onClickNext}>Next</Button> <PrimaryButton disabled={((this.state.currentPage - 1) * this.props.pageSize) + this.state.items.length >= this.state.itemCount} onClick={this._onClickNext}>Next</PrimaryButton>
</li> </li>
</ul> </ul>
@ -399,5 +367,4 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
</div> </div>
); );
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

View File

@ -1,4 +1,5 @@
{ {
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.9/includes/tsconfig-web.json",
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
@ -10,25 +11,25 @@
"experimentalDecorators": true, "experimentalDecorators": true,
"skipLibCheck": true, "skipLibCheck": true,
"outDir": "lib", "outDir": "lib",
"inlineSources": false,
"strictNullChecks": false,
"noUnusedLocals": false,
"typeRoots": [ "typeRoots": [
"./node_modules/@types", "./node_modules/@types",
"./node_modules/@microsoft" "./node_modules/@microsoft"
], ],
"types": [ "types": [
"es6-promise",
"webpack-env" "webpack-env"
], ],
"lib": [ "lib": [
"es5", "es5",
"dom", "dom",
"es2015.collection" "es2015.collection",
"es2015.promise"
] ]
}, },
"include": [ "include": [
"src/**/*.ts" "src/**/*.ts",
], "src/**/*.tsx"
"exclude": [
"node_modules",
"lib"
] ]
} }

View File

@ -1,7 +1,5 @@
{ {
"rulesDirectory": [ "extends": "./node_modules/@microsoft/sp-tslint-rules/base-tslint.json",
"tslint-microsoft-contrib"
],
"rules": { "rules": {
"class-name": false, "class-name": false,
"export-name": false, "export-name": false,
@ -19,7 +17,6 @@
"no-switch-case-fall-through": true, "no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true, "no-unnecessary-semicolons": true,
"no-unused-expression": true, "no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true, "no-with-statement": true,
"semicolon": true, "semicolon": true,
"trailing-comma": false, "trailing-comma": false,