* initialize

* wer

* asd

* ok

* COOOOL

* asd

* move most to panel

* switch to react-slick

* gOT cSS

* poseter

* asd

* Leave image until user selcte to play

* sdg

* qwe

* asd

* addiing back 3d carousel

* Added back 3d carousel

* coverflow working

* all 3 working

* fixup  linting

* Update README.md

* fix readme

* Update README.md

* got it working on rc0

* autoplay

* qwd

* rename Cpverflow to Coverflow

* cdn
qw

* SPHttpClient.configurations.v1

* as

* AS

* added instructions on merging existing github procjects

* added controls to set playerwidth and height

* Update README.md

fixup readme
This commit is contained in:
Russell gove 2017-02-03 11:02:07 -05:00 committed by Vesa Juvonen
parent 27b2672a77
commit 1e20f20229
53 changed files with 2609 additions and 1 deletions

View File

@ -56,7 +56,21 @@ When you are submitting a new sample, it has to follow up below guidelines
Please see following wiki post from the GitHub repository wiki for exact steps on submitting new pull requests. Please see following wiki post from the GitHub repository wiki for exact steps on submitting new pull requests.
* How to submit a PR to SharePoint repository? - Step-by-step with commands and pictures coming soon * How to submit a PR to SharePoint repository? - Step-by-step with commands and pictures coming soon
## Meging your existing github projects with this repository
If the sample you wish to contrubute is tored in your own Github repository, you can use the following steps to merge it with the Psp-dev-fx-webparts repository:
- Fork the sp-dev-fx-webparts repository om hithub
- create a local git rpository
md sp-dev-fx-webparts
cd sp-dev-fx-webparts
git init
- pull your forked copy of sp-dev-fx-webparts into your local repository
git remote add origin https://github.com/yourgitaccount/sp-dev-fx-webparts.git
git pull origin dev
- pull your other project from github into the samples folder of your local copy of sp-dev-fx-webparts
git subtree add --prefix=samples/projectname https://github.com/yourgitaccount/projectname.git master
- push the changes up to your forked repository
git push orgin dev
## Signing the CLA ## Signing the CLA
Before we can accept your pull requests you will be asked to sign electronically Contributor License Agreement (CLA), which is prerequisite for any contributions to PnP repository. This will be one time process, so for any future contributions you will not be asked to re-sign anything. After the CLA has been signed, our PnP core team members will have a look on your submission for final verification of the submission. Please do not delete your development branch until the submission has been closed. Before we can accept your pull requests you will be asked to sign electronically Contributor License Agreement (CLA), which is prerequisite for any contributions to PnP repository. This will be one time process, so for any future contributions you will not be asked to re-sign anything. After the CLA has been signed, our PnP core team members will have a look on your submission for final verification of the submission. Please do not delete your development branch until the submission has been closed.

View File

@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

View File

@ -0,0 +1 @@
* text=auto

32
samples/react-videolibrary/.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Build generated files
dist
lib
solution
temp
*.spapp
# Coverage directory used by tools like istanbul
coverage
# OSX
.DS_Store
# Visual Studio files
.ntvs_analysis.dat
.vs
bin
obj
# Resx Generated Code
*.resx.ts
# Styles Generated Code
*.scss.ts

View File

@ -0,0 +1,14 @@
# Folders
.vscode
coverage
node_modules
sharepoint
src
temp
# Files
*.csproj
.git*
.yo-rc.json
gulpfile.js
tsconfig.json

View File

@ -0,0 +1,3 @@
{
"typescript.check.workspaceVersion": false
}

View File

@ -0,0 +1,7 @@
{
"@microsoft/generator-sharepoint": {
"libraryName": "videotst",
"libraryId": "841958c7-1d50-408b-9df4-feac7165d4a0",
"framework": "react"
}
}

View File

@ -0,0 +1,84 @@
# React Video Library
## Summary
A set of 3 SPFX webparts that use different open-source carousels (react-3d-carousel, reactjs-coverface, and react-slick)
to display videos stored on an O365 Video Channel. The idea being to display a carousel of the thumbnail images, and then
when a user clicks on one of the thumbnails, replace the tumbnail with a video player and start the video up.
The first webpart used react-3d-carousel. The carousel looks great, but i found no way to swap out the image and replace
it with a video player. This carousel would be fine for displayin a picture library though,
The second webpart used react-slick. The carousel is not as fancy as react-3d-carousel, but i was able to to swap out the
image and replace it with a video player once a user clicked it. I had trouble with the css and getting the next and previous
buttons to show. If you run the webpart, the buttons are there, they are just not visible.
Finally I tried reactjs-coverface. It has nice scrolling through the images withe the mousweheel, and some cool 3d effects.
It was also simple to swap the image with a video player once a user clicked it (same code as react-slick). This is the best
of the three for my purposes.
In the future I want to modify this webpart to link a Sharepoint list with the video channel so that users can enter additional
metadata for the video and be anle to search/filter the videos using this metadata.
See also https://github.com/russgove/O365VideoSync. It's a console app that you can schedule to run to synchronize an Office 365 Video Channel with a sharepoint list (on prem or otherwise).
![alt tag](/samples/react-spfx-multilist-grid/src/images/editListItems.PNG)
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/drop-drop5-red.svg)
## Applies to
* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant)
## Prerequisites
> React, react-3d-carousel reactjs-coverface react-slick
## Solution
Solution|Author(s)
--------|---------
react-VideoLibrary | Russell Gove
## Version history
Version|Date|Comments
-------|----|--------
0.1|December 31, 2016|Initial version
## Disclaimer
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
---
## Minimal Path to Awesome
- Clone this repository
- in the command line run:
- `npm install`
- `tsd install`
- `gulp serve`
> Include any additional steps as needed.
## Features
A set of 3 SPFX webparts that use different open-source carousels (react-3d-carousel, reactjs-coverface, and react-slick)
to display videos stored on an O365 Video Channel.
Usage:
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-multilist-grid" />

View File

@ -0,0 +1,27 @@
{
"entries": [
{
"entry": "./lib/webparts/videoLibrary/VideoLibraryWebPart.js",
"manifest": "./src/webparts/videoLibrary/VideoLibraryWebPart.manifest.json",
"outputPath": "./dist/video-library.bundle.js"
},
{
"entry": "./lib/webparts/videoLibraryReact3DCarousel/VideoLibraryReact3DCarouselWebPart.js",
"manifest": "./src/webparts/videoLibraryReact3DCarousel/VideoLibraryReact3DCarouselWebPart.manifest.json",
"outputPath": "./dist/video-library-react-3-d-carousel.bundle.js"
},
{
"entry": "./lib/webparts/videoLibraryCoverFlow/VideoLibraryCoverFlowWebPart.js",
"manifest": "./src/webparts/videoLibraryCoverFlow/VideoLibraryCoverFlowWebPart.manifest.json",
"outputPath": "./dist/video-library-cpver-flow.bundle.js"
}
],
"externals": {
"@microsoft/sp-module-loader": "node_modules/@microsoft/sp-module-loader/dist/sp-module-loader.js"
},
"localizedResources": {
"videoLibraryStrings": "webparts/videoLibrary/loc/{locale}.js",
"videoLibraryReact3DCarouselStrings": "webparts/videoLibraryReact3DCarousel/loc/{locale}.js",
"videoLibraryCoverFlowStrings": "webparts/videoLibraryCoverFlow/loc/{locale}.js"
}
}

View File

@ -0,0 +1,6 @@
{
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "videotst",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,10 @@
{
"solution": {
"name": "videotst-client-side-solution",
"id": "841958c7-1d50-408b-9df4-feac7165d4a0",
"version": "1.0.0.0"
},
"paths": {
"zippedPackage": "solution/videotst.spapp"
}
}

View File

@ -0,0 +1,3 @@
{
"deployCdnPath": "temp/deploy"
}

View File

@ -0,0 +1,9 @@
{
"port": 4321,
"initialPage": "https://localhost:5432/workbench",
"https": true,
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
}

View File

@ -0,0 +1,50 @@
{
// Display errors as warnings
"displayAsWarning": true,
// The TSLint task may have been configured with several custom lint rules
// before this config file is read (for example lint rules from the tslint-microsoft-contrib
// project). If true, this flag will deactivate any of these rules.
"removeExistingRules": true,
// When true, the TSLint task is configured with some default TSLint "rules.":
"useDefaultConfigAsBase": false,
// Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules
// which are active, other than the list of rules below.
"lintConfig": {
// Opt-in to Lint rules which help to eliminate bugs in JavaScript
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"label-undefined": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-case": true,
"no-duplicate-key": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-unused-imports": true,
"no-unused-variable": true,
"no-unreachable": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"valid-typeof": true,
"variable-name": false,
"whitespace": false,
"prefer-const": true
}
}
}

View File

@ -0,0 +1,3 @@
{
"cdnBasePath": "https://rgove3.sharepoint.com/sites/cdn/spfxapps/"
}

View File

@ -0,0 +1,6 @@
'use strict';
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
build.initialize(gulp);

View File

