Merge pull request #2711 from AriGunawan/features/react_image_gallery_update_spfx

react-image-gallery: Update SPFx version
This commit is contained in:
Hugo Bernier 2022-06-02 10:23:05 -07:00 committed by GitHub
commit 7b60b58f10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 40334 additions and 10006 deletions

View File

@ -0,0 +1,39 @@
// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer
{
"name": "SPFx 1.14.0",
"image": "docker.io/m365pnp/spfx:1.14.0",
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4321,
35729
],
"portsAttributes": {
"4321": {
"protocol": "https",
"label": "Manifest",
"onAutoForward": "silent",
"requireLocalPort": true
},
// Not needed for SPFx>= 1.12.1
// "5432": {
// "protocol": "https",
// "label": "Workbench",
// "onAutoForward": "silent"
// },
"35729": {
"protocol": "https",
"label": "LiveReload",
"onAutoForward": "silent",
"requireLocalPort": true
}
},
"postCreateCommand": "bash .devcontainer/spfx-startup.sh",
"remoteUser": "node"
}

View File

@ -0,0 +1,33 @@
echo
echo -e "\e[1;94mInstalling Node dependencies\e[0m"
npm install
## commands to create dev certificate and copy it to the root folder of the project
echo
echo -e "\e[1;94mGenerating dev certificate\e[0m"
gulp trust-dev-cert
# Convert the generated PEM certificate to a CER certificate
openssl x509 -inform PEM -in ~/.rushstack/rushstack-serve.pem -outform DER -out ./spfx-dev-cert.cer
# Copy the PEM ecrtificate for non-Windows hosts
cp ~/.rushstack/rushstack-serve.pem ./spfx-dev-cert.pem
## add *.cer to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.cer' ./.gitignore
then
echo "# .CER Certificates" >> .gitignore
echo "*.cer" >> .gitignore
fi
## add *.pem to .gitignore to prevent certificates from being saved in repo
if ! grep -Fxq '*.pem' ./.gitignore
then
echo "# .PEM Certificates" >> .gitignore
echo "*.pem" >> .gitignore
fi
echo
echo -e "\e[1;92mReady!\e[0m"
echo -e "\n\e[1;94m**********\nOptional: if you plan on using gulp serve, don't forget to add the container certificate to your local machine. Please visit https://aka.ms/spfx-devcontainer for more information\n**********"

View File

@ -9,6 +9,7 @@ node_modules
# Build generated files
dist
lib
release
solution
temp
*.sppkg
@ -30,3 +31,6 @@ obj
# Styles Generated Code
*.scss.ts
*.cer
*.pem

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": {
"plusBeta": false,
"isCreatingSolution": true,
"environment": "spo",
"version": "1.6.0",
"version": "1.14.0",
"libraryName": "image-gallery",
"libraryId": "876cbbe8-6974-4676-9da3-5c190ac8164f",
"libraryId": "e55194cd-8581-401c-b4fc-06169480813f",
"environment": "spo",
"packageManager": "npm",
"solutionName": "image-gallery",
"solutionShortDescription": "image-gallery description",
"skipFeatureDeployment": true,
"isDomainIsolated": false,
"componentType": "webpart"
}
}
}

View File

