Add files via upload

This commit is contained in:
Jerry Yasir 2020-11-20 22:07:50 -05:00 committed by GitHub
parent 0feb8b6b4f
commit 6885275f42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 468 additions and 98 deletions

View File

@ -21,7 +21,8 @@
"description": { "default": "Soccer Hightlights Web Parts show latest hightlights from around the world." },
"officeFabricIconFontName": "Soccer",
"properties": {
"tile": "Soocer Highlights"
"title": "Soccer Highlights",
"pageSize": 5
}
}]
}

View File

@ -5,6 +5,7 @@ import {
IPropertyPaneConfiguration,
PropertyPaneTextField,
PropertyPaneSlider,
PropertyPaneToggle,
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
@ -18,6 +19,7 @@ export interface IReactSpFxWebPartProps {
title: string;
pageSize: number;
displayMode: DisplayMode;
showFlatMode: boolean;
updateProperty: (value: string) => void;
}
@ -29,7 +31,8 @@ export default class ReactSpFxWebPart extends BaseClientSideWebPart<IReactSpFxWe
{
title: this.properties.title,
displayMode: this.properties.displayMode,
pageSize: this.properties.pageSize,
pageSize: this.properties.pageSize == undefined ? 10 : this.properties.pageSize,
showFlatMode: this.properties.showFlatMode,
updateProperty: (value: string) => {this.properties.title = value;},
onConfigure: () => {
this.context.propertyPane.open();
@ -66,10 +69,14 @@ export default class ReactSpFxWebPart extends BaseClientSideWebPart<IReactSpFxWe
label:"Highlights Per Page",
min:1,
max:20,
value:2,
value:5,
showValue:true,
step:1
})
}),
PropertyPaneToggle('showFlatMode', {
label: "Show Videos in Flat Mode",
checked: false
})
]
}
]

View File