@ -0,0 +1,38 @@
{
"name": "videotst",
"version": "0.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"dependencies": {
"@microsoft/sp-client-base": "~0.7.0",
"@microsoft/sp-core-library": "~0.1.2",
"@microsoft/sp-webpart-base": "~0.4.0",
"@types/webpack-env": ">=1.12.1 <1.14.0",
"@types/react": "0.14.46",
"@types/react-addons-shallow-compare": "0.14.17",
"@types/react-addons-test-utils": "0.14.15",
"@types/react-addons-update": "0.14.14",
"@types/react-dom": "0.14.18",
"office-ui-fabric-react": "0.69.0",
"react": "0.14.8",
"react-3d-carousel": "0.0.6",
"react-dom": "0.14.8",
"react-slick": "^0.14.5",
"reactjs-coverflow": "^1.0.5"
},
"devDependencies": {
"@microsoft/sp-build-web": "~0.9.0",
"@microsoft/sp-module-interfaces": "~0.7.0",
"@microsoft/sp-webpart-workbench": "~0.8.0",
"gulp": "~3.9.1",
"@types/chai": ">=3.4.34 <3.6.0",
"@types/mocha": ">=2.2.33 <2.6.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
}
}

View File

@ -0,0 +1,5 @@
var context = require.context('.', true, /.+\.test\.js?$/);
context.keys().forEach(context);
module.exports = context;

View File

@ -0,0 +1,167 @@
import {Promise} from "es6-promise"; // added fro rc0
import { IWebPartContext } from '@microsoft/sp-webpart-base';
import { SPHttpClient ,SPHttpClientConfigurations} from '@microsoft/sp-http';
export class VideoServiceSettings {
public ChannelUrlTemplate: string;
public IsVideoPortalEnabled: string;
public PlayerUrlTemplate: string;
public VideoPortalLayoutsUrl: string;
public VideoPortalUrl: string;
public O365VideoPageUrl: string;
}
export class VideoChannel {
public Description: string;
public Id: string;
public ServerRelativeUrl: string;
public TileHtmlColor: string;
public Title: string;
public YammerEnabled: string;
}
export class Video {
public ChannelID: string;
/** CreatedDate -- The date the video was originally uploaded. */
public CreatedDate: string;
public Description: string;
public DisplayFormUrl: string;
public FileName: string;
public ID: string;
public OwnerName: string;
public ServerRelativeUrl: string;
/**ThumbnailURL -- The URL of the thumbnail image of the video. */
public ThumbnailUrl: string;
/**Title -- The title of the video. */
public Title: string;
public Url: string;
public VideoDownloadUrl: string;
/**Title -- The title of the video. */
public VideoDurationInSeconds: number;
public VideoProcessingStatus: number;
public ViewCount: number;
public YammerObjectUrl: string;
}
export enum VideoProcessingStatus {
/** 0 -- (default) -- The video has not yet been processed for playback. */
NotProcessd = 0,
/**1 -- The video has been picked up and is being processed. */
BeingProcessed = 1,
/**2 -- The video is ready to play. */
Ready = 2,
/**3 -- The video encountered an error while it was being uploaded to Azure Media Services for processing. */
AzureError = 3,
/**4 -- Error -- Generic error--Unable to process the video for streaming. */
GenericError = 4,
/**5 -- Error -- Timeout error--Unable to process the video for streaming. */
TimeoutError = 5,
/**6 -- Error -- Unsupported format --The video file type is not supported for streaming playback by Azure Media Services. */
UnsupportedFormatError = 6
}
export class O365Video {
public videoServiceSettings: VideoServiceSettings;
public isInitialized: boolean;
public videoChannels: Array<VideoChannel>;
public httpClient: SPHttpClient;
public siteAbsoluteUrl: string;
constructor(context: IWebPartContext) {
this.httpClient = context.spHttpClient;
this.isInitialized = false;
this.siteAbsoluteUrl = context.pageContext.site.absoluteUrl;
}
public Initialize(): Promise<VideoServiceSettings> {
const url = this.siteAbsoluteUrl + "/_api/VideoService.Discover";
// return this.httpClient.get(url).then(response => { //pre rc0
return this.httpClient.get(url,SPHttpClient.configurations.v1).then(response => {
if (response.ok) {
console.log("Returned OK from httpClient");
debugger;
const results = response.json().then(settings => {
this.videoServiceSettings = new VideoServiceSettings();
this.videoServiceSettings.ChannelUrlTemplate = settings.ChannelUrlTemplate;
this.videoServiceSettings.IsVideoPortalEnabled = settings.IsVideoPortalEnabled;
this.videoServiceSettings.PlayerUrlTemplate = settings.PlayerUrlTemplate;
this.videoServiceSettings.VideoPortalLayoutsUrl = settings.VideoPortalLayoutsUrl;
this.videoServiceSettings.VideoPortalUrl = settings.VideoPortalUrl;
this.videoServiceSettings.O365VideoPageUrl = settings.O365VideoPageUrl;
return this.videoServiceSettings;
});
this.isInitialized = true;
return results;
} else {
this.isInitialized = true;
console.log("WARNING - failed to hit URL " + url + ". Error = " + response.statusText);
throw "Error " + response.statusText;
}
});
}
public getChannels(): Promise<Array<VideoChannel>> {
const url = this.videoServiceSettings.VideoPortalUrl + "/_api/VideoService/Channels";
return this.httpClient.get(url,SPHttpClient.configurations.v1).then(response => {
if (response.ok) {
console.log("Returned OK from httpClient");
return response.json().then(channels => {
this.videoChannels = channels.value.map(c => {
const channel = new VideoChannel();
channel.Description = c.Description;
channel.Id = c.Id;
channel.ServerRelativeUrl = c.ServerRelativeUrl;
channel.TileHtmlColor = c.TileHtmlColor;
channel.Title = c.Title;
channel.YammerEnabled = c.YammerEnabled;
return channel;
});
return this.videoChannels;
});
} else {
console.log("WARNING - failed to hit URL " + url + ". Error = " + response.statusText);
throw "Error " + response.statusText;
}
});
}
public GetVideos(ChannelId: string): Promise<Array<Video>> {
const url = this.videoServiceSettings.VideoPortalUrl + "/_api/VideoService/Channels('" + ChannelId + "')/Videos";
return this.httpClient.get(url,SPHttpClient.configurations.v1).then(response => {
if (response.ok) {
return response.json().then(v => {
const videos = v.value.map(c => {
let video = new Video();
video.ChannelID = c.ChannelID;
video.Description = c.Description;
video.DisplayFormUrl = c.DisplayFormUrl;
video.FileName = c.FileName;
video.ID = c.ID;
video.OwnerName = c.OwnerName;
video.ServerRelativeUrl = c.ServerRelativeUrl;
video.Title = c.Title;
video.ThumbnailUrl = c.ThumbnailUrl;
video.Url = c.Url;
video.VideoDownloadUrl = c.VideoDownloadUrl;
video.VideoDurationInSeconds = c.VideoDurationInSeconds;
video.VideoProcessingStatus = c.VideoProcessingStatus;
video.ViewCount = c.ViewCount;
video.YammerObjectUrl = c.YammerObjectUrl;
return video;
});
return videos;
});
} else {
console.log("WARNING - failed to hit URL " + url + ". Error = " + response.statusText);
throw "Error " + response.statusText;
}
});
}
public GetChannelByName(ChannelTitle: string): Promise<VideoChannel> {
return this.getChannels().then(channels => {
const matches = channels.filter((value, index, array) => { return value.Title === ChannelTitle; });
return matches[0];
});
}
}

View File

@ -0,0 +1,9 @@
import { O365Video } from "../O365VUtilities";
export interface IVideoLibraryWebPartProps {
description: string;
videoChannel: string;
o365Video: O365Video;
layout:string;
duration:number;
panels:number;
}

View File

@ -0,0 +1,119 @@
.slick-slide {
height: 80vh;
background: #2196f3;
text-align: center;
color: white;
font-size: 20px;
display: table !important;
}
.slick-slide div {
display: table-cell;
vertical-align: middle;
}
.slide-0 {
background: red;
}
.slide-1 {
background: orange;
}
.slide-2 {
background: green;
}
.slide-3{
background: black;
}
.hide {
display: none;
}
.footer1 {
height: 100px;
color: white;
background: blue;
}
.footer2 {
height: 100px;
color: white;
background: green;
}
.footer-container {
height: 100px;
position: relative;
text-align: center;
font-size: 20px;
}
.footer-container div {
padding: 2em;
}
.example-enter {
position: absolute;
top: 0;
left: 0;
right: 0;
opacity: 0.01;
}
.example-enter.example-enter-active {
opacity: 1;
transition: opacity .5s ease-in;
}
.example-leave {
position: absolute;
top: 0;
left: 0;
right: 0;
opacity: 1;
}
.example-leave.example-leave-active {
opacity: 0.01;
transition: opacity .5s ease-in;
}
.videoLibrary {
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
padding: 20px;
}
.listItem {
max-width: 715px;
margin: 5px auto 5px auto;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.button {
text-decoration: none;
}
}
.slides {
position: relative;
.slick-prev, .slick-next {
position: absolute;
top: 50%;
}
.slick-prev {
left: 5%;
}
.slick-next {
right: 5%;
}
}

View File