@ -1,16 +1,15 @@
# Filterable Image Gallery
## 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)
## Compatibility
![SPFx 1.6](https://img.shields.io/badge/SPFx-1.6.0-green.svg)
![Node.js v8 | v6](https://img.shields.io/badge/Node.js-v8%20%7C%20v6-green.svg)
![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)
![Compatible with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Compatible-green.svg)
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
@ -20,40 +19,45 @@ This sample describes an SPFx application which implements an image gallery with
## Applies to
* [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|Author(s)
--------|---------
react-image-gallery | [Ejaz Hussain](https://github.com/ejazhussain)
react-image-gallery | [Ari Gunawan](https://github.com/AriGunawan) ([@arigunawan3023](https://twitter.com/arigunawan3023))
## Version history
Version|Date|Comments
-------|----|--------
1.0|March 01, 2019|Initial release
1.1|May 23, 2022|Update SPFx version, update README, and fix minor issues
## Minimal Path to Awesome
- Clone this repository
- in the command line run:
- `npm install`
- `gulp serve`
* Clone this repository (or [download this solution as a .ZIP file](https://pnp.github.io/download-partial/?url=https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-image-gallery) then unzip it)
* From your command line, change your current directory to the directory containing this sample (`react-image-gallery`, located under `samples`)
* in the command line run:
* `npm install`
* `gulp serve`
* 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
* Tag each image with Department Metadata Column
* Also fill in Title field for each image, this is require for typed search functionality
- Create a Department Term set with associated child terms, for example, HR, Information Services, Sales, Marketing
- Create an Image Library and add some sample images
- Tag each image with Department Metadata Column
- Also fill in Title field for each image, this is require for typed search functionality
> This sample can also be opened with [VS Code Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). Visit <https://aka.ms/spfx-devcontainer> for further instructions.
## Features
Here are the main features for this application
- Taxonomy-based filtering
- Typed Search
- Right-side popup panel
- Server-side pagination using REST API
* Taxonomy-based filtering
* Typed Search
* Right-side popup panel
* Server-side pagination using REST API
## Help
@ -65,12 +69,11 @@ You can try looking at [issues related to this sample](https://github.com/pnp/sp
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 while 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=@ejazhussain&title=react-image-gallery%20-%20).
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=@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=@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

View File

@ -9,7 +9,7 @@
"This sample describe a SPFX application which implement an image gallery with taxonomy base filtering and typed search. This application also implement pagination."
],
"creationDateTime": "2019-03-01",
"updateDateTime": "2019-03-01",
"updateDateTime": "2022-05-23",
"products": [
"SharePoint"
],
@ -20,7 +20,7 @@
},
{
"key": "SPFX-VERSION",
"value": "1.6.0"
"value": "1.14.0"
}
],
"thumbnails": [

View File

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

View File

@ -3,9 +3,36 @@
"solution": {
"name": "image-gallery-client-side-solution",
"id": "876cbbe8-6974-4676-9da3-5c190ac8164f",
"version": "1.0.0.0",
"version": "1.1.0.0",
"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": {
"zippedPackage": "solution/image-gallery.sppkg"

View File

@ -2,9 +2,5 @@
"$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/"
}
"initialPage": "https://enter-your-SharePoint-site/_layouts/workbench.aspx"
}

View File

@ -1,7 +1,16 @@
'use strict';
const gulp = require('gulp');
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.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",
"version": "0.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"main": "lib/index.js",
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.6.0",
"@microsoft/sp-lodash-subset": "1.6.0",
"@microsoft/sp-office-ui-fabric-core": "1.6.0",
"@microsoft/sp-webpart-base": "1.6.0",
"@pnp/common": "^1.2.7",
"@pnp/graph": "^1.2.7",
"@pnp/logging": "^1.2.7",
"@pnp/odata": "^1.2.7",
"@pnp/sp": "^1.2.7",
"@pnp/spfx-controls-react": "1.11.0",
"@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"
"@microsoft/sp-core-library": "1.14.0",
"@microsoft/sp-lodash-subset": "1.14.0",
"@microsoft/sp-office-ui-fabric-core": "1.14.0",
"@microsoft/sp-property-pane": "1.14.0",
"@microsoft/sp-webpart-base": "1.14.0",
"@pnp/spfx-controls-react": "^3.7.2",
"office-ui-fabric-react": "7.174.1",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-js-pagination": "^3.0.3"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.6.0",
"@microsoft/sp-module-interfaces": "1.6.0",
"@microsoft/sp-webpart-workbench": "1.6.0",
"tslint-microsoft-contrib": "~5.0.0",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"ajv": "~5.2.2"
"@types/react": "16.9.51",
"@types/react-dom": "16.9.8",
"@microsoft/sp-build-web": "1.14.0",
"@microsoft/sp-tslint-rules": "1.14.0",
"@microsoft/sp-module-interfaces": "1.14.0",
"@microsoft/rush-stack-compiler-3.9": "0.4.47",
"gulp": "~4.0.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 { reject } from 'lodash';
import { IListService, IImage } from '../Interfaces';
import { sp, spODataEntityArray, Item } from "@pnp/sp";
import Constants from '../Common/constants';
import { stringIsNullOrEmpty } from '@pnp/common';
import { SPHttpClient } from "@microsoft/sp-http";
import { IListService } from "../Interfaces";
export class ListService implements IListService {
private spHttpClient: SPHttpClient;
private spHttpClient: SPHttpClient;
constructor(spHttpClient?: SPHttpClient) {
this.spHttpClient = spHttpClient;
}
constructor(spHttpClient?: SPHttpClient) {
this.spHttpClient = spHttpClient;
public async readItems(url: string): Promise<any> {
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> {
try {
// return new Promise<any>(async (resolve) => {
const response = await this.spHttpClient.get(url, SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
}
});
// 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 => {
const items: any = await response.json();
let result = {};
if (items.value.length) {
// resolve(response.value);
// });
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 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);
// });
// });
return result.value;
} catch (error) {
return error;
}
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': ''
}
});
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);
// });
// });
}
// 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);
// });
// });
}
}