@ -5,11 +5,12 @@ export interface IReactSpFxProps {
title: string;
displayMode: DisplayMode;
pageSize: number;
showFlatMode: boolean;
updateProperty: (value: string) => void;
onConfigure: () => void;
}
export interface IVidesList {
export interface IVideosList {
videos: IVideo[];
}
@ -19,18 +20,16 @@ export interface IVideo {
}
export interface ISportsHighlightsState {
sportHighlightState: [];
sportHighlightState: any;
}
export interface ISportsHighlightPagingState {
currentPage: number;
indexOfLastHighlight: number;
indexOfFirstHighlight: number;
highLightsPerPage: number;
pagedSportHighlights?: ISportsHighlightProps[];
slicedSportHighlights?: ISportsHighlightProps[];
}
export interface ISportsHightLightsProps {
pageSize:number;
sportsHighlights: [];
showFlatMode: boolean;
}
export interface ISide {
@ -41,8 +40,13 @@ export interface ICompetition{
id: number;
}
export interface ISportsHighlights {
highLights?: ISportsHighlightProps[];
}
export interface ISportsHighlightProps {
title: string;
embed: string
id: string;
date: Date;
side1: ISide;

View File

@ -11,6 +11,7 @@
border: 1px solid lightblue;
height: 100px;
padding: 10px 0;
width: 100%;
display: flex;
justify-content: center;
}

View File

@ -6,35 +6,18 @@ import SportsHighlightsList from "./SportsHighlightsList";
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder";
import axios from "axios";
import { setVirtualParent } from "office-ui-fabric-react";
import SportsHighlights from "./SportsHighlightsList";
import "bootstrap/dist/css/bootstrap.css";
export default class ReactSpFx extends React.Component<
IReactSpFxProps,
ISportsHighlightsState
> {
export default class ReactSpFx extends React.Component<IReactSpFxProps> {
public constructor(props: IReactSpFxProps, state: ISportsHighlightsState) {
super(props);
this.state = {
sportHighlightState: [],
};
}
GetData = async () => {
const resp = await axios.get(`https://www.scorebat.com/video-api/v1/`);
const data = await resp.data;
this.setState({ sportHighlightState: data });
};
async componentDidMount() {
this.GetData();
}
public render(): React.ReactElement<IReactSpFxProps> {
const globalComponent = this;
console.log(this.props.pageSize);
return (
<div className={styles.container}>
{!this.props.title && (
@ -56,9 +39,7 @@ export default class ReactSpFx extends React.Component<
</div>
)}
<span></span>
<SportsHighlightsList
sportsHighlights={this.state.sportHighlightState}
pageSize={this.props.pageSize}
<SportsHighlightsList pageSize={this.props.pageSize} showFlatMode={this.props.showFlatMode}
/>
</div>
);

View File

@ -0,0 +1,112 @@
import React from "react";
import ReactDOM from "react-dom";
import { ISportsHighlightProps, ISportsHighlights } from "./IReactSpFxProps";
import SportsVideo from "./SportsVideo";
import ReactMarkdownWithHtml from "react-markdown/with-html";
import { FilmstripLayout } from './filmstripLayout/index';
import styles from './filmstripLayout/FilmstripLayout.module.scss';
import {
DocumentCard,
DocumentCardActivity,
DocumentCardPreview,
DocumentCardDetails,
DocumentCardTitle,
IDocumentCardPreviewProps,
DocumentCardLocation,
DocumentCardType,
} from "office-ui-fabric-react/lib/DocumentCard";
import { ImageFit } from "office-ui-fabric-react/lib/Image";
export interface IDialogState
{
isDialogOpen: boolean;
}
export default class SportVideoListFilmStripView extends React.Component<
ISportsHighlights,
IDialogState
> {
constructor(props: ISportsHighlights, state: IDialogState) {
super(props);
this.state = {
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> {
const videos = this.props.highLights;
return (
<div>
<div className={styles.filmStrip}>
<FilmstripLayout
ariaLabel={
"Sample filmstrip layout web part, showing sample items., Use right and left arrow keys to navigate between cards in the film strip."
}
>
{this.props.highLights.map((video: ISportsHighlightProps, i) => {
let videoDate = new Date(video.date.toString());
const previewProps: IDocumentCardPreviewProps = {
previewImages: [
{
previewImageSrc: video.thumbnail,
imageFit: ImageFit.cover,
height: 130,
},
],
};
return (
<div
key={i}
data-is-focusable={true}
role="listitem"
aria-label={video.title}
>
<DocumentCard
type={DocumentCardType.normal}
onClick={(ev: React.SyntheticEvent<HTMLElement>) =>
this.openDialog(video)
}
>
{/* <DocumentCardPreview {...previewProps} /> */}
<DocumentCardDetails>
{/* <DocumentCardTitle
title={video.title}
shouldTruncate={false}
/> */}
<div
id={"video" + video.title}
dangerouslySetInnerHTML={{
__html: video.embed,
}}
style={{ width: 268, padding: "2px" }}
title={"click to play " + video.title}
/>
<DocumentCardActivity
activity={
videoDate.toLocaleString()
}
people={[
{
name: video.competition.name,
profileImageSrc: video.thumbnail,
},
]}
/>
</DocumentCardDetails>
</DocumentCard>
</div>
);
})}
</FilmstripLayout>
</div>
</div>
);
}
}

View File

@ -3,90 +3,103 @@ import ReactDOM from "react-dom";
import {
ISportsHightLightsProps,
ISportsHighlightPagingState,
IVideo,
ISportsHighlightsState,
ISportsHighlightProps,
} from "./IReactSpFxProps";
import SportsHighlight from "./SportsHighlight";
//import SportsHighlight from "./SportsHighlight";
import SportVideoListFilmStripView from "./SportVideoListFilmStripView";
import Paging from "react-paging";
import Pagination from "./Pagination";
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import axios from "axios";
import SportsVideoList from "./SportsVideoList";
export default class SportsHighlightsList extends React.Component<
ISportsHightLightsProps,
ISportsHighlightPagingState
> {
constructor(props) {
constructor(
props: ISportsHightLightsProps,
state: ISportsHighlightPagingState
) {
super(props);
this.state = {
currentPage: 1,
indexOfFirstHighlight: 1,
indexOfLastHighlight: 5,
highLightsPerPage: this.props.pageSize,
pagedSportHighlights: [],
slicedSportHighlights: [],
};
}
paginate = (page) => {
console.clear();
const indexOfLastHighlight =
this.state.currentPage * this.state.highLightsPerPage;
const indexOfFirstHighlight =
indexOfLastHighlight - this.state.highLightsPerPage;
this.setState({
currentPage: page,
indexOfLastHighlight: indexOfLastHighlight,
indexOfFirstHighlight: indexOfFirstHighlight,
});
public GetData = async () => {
const resp = await axios.get(`https://www.scorebat.com/video-api/v1/`);
const data : ISportsHighlightProps[] = await resp.data;
let slicedItems : ISportsHighlightProps[] = data.slice(0, this.props.pageSize);
this.setState({ pagedSportHighlights: data, slicedSportHighlights: slicedItems });
};
//paginate = (pageNumber) => setCurrentPage(pageNumber);
render(): React.ReactElement<ISportsHightLightsProps> {
const sportsHighlights = this.props.sportsHighlights;
var {
currentPage,
indexOfFirstHighlight,
indexOfLastHighlight,
} = this.state;
async componentDidMount() {
console.log("Mounted");
this.GetData();
}
if (typeof indexOfFirstHighlight === "undefined" || indexOfFirstHighlight == 0)
{
indexOfFirstHighlight=1
}
if (
typeof indexOfFirstHighlight === "undefined" ||
indexOfFirstHighlight == 0
) {
indexOfFirstHighlight = 1;
}
const CurrentsportsHighlights = this.props.sportsHighlights.slice(
indexOfFirstHighlight,
indexOfLastHighlight
public render(): React.ReactElement<ISportsHightLightsProps> {
let pageCountDivisor: number = this.props.pageSize;
let pageCount: number;
let pageButtons = [];
let highlightItems = this.state.pagedSportHighlights;
let _pagedButtonClick = (pageNumber: number, listData: any) => {
let startIndex: number = (pageNumber - 1) * pageCountDivisor;
let endIndex = startIndex + pageCountDivisor;
let listItemsCollection = [...listData];
let slicedItems: ISportsHighlightProps[] = listItemsCollection.slice(
startIndex,
endIndex
);
this.setState({
slicedSportHighlights: slicedItems
});
};
console.clear();
console.log("indexOfFirstHighlight:" + indexOfFirstHighlight);
console.log("indexOfLastHighlight : " + indexOfLastHighlight);
console.log("currentPage :" + currentPage);
console.log("Page Size :" + this.props.pageSize);
// const pagedItems: JSX.Element = (
// <SportVideoListFilmStripView highLights={this.state.slicedSportHighlights} />
// );
//this.updateLastPage(currentPage);
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) {
pageCount = Math.ceil(highlightItems.length / pageCountDivisor);
}
for (let i = 0; i < pageCount; i++) {
pageButtons.push(
<PrimaryButton key={i} style={{width:"50px"}}
onClick={() => {
_pagedButtonClick(i + 1, highlightItems);
}}
>
{" "}
{i + 1}{" "}
</PrimaryButton>
);
}
return (
<div>
<div className="paginationDiv">
<Pagination
highLightsPerPage={10}
totalHighlights={sportsHighlights.length}
paginate={this.paginate}
/>
<div className={`ms-Grid-row`} style={{paddingLeft:"8px"}}>
<div className="ms-Grid-col ms-u-lg12">{pageButtons}</div>
</div>
<div>
{CurrentsportsHighlights.map((highLight, i) => (
<SportsHighlight {...highLight} key={i} />
))}
<hr />
<div className="ms-Grid-row">
<div className="ms-Grid-col ms-u-lg12">{pagedItems}</div>
</div>
</div>
);

View File

@ -10,14 +10,14 @@ export default class SportsVideo extends React.Component<IVideo> {
return (
<div>
<div
style={{ fontSize: "12px", fontWeight: "bold", textAlign: "center", color:"blue" }}
style={{ fontSize: "18px", fontWeight: "bold", textAlign: "center", color:"blue" }}
title={"click to play " + title}
>
{title}
</div>
<div id={"video" + title}
dangerouslySetInnerHTML={{ __html: embed }}
style={{ width: 500, padding: "8px" }}
style={{ padding: "8px" }}
title={"click to play " + title}
/>
</div>

View File

@ -1,14 +1,14 @@
import React from "react";
import ReactDOM from "react-dom";
import { IVidesList } from "./IReactSpFxProps";
import { IVideosList } from "./IReactSpFxProps";
import SportsVideo from "./SportsVideo";
import ReactMarkdownWithHtml from "react-markdown/with-html";
export default class SportsVideoList extends React.Component<IVidesList> {
export default class SportsVideoList extends React.Component<IVideosList> {
public render() {
const videos = this.props.videos;
return (
<div style={{ margin: "1px" }}>
<div style={{ margin: "1px", alignContent:"center" }}>
{this.props.videos.map((video, i) => (
<SportsVideo
key={i}

View File

@ -0,0 +1,117 @@
@import "~office-ui-fabric-react/dist/sass/References.scss";
:export {
centerPadding: 10px;
}
.filmstripLayout {
position: relative;
&.filmStrip {
margin-bottom: 27px;
margin-left: -10px;
margin-right: -10px;
:global(.slick-slide) {
box-sizing: border-box;
padding: 0 10px;
}
}
.sliderButtons {
opacity: 0;
}
&:hover .sliderButtons {
opacity: 1;
&:hover {
color: $ms-color-white;
}
}
}
.indexButtonContainer {
position: absolute;
top: 0;
bottom: 0;
z-index: 1;
}
.indexButton {
font-size: 28px;
font-weight: 400;
height: 40px;
padding: 0;
border: 0;
background: 0 0;
cursor: pointer;
color: $ms-color-white;
width: 40px;
min-width: 20px;
margin-left: 0;
line-height: 40px;
box-sizing: content-box;
background-color: $ms-color-black;
opacity: 0.6;
position: absolute;
top: 50%;
transform: translateY(-50%);
transition: all 0.3s;
&:hover {
color: $ms-color-white;
background-color: $ms-color-black;
opacity: 0.6;
}
&:active {
outline: -webkit-focus-ring-color auto 1px;
}
}
.carouselDotsContainer {
.carouselDot {
display: inline-block;
background-color: $ms-color-black;
height: 4px;
width: 4px;
opacity: 0.5;
border: 2px solid $ms-color-black;
border-radius: 4px;
cursor: pointer;
opacity: 0.25;
outline: 0;
&:hover {
opacity: 1;
}
}
}
:global(.slick-active) {
.carouselDotsContainer {
.carouselDot {
background-color: "[theme:themeDark, default: #005a9e]";
opacity: 0.75;
border-color: "[theme:themeDark, default: #005a9e]";
&:hover {
opacity: 1;
}
}
}
}
.indexButton:global(.ms-Button-flexContainer):hover:global(.ms-Icon),
.indexButton:global(.ms-Icon:hover),
.indexButton:hover:global(.ms-Icon) {
color: $ms-color-white;
}
.leftPositioned {
left: 0;
}
.rightPositioned {
right: 0;
}

View File

@ -0,0 +1,16 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'carouselDot': string;
'carouselDotsContainer': string;
'centerPadding': string;
'filmStrip': string;
'filmstripLayout': string;
'indexButton': string;
'indexButtonContainer': string;
'leftPositioned': string;
'rightPositioned': string;
'sliderButtons': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@ -0,0 +1,117 @@
import { css } from "@uifabric/utilities/lib/css";
import { IconButton } from "office-ui-fabric-react/lib/Button";
import * as React from "react";
import Slider from "react-slick";
import { SPComponentLoader } from "@microsoft/sp-loader";
import styles from "./FilmstripLayout.module.scss";
import { useRef } from "react";
import useComponentSize, { ComponentSize } from "@rehooks/component-size";
/**
* Filmstrip layout
* Presents the child compoments as a slick slide
*/
export const FilmstripLayout = (props: {
children: any;
ariaLabel?: string;
}) => {
let ref: React.MutableRefObject<HTMLDivElement> = useRef<HTMLDivElement>(
null
);
let size: ComponentSize = useComponentSize(ref);
let { width } = size;
// // the slick slider used in normal views
let _slider: React.MutableRefObject<Slider> = useRef<Slider>(null);
SPComponentLoader.loadCss(
"https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.css"
);
SPComponentLoader.loadCss(
"https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick-theme.min.css"
);
// slick seems to have an issue with having "infinite" mode set to true and having less items than the number of slides per page
// set infinite to true only if there are more than 3 children
let numSlides: number = 3;
if (width) {
if (width > 927) {
numSlides = 4;
} else if (width <= 695) {
numSlides = 2;
}
}
var isInfinite: boolean = React.Children.count(props.children) > numSlides;
var settings: any = {
accessibility: true,
arrows: false,
autoplaySpeed: 5000,
dots: true,
customPaging: (i: number) => {
return (
<a>
<div
role="button"
className={styles.carouselDotsContainer}
aria-label={`Carousel Dot ${i}`}
data-is-focusable={true}
tabIndex={0}
>
<span className={styles.carouselDot} tabIndex={-1}></span>
</div>
</a>
);
},
infinite: isInfinite,
slidesToShow: numSlides,
slidesToScroll: numSlides,
speed: 500,
centerPadding: styles.centerPadding,
pauseOnHover: true,
variableWidth: false,
useCSS: true,
rows: 1,
respondTo: "slider",
};
return (
<div>
<div
className={css(styles.filmstripLayout, styles.filmStrip)}
aria-label={props.ariaLabel}
ref={ref}
>
<Slider ref={_slider} {...settings}>
{props.children}
</Slider>
<div
className={css(styles.indexButtonContainer, styles.sliderButtons)}
style={{ left: "10px" }}
onClick={() => _slider.current.slickPrev()}
>
<IconButton
className={css(styles.indexButton, styles.leftPositioned)}
iconProps={{
iconName: "ChevronLeft",
styles: { root: { fontSize: "28px", fontWeight: "400" } },
}}
/>
</div>
<div
className={css(styles.indexButtonContainer, styles.sliderButtons)}
style={{ right: "10px" }}
onClick={() => _slider.current.slickNext()}
>
<IconButton
className={css(styles.indexButton, styles.rightPositioned)}
iconProps={{
iconName: "ChevronRight",
styles: { root: { fontSize: "28px", fontWeight: "400" } },
}}
/>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1 @@
export * from "./FilmstripLayout";