@ -0,0 +1,24 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"id": "f0d034d3-6b7d-4907-a6c1-357c2e2e03af",
"alias": "VideoLibraryWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"manifestVersion": 2,
"preconfiguredEntries": [{
"groupId": "f0d034d3-6b7d-4907-a6c1-357c2e2e03af",
"group": { "default": "Under Development" },
"title": { "default": "React-slick Video Library" },
"description": {
"default": "VideoLibrary implemented using react-slick -- Shows videos from an O365 Video Channel"
},
"officeFabricIconFontName": "Ribbon",
"properties": {
"description": "React-slick Video Library",
"layout":"prism",
"duration":199
}
}]
}

View File

@ -0,0 +1,116 @@
import * as React from "react";
import * as ReactDom from "react-dom";
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration ,
PropertyPaneTextField,
PropertyPaneDropdown, IPropertyPaneDropdownOption,
PropertyPaneSlider
} from "@microsoft/sp-webpart-base";
import { O365Video } from "../O365VUtilities";
import * as strings from "videoLibraryStrings";
import VideoLibrary, { IVideoLibraryProps } from "./components/VideoLibrary";
import { IVideoLibraryWebPartProps } from "./IVideoLibraryWebPartProps";
import {SPComponentLoader} from "@microsoft/sp-loader";
export default class VideoLibraryWebPart extends BaseClientSideWebPart<IVideoLibraryWebPartProps> {
private O365Video: O365Video;
private channels: Array<IPropertyPaneDropdownOption>;
private channelsFetched: boolean;
public onInit<T>(): Promise<T> {
SPComponentLoader.loadCss("https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css");
SPComponentLoader.loadCss("https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css");
this.O365Video = new O365Video(this.context);
return Promise.resolve(null);
}
public render(): void {
const props: IVideoLibraryProps = {
description: this.properties.description,
videoChannel: this.properties.videoChannel,
o365Video: this.O365Video,
layout: this.properties.layout,
duration: this.properties.duration,
panels: this.properties.panels
};
const element: React.ReactElement<IVideoLibraryProps> = React.createElement(VideoLibrary, props);
ReactDom.render(element, this.domElement);
}
public getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
debugger;
if (!this.O365Video.isInitialized) {
this.O365Video.Initialize().then(x => {
this.O365Video.getChannels().then(channels => {
this.context.propertyPane.refresh();
});
});
}
if (!this.channelsFetched && this.O365Video.isInitialized) {
this.O365Video.getChannels().then(channels => {
this.channels = channels.map((c, i, a) => {
let opt: IPropertyPaneDropdownOption = {
key: c.Id,
text: c.Title,
index: i,
};
return opt;
});
this.channelsFetched = true;
this.context.propertyPane.refresh();
});
}
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField("description", {
label: strings.DescriptionFieldLabel
}),
PropertyPaneDropdown("videoChannel", {
label: strings.VideoChannelFieldLabel,
options: this.channels,
}),
PropertyPaneDropdown("layout", {
label: strings.LayoutFieldLabel,
options: [
{ key: "prism", text: "prism" },
{ key: "clssic", text: "classic" }
]
}),
PropertyPaneSlider("duration", {
label: strings.DurationFieldLabel,
min: 1,
max: 1000
}),
PropertyPaneSlider("panels", {
label: strings.PanelsFieldLabel,
min: 1,
max: 5
}),
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,84 @@
import * as React from "react";
import { css } from "office-ui-fabric-react";
var Slick = require("react-slick");
import { IVideoLibraryWebPartProps } from "../IVideoLibraryWebPartProps";
import { Video} from "../../O365VUtilities";
export interface IVideoLibraryProps extends IVideoLibraryWebPartProps {
}
export interface IVideoLibraryState {
ease: string;
width: number;
playerUrlTemplate: string;
videos: Array<Video>;
selectedVideo: number;
}
class PrevArrow extends React.Component<any, any> {
public render() {
debugger;
return (<button>Next</button>);
}
}
export default class VideoLibrary extends React.Component<IVideoLibraryProps, IVideoLibraryState> {
constructor(props: IVideoLibraryProps) {
super(props);
this.afterChange = this.afterChange.bind(this);
this.playVideo = this.playVideo.bind(this);
this.state = {
ease: "linear",
width: 400,
playerUrlTemplate: null,
videos: [],
selectedVideo: -1,
};
}
public componentWillMount(nextProps) {
// Load new data when the dataSource property changes.
this.props.o365Video.Initialize().then((settings) => {
debugger;
// this.state.playerUrlTemplate = settings.PlayerUrlTemplate; // this url does not work. You neeed the channel
this.state.playerUrlTemplate = settings.VideoPortalLayoutsUrl + "/VideoEmbedHost.aspx?chId={0}&vId={1}&width=640&height=360&autoPlay=true&showInfo=true";
if (this.props.videoChannel) {
this.props.o365Video.GetVideos(this.props.videoChannel).then((videos) => {
this.state.videos = videos;
this.setState(this.state);
});
}
});
}
public afterChange(slideNumber: number) {
this.state.selectedVideo = -1;
this.setState(this.state);
}
public playVideo(event) {
debugger;
this.state.selectedVideo = parseInt(event.target.dataset.videonumber);
this.setState(this.state);
}
public render(): JSX.Element {
if (this.state.videos.length === 0) {
return (<div />);
}
return (
<div>
<div >
<Slick arrows={true} afterChange={this.afterChange} width="80%" dots={true} >
{this.state.videos.map((v, i, a) => {
if (i === this.state.selectedVideo) {
const src = this.state.playerUrlTemplate.replace("{1}", v.ID).replace("{0}", v.ChannelID);
return (<iframe src={src} style={{ height: "540px", width: "200px" }} />);
}
else {
return (<img src={v.ThumbnailUrl} data-videonumber={i} style={{ height: "540px", width: "200px" }} onClick={this.playVideo} />);
}
})}
</Slick>
</div>
</div>
);
}
}

View File

@ -0,0 +1,13 @@
define([], function () {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field",
"VideoChannelFieldLabel": "Video Channel",
"ListNameFieldLabel": "List Name",
"LayoutFieldLabel": "Layout",
"DurationFieldLabel": "Transition Time",
"PanelsFieldLabel": "Panels",
"EaseFieldLabel": "Ease"
}
});

View File

@ -0,0 +1,16 @@
declare interface IVideoLibraryStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
VideoChannelFieldLabel: string;
ListNameFieldLabel: string;
LayoutFieldLabel: string;
DurationFieldLabel: string;
PanelsFieldLabel: string;
EaseFieldLabel: string;
}
declare module "videoLibraryStrings" {
const strings: IVideoLibraryStrings;
export = strings;
}

View File

@ -0,0 +1,7 @@
/// <reference types="mocha" />
import { assert } from 'chai';
describe('VideoLibraryWebPart', () => {
it('should do something', () => {
assert.ok(true);
});
});

View File

@ -0,0 +1,19 @@
import { O365Video } from "../O365VUtilities";
export interface IVideoLibraryCoverFlowWebPartProps {
description: string;
videoChannel: string;
o365Video: O365Video;
coverflowStartPosition: number;
coverflowMargin: number;
coverflowWidth: number;
coverflowEnableScroll: boolean;
iframeHeight: number;
iframeWidth: number;
coverflowHeight: number;
coverflowAnimationSpeed: number;
imgHeight: number;
imgWidth: number;
playerHeight: number;
playerWidth: number;
}

View File

@ -0,0 +1,26 @@
.videoLibraryCpverFlow {
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
padding: 20px;
}
.listItem {
max-width: 715px;
margin: 5px auto 5px auto;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.button {
text-decoration: none;
}
}

View File

@ -0,0 +1,38 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"id": "aa6e9d44-a2a4-4632-842f-e015fffa14b7",
"alias": "VideoLibraryCverFlowWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"manifestVersion": 2,
"preconfiguredEntries": [
{
"groupId": "aa6e9d44-a2a4-4632-842f-e015fffa14b7",
"group": {
"default": "Under Development"
},
"title": {
"default": "CoverFlow Video Library"
},
"description": {
"default": "VideoLibrary implemented using reactjs-coverflow -- Shows videos from an O365 Video Channel"
},
"officeFabricIconFontName": "VisioLogo",
"properties": {
"description": "Coverflow Video Library",
"coverflowStartPosition": 0,
"coverflowMargin": 25,
"coverflowWidth": 800,
"coverflowHeight": 250,
"coverflowEnableScroll": true,
"iframeHeight": 215,
"iframeWidth": 300,
"playerHeight": 210,
"playerWidth": 295,
"coverflowAnimationSpeed": 0.8,
"imgHeight": 225,
"imgWidth": 185
}
}
]
}

View File

@ -0,0 +1,166 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField,
PropertyPaneDropdown, IPropertyPaneDropdownOption,
PropertyPaneSlider,
PropertyPaneToggle
} from "@microsoft/sp-webpart-base";
import { O365Video } from "../O365VUtilities";
import * as strings from 'videoLibraryCoverFlowStrings';
import VideoLibraryCoverFlow, { IVideoLibraryCoverFlowProps } from './components/VideoLibraryCoverFlow';
import { IVideoLibraryCoverFlowWebPartProps } from './IVideoLibraryCoverFlowWebPartProps';
debugger;
require('coverflow.css');
export default class VideoLibraryCpverFlowWebPart extends BaseClientSideWebPart<IVideoLibraryCoverFlowWebPartProps> {
private O365Video: O365Video;
private channels: Array<IPropertyPaneDropdownOption>;
private channelsFetched: boolean;
public onInit<T>(): Promise<T> {
this.O365Video = new O365Video(this.context);
return Promise.resolve(null);
}
public render(): void {
const props: IVideoLibraryCoverFlowWebPartProps = {
description: this.properties.description,
videoChannel: this.properties.videoChannel,
o365Video: this.O365Video,
iframeHeight: this.properties.iframeHeight,
iframeWidth: this.properties.iframeWidth,
playerHeight: this.properties.playerHeight,
playerWidth: this.properties.playerWidth,
imgHeight: this.properties.imgHeight,
imgWidth: this.properties.imgWidth,
coverflowWidth: this.properties.coverflowWidth,
coverflowHeight: this.properties.coverflowHeight,
coverflowMargin: this.properties.coverflowMargin,
coverflowAnimationSpeed: this.properties.coverflowAnimationSpeed,
coverflowStartPosition: this.properties.coverflowStartPosition,
coverflowEnableScroll: this.properties.coverflowEnableScroll,
};
const element: React.ReactElement<IVideoLibraryCoverFlowWebPartProps> = React.createElement(VideoLibraryCoverFlow, props);
ReactDom.render(element, this.domElement);
}
public getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
debugger;
if (!this.O365Video.isInitialized) {
this.O365Video.Initialize().then(x => {
this.O365Video.getChannels().then(channels => {
this.context.propertyPane.refresh();
});
});
}
if (!this.channelsFetched && this.O365Video.isInitialized) {
this.O365Video.getChannels().then(channels => {
this.channels = channels.map((c, i, a) => {
const opt: IPropertyPaneDropdownOption = {
key: c.Id,
text: c.Title,
index: i,
};
return opt;
});
this.channelsFetched = true;
this.context.propertyPane.refresh();
});
}
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
}),
PropertyPaneDropdown("videoChannel", {
label: strings.VideoChannelFieldLabel,
options: this.channels,
}),
PropertyPaneSlider("coverflowHeight", {
label: strings.CoverflowHeightFieldLabel,
min: 100,
max: 1000
}),
PropertyPaneSlider("coverflowWidth", {
label: strings.CoverflowWidthFieldLabel,
min: 100,
max: 1900
}),
PropertyPaneSlider("coverflowMargin", {
label: strings.CoverflowMarginFieldLabel,
min: 0,
max: 100
}),
PropertyPaneSlider("coverflowAnimationSpeed", {
label: strings.CoverflowAnimationSpeedFieldLabel,
min: 0.0,
max: 1.0,
step: 0.1
}),
PropertyPaneToggle("coverflowEnableScroll", {
label: strings.CoverflowEnableScrollFieldLabel,
}),
PropertyPaneSlider("coverflowStartPosition", {
label: strings.CoverflowStartPositionFieldLabel,
min: 0,
max: 25
}),
PropertyPaneSlider("imgWidth", {
label: strings.ImgWidthFieldLabel,
min: 100,
max: 1900
}),
PropertyPaneSlider("imgHeight", {
label: strings.ImgHeightFieldLabel,
min: 100,
max: 1000
}),
PropertyPaneSlider("iframeWidth", {
label: strings.IframeWidthFieldLabel,
min: 100,
max: 1900
}),
PropertyPaneSlider("iframeHeight", {
label: strings.IframeHeightFieldLabel,
min: 100,
max: 1000
}),
PropertyPaneSlider("playerWidth", {
label: strings.PlayerWidthFieldLabel,
min: 100,
max: 1900
}),
PropertyPaneSlider("playerHeight", {
label: strings.PlayerHeightFieldLabel,
min: 100,
max: 1000
}),
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,127 @@
import * as React from "react";
import { css } from "office-ui-fabric-react";
var Coverflow = require('reactjs-coverflow');
import { IVideoLibraryCoverFlowWebPartProps } from "../IVideoLibraryCoverFlowWebPartProps";
import { Video } from "../../O365VUtilities";
export interface IVideoLibraryCoverFlowProps extends IVideoLibraryCoverFlowWebPartProps {
}
export interface IVideoLibraryState {
ease: string;
width: number;
playerUrlTemplate: string;
videos: Array<Video>;
selectedVideo: number;
}
export default class VideoLibrary extends React.Component<IVideoLibraryCoverFlowProps, IVideoLibraryState> {
constructor(props: IVideoLibraryCoverFlowProps) {
super(props);
this.afterChange = this.afterChange.bind(this);
this.playVideo = this.playVideo.bind(this);
this.next = this.next.bind(this);
this.previous = this.previous.bind(this);
this.state = {
ease: "linear",
width: 400,
playerUrlTemplate: null,
videos: [],
selectedVideo: -1,
};
}
public componentWillMount(nextProps) {
// Load new data when the dataSource property changes.
this.props.o365Video.Initialize().then((settings) => {
debugger;
// this.state.playerUrlTemplate = settings.PlayerUrlTemplate; // this url does not work. You neeed the channel
this.state.playerUrlTemplate = settings.VideoPortalLayoutsUrl + "/VideoEmbedHost.aspx?chId={0}&vId={1}&width={2}&height={3}&autoPlay=true&showInfo=true";
if (this.props.videoChannel) {
this.props.o365Video.GetVideos(this.props.videoChannel).then((videos) => {
this.state.videos = videos;
this.setState(this.state);
});
}
});
}
public componentWillReceiveProps(nextProps: IVideoLibraryCoverFlowProps) {
if (nextProps.videoChannel) {
this.props.o365Video.GetVideos(nextProps.videoChannel).then((videos) => {
this.state.videos = videos;
this.setState(this.state);
});
}
}
public afterChange(slideNumber: number) {
this.state.selectedVideo = -1;
this.setState(this.state);
}
public playVideo(event) {
debugger;
this.state.selectedVideo = parseInt(event.target.dataset.videonumber);
this.setState(this.state);
}
public previous(e) {
e.preventDefault();
const cf = this.refs["coverflow"] as any;
cf.previous();
}
public next(e) {
e.preventDefault();
const cf = this.refs["coverflow"] as any;
cf.next();
}
public render(): JSX.Element {
debugger;
if (this.state.videos.length === 0) {
return (<div />);
}
// TODO: stop using the Iframe, use a <video> tah instead
return (
<div>
<div >
<Coverflow style={{ width: this.props.coverflowWidth + "px", height: this.props.coverflowHeight + "px" }} ref="coverflow"
margin={this.props.coverflowMargin + "px"}
startPosition={this.props.coverflowStartPosition}
enableScroll={this.props.coverflowEnableScroll}
animationSpeed={this.props.coverflowAnimationSpeed}>
{this.state.videos.map((v, i, a) => {
if (i === this.state.selectedVideo) {
const src = this.state.playerUrlTemplate
.replace("{1}", v.ID)
.replace("{0}", v.ChannelID)
.replace("{2}", this.props.playerWidth.toString())
.replace("{3}", this.props.playerHeight.toString());
return (<iframe src={src} style={{ height: this.props.iframeHeight + "px", width: this.props.iframeWidth + "px" }} />);
}
else {
return (
<figure title={v.Title}>
<img
className="reactjs-coverflow_cover"
src={v.ThumbnailUrl}
data-videonumber={i}
style={{ height: this.props.imgHeight + "px", width: this.props.imgWidth + "px" }}
onClick={this.playVideo} />
<figcaption>
{v.Description}</figcaption>
</figure>
);
}
})}
</Coverflow>
<div className="reactjs-coverflow_actions" data-radium="true" >
<button type="button" className="reactjs-coverflow_button" onClick={this.previous}>Previous</button>
<button type="button" className="reactjs-coverflow_button" onClick={this.next}>Next</button></div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,102 @@
.reactjs-coverflow_Coverflow {
position: relative;
background: rgba(0, 0, 0, 0.1);
margin: 0 auto;
padding: 0;
overflow: hidden; }
.coverflow__coverflow__2Wjhx {
position: relative;
width: 100%;
height: 100%;
margin: 0; }
.coverflow__stage__34J8j {
height: 100%;
width: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
margin: 0;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-perspective: 500px;
perspective: 500px; }
.reactjs-coverflow_Element {
display: block;
position: relative;
margin: 0;
padding: 0;
-webkit-box-flex: 0;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
cursor: pointer;
-webkit-transition: -webkit-transform 600ms ease;
transition: -webkit-transform 600ms ease;
transition: transform 600ms ease;
transition: transform 600ms ease, -webkit-transform 600ms ease;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
z-index: 9;
-ms-flex-item-align: center;
align-self: center;
-webkit-box-reflect: below 1px -webkit-linear-gradient(bottom, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.1) 20%, transparent 30%, transparent); }
.reactjs-coverflow_cover {
display: block;
width: 100%;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5); }
.coverflow__preloader__BEZga {
display: hidden; }
.coverflow__text {
position: absolute;
bottom: 0;
left: 0;
right: 0;
text-align: center;
font-size: .9em;
color: white;
padding: 5px;
overflow: hidden;
background: rgba(0, 0, 0, 0.6); }
.coverflow__actions {
position: absolute;
bottom: 30px;
left: 0;
right: 0;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center; }
.coverflow__actions .coverflow__button {
border: 1px solid #616161;
border-radius: 2px;
min-width: 120px;
padding: 5px;
margin: 2px;
background: #616161;
color: rgba(255, 255, 255, 0.9); }
.coverflow__actions__CkKK0 .coverflow__button__176dX:hover {
background: black;
color: white; }
@-webkit-keyframes coverflow__prevent__21uXf {
0% {
pointer-events: none; }
100% {
pointer-events: auto; } }
@keyframes coverflow__prevent__21uXf {
0% {
pointer-events: none; }
100% {
pointer-events: auto; } }

View File

@ -0,0 +1,26 @@
define([], function () {
return {
"VideoChannelFieldLabel": "O365 Video Channel to play",
"PropertyPaneDescription": "Description",
"CoverflowWidthFieldLabel": "Overall control width (in px)",
"CoverflowHeightFieldLabel": "Overall control height (in px)",
"CoverflowMarginFieldLabel": "Margin between slides (in px)",
"CoverflowAnimationSpeedFieldLabel": "Animation Speed when scrolling",
"CoverflowEnableScrollFieldLabel": "Enable mousewheel Scrolling",
"CoverflowStartPositionFieldLabel": "Starting frame ",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field",
"ImgWidthFieldLabel": "Static Image Width",
"ImgHeightFieldLabel": "Static Image Height",
"IframeWidthFieldLabel": "IFrame Width",
"IframeHeightFieldLabel": "IFrame Height",
"PlayerWidthFieldLabel": "Video Player Width",
"PlayerHeightFieldLabel": "Video Player Height",
}
});

View File

@ -0,0 +1,23 @@
declare interface IVideoLibraryCoverFlowStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
VideoChannelFieldLabel: string;
CoverflowWidthFieldLabel: string;
CoverflowHeightFieldLabel: string;
ImgWidthFieldLabel: string;
ImgHeightFieldLabel: string;
IframeWidthFieldLabel: string;
IframeHeightFieldLabel: string;
PlayerWidthFieldLabel: string;
PlayerHeightFieldLabel: string;
CoverflowMarginFieldLabel: string;
CoverflowAnimationSpeedFieldLabel: string;
CoverflowStartPositionFieldLabel: string;
CoverflowEnableScrollFieldLabel: string;
}
declare module 'videoLibraryCoverFlowStrings' {
const strings: IVideoLibraryCoverFlowStrings;
export = strings;
}