View File

@ -12,15 +12,18 @@
// 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", "TeamsPersonalApp", "TeamsTab", "SharePointFullPage"],
"supportsThemeVariants": true,
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "Other" },
"title": { "default": "imageGallery" },
"description": { "default": "imageGallery description" },
"officeFabricIconFontName": "Page",
"title": { "default": "React Image Gallery" },
"description": { "default": "React component that shows image gallery from a list" },
"officeFabricIconFontName": "PhotoCollection",
"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 { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField,
PropertyPaneSlider
} from '@microsoft/sp-webpart-base';
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'ImageGalleryWebPartStrings';
import ImageGallery from './components/ImageGallery';
import { IImageGalleryProps } from './components/IImageGalleryProps';
import ConfigureWebPart from './components/ConfigureWebPart/ConfigureWebPart';
import { sp } from '@pnp/sp';
import { ListService } from '../../Services/ListService';
export interface IImageGalleryWebPartProps {
@ -22,17 +21,12 @@ export interface IImageGalleryWebPartProps {
}
export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGalleryWebPartProps> {
private listService: ListService
private listService: ListService;
protected async onInit(): Promise<void> {
const _ = await super.onInit();
this.listService = new ListService(this.context.spHttpClient);
sp.setup({
spfxContext: this.context
});
}
public render(): void {
@ -43,17 +37,15 @@ export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGal
// }
// );
let element: any;
if (this.properties.imageLibrary && this.properties.pageSize) {
element = React.createElement<IImageGalleryProps>(
ImageGallery,
{
listName: this.properties.imageLibrary,
context: this.context,
siteUrl: this.context.pageContext.site.absoluteUrl,
siteUrl: this.context.pageContext.web.absoluteUrl,
pageSize: this.properties.pageSize
}
@ -72,7 +64,6 @@ export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGal
);
}
ReactDom.render(element, this.domElement);
}
@ -83,6 +74,7 @@ export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGal
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected get disableReactivePropertyChanges(): boolean {
return true;
}
@ -91,7 +83,6 @@ export default class ImageGalleryWebPart extends BaseClientSideWebPart<IImageGal
return {
pages: [
{
groups: [
{
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 { IWebPartContext } from '@microsoft/sp-webpart-base';
import { WebPartContext } from '@microsoft/sp-webpart-base';
import { MessageBar, MessageBarType } from 'office-ui-fabric-react/lib/MessageBar';
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import styles from './ConfigureWebPart.module.scss';
export interface IConfigureWebPartProps {
webPartContext: IWebPartContext;
webPartContext: WebPartContext;
title: string;
description?: string;
buttonText?: string;
}
const ConfigureWebPart: React.SFC<IConfigureWebPartProps> = (props) => {
const ConfigureWebPart: React.FunctionComponent<IConfigureWebPartProps> = (props) => {
const {
webPartContext,
title,

View File

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

View File

@ -179,4 +179,9 @@
background-color: #fff;
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 styles from './ImageGallery.module.scss';
import { IImageGalleryProps } from './IImageGalleryProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { css, classNamesFunction, IStyleFunction } from '@uifabric/utilities/lib';
import { css } from '@uifabric/utilities/lib';
import { TaxonomyPicker, IPickerTerms } from "@pnp/spfx-controls-react/lib/TaxonomyPicker";
import { TextField } from 'office-ui-fabric-react/lib/TextField';
import { IListService, IImage } from '../../../Interfaces';
import { ListService } from '../../../Services/ListService';
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 { Icon } from 'office-ui-fabric-react/lib/Icon';
import { MessageBar, MessageBarType } from 'office-ui-fabric-react/lib/MessageBar';
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
import { Button } from 'office-ui-fabric-react/lib/Button';
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import { IImageGalleryState } from './IImageGalleryState';
export default class ImageGallery extends React.Component<IImageGalleryProps, IImageGalleryState> {
private _spService: IListService;
private selectQuery: string[] = [];
private expandQuery: string[] = [];
@ -41,10 +34,7 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
pageSize: this.props.pageSize,
currentPage: 1,
nextLink: "",
}
};
this._onTaxPickerChange = this._onTaxPickerChange.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._onSearchChange = this._onSearchChange.bind(this);
this._spService = new ListService(this.props.context.spHttpClient);
}
public async componentDidMount() {
//Get Images from the library
let value = await this._spService.getListItemsCount(`${this.props.siteUrl}/_api/web/lists/GetByTitle('${this.props.listName}')/ItemCount`);
this.setState({
itemCount: value
@ -83,17 +68,16 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
});
let response = await this._spService.readItems(url);
if (objectDefinedNotNull(response)) {
if (objectDefinedNotNull(response.nextLink)) {
if (response) {
if (response.nextLink) {
this.urlCollection.push(response.nextLink);
}
this.setState({
showLoader: false,
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}`
});
}
else {
this.setState({
@ -102,12 +86,10 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
status: "",
itemsNotFound: true
});
}
}
private async _onClickNext() {
this.setState({
currentPage: this.state.currentPage + 1,
showLoader: true
@ -115,12 +97,13 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
let url = this.urlCollection[this.urlCollection.length - 1];
this._readItems(url);
}
private async _onClickPrevious() {
let url = "";
if (this.urlCollection.length > 1) {
this.setState({
currentPage: this.state.currentPage - 1
})
});
this.urlCollection.pop();
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 {
this.selectQuery = [];
this.expandQuery = [];
@ -179,33 +160,30 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
return queryParam + selectColumns + filterColumns;
}
private buildFilterQuery(taxQuery: string, searchQuery: string) {
let result: string = "";
if (!stringIsNullOrEmpty(taxQuery) && stringIsNullOrEmpty(searchQuery)) {
if (!taxQuery && searchQuery) {
result = `TaxCatchAll/Term eq '${taxQuery}'`;
}
if (stringIsNullOrEmpty(taxQuery) && !stringIsNullOrEmpty(searchQuery)) {
if (taxQuery && !searchQuery) {
result = `startswith(Title,'${searchQuery}')`;
}
if (!stringIsNullOrEmpty(taxQuery) && !stringIsNullOrEmpty(searchQuery)) {
if (!taxQuery && !searchQuery) {
result = `(TaxCatchAll/Term eq '${taxQuery}') and (startswith(Title,'${searchQuery}'))`;
}
if (stringIsNullOrEmpty(taxQuery) && stringIsNullOrEmpty(searchQuery)) {
if (taxQuery && searchQuery) {
result = "";
}
return result;
}
private async _onTaxPickerChange(terms: IPickerTerms) {
private async _onTaxPickerChange(terms: IPickerTerms) {
this.urlCollection = [];
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}`;
this.urlCollection.push(url);
this._readItems(url);
}
private async _onSearchChange(query: any) {
private async _onSearchChange(event: any, query: any) {
this.urlCollection = [];
this.setState({
sQuery: query
@ -235,12 +213,10 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
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}`);
if (objectDefinedNotNull(response)) {
if (response) {
this.setState({
itemCount: response.items.length
});
}
else {
this.setState({
@ -248,12 +224,12 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
});
}
queryParam = this.buildQueryParams(this.state.dQuery, query);
let url = `${this.props.siteUrl}/_api/web/lists/GetByTitle('${this.props.listName}')/items/${queryParam}`;
this.urlCollection.push(url);
this._readItems(url);
}
private _onImageClick(selectedImage: any): void {
this.setState({
selectedImage,
@ -263,7 +239,6 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
}
public render(): React.ReactElement<IImageGalleryProps> {
const spinnerStyles = props => ({
circle: [
{
@ -279,13 +254,11 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
]
});
let result = [];
let tagList;
if (this.state.items.length) {
result = this.state.items.map((item, index) => {
return (
<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>
</div>
</div>
)
);
});
}
if (objectDefinedNotNull(this.state.selectedImage.Department)) {
if (this.state.selectedImage.Department) {
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 (
<div className={styles.imageGallery}>
<div className={styles.container} dir="ltr">
<div className={css(styles.row, styles.header)}>
<div className={css(styles.column, styles.mslg12, styles.pageTitle)}>
<h1>Image Gallery <small> Filterable</small></h1></div>
</div>
<div className={css(styles.row, styles.filters)}>
<div className={css(styles.column, styles.mslg12, styles.panel)}>
<div className={styles.panelBody}>
<div className={css(styles.column, styles.mslg3, styles.filter)}>
<div className={styles.filterContainer}>
<div>
<TaxonomyPicker
allowMultipleSelections={false}
termsetNameOrID="Departments"
@ -325,10 +298,9 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
onChange={this._onTaxPickerChange}
isTermSetSelectable={false}
/>
</div>
<div className={css(styles.column, styles.mslg3, "ms-u-lgPush6", styles.searchBox)}>
<TextField label="Search" className={styles.searchBoxInputField} placeholder="Enter search term" onChanged={this._onSearchChange} />
<div>
<TextField label="Search" className={styles.searchBoxInputField} placeholder="Enter search term" onChange={this._onSearchChange} />
</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.column, styles.mslg12, styles.panel)}>
<div className={styles.panelBody}>
{
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)}>
{result.length > 0 ? result : ""}
{!result.length && this.state.itemsNotFound ? <MessageBar
messageBarType={MessageBarType.warning}
@ -375,7 +344,6 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
</div>
</div>
</Panel>
</div>
</div>
</div>
@ -386,10 +354,10 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
<div className={styles.status}>{this.state.status}</div>
<ul className={styles.pager}>
<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>
<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>
</ul>
@ -399,5 +367,4 @@ export default class ImageGallery extends React.Component<IImageGalleryProps, II
</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": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
@ -10,25 +11,25 @@
"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"
"es2015.collection",
"es2015.promise"
]
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"lib"
"src/**/*.ts",
"src/**/*.tsx"
]
}

View File

@ -1,7 +1,5 @@
{
"rulesDirectory": [
"tslint-microsoft-contrib"
],
"extends": "./node_modules/@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
@ -19,7 +17,6 @@
"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,