Fixed pagination with Filmstrip

This commit is contained in:
Hugo Bernier 2020-11-22 19:16:41 -05:00
parent c24bb65e3a
commit aab6481bb2
8 changed files with 35 additions and 157 deletions

View File

@ -3,7 +3,7 @@
## Summary ## Summary
- This react web part sample displays Soccer Highlights from a public Soccer API. - This react web part sample displays Soccer Highlights from a public Soccer API.
- It shows a maxium of 100 highlights at one time. - It shows a maximum of 100 highlights at one time.
- The web part show live status of game scores and ability to watch them live in small or full screen view. - The web part show live status of game scores and ability to watch them live in small or full screen view.
- You can view the highlights as FilmStrip Control (Thanks to Hugo for the tip and great blog) or Flat Mode. - You can view the highlights as FilmStrip Control (Thanks to Hugo for the tip and great blog) or Flat Mode.
- You can configure highlights per page and use Paging. - You can configure highlights per page and use Paging.
@ -16,13 +16,13 @@
### Usage ### Usage
**1) Deploy the package to SharePoint Online App Catalog. 1) Deploy the package to SharePoint Online App Catalog.
**2) Add the Web Part to Page, Configure the web Part, provide Title and Page Size.** 2) Add the Web Part to Page, Configure the web Part, provide Title and Page Size.
**3) Add the Web Part to Page, Configure the web Part, provide Title and Page Size.** 3) Add the Web Part to Page, Configure the web Part, provide Title and Page Size.
**4) Click on Pager to move Pages or arrows or dots in filmstrip view. ** 4) Click on Pager to move Pages or arrows or dots in filmstrip view.
## Used SharePoint Framework Version ## Used SharePoint Framework Version
@ -34,6 +34,7 @@
- [Office 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment) - [Office 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
## Prerequisites ## Prerequisites
None None
## Solution ## Solution

View File

@ -4971,22 +4971,6 @@
"integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==",
"dev": true "dev": true
}, },
"active-event-stack": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/active-event-stack/-/active-event-stack-1.0.0.tgz",
"integrity": "sha1-a1uS661xmvrpgs1R9Jw4xbaADFA=",
"requires": {
"immutable": "^3.7.4",
"lodash": "^3.9.3"
},
"dependencies": {
"lodash": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
"integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
}
}
},
"adal-angular": { "adal-angular": {
"version": "1.0.16", "version": "1.0.16",
"resolved": "https://registry.npmjs.org/adal-angular/-/adal-angular-1.0.16.tgz", "resolved": "https://registry.npmjs.org/adal-angular/-/adal-angular-1.0.16.tgz",
@ -11084,11 +11068,6 @@
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
"dev": true "dev": true
}, },
"immutable": {
"version": "3.8.2",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
"integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM="
},
"import-cwd": { "import-cwd": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
@ -17288,18 +17267,6 @@
} }
} }
}, },
"react-dialog": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/react-dialog/-/react-dialog-1.0.2.tgz",
"integrity": "sha1-gT/tQPSSlGeTRqMpjUYPe5iCGAE=",
"requires": {
"active-event-stack": "^1.0.0",
"classnames": "^2.2.3",
"react-draggable": "^2.2.6",
"react-onclickoutside": "^5.10.0",
"react-resizable": "^1.7.1"
}
},
"react-dom": { "react-dom": {
"version": "16.8.5", "version": "16.8.5",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.5.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.5.tgz",
@ -17328,14 +17295,6 @@
"resolved": "https://registry.npmjs.org/react-dom-factories/-/react-dom-factories-1.0.2.tgz", "resolved": "https://registry.npmjs.org/react-dom-factories/-/react-dom-factories-1.0.2.tgz",
"integrity": "sha1-63cFxNs2+1AbOqOP91lhaqD/luA=" "integrity": "sha1-63cFxNs2+1AbOqOP91lhaqD/luA="
}, },
"react-draggable": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-2.2.6.tgz",
"integrity": "sha1-OoBuEPLaa6v+pBNr5lEOibDXaQE=",
"requires": {
"classnames": "^2.2.5"
}
},
"react-is": { "react-is": {
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -17370,14 +17329,6 @@
} }
} }
}, },
"react-onclickoutside": {
"version": "5.11.1",
"resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-5.11.1.tgz",
"integrity": "sha1-ADFOUlZ89V+rqUyrus0RlhkHBiM=",
"requires": {
"create-react-class": "^15.5.x"
}
},
"react-paging": { "react-paging": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/react-paging/-/react-paging-0.2.1.tgz", "resolved": "https://registry.npmjs.org/react-paging/-/react-paging-0.2.1.tgz",
@ -17442,26 +17393,6 @@
"react-dom-factories": "^1.0.0" "react-dom-factories": "^1.0.0"
} }
}, },
"react-resizable": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-1.11.0.tgz",
"integrity": "sha512-VoGz2ddxUFvildS8r8/29UZJeyiM3QJnlmRZSuXm+FpTqq/eIrMPc796Y9XQLg291n2hFZJtIoP1xC3hSTw/jg==",
"requires": {
"prop-types": "15.x",
"react-draggable": "^4.0.3"
},
"dependencies": {
"react-draggable": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.3.tgz",
"integrity": "sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==",
"requires": {
"classnames": "^2.2.5",
"prop-types": "^15.6.0"
}
}
}
},
"react-slick": { "react-slick": {
"version": "0.27.13", "version": "0.27.13",
"resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.27.13.tgz", "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.27.13.tgz",

View File

@ -62,9 +62,6 @@ export default class ReactSpFxWebPart extends BaseClientSideWebPart<IReactSpFxWe
{ {
groupName: strings.BasicGroupName, groupName: strings.BasicGroupName,
groupFields: [ groupFields: [
PropertyPaneTextField('title', {
label: strings.DescriptionFieldLabel
}),
PropertyPaneSlider('pageSize',{ PropertyPaneSlider('pageSize',{
label:"Highlights Per Page", label:"Highlights Per Page",
min:1, min:1,

View File

@ -46,7 +46,7 @@ export interface ISportsHighlights {
export interface ISportsHighlightProps { export interface ISportsHighlightProps {
title: string; title: string;
embed: string embed: string;
id: string; id: string;
date: Date; date: Date;
side1: ISide; side1: ISide;
@ -54,6 +54,7 @@ export interface ISportsHighlightProps {
competition: ICompetition; competition: ICompetition;
thumbnail: string; thumbnail: string;
videos: IVideo[]; videos: IVideo[];
url: string;
} }

View File

@ -1,14 +1,10 @@
import * as React from "react"; import * as React from "react";
import styles from "./ReactSpFx.module.scss"; import styles from "./ReactSpFx.module.scss";
import { IReactSpFxProps, ISportsHighlightsState } from "./IReactSpFxProps"; import { IReactSpFxProps, ISportsHighlightsState } from "./IReactSpFxProps";
import { escape } from "@microsoft/sp-lodash-subset";
import SportsHighlightsList from "./SportsHighlightsList"; import SportsHighlightsList from "./SportsHighlightsList";
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle"; import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder";
import { setVirtualParent } from "office-ui-fabric-react";
import SportsHighlights from "./SportsHighlightsList";
import "bootstrap/dist/css/bootstrap.css"; import "bootstrap/dist/css/bootstrap.css";
export default class ReactSpFx extends React.Component<IReactSpFxProps> { export default class ReactSpFx extends React.Component<IReactSpFxProps> {
@ -17,28 +13,13 @@ export default class ReactSpFx extends React.Component<IReactSpFxProps> {
} }
public render(): React.ReactElement<IReactSpFxProps> { public render(): React.ReactElement<IReactSpFxProps> {
const globalComponent = this;
return ( return (
<div className={styles.container}> <div className={styles.container}>
{!this.props.title && (
<Placeholder
iconName="Soccer Highlights"
iconText="Configure your web part"
description="Set Web Part Title and choose Page Size to hide this section."
buttonLabel="Configure the Web Part"
onConfigure={this.props.onConfigure}
/>
)}
{typeof this.props.title !== "undefined" && (
<div id="mainDiv">
<WebPartTitle <WebPartTitle
displayMode={this.props.displayMode} displayMode={this.props.displayMode}
title={this.props.title} title={this.props.title}
updateProperty={this.props.updateProperty} updateProperty={this.props.updateProperty}
/> />
</div>
)}
<span></span>
<SportsHighlightsList pageSize={this.props.pageSize} showFlatMode={this.props.showFlatMode} <SportsHighlightsList pageSize={this.props.pageSize} showFlatMode={this.props.showFlatMode}
/> />
</div> </div>

View File

@ -1,8 +1,5 @@
import React from "react"; import React from "react";
import ReactDOM from "react-dom";
import { ISportsHighlightProps, ISportsHighlights } from "./IReactSpFxProps"; import { ISportsHighlightProps, ISportsHighlights } from "./IReactSpFxProps";
import SportsVideo from "./SportsVideo";
import ReactMarkdownWithHtml from "react-markdown/with-html";
import { FilmstripLayout } from './filmstripLayout/index'; import { FilmstripLayout } from './filmstripLayout/index';
import styles from './filmstripLayout/FilmstripLayout.module.scss'; import styles from './filmstripLayout/FilmstripLayout.module.scss';
@ -13,7 +10,6 @@ import {
DocumentCardDetails, DocumentCardDetails,
DocumentCardTitle, DocumentCardTitle,
IDocumentCardPreviewProps, IDocumentCardPreviewProps,
DocumentCardLocation,
DocumentCardType, DocumentCardType,
} from "office-ui-fabric-react/lib/DocumentCard"; } from "office-ui-fabric-react/lib/DocumentCard";
@ -32,24 +28,20 @@ export default class SportVideoListFilmStripView extends React.Component<
isDialogOpen: false, isDialogOpen: false,
}; };
} }
openDialog = (video: ISportsHighlightProps) => {
console.log("I awas clicked:" + video.title);
this.setState({ isDialogOpen: true });
};
handleClose = () => this.setState({ isDialogOpen: false });
public render(): React.ReactElement<ISportsHighlights> { public render(): React.ReactElement<ISportsHighlights> {
const videos = this.props.highLights; const videos = this.props.highLights;
console.log("Video Highlights", videos);
return ( return (
<div> <div>
<div className={styles.filmStrip}> <div className={styles.filmStrip}>
<FilmstripLayout <FilmstripLayout
ariaLabel={ ariaLabel={
"Sample filmstrip layout web part, showing sample items., Use right and left arrow keys to navigate between cards in the film strip." "Soccer highlights web part, showing soccer highlights. Use right and left arrow keys to navigate between cards in the film strip."
} }
> >
{this.props.highLights.map((video: ISportsHighlightProps, i) => { {videos.map((video: ISportsHighlightProps, i) => {
let videoDate = new Date(video.date.toString()); let videoDate = new Date(video.date.toString());
const previewProps: IDocumentCardPreviewProps = { const previewProps: IDocumentCardPreviewProps = {
previewImages: [ previewImages: [
@ -70,23 +62,13 @@ export default class SportVideoListFilmStripView extends React.Component<
> >
<DocumentCard <DocumentCard
type={DocumentCardType.normal} type={DocumentCardType.normal}
onClick={(ev: React.SyntheticEvent<HTMLElement>) => onClickHref={video.url}
this.openDialog(video)
}
> >
{/* <DocumentCardPreview {...previewProps} /> */} <DocumentCardPreview {...previewProps} />
<DocumentCardDetails> <DocumentCardDetails>
{/* <DocumentCardTitle <DocumentCardTitle
title={video.title} title={video.side1.name+ ' vs ' + video.side2.name}
shouldTruncate={false} shouldTruncate={true}
/> */}
<div
id={"video" + video.title}
dangerouslySetInnerHTML={{
__html: video.embed,
}}
style={{ width: 268, padding: "2px" }}
title={"click to play " + video.title}
/> />
<DocumentCardActivity <DocumentCardActivity
activity={ activity={

View File

@ -1,14 +1,10 @@
import React from "react"; import React from "react";
import ReactDOM from "react-dom";
import { import {
ISportsHightLightsProps, ISportsHightLightsProps,
ISportsHighlightPagingState, ISportsHighlightPagingState,
ISportsHighlightsState,
ISportsHighlightProps, ISportsHighlightProps,
} from "./IReactSpFxProps"; } from "./IReactSpFxProps";
//import SportsHighlight from "./SportsHighlight";
import SportVideoListFilmStripView from "./SportVideoListFilmStripView"; import SportVideoListFilmStripView from "./SportVideoListFilmStripView";
import Paging from "react-paging";
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button'; import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import axios from "axios"; import axios from "axios";
import SportsVideoList from "./SportsVideoList"; import SportsVideoList from "./SportsVideoList";
@ -34,18 +30,20 @@ export default class SportsHighlightsList extends React.Component<
const data : ISportsHighlightProps[] = await resp.data; const data : ISportsHighlightProps[] = await resp.data;
let slicedItems : ISportsHighlightProps[] = data.slice(0, this.props.pageSize); let slicedItems : ISportsHighlightProps[] = data.slice(0, this.props.pageSize);
this.setState({ pagedSportHighlights: data, slicedSportHighlights: slicedItems }); this.setState({ pagedSportHighlights: data, slicedSportHighlights: slicedItems });
}; }
async componentDidMount() { public async componentDidMount() {
console.log("Mounted"); console.log("Mounted");
this.GetData(); this.GetData();
} }
public render(): React.ReactElement<ISportsHightLightsProps> { public render(): React.ReactElement<ISportsHightLightsProps> {
if(!this.props.showFlatMode){
return <SportVideoListFilmStripView highLights={this.state.slicedSportHighlights} />;
}
let pageCountDivisor: number = this.props.pageSize; let pageCountDivisor: number = this.props.pageSize;
let pageCount: number; let pageCount: number;
let pageButtons = []; let pageButtons = [];
@ -64,18 +62,7 @@ export default class SportsHighlightsList extends React.Component<
}); });
}; };
// const pagedItems: JSX.Element = ( var pagedItems: JSX.Element = (<SportsVideoList videos={this.state.slicedSportHighlights} /> );
// <SportVideoListFilmStripView highLights={this.state.slicedSportHighlights} />
// );
var pagedItems: JSX.Element = null;
if(!this.props.showFlatMode){
pagedItems = (<SportVideoListFilmStripView highLights={this.state.slicedSportHighlights} />);
}
else{
pagedItems = (<SportsVideoList videos={this.state.slicedSportHighlights} /> );
}
if (highlightItems.length > 0) { if (highlightItems.length > 0) {
pageCount = Math.ceil(highlightItems.length / pageCountDivisor); pageCount = Math.ceil(highlightItems.length / pageCountDivisor);
} }
@ -91,13 +78,11 @@ export default class SportsHighlightsList extends React.Component<
</PrimaryButton> </PrimaryButton>
); );
} }
return ( return (
<div> <div>
<div className={`ms-Grid-row`} style={{paddingLeft:"8px"}}> <div className={`ms-Grid-row`} style={{paddingLeft:"8px"}}>
<div className="ms-Grid-col ms-u-lg12">{pageButtons}</div> <div className="ms-Grid-col ms-u-lg12">{pageButtons}</div>
</div> </div>
<hr />
<div className="ms-Grid-row"> <div className="ms-Grid-row">
<div className="ms-Grid-col ms-u-lg12">{pagedItems}</div> <div className="ms-Grid-col ms-u-lg12">{pagedItems}</div>
</div> </div>

View File

@ -1,6 +1,6 @@
define([], function() { define([], function() {
return { return {
"PropertyPaneDescription": "Title", "PropertyPaneDescription": "Display the highlights and goals of the latest soccer matches ",
"BasicGroupName": "General", "BasicGroupName": "General",
"DescriptionFieldLabel": "Title" "DescriptionFieldLabel": "Title"
} }