View File

@ -0,0 +1,7 @@
/// <reference types="mocha" />
import { assert } from 'chai';
describe('VideoLibraryCpverFlowWebPart', () => {
it('should do something', () => {
assert.ok(true);
});
});

View File

@ -0,0 +1,9 @@
import {O365Video } from "../O365VUtilities";
export interface IVideoLibraryReact3DCarouselWebPartProps {
description: string;
videoChannel: string;
o365Video: O365Video;
layout: string;
duration: number;
panels: number;
}

View File

@ -0,0 +1,57 @@
.videoLibrary {
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
.react-3d-carousel {
width: 400px;
height: 300px;
position: relative;
perspective: 1000px;
}
.react-3d-carousel .carousel {
width: 100%;
height: 100%;
position: absolute;
transform-style: preserve-3d;
}
.react-3d-carousel .carousel figure {
display: block;
position: absolute;
width: 400px;
height: 300px;
left: 10px;
top: 10px;
border: 2px solid black;
line-height: 116px;
font-size: 80px;
font-weight: bold;
color: white;
text-align: center;
}
.react-3d-carousel .carousel figure {
background: hsla( 160, 100%, 50%, 0.8);
}
}
.row {
padding: 20px;
}
.listItem {
max-width: 715px;
margin: 5px auto 5px auto;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.button {
text-decoration: none;
}
}

View File

@ -0,0 +1,24 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"id": "2f1538d9-3b9a-4033-8f3b-2cdf2ec3c64e",
"alias": "VideoLibraryReact3DCarouselWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"manifestVersion": 2,
"preconfiguredEntries": [{
"groupId": "2f1538d9-3b9a-4033-8f3b-2cdf2ec3c64e",
"group": { "default": "Under Development" },
"title": { "default": "React-3d-carousel Video Library" },
"description": {
"default": "VideoLibrary implemented using react-3d-carousel -- Shows videos from an O365 Video Channel"
},
"officeFabricIconFontName": "Ribbon",
"properties": {
"description": "React-3d-carousel Video Library",
"layout":"prism",
"duration":199
}
}]
}

View File

@ -0,0 +1,114 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration ,
PropertyPaneTextField,
PropertyPaneDropdown, IPropertyPaneDropdownOption,
PropertyPaneSlider
} from '@microsoft/sp-webpart-base';
import { O365Video } from "../O365VUtilities";
import * as strings from 'videoLibraryStrings';
import VideoLibrary, { IVideoLibraryProps } from './components/VideoLibraryReact3DCarousel';
import { IVideoLibraryReact3DCarouselWebPartProps } from './IVideoLibraryReact3DCarouselWebPartProps';
require("./carousel.css"); // needed to copy this to lib folder to get it displayed
export default class VideoLibraryWebPart extends BaseClientSideWebPart<IVideoLibraryReact3DCarouselWebPartProps> {
private O365Video: O365Video;
private channels: Array<IPropertyPaneDropdownOption>;
private channelsFetched: boolean;
public onInit<T>(): Promise<T> {
this.O365Video = new O365Video(this.context);
return Promise.resolve(null);
}
public render(): void {
const props: IVideoLibraryProps = {
description: this.properties.description,
videoChannel: this.properties.videoChannel,
o365Video: this.O365Video,
layout: this.properties.layout,
duration:this.properties.duration,
panels:this.properties.panels
};
const element: React.ReactElement<IVideoLibraryProps> = React.createElement(VideoLibrary, props);
ReactDom.render(element, this.domElement);
}
public getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
debugger;
if (!this.O365Video.isInitialized) {
this.O365Video.Initialize().then(x => {
this.O365Video.getChannels().then(channels => {
this.context.propertyPane.refresh();
});
});
}
if (!this.channelsFetched && this.O365Video.isInitialized) {
this.O365Video.getChannels().then(channels => {
this.channels = channels.map((c, i, a) => {
let opt: IPropertyPaneDropdownOption = {
key: c.Id,
text: c.Title,
index: i,
};
return opt;
});
this.channelsFetched = true;
this.context.propertyPane.refresh();
});
}
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField("description", {
label: strings.DescriptionFieldLabel
}),
PropertyPaneDropdown("videoChannel", {
label: strings.VideoChannelFieldLabel,
options: this.channels,
}),
PropertyPaneDropdown("layout", {
label: strings.LayoutFieldLabel,
options: [
{ key: "prism", text: "prism" },
{ key: "clssic", text: "classic" }
]
}),
PropertyPaneSlider("duration", {
label: strings.DurationFieldLabel,
min: 1,
max: 1000
}),
PropertyPaneSlider("panels", {
label: strings.PanelsFieldLabel,
min: 1,
max: 5
}),
// PropertyPaneTextField("listName", channelDropDownProps),
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,73 @@
.react-3d-carousel {
width: 400px;
height: 300px;
position: relative;
perspective: 1000px;
}
.react-3d-carousel .carousel {
width: 100%;
height: 100%;
position: absolute;
transform-style: preserve-3d;
}
.react-3d-carousel .carousel figure {
display: block;
position: absolute;
width: 400px;
height: 300px;
left: 10px;
top: 10px;
border: 2px solid black;
line-height: 116px;
font-size: 80px;
font-weight: bold;
color: white;
text-align: center;
}
.react-3d-carousel .carousel figure {
background: hsla( 160, 100%, 50%, 0.8);
}
.react-3d-carousel .prev {
position: absolute;
height: 100%;
width: 15%;
top: 0px;
left: 0px;
}
.react-3d-carousel .prev:before {
content: url("http://s3.postimg.org/o6xpvkidb/chevron_left_white.png");
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 43px;
height: 49px;
margin: auto;
}
.react-3d-carousel .next {
position: absolute;
height: 100%;
width: 15%;
top: 0px;
right: -5px;
}
.react-3d-carousel .next:before
{
content: url("http://s2.postimg.org/l0n6eaoad/chevron_right_white.png");
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 43px;
height: 49px;
margin: auto;
}

View File

@ -0,0 +1,92 @@
import * as React from 'react';
import { css } from 'office-ui-fabric-react';
var Carousel = require('react-3d-carousel');
var Ease = require('ease-functions');
import styles from '../VideoLibraryReact3DCarousel.module.scss';
import { IVideoLibraryReact3DCarouselWebPartProps } from '../IVideoLibraryReact3DCarouselWebPartProps';
import { Video, } from "../../O365VUtilities";
export interface IVideoLibraryProps extends IVideoLibraryReact3DCarouselWebPartProps {
}
export interface IVideoLibraryState {
images: Array<string>;
ease: string;
width: number;
videos: Array<Video>;
}
export default class VideoLibrary extends React.Component<IVideoLibraryProps, IVideoLibraryState> {
constructor(props: IVideoLibraryProps) {
super(props);
this.onEase = this.onEase.bind(this);
this.state = {
ease: "linear",
width: 400,
images: [],
videos: []
};
}
public componentWillMount(nextProps) {
// Load new data when the dataSource property changes.
this.props.o365Video.Initialize().then((settings) => {
if (this.props.videoChannel) {
this.props.o365Video.GetVideos(this.props.videoChannel).then((videos) => {
this.state.videos = videos;
this.state.images = videos.map((v, i, a) => {
return v.ThumbnailUrl;
});
this.setState(this.state);
});
}
});
}
public onEase(event) {
this.state.ease = event.target.value;
this.setState(this.state);
}
public render(): JSX.Element {
const easeList = Object.keys(Ease).map(function (d) {
return (<option key={d} value={d}>{d}</option>);
});
if (this.state.images.length === 0) {
return (<div />);
}
return (
<div className={styles.videoLibrary}>
<div className={styles.container} id="content">
<Carousel width={this.state.width}
panels={this.props.panels}
images={this.state.images}
ease={this.state.ease}
duration={this.props.duration}
layout={this.props.layout} />
<table>
<tr>
<td>
Ease
</td>
<td>
<select onChange={this.onEase} value={this.state.ease}>
{easeList}
</select>
</td>
</tr>
</table>
</div>
</div>
);
}
}

View File

@ -0,0 +1,13 @@
define([], function () {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field",
"VideoChannelFieldLabel": "Video Channel",
"ListNameFieldLabel": "List Name",
"LayoutFieldLabel": "Layout",
"DurationFieldLabel": "Transition Time",
"PanelsFieldLabel": "Panels",
"EaseFieldLabel": "Ease"
}
});

View File

@ -0,0 +1,17 @@
declare interface IVideoLibraryReact3DCarouselStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
VideoChannelFieldLabel: string;
ListNameFieldLabel: string;
LayoutFieldLabel: string;
DurationFieldLabel: string;
PanelsFieldLabel: string;
EaseFieldLabel: string;
}
declare module "videoLibraryReact3DCarouselStrings" {
const strings: IVideoLibraryStrings;
export = strings;
}

View File

@ -0,0 +1,8 @@
/// <reference types="mocha" />
import { assert } from 'chai';
describe('VideoLibraryReact3DCarouselWebPart', () => {
it('should do something', () => {
assert.ok(true);
});
});

View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"types": [ "webpack-env" ]
}
}

View File

@ -0,0 +1,5 @@
{
"globalDependencies": {
"react-slick": "registry:dt/react-slick#0.0.0+20161121205806"
}
}

View File

@ -0,0 +1,13 @@
// Type definitions for webpack in Microsoft ODSP projects
// Project: ODSP-WEBPACK
/*
* This definition of webpack require overrides all other definitions of require in our toolchain
* Make sure all other definitions of require are commented out e.g. in node.d.ts
*/
declare var require: {
(path: string): any;
(paths: string[], callback: (...modules: any[]) => void): void;
resolve: (id: string) => string;
ensure: (paths: string[], callback: (require: <T>(path: string) => T) => void, path: string) => void;
};

View File

@ -0,0 +1,10 @@
// Type definitions for Microsoft ODSP projects
// Project: ODSP
/// <reference path="odsp-webpack.d.ts" />
/* Global definition for DEBUG builds */
declare const DEBUG: boolean;
/* Global definition for UNIT_TEST builds */
declare const UNIT_TEST: boolean;

View File

@ -0,0 +1,15 @@
// Type definitions for assertion-error 1.0.0
// Project: https://github.com/chaijs/assertion-error
// Definitions by: Bart van der Schoor <https://github.com/Bartvds>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
declare module 'assertion-error' {
class AssertionError implements Error {
constructor(message: string, props?: any, ssf?: Function);
name: string;
message: string;
showDiff: boolean;
stack: string;
}
export = AssertionError;
}

View File

@ -0,0 +1,631 @@
// Type definitions for Knockout v3.2.0
// Project: http://knockoutjs.com
// Definitions by: Boris Yankov <https://github.com/borisyankov/>, Igor Oleinikov <https://github.com/Igorbek/>, Clément Bourgeois <https://github.com/moonpyk/>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
interface KnockoutSubscribableFunctions<T> {
[key: string]: KnockoutBindingHandler;
notifySubscribers(valueToWrite?: T, event?: string): void;
}
interface KnockoutComputedFunctions<T> {
[key: string]: KnockoutBindingHandler;
}
interface KnockoutObservableFunctions<T> {
[key: string]: KnockoutBindingHandler;
equalityComparer(a: any, b: any): boolean;
}
interface KnockoutObservableArrayFunctions<T> {
// General Array functions
indexOf(searchElement: T, fromIndex?: number): number;
slice(start: number, end?: number): T[];
splice(start: number): T[];
splice(start: number, deleteCount: number, ...items: T[]): T[];
pop(): T;
push(...items: T[]): void;
shift(): T;
unshift(...items: T[]): number;
reverse(): KnockoutObservableArray<T>;
sort(): KnockoutObservableArray<T>;
sort(compareFunction: (left: T, right: T) => number): KnockoutObservableArray<T>;
// Ko specific
[key: string]: KnockoutBindingHandler;
replace(oldItem: T, newItem: T): void;
remove(item: T): T[];
remove(removeFunction: (item: T) => boolean): T[];
removeAll(items: T[]): T[];
removeAll(): T[];
destroy(item: T): void;
destroy(destroyFunction: (item: T) => boolean): void;
destroyAll(items: T[]): void;
destroyAll(): void;
}
interface KnockoutSubscribableStatic {
fn: KnockoutSubscribableFunctions<any>;
new <T>(): KnockoutSubscribable<T>;
}
interface KnockoutSubscription {
dispose(): void;
}
interface KnockoutSubscribable<T> extends KnockoutSubscribableFunctions<T> {
subscribe(callback: (newValue: T) => void, target?: any, event?: string): KnockoutSubscription;
subscribe<TEvent>(callback: (newValue: TEvent) => void, target: any, event: string): KnockoutSubscription;
extend(requestedExtenders: { [key: string]: any; }): KnockoutSubscribable<T>;
getSubscriptionsCount(): number;
}
interface KnockoutComputedStatic {
fn: KnockoutComputedFunctions<any>;
<T>(): KnockoutComputed<T>;
<T>(func: () => T, context?: any, options?: any): KnockoutComputed<T>;
<T>(def: KnockoutComputedDefine<T>, context?: any): KnockoutComputed<T>;
}
interface KnockoutComputed<T> extends KnockoutObservable<T>, KnockoutComputedFunctions<T> {
fn: KnockoutComputedFunctions<any>;
dispose(): void;
isActive(): boolean;
getDependenciesCount(): number;
extend(requestedExtenders: { [key: string]: any; }): KnockoutComputed<T>;
}
interface KnockoutObservableArrayStatic {
fn: KnockoutObservableArrayFunctions<any>;
<T>(value?: T[]): KnockoutObservableArray<T>;
}
interface KnockoutObservableArray<T> extends KnockoutObservable<T[]>, KnockoutObservableArrayFunctions<T> {
extend(requestedExtenders: { [key: string]: any; }): KnockoutObservableArray<T>;
}
interface KnockoutObservableStatic {
fn: KnockoutObservableFunctions<any>;
<T>(value?: T): KnockoutObservable<T>;
}
interface KnockoutObservable<T> extends KnockoutSubscribable<T>, KnockoutObservableFunctions<T> {
(): T;
(value: T): void;
peek(): T;
valueHasMutated?:{(): void;};
valueWillMutate?:{(): void;};
extend(requestedExtenders: { [key: string]: any; }): KnockoutObservable<T>;
}
interface KnockoutComputedDefine<T> {
read(): T;
write? (value: T): void;
disposeWhenNodeIsRemoved?: Node;
disposeWhen? (): boolean;
owner?: any;
deferEvaluation?: boolean;
pure?: boolean;
}
interface KnockoutBindingContext {
$parent: any;
$parents: any[];
$root: any;
$data: any;
$rawData: any | KnockoutObservable<any>;
$index?: KnockoutObservable<number>;
$parentContext?: KnockoutBindingContext;
$component: any;
$componentTemplateNodes: Node[];
extend(properties: any): any;
createChildContext(dataItemOrAccessor: any, dataItemAlias?: any, extendCallback?: Function): any;
}
interface KnockoutAllBindingsAccessor {
(): any;
get(name: string): any;
has(name: string): boolean;
}
interface KnockoutBindingHandler {
after?: Array<string>;
init?: (element: any, valueAccessor: () => any, allBindingsAccessor?: KnockoutAllBindingsAccessor, viewModel?: any, bindingContext?: KnockoutBindingContext) => void | { controlsDescendantBindings: boolean; };
update?: (element: any, valueAccessor: () => any, allBindingsAccessor?: KnockoutAllBindingsAccessor, viewModel?: any, bindingContext?: KnockoutBindingContext) => void;
options?: any;
preprocess?: (value: string, name: string, addBindingCallback?: (name: string, value: string) => void) => string;
}
interface KnockoutBindingHandlers {
[bindingHandler: string]: KnockoutBindingHandler;
// Controlling text and appearance
visible: KnockoutBindingHandler;
text: KnockoutBindingHandler;
html: KnockoutBindingHandler;
css: KnockoutBindingHandler;
style: KnockoutBindingHandler;
attr: KnockoutBindingHandler;
// Control Flow
foreach: KnockoutBindingHandler;
if: KnockoutBindingHandler;
ifnot: KnockoutBindingHandler;
with: KnockoutBindingHandler;
// Working with form fields
click: KnockoutBindingHandler;
event: KnockoutBindingHandler;
submit: KnockoutBindingHandler;
enable: KnockoutBindingHandler;
disable: KnockoutBindingHandler;
value: KnockoutBindingHandler;
textInput: KnockoutBindingHandler;
hasfocus: KnockoutBindingHandler;
checked: KnockoutBindingHandler;
options: KnockoutBindingHandler;
selectedOptions: KnockoutBindingHandler;
uniqueName: KnockoutBindingHandler;
// Rendering templates
template: KnockoutBindingHandler;
// Components (new for v3.2)
component: KnockoutBindingHandler;
}
interface KnockoutMemoization {
memoize(callback: () => string): string;
unmemoize(memoId: string, callbackParams: any[]): boolean;
unmemoizeDomNodeAndDescendants(domNode: any, extraCallbackParamsArray: any[]): boolean;
parseMemoText(memoText: string): string;
}
interface KnockoutVirtualElement {}
interface KnockoutVirtualElements {
allowedBindings: { [bindingName: string]: boolean; };
emptyNode(node: KnockoutVirtualElement ): void;
firstChild(node: KnockoutVirtualElement ): KnockoutVirtualElement;
insertAfter( container: KnockoutVirtualElement, nodeToInsert: Node, insertAfter: Node ): void;
nextSibling(node: KnockoutVirtualElement): Node;
prepend(node: KnockoutVirtualElement, toInsert: Node ): void;
setDomNodeChildren(node: KnockoutVirtualElement, newChildren: { length: number;[index: number]: Node; } ): void;
childNodes(node: KnockoutVirtualElement ): Node[];
}
interface KnockoutExtenders {
throttle(target: any, timeout: number): KnockoutComputed<any>;
notify(target: any, notifyWhen: string): any;
rateLimit(target: any, timeout: number): any;
rateLimit(target: any, options: { timeout: number; method?: string; }): any;
trackArrayChanges(target: any): any;
}
//
// NOTE TO MAINTAINERS AND CONTRIBUTORS : pay attention to only include symbols that are
// publicly exported in the minified version of ko, without that you can give the false
// impression that some functions will be available in production builds.
//
interface KnockoutUtils {
//////////////////////////////////
// utils.domData.js
//////////////////////////////////
domData: {
get (node: Element, key: string): any;
set (node: Element, key: string, value: any): void;
getAll(node: Element, createIfNotFound: boolean): any;
clear(node: Element): boolean;
};
//////////////////////////////////
// utils.domNodeDisposal.js
//////////////////////////////////
domNodeDisposal: {
addDisposeCallback(node: Element, callback: Function): void;
removeDisposeCallback(node: Element, callback: Function): void;
cleanNode(node: Node): Element;
removeNode(node: Node): void;
};
addOrRemoveItem<T>(array: T[] | KnockoutObservable<T>, value: T, included: T): void;
arrayFilter<T>(array: T[], predicate: (item: T) => boolean): T[];
arrayFirst<T>(array: T[], predicate: (item: T) => boolean, predicateOwner?: any): T;
arrayForEach<T>(array: T[], action: (item: T, index: number) => void): void;
arrayGetDistinctValues<T>(array: T[]): T[];
arrayIndexOf<T>(array: T[], item: T): number;
arrayMap<T, U>(array: T[], mapping: (item: T) => U): U[];
arrayPushAll<T>(array: T[] | KnockoutObservableArray<T>, valuesToPush: T[]): T[];
arrayRemoveItem(array: any[], itemToRemove: any): void;
compareArrays<T>(a: T[], b: T[]): Array<KnockoutArrayChange<T>>;
extend(target: Object, source: Object): Object;
fieldsIncludedWithJsonPost: any[];
getFormFields(form: any, fieldName: string): any[];
objectForEach(obj: any, action: (key: any, value: any) => void): void;
parseHtmlFragment(html: string): any[];
parseJson(jsonString: string): any;
postJson(urlOrForm: any, data: any, options: any): void;
peekObservable<T>(value: KnockoutObservable<T>): T;
range(min: any, max: any): any;
registerEventHandler(element: any, eventType: any, handler: Function): void;
setHtml(node: Element, html: () => string): void;
setHtml(node: Element, html: string): void;
setTextContent(element: any, textContent: string | KnockoutObservable<string>): void;
stringifyJson(data: any, replacer?: Function, space?: string): string;
toggleDomNodeCssClass(node: any, className: string, shouldHaveClass: boolean): void;
triggerEvent(element: any, eventType: any): void;
unwrapObservable<T>(value: KnockoutObservable<T> | T): T;
// NOT PART OF THE MINIFIED API SURFACE (ONLY IN knockout-{version}.debug.js) https://github.com/SteveSanderson/knockout/issues/670
// forceRefresh(node: any): void;
// ieVersion: number;
// isIe6: boolean;
// isIe7: boolean;
// jQueryHtmlParse(html: string): any[];
// makeArray(arrayLikeObject: any): any[];
// moveCleanedNodesToContainerElement(nodes: any[]): HTMLElement;
// replaceDomNodes(nodeToReplaceOrNodeArray: any, newNodesArray: any[]): void;
// setDomNodeChildren(domNode: any, childNodes: any[]): void;
// setElementName(element: any, name: string): void;
// setOptionNodeSelectionState(optionNode: any, isSelected: boolean): void;
// simpleHtmlParse(html: string): any[];
// stringStartsWith(str: string, startsWith: string): boolean;
// stringTokenize(str: string, delimiter: string): string[];
// stringTrim(str: string): string;
// tagNameLower(element: any): string;
}
interface KnockoutArrayChange<T> {
status: string;
value: T;
index: number;
moved?: number;
}
//////////////////////////////////
// templateSources.js
//////////////////////////////////
interface KnockoutTemplateSourcesDomElement {
text(): any;
text(value: any): void;
data(key: string): any;
data(key: string, value: any): any;
}
interface KnockoutTemplateAnonymous extends KnockoutTemplateSourcesDomElement {
nodes(): any;
nodes(value: any): void;
}
interface KnockoutTemplateSources {
domElement: {
prototype: KnockoutTemplateSourcesDomElement
new (element: Element): KnockoutTemplateSourcesDomElement
};
anonymousTemplate: {
prototype: KnockoutTemplateAnonymous;
new (element: Element): KnockoutTemplateAnonymous;
};
}
//////////////////////////////////
// nativeTemplateEngine.js
//////////////////////////////////
interface KnockoutNativeTemplateEngine {
renderTemplateSource(templateSource: Object, bindingContext?: KnockoutBindingContext, options?: Object): any[];
}
//////////////////////////////////
// templateEngine.js
//////////////////////////////////
interface KnockoutTemplateEngine extends KnockoutNativeTemplateEngine {
createJavaScriptEvaluatorBlock(script: string): string;
makeTemplateSource(template: any, templateDocument?: Document): any;
renderTemplate(template: any, bindingContext: KnockoutBindingContext, options: Object, templateDocument: Document): any;
isTemplateRewritten(template: any, templateDocument: Document): boolean;
rewriteTemplate(template: any, rewriterCallback: Function, templateDocument: Document): void;
}
/////////////////////////////////
interface KnockoutStatic {
utils: KnockoutUtils;
memoization: KnockoutMemoization;
bindingHandlers: KnockoutBindingHandlers;
getBindingHandler(handler: string): KnockoutBindingHandler;
virtualElements: KnockoutVirtualElements;
extenders: KnockoutExtenders;
applyBindings(viewModelOrBindingContext?: any, rootNode?: any): void;
applyBindingsToDescendants(viewModelOrBindingContext: any, rootNode: any): void;
applyBindingAccessorsToNode(node: Node, bindings: (bindingContext: KnockoutBindingContext, node: Node) => {}, bindingContext: KnockoutBindingContext): void;
applyBindingAccessorsToNode(node: Node, bindings: {}, bindingContext: KnockoutBindingContext): void;
applyBindingAccessorsToNode(node: Node, bindings: (bindingContext: KnockoutBindingContext, node: Node) => {}, viewModel: any): void;
applyBindingAccessorsToNode(node: Node, bindings: {}, viewModel: any): void;
applyBindingsToNode(node: Node, bindings: any, viewModelOrBindingContext?: any): any;
subscribable: KnockoutSubscribableStatic;
observable: KnockoutObservableStatic;
computed: KnockoutComputedStatic;
pureComputed<T>(evaluatorFunction: () => T, context?: any): KnockoutComputed<T>;
pureComputed<T>(options: KnockoutComputedDefine<T>, context?: any): KnockoutComputed<T>;
observableArray: KnockoutObservableArrayStatic;
contextFor(node: any): any;
isSubscribable(instance: any): boolean;
toJSON(viewModel: any, replacer?: Function, space?: any): string;
toJS(viewModel: any): any;
isObservable(instance: any): boolean;
isWriteableObservable(instance: any): boolean;
isComputed(instance: any): boolean;
dataFor(node: any): any;
removeNode(node: Element): void;
cleanNode(node: Element): Element;
renderTemplate(template: Function, viewModel: any, options?: any, target?: any, renderMode?: any): any;
renderTemplate(template: string, viewModel: any, options?: any, target?: any, renderMode?: any): any;
unwrap<T>(value: KnockoutObservable<T> | T): T;
computedContext: KnockoutComputedContext;
//////////////////////////////////
// templateSources.js
//////////////////////////////////
templateSources: KnockoutTemplateSources;
//////////////////////////////////
// templateEngine.js
//////////////////////////////////
templateEngine: {
prototype: KnockoutTemplateEngine;
new (): KnockoutTemplateEngine;
};
//////////////////////////////////
// templateRewriting.js
//////////////////////////////////
templateRewriting: {
ensureTemplateIsRewritten(template: Node, templateEngine: KnockoutTemplateEngine, templateDocument: Document): any;
ensureTemplateIsRewritten(template: string, templateEngine: KnockoutTemplateEngine, templateDocument: Document): any;
memoizeBindingAttributeSyntax(htmlString: string, templateEngine: KnockoutTemplateEngine): any;
applyMemoizedBindingsToNextSibling(bindings: any, nodeName: string): string;
};
//////////////////////////////////
// nativeTemplateEngine.js
//////////////////////////////////
nativeTemplateEngine: {
prototype: KnockoutNativeTemplateEngine;
new (): KnockoutNativeTemplateEngine;
instance: KnockoutNativeTemplateEngine;
};
//////////////////////////////////
// jqueryTmplTemplateEngine.js
//////////////////////////////////
jqueryTmplTemplateEngine: {
prototype: KnockoutTemplateEngine;
renderTemplateSource(templateSource: Object, bindingContext: KnockoutBindingContext, options: Object): Node[];
createJavaScriptEvaluatorBlock(script: string): string;
addTemplate(templateName: string, templateMarkup: string): void;
};
//////////////////////////////////
// templating.js
//////////////////////////////////
setTemplateEngine(templateEngine: KnockoutNativeTemplateEngine): void;
renderTemplate(template: Function, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
renderTemplate(template: any, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
renderTemplate(template: Function, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
renderTemplate(template: any, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
renderTemplate(template: Function, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
renderTemplate(template: any, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
renderTemplate(template: Function, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
renderTemplate(template: any, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
renderTemplateForEach(template: Function, arrayOrObservableArray: any[], options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
renderTemplateForEach(template: any, arrayOrObservableArray: any[], options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
renderTemplateForEach(template: Function, arrayOrObservableArray: KnockoutObservable<any>, options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
renderTemplateForEach(template: any, arrayOrObservableArray: KnockoutObservable<any>, options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
expressionRewriting: {
bindingRewriteValidators: any;
parseObjectLiteral: { (objectLiteralString: string): any[] }
};
/////////////////////////////////
bindingProvider: {
instance: KnockoutBindingProvider;
new (): KnockoutBindingProvider;
}
/////////////////////////////////
// selectExtensions.js
/////////////////////////////////
selectExtensions: {
readValue(element: HTMLElement): any;
writeValue(element: HTMLElement, value: any): void;
};
components: KnockoutComponents;
}
interface KnockoutBindingProvider {
nodeHasBindings(node: Node): boolean;
getBindings(node: Node, bindingContext: KnockoutBindingContext): {};
getBindingAccessors?(node: Node, bindingContext: KnockoutBindingContext): { [key: string]: string; };
}
interface KnockoutComputedContext {
getDependenciesCount(): number;
isInitial: () => boolean;
isSleeping: boolean;
}
//
// refactored types into a namespace to reduce global pollution
// and used Union Types to simplify overloads (requires TypeScript 1.4)
//
declare module KnockoutComponentTypes {
interface Config {
viewModel?: ViewModelFunction | ViewModelSharedInstance | ViewModelFactoryFunction | AMDModule;
template: string | Node[]| DocumentFragment | TemplateElement | AMDModule;
synchronous?: boolean;
}
interface ComponentConfig {
viewModel?: ViewModelFunction | ViewModelSharedInstance | ViewModelFactoryFunction | AMDModule;
template: any;
createViewModel?: any;
}
interface EmptyConfig {
}
// common AMD type
interface AMDModule {
require: string;
}
// viewmodel types
interface ViewModelFunction {
(params?: any): any;
}
interface ViewModelSharedInstance {
instance: any;
}
interface ViewModelFactoryFunction {
createViewModel: (params?: any, componentInfo?: ComponentInfo) => any;
}
interface ComponentInfo {
element: Node;
templateNodes: Node[];
}
interface TemplateElement {
element: string | Node;
}
interface Loader {
getConfig? (componentName: string, callback: (result: ComponentConfig) => void): void;
loadComponent? (componentName: string, config: ComponentConfig, callback: (result: Definition) => void): void;
loadTemplate? (componentName: string, templateConfig: any, callback: (result: Node[]) => void): void;
loadViewModel? (componentName: string, viewModelConfig: any, callback: (result: any) => void): void;
suppressLoaderExceptions?: boolean;
}
interface Definition {
template: Node[];
createViewModel? (params: any, options: { element: Node; }): any;
}
}
interface KnockoutComponents {
// overloads for register method:
register(componentName: string, config: KnockoutComponentTypes.Config | KnockoutComponentTypes.EmptyConfig): void;
isRegistered(componentName: string): boolean;
unregister(componentName: string): void;
get(componentName: string, callback: (definition: KnockoutComponentTypes.Definition) => void): void;
clearCachedDefinition(componentName: string): void
defaultLoader: KnockoutComponentTypes.Loader;
loaders: KnockoutComponentTypes.Loader[];
getComponentNameForNode(node: Node): string;
}
declare var ko: KnockoutStatic;
declare module "knockout" {
export = ko;
}

View File

@ -0,0 +1,5 @@
/// <reference path="@ms/odsp.d.ts" />
/// <reference path="@ms/odsp-webpack.d.ts" />
/// <reference path="assertion-error/assertion-error.d.ts" />
/// <reference path="knockout/knockout.d.ts" />

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Gulp" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{841958c7-1d50-408b-9df4-feac7165d4a0}</ProjectGuid>
<ProjectHome />
<ProjectView>ProjectFiles</ProjectView>
<StartupFile>node_modules\gulp\bin\gulp.js</StartupFile>
<WorkingDirectory>.</WorkingDirectory>
<OutputPath>.</OutputPath>
<ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
<TypeScriptSourceMap>true</TypeScriptSourceMap>
<TypeScriptModuleKind>CommonJS</TypeScriptModuleKind>
<EnableTypeScript>false</EnableTypeScript>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ScriptArguments>serve</ScriptArguments>
<StartWebBrowser>True</StartWebBrowser>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'" />
<PropertyGroup Condition="'$(Configuration)' == 'Release'" />
<Target Name="Gulp">
<Message Text="Running gulp2vs.js" Importance="normal" />
<Exec Command="node.exe &quot;$(MSBuildThisFileDirectory)\node_modules\@microsoft\npmx-lib\lib\gulp2vs.js&quot;" />
</Target>
<ItemGroup>
<Content Include="*.js" />
<Content Include="*.json" />
<Content Include="*.md" />
<Content Include="config\**\*.json" />
<Content Include="docs\*.md" />
<Content Include="sharepoint\feature_xml\**\*.*" />
<Content Include="src\**\*.html" />
<Content Include="src\**\*.js" />
<Content Include="src\**\*.json" />
<Content Include="src\**\*.less" />
<Content Include="src\**\*.resx" />
<Content Include="src\**\*.scss" />
<Content Include="src\**\*.ts" />
<Content Include="src\**\*.tsx" />
<Content Include="typings\**\*.ts" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<!--Do not delete the following Import Project. While this appears to do nothing it is a marker for setting TypeScript properties before our import that depends on them.-->
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="False" />
<Import Project="$(VSToolsPath)\Node.js Tools\Microsoft.NodejsTools.targets" />
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>False</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>0</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:48022/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>True</UseCustomServer>
<CustomServerUrl>http://localhost:1337</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}" User="">
<WebProjectProperties>
<StartPageUrl>
</StartPageUrl>
<StartAction>CurrentPage</StartAction>
<AspNetDebugging>True</AspNetDebugging>
<SilverlightDebugging>False</SilverlightDebugging>
<NativeDebugging>False</NativeDebugging>
<SQLDebugging>False</SQLDebugging>
<ExternalProgram>
</ExternalProgram>
<StartExternalURL>
</StartExternalURL>
<StartCmdLineArguments>
</StartCmdLineArguments>
<StartWorkingDirectory>
</StartWorkingDirectory>
<EnableENC>False</EnableENC>
<AlwaysStartWebServerOnDebug>False</AlwaysStartWebServerOnDebug>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>