├── docs ├── library.jsx ├── brandai.json ├── package.json ├── webpack.config.js ├── gulpfile.js └── README.md ├── .gitignore ├── examples ├── address-book │ ├── profile.png │ ├── README.md │ ├── package.json │ ├── webpack.config.js │ ├── OtherPage.jsx │ ├── ProfilePicture.jsx │ ├── index.html │ ├── main.css │ ├── index.jsx │ ├── FakeData.js │ └── PeoplePage.jsx ├── showcase │ ├── examples │ │ ├── BackButton.jsx │ │ ├── Tooltip.jsx │ │ ├── Flyout.jsx │ │ ├── DatePicker.jsx │ │ ├── Pivot.jsx │ │ ├── FlipView.jsx │ │ ├── ToggleSwitch.jsx │ │ ├── ItemContainer.jsx │ │ ├── TimePicker.jsx │ │ ├── Rating.jsx │ │ ├── ContentDialog.jsx │ │ ├── ListView.jsx │ │ ├── Hub.jsx │ │ ├── SplitView.jsx │ │ ├── Menu.jsx │ │ ├── AutoSuggestBox.jsx │ │ ├── SemanticZoom.jsx │ │ ├── AppBar.jsx │ │ └── ToolBar.jsx │ ├── README.md │ ├── package.json │ ├── webpack.config.js │ ├── index.html │ └── index.jsx └── movies │ ├── package.json │ ├── README.md │ ├── webpack.config.js │ ├── Score.jsx │ ├── index.html │ ├── DetailPage.jsx │ ├── SearchPage.jsx │ ├── index.jsx │ └── FakeData.js ├── bower.json ├── React.WinJS.nuspec ├── package.json ├── LICENSE.txt ├── tasks └── check-bom.js ├── README.md ├── Gruntfile.js └── react-winjs.js /docs/library.jsx: -------------------------------------------------------------------------------- 1 | module.exports = require('../react-winjs.js'); 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .DS_Store 4 | browser-bundle.js 5 | npm-debug.log 6 | -------------------------------------------------------------------------------- /examples/address-book/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winjs/react-winjs/HEAD/examples/address-book/profile.png -------------------------------------------------------------------------------- /docs/brandai.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-winjs-brandai", 3 | "libraryPath": "../", 4 | "dist": "dist", 5 | "libraryObjectName": "ReactWinJS", 6 | "reactVersion": "15.0.1", 7 | "dynamicExtraction": true, 8 | "fileOrder": ["base.min.js", "ui.min.js"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/showcase/examples/BackButton.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | render: function () { 8 | return ( 9 | 10 | ); 11 | } 12 | }); -------------------------------------------------------------------------------- /examples/showcase/README.md: -------------------------------------------------------------------------------- 1 | # react-winjs Showcase 2 | 3 | This example demonstrates each of the `react-winjs` components. See it in action [here](http://winjs.github.io/react-winjs/examples/showcase/index.html). 4 | 5 | ## Usage 6 | 7 | ``` 8 | npm install 9 | npm start 10 | ``` 11 | 12 | Open `index.html` in your browser. -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-winjs-brandai-config", 3 | "version": "0.0.0", 4 | "description": "Configuration for brand.ai component documentation", 5 | "author": "", 6 | "license": "", 7 | "dependencies": {}, 8 | "devDependencies": { 9 | "gulp": "^3.9.0", 10 | "gulp-util": "^3.0.6", 11 | "jsx-loader": "^0.13.2", 12 | "webpack": "^1.12.2", 13 | "winjs": "^4.4.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/movies/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-winjs-movies", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack --progress --colors --watch", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "", 12 | "devDependencies": { 13 | "jsx-loader": "^0.13.2", 14 | "webpack": "^1.9.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/address-book/README.md: -------------------------------------------------------------------------------- 1 | # react-winjs Address Book 2 | 3 | This example demonstrates using `react-winjs` components to build an adaptive app for managing an address book. It works well at mobile, tablet, and desktop screen sizes. See it in action [here](http://winjs.github.io/react-winjs/examples/address-book/index.html). 4 | 5 | ## Usage 6 | 7 | ``` 8 | npm install 9 | npm start 10 | ``` 11 | 12 | Open `index.html` in your browser. -------------------------------------------------------------------------------- /examples/showcase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-winjs-showcase", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack --progress --colors --watch", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "", 12 | "devDependencies": { 13 | "jsx-loader": "^0.13.2", 14 | "webpack": "^1.9.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/address-book/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-winjs-address-book", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack --progress --colors --watch", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "", 12 | "devDependencies": { 13 | "jsx-loader": "^0.13.2", 14 | "webpack": "^1.9.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/movies/README.md: -------------------------------------------------------------------------------- 1 | # react-winjs Movies 2 | 3 | This example demonstrates a couple of `react-winjs` components in a small app for looking up movies. See it in action [here](http://winjs.github.io/react-winjs/examples/movies/index.html). 4 | 5 | It is inspired by the [react-native movies example](https://github.com/facebook/react-native/tree/master/Examples/Movies). 6 | 7 | ## Usage 8 | 9 | ``` 10 | npm install 11 | npm start 12 | ``` 13 | 14 | Open `index.html` in your browser. -------------------------------------------------------------------------------- /docs/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | /* 3 | Export react-winjs as a library object (a ReactWinJS var). 4 | */ 5 | module.exports = { 6 | entry: './library.jsx', 7 | output: { 8 | filename: 'dist/browser-bundle.js', 9 | library: 'ReactWinJS', 10 | libraryTarget: 'var' 11 | }, 12 | module: { 13 | loaders: [ 14 | {test: /\.jsx/, loader: 'jsx-loader'} 15 | ] 16 | }, 17 | externals: { 18 | 'react': 'React', 19 | 'react/addons': 'React' 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /examples/movies/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | cache: true, 5 | entry: './index.jsx', 6 | output: { 7 | filename: 'browser-bundle.js' 8 | }, 9 | module: { 10 | loaders: [ 11 | {test: /\.jsx/, loader: 'jsx-loader'} 12 | ] 13 | }, 14 | resolve: { 15 | alias: { 16 | 'react-winjs': path.join(__dirname, '../../react-winjs') 17 | } 18 | }, 19 | externals: { 20 | 'react': 'React', 21 | 'react-dom': 'ReactDOM' 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /examples/showcase/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | cache: true, 5 | entry: './index.jsx', 6 | output: { 7 | filename: 'browser-bundle.js' 8 | }, 9 | module: { 10 | loaders: [ 11 | {test: /\.jsx/, loader: 'jsx-loader'} 12 | ] 13 | }, 14 | resolve: { 15 | alias: { 16 | 'react-winjs': path.join(__dirname, '../../react-winjs') 17 | } 18 | }, 19 | externals: { 20 | 'react': 'React', 21 | 'react-dom': 'ReactDOM' 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /examples/address-book/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | cache: true, 5 | entry: './index.jsx', 6 | output: { 7 | filename: 'browser-bundle.js' 8 | }, 9 | module: { 10 | loaders: [ 11 | {test: /\.jsx/, loader: 'jsx-loader'} 12 | ] 13 | }, 14 | resolve: { 15 | alias: { 16 | 'react-winjs': path.join(__dirname, '../../react-winjs') 17 | } 18 | }, 19 | externals: { 20 | 'react': 'React', 21 | 'react-dom': 'ReactDOM' 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /examples/showcase/examples/Tooltip.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | render: function () { 8 | var contentComponent =
This can contain arbitrary content, like images
; 9 | 10 | return ( 11 | 13 |
This has a tooltip, hover and see...
14 |
15 | ); 16 | } 17 | }); -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-winjs", 3 | "main": "react-winjs.js", 4 | "homepage": "https://github.com/winjs/react-winjs", 5 | "authors": [ 6 | "Microsoft Corporation and other contributors" 7 | ], 8 | "description": "React wrapper around WinJS's controls", 9 | "keywords": [ 10 | "winjs", 11 | "react" 12 | ], 13 | "license": "MIT", 14 | "ignore": [ 15 | "**/.*", 16 | "node_modules", 17 | "bower_components", 18 | "test", 19 | "tests", 20 | "examples", 21 | "package.json", 22 | "*.nuspec", 23 | "Gruntfile.js" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /examples/address-book/OtherPage.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | 5 | var urlToContent = { 6 | "new": "What's New", 7 | groups: "Groups", 8 | settings: "Settings" 9 | }; 10 | 11 | var OtherPage = React.createClass({ 12 | propTypes: { 13 | location: React.PropTypes.array.isRequired 14 | }, 15 | render: function () { 16 | var title = urlToContent[this.props.location] || "Other"; 17 | return

{title}

18 | } 19 | }); 20 | 21 | module.exports = OtherPage; 22 | -------------------------------------------------------------------------------- /examples/movies/Score.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | function formattedScore(score) { 4 | return score >= 0 ? score + "%" : "N/A"; 5 | } 6 | 7 | var maxColor = 200; 8 | function colorForScore(score) { 9 | if (score >= 0) { 10 | var scoreAsColor = Math.round(score / 100 * maxColor); 11 | return "rgb(" + [maxColor - scoreAsColor, scoreAsColor, 0].join(",") + ")"; 12 | } else { 13 | return "rgb(0, 0, 0)"; 14 | } 15 | 16 | } 17 | 18 | function textColoredForScore(text, score) { 19 | return {text}; 20 | } 21 | 22 | module.exports = { 23 | formattedScore: formattedScore, 24 | textColoredForScore: textColoredForScore 25 | }; -------------------------------------------------------------------------------- /examples/showcase/examples/Flyout.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | handleShow: function (eventObject) { 8 | var anchor = eventObject.currentTarget; 9 | this.refs.flyout.winControl.show(anchor); 10 | }, 11 | render: function () { 12 | return ( 13 |
14 | 15 | 16 | 17 |
This is the flyout content!!
18 |
19 |
20 | ); 21 | } 22 | }); -------------------------------------------------------------------------------- /React.WinJS.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | React.WinJS 5 | 1.0.0 6 | Microsoft Corporation and other contributors 7 | https://raw.githubusercontent.com/winjs/react-winjs/master/LICENSE.txt 8 | https://github.com/winjs/react-winjs 9 | false 10 | React wrapper around WinJS's controls 11 | winjs react 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"); 2 | var gutil = require("gulp-util"); 3 | var webpack = require("webpack"); 4 | 5 | gulp.task("build", ["build:webpack", "build:winjs"]); 6 | 7 | /* 8 | Copy over core winjs files to the dist folder 9 | */ 10 | gulp.task("build:winjs", function() { 11 | return gulp.src([ 12 | 'node_modules/winjs/js/base.min.js', 13 | 'node_modules/winjs/js/ui.min.js', 14 | 'node_modules/winjs/css/ui-light.min.css', 15 | 'node_modules/winjs/fonts/*' 16 | ], { base: 'node_modules/winjs' }).pipe(gulp.dest('dist')) 17 | }); 18 | 19 | /* 20 | Build library 21 | */ 22 | gulp.task("build:webpack", function(callback) { 23 | webpack(require('./webpack.config'), function(err, stats) { 24 | if(err) throw new gutil.PluginError("webpack", err); 25 | gutil.log("[webpack]", stats.toString()); 26 | callback(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-winjs", 3 | "version": "2.5.0", 4 | "description": "React wrapper around WinJS's controls", 5 | "main": "react-winjs.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "winjs", 11 | "react" 12 | ], 13 | "author": "Microsoft Corporation and other contributors", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/winjs/react-winjs" 17 | }, 18 | "license": "MIT", 19 | "peerDependencies": { 20 | "react": "15.0.x", 21 | "react-dom": "15.0.x", 22 | "winjs": "4.4.x" 23 | }, 24 | "devDependencies": { 25 | "grunt": "^0.4.5", 26 | "grunt-contrib-clean": "^0.6.0", 27 | "grunt-contrib-compress": "^0.13.0", 28 | "grunt-contrib-copy": "^0.8.0", 29 | "grunt-github-releaser": "^0.1.17", 30 | "grunt-nuget": "^0.1.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/address-book/ProfilePicture.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | 5 | function cssUrl(url) { 6 | return "url(" + url + ")"; 7 | } 8 | 9 | var ProfilePicture = React.createClass({ 10 | render: function () { 11 | var size = this.props.size; 12 | return ( 13 |
23 | 24 |
25 | ); 26 | } 27 | }); 28 | 29 | module.exports = ProfilePicture; 30 | -------------------------------------------------------------------------------- /examples/showcase/examples/DatePicker.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | handleDateChange: function (eventObject) { 8 | var datePicker = eventObject.currentTarget.winControl; 9 | this.setState({ date: datePicker.current }); 10 | }, 11 | getInitialState: function () { 12 | return { 13 | date: new Date() 14 | }; 15 | }, 16 | render: function () { 17 | return ( 18 |
19 |

Date: {this.state.date.toDateString()}

20 | 25 |
26 | ); 27 | } 28 | }); -------------------------------------------------------------------------------- /examples/showcase/examples/Pivot.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | render: function () { 8 | return ( 9 | 10 | 11 |
Pivots are useful for varied content.
12 |
13 | 14 |
This pivot is boring.
15 |
16 | 17 |
Because it's only purpose is to show how to create a pivot.
18 |
19 |
20 | ); 21 | } 22 | }); -------------------------------------------------------------------------------- /examples/address-book/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | react-winjs Address Book 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/showcase/examples/FlipView.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | flipViewItemRenderer: ReactWinJS.reactRenderer(function (item) { 8 | return ( 9 |
10 | The rating of this flip view item is: {item.data.rating} 11 |
12 | ); 13 | }), 14 | getInitialState: function () { 15 | return { 16 | ratingsList: new WinJS.Binding.List([ 17 | { rating: 4 }, 18 | { rating: 2 } 19 | ]) 20 | }; 21 | }, 22 | render: function () { 23 | return ( 24 | 28 | ); 29 | } 30 | }); -------------------------------------------------------------------------------- /examples/showcase/examples/ToggleSwitch.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | handleToggle: function (eventObject) { 8 | var toggleCommand = eventObject.currentTarget.winControl; 9 | this.setState({ toggleSelected: toggleCommand.checked }); 10 | }, 11 | getInitialState: function () { 12 | return { 13 | toggleSelected: false 14 | }; 15 | }, 16 | render: function () { 17 | return ( 18 |
19 |

Toggle selected: {this.state.toggleSelected.toString()}

20 | 21 | 26 |
27 | ); 28 | } 29 | }); -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Adam Comella 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /tasks/check-bom.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | (function () { 3 | "use strict"; 4 | 5 | module.exports = function (grunt) { 6 | 7 | // Verifies that files begin with a UTF8 BOM. Files without one will not be able to pass the 8 | // Windows App Certification Kit test. 9 | 10 | grunt.registerMultiTask("check-bom", function () { 11 | function checkBom(filePath) { 12 | if (grunt.file.exists(filePath)) { 13 | var content = grunt.file.read(filePath, { encoding: null }); 14 | if (content.length < 3 || content[0] !== 0xef || content[1] !== 0xbb || content[2] !== 0xbf) { 15 | grunt.fail.fatal("check-bom File is missing BOM: " + filePath); 16 | } 17 | } else { 18 | grunt.log.warn("check-bom No such file: " + filePath); 19 | } 20 | } 21 | 22 | this.filesSrc.forEach(checkBom); 23 | }); 24 | 25 | }; 26 | })(); 27 | -------------------------------------------------------------------------------- /examples/showcase/examples/ItemContainer.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | handleSelectionChanged: function (eventObject) { 8 | var itemContainer = eventObject.currentTarget.winControl; 9 | this.setState({ selected: itemContainer.selected }); 10 | }, 11 | getInitialState: function () { 12 | return { 13 | selected: true 14 | }; 15 | }, 16 | render: function () { 17 | return ( 18 | 23 |
24 |

Marvelous Mint

25 |
Gelato
26 | Selected: {this.state.selected.toString()} 27 |
28 |
29 | ); 30 | } 31 | }); -------------------------------------------------------------------------------- /examples/showcase/examples/TimePicker.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | function formattedTime(time) { 7 | var rawHours = time.getHours(); 8 | var amPM = rawHours < 12 ? "AM" : "PM"; 9 | var hours = rawHours < 12 ? rawHours : (rawHours - 12); 10 | hours = hours === 0 ? 12 : hours; 11 | var rawMinutes = time.getMinutes(); 12 | var minutes = (rawMinutes < 10 ? "0" : "") + rawMinutes; 13 | 14 | return hours + ":" + minutes + " " + amPM; 15 | } 16 | 17 | module.exports = React.createClass({ 18 | handleTimeChange: function (eventObject) { 19 | var timePicker = eventObject.currentTarget.winControl; 20 | this.setState({ time: timePicker.current }); 21 | }, 22 | getInitialState: function () { 23 | return { 24 | time: new Date() 25 | }; 26 | }, 27 | render: function () { 28 | return ( 29 |
30 |

Time: {formattedTime(this.state.time)}

31 | 32 | 35 |
36 | ); 37 | } 38 | }); -------------------------------------------------------------------------------- /examples/showcase/examples/Rating.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | handleChangeRating: function (eventObject) { 8 | var ratingControl = eventObject.currentTarget.winControl; 9 | this.setState({ rating: ratingControl.userRating }); 10 | }, 11 | handleAddToRating: function (amount) { 12 | this.setState({ rating: this.state.rating + amount }); 13 | }, 14 | getInitialState: function () { 15 | return { 16 | rating: 0 17 | }; 18 | }, 19 | render: function () { 20 | return ( 21 |
22 |
23 | 24 | 25 |
26 |

Current Rating: {this.state.rating}

27 | 28 | 32 |
33 | ); 34 | } 35 | }); -------------------------------------------------------------------------------- /examples/showcase/examples/ContentDialog.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | handleShow: function () { 8 | this.refs.dialog.winControl.show().then(function (eventObject) { 9 | this.setState({ dialogResult: eventObject.result }); 10 | }.bind(this)); 11 | }, 12 | getInitialState: function () { 13 | return { 14 | dialogResult: null 15 | }; 16 | }, 17 | render: function () { 18 | return ( 19 |
20 |

Dialog Result: {this.state.dialogResult || ""}

21 | 22 | 23 | 28 |
29 | This content will appear in the body of the ContentDialog. You can put arbitrary HTML in here. 30 |
31 |
32 |
33 | ); 34 | } 35 | }); -------------------------------------------------------------------------------- /examples/showcase/examples/ListView.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | // See CSS styles for win-container in index.html 7 | 8 | module.exports = React.createClass({ 9 | itemRenderer: ReactWinJS.reactRenderer(function (item) { 10 | return
{item.data.title}
; 11 | }), 12 | getInitialState: function () { 13 | return { 14 | list: new WinJS.Binding.List([ 15 | { title: "Apple" }, 16 | { title: "Banana" }, 17 | { title: "Grape" }, 18 | { title: "Lemon" }, 19 | { title: "Mint" }, 20 | { title: "Orange" }, 21 | { title: "Pineapple" }, 22 | { title: "Strawberry"} 23 | ]), 24 | layout: { type: WinJS.UI.ListLayout } 25 | }; 26 | }, 27 | render: function () { 28 | return ( 29 | 37 | ); 38 | } 39 | }); -------------------------------------------------------------------------------- /examples/movies/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | react-winjs Movies 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 33 | 34 | 35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/showcase/examples/Hub.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | handleHeaderInvoked: function (eventObject) { 8 | if (eventObject.detail.index === 1) { 9 | this.setState({ counter: this.state.counter + 1 }); 10 | } 11 | }, 12 | getInitialState: function () { 13 | return { 14 | counter: 0 15 | }; 16 | }, 17 | render: function () { 18 | return ( 19 | 22 | 26 |
Hubs are useful for varied content.
27 |
28 | 29 |
30 | This section's header was clicked {this.state.counter} time(s). 31 | This hub is boring. 32 |
33 |
34 | 35 |
Because it's only purpose is to show how to create a hub.
36 |
37 |
38 | ); 39 | } 40 | }); -------------------------------------------------------------------------------- /examples/showcase/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | react-winjs Showcase 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 60 | 61 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-winjs 2 | 3 | A React wrapper around WinJS's controls. Implemented using the technique described on [this WinJS wiki page](https://github.com/winjs/winjs/wiki/Using-WinJS-with-React). 4 | 5 | - [Documentation](https://github.com/winjs/react-winjs/wiki/Documentation) 6 | - [Live Component Library](https://brand.ai/styleguide/WinJS) 7 | 8 | Live demos: 9 | - [Showcase](http://winjs.github.io/react-winjs/examples/showcase/index.html) ([source](https://github.com/winjs/react-winjs/tree/master/examples/showcase)): shows an example usage of each component. 10 | - [Movies](http://winjs.github.io/react-winjs/examples/movies/index.html) ([source](https://github.com/winjs/react-winjs/tree/master/examples/movies)): demonstrates a couple of react-winjs components in a small app for looking up movies. 11 | - [Address Book](http://winjs.github.io/react-winjs/examples/address-book/index.html) ([source](https://github.com/winjs/react-winjs/tree/master/examples/address-book)): demonstrates how to use react-winjs components to build an adaptive app which works well on mobile, tablet, and desktop computers. 12 | 13 | ## Installation 14 | 15 | ``` 16 | npm install react-winjs --save 17 | ``` 18 | 19 | ## Usage 20 | 21 | Include [WinJS 4.4](http://try.buildwinjs.com/#get) on your page. For example: 22 | 23 | ```html 24 | 25 | 26 | 27 | ``` 28 | 29 | Then require `react-winjs` and use it: 30 | 31 | ```jsx 32 | var React = require('react'); 33 | var ReactDOM = require('react-dom'); 34 | var ReactWinJS = require('react-winjs'); 35 | 36 | var App = React.createClass({ 37 | render: function () { 38 | return ; 39 | } 40 | }); 41 | 42 | ReactDOM.render(, document.getElementById("app")); 43 | ``` 44 | 45 | See the [documentation](https://github.com/winjs/react-winjs/wiki/Documentation) and [examples](https://github.com/winjs/react-winjs/tree/master/examples) for more details. 46 | 47 | ## License 48 | 49 | MIT 50 | -------------------------------------------------------------------------------- /examples/showcase/examples/SplitView.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | var splitViewId = "mainSplitView"; 7 | 8 | module.exports = React.createClass({ 9 | handleTogglePane: function () { 10 | this.setState({ paneOpened: !this.state.paneOpened }); 11 | }, 12 | handleAfterClose: function () { 13 | this.setState({ paneOpened: false }); 14 | }, 15 | handleChangeContent: function (newContent) { 16 | this.setState({ 17 | content: newContent, 18 | paneOpened: false 19 | }); 20 | }, 21 | getInitialState: function () { 22 | return { 23 | content: "Home", 24 | paneOpened: false 25 | }; 26 | }, 27 | render: function () { 28 | var paneComponent = ( 29 |
30 |
31 | 35 |
36 | 37 | 41 | 45 | 49 |
50 | ); 51 | var contentComponent = ( 52 |

{this.state.content}

53 | ); 54 | 55 | return ( 56 | 63 | ); 64 | } 65 | }); -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # React-WinJS Component Documentation (see it live [here](https://brandai.com/styleguide/WinJS)) 2 | 3 | This folder contains configuration required to build documentation for react-winjs using [Brand AI](https://brand.ai). 4 | 5 | ## Flow 6 | ### Build 7 | Use gulp to build a distribution folder containing: 8 | * library version of react-winjs 9 | * WinJS core scripts and stylesheets. 10 | 11 | See [webpack.config.json](https://github.com/winjs/react-winjs/blob/master/docs/webpack.config.js) and [gulpfile.json](https://github.com/winjs/react-winjs/blob/master/docs/gulpfile.js) for more info on that. 12 | 13 | ```sh 14 | $ cd react-winjs/docs 15 | $ npm install 16 | $ gulp build 17 | ``` 18 | 19 | ### Deploy 20 | Deploying to Brand.ai is done through [brandai-tools](https://github.com/brandai/brandai-tools). 21 | ```sh 22 | $ npm install -g brandai-tools 23 | $ brandai login 24 | $ brandai deploy 25 | ``` 26 | 27 | ### Config 28 | [brandai.json](https://github.com/brandai/react-winjs/blob/master/docs/brandai.json) contains all the component extraction and deployment information. 29 | 30 | * `name` - the component library name in Brand.ai - each documentation project is associated with one or more component libraries 31 | * `libraryPath` - relative path for the component library project root (used to read library package.json) 32 | * `dist` - name of the dist folder containing the library and its dependencies - used both for rendering examples and for dynamic analysis of components and their properties 33 | * `libraryObjectName` - name of the library on the global object (we set it to `ReactWinJS` in `webpack.config.js`. 34 | * `reactVersion` - the version of React required for running this library (automatically gets pulled when rendering the components) 35 | * `dynamicExtraction` - this tells Brand.ai to dynamically extract components - run the files in the `dist` folder and extract the required info from live components, rather than statically parse .jsx files. 36 | * `fileOrder` - when loading dependencies, file names appearing here will be loaded first (we can't load react-winjs until the winjs core files are there). 37 | 38 | #### Example package.json 39 | ```json 40 | { 41 | "name": "react-winjs", 42 | "libraryPath": "../", 43 | "dist": "dist", 44 | "libraryObjectName": "ReactWinJS", 45 | "reactVersion": "15.0.1", 46 | "dynamicExtraction": true, 47 | "fileOrder": ["base.min.js", "ui.min.js"] 48 | } 49 | -------------------------------------------------------------------------------- /examples/showcase/examples/Menu.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | handleShowMenu: function (eventObject) { 8 | var anchor = eventObject.currentTarget; 9 | this.refs.menu.winControl.show(anchor); 10 | }, 11 | handleUpdateResult: function (result) { 12 | this.setState({ result: result }); 13 | }, 14 | handleToggleMe: function (eventObject) { 15 | var toggleCommand = eventObject.currentTarget.winControl; 16 | this.setState({ toggleSelected: toggleCommand.selected }); 17 | }, 18 | getInitialState: function () { 19 | return { 20 | result: null, 21 | toggleSelected: true 22 | }; 23 | }, 24 | render: function () { 25 | var subMenu = ( 26 | 27 | 31 | 35 | 36 | ); 37 | 38 | return ( 39 |
40 | 41 |

Clicked command: {this.state.result || ""}

42 |

Toggle selected: {this.state.toggleSelected.toString()}

43 | 44 | 45 | 46 | 50 | 51 | 56 | 57 | 58 | 59 | 63 | 64 | 65 |
66 | ); 67 | } 68 | }); -------------------------------------------------------------------------------- /examples/movies/DetailPage.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | var Score = require('./Score.jsx'); 6 | var formattedScore = Score.formattedScore; 7 | var textColoredForScore = Score.textColoredForScore; 8 | 9 | var styles = { 10 | root: { 11 | marginLeft: "5px", 12 | marginRight: "5px" 13 | }, 14 | header: { 15 | root: { 16 | display: "flex", 17 | flexDirection: "row" 18 | }, 19 | backButton: { 20 | marginRight: "10px", 21 | flex: "none" 22 | }, 23 | title: { 24 | marginBottom: "10px", 25 | flex: "1 1" 26 | } 27 | }, 28 | detail: { 29 | root: { 30 | display: "flex" 31 | }, 32 | poster: { 33 | marginRight: "10px", 34 | flex: "none", 35 | alignSelf: "center" 36 | }, 37 | info: { 38 | flex: "1 1", 39 | alignSelf: "flex-start" 40 | }, 41 | rating: { 42 | display: "inline-block", 43 | padding: "2px", 44 | border: "2px solid black", 45 | marginBottom: "15px", 46 | fontFamily: "Palatino", 47 | fontSize: "20px" 48 | }, 49 | actors: { 50 | marginTop: "0", 51 | marginBottom: "0" 52 | } 53 | } 54 | }; 55 | 56 | module.exports = React.createClass({ 57 | render: function () { 58 | var movie = this.props.movie; 59 | var criticsScore = movie.ratings.critics_score; 60 | var audienceScore = movie.ratings.audience_score; 61 | var actors = movie.abridged_cast.map(function (person) { 62 | return
  • {person.name}
  • ; 63 | }); 64 | 65 | return ( 66 |
    67 |
    68 | 69 |

    {movie.title} ({movie.year})

    70 |
    71 |
    72 | 73 |
    74 |
    {movie.mpaa_rating}
    75 |

    Critics:

    76 |

    {textColoredForScore(formattedScore(criticsScore), criticsScore)}

    77 |

    Audience:

    78 |

    {textColoredForScore(formattedScore(audienceScore), audienceScore)}

    79 |
    80 |
    81 |
    82 |
    {movie.synopsis}
    83 |
    84 |
    85 | Actors 86 |
      {actors}
    87 |
    88 |
    89 | ); 90 | } 91 | }); -------------------------------------------------------------------------------- /examples/showcase/examples/AutoSuggestBox.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | var suggestionList = ["Shanghai", "Istanbul", "Karachi", "Delhi", "Mumbai", "Moscow", "Seoul", "Beijing", "Jakarta", 7 | "Tokyo", "Mexico City", "Kinshasa", "New York City", "Lagos", "London", "Lima", "Bogota", "Tehran", "Ho Chi Minh City", "Hong Kong", 8 | "Bangkok", "Dhaka", "Cairo", "Hanoi", "Rio de Janeiro", "Lahore", "Chonquing", "Bengaluru", "Tianjin", "Baghdad", "Riyadh", "Singapore", 9 | "Santiago", "Saint Petersburg", "Surat", "Chennai", "Kolkata", "Yangon", "Guangzhou", "Alexandria", "Shenyang", "Hyderabad", "Ahmedabad", 10 | "Ankara", "Johannesburg", "Wuhan", "Los Angeles", "Yokohama", "Abidjan", "Busan", "Cape Town", "Durban", "Pune", "Jeddah", "Berlin", 11 | "Pyongyang", "Kanpur", "Madrid", "Jaipur", "Nairobi", "Chicago", "Houston", "Philadelphia", "Phoenix", "San Antonio", "San Diego", 12 | "Dallas", "San Jose", "Jacksonville", "Indianapolis", "San Francisco", "Austin", "Columbus", "Fort Worth", "Charlotte", "Detroit", 13 | "El Paso", "Memphis", "Baltimore", "Boston", "Seattle Washington", "Nashville", "Denver", "Louisville", "Milwaukee", "Portland", 14 | "Las Vegas", "Oklahoma City", "Albuquerque", "Tucson", "Fresno", "Sacramento", "Long Beach", "Kansas City", "Mesa", "Virginia Beach", 15 | "Atlanta", "Colorado Springs", "Omaha", "Raleigh", "Miami", "Cleveland", "Tulsa", "Oakland", "Minneapolis", "Wichita", "Arlington", 16 | "Bakersfield", "New Orleans", "Honolulu", "Anaheim", "Tampa", "Aurora", "Santa Ana", "St. Louis", "Pittsburgh", "Corpus Christi", 17 | "Riverside", "Cincinnati", "Lexington", "Anchorage", "Stockton", "Toledo", "St. Paul", "Newark", "Greensboro", "Buffalo", "Plano", 18 | "Lincoln", "Henderson", "Fort Wayne", "Jersey City", "St. Petersburg", "Chula Vista", "Norfolk", "Orlando", "Chandler", "Laredo", "Madison", 19 | "Winston-Salem", "Lubbock", "Baton Rouge", "Durham", "Garland", "Glendale", "Reno", "Hialeah", "Chesapeake", "Scottsdale", "North Las Vegas", 20 | "Irving", "Fremont", "Irvine", "Birmingham", "Rochester", "San Bernardino", "Spokane", "Toronto", "Montreal", "Vancouver", "Ottawa-Gatineau", 21 | "Calgary", "Edmonton", "Quebec City", "Winnipeg", "Hamilton"]; 22 | 23 | module.exports = React.createClass({ 24 | handleSuggestionsRequested: function (eventObject) { 25 | var queryText = eventObject.detail.queryText, 26 | query = queryText.toLowerCase(), 27 | suggestionCollection = eventObject.detail.searchSuggestionCollection; 28 | 29 | if (queryText.length > 0) { 30 | for (var i = 0, len = suggestionList.length; i < len; i++) { 31 | if (suggestionList[i].substr(0, query.length).toLowerCase() === query) { 32 | suggestionCollection.appendQuerySuggestion(suggestionList[i]); 33 | } 34 | } 35 | } 36 | }, 37 | handleQuerySubmitted: function (eventObject) { 38 | this.setState({ query: eventObject.detail.queryText }); 39 | }, 40 | getInitialState: function () { 41 | return { 42 | query: null 43 | }; 44 | }, 45 | render: function () { 46 | return ( 47 |
    48 | 52 | 53 | {

    Submitted Query: {this.state.query || ""}

    } 54 |
    55 | ); 56 | } 57 | }); -------------------------------------------------------------------------------- /examples/showcase/examples/SemanticZoom.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | // See CSS styles for win-container in index.html 7 | 8 | function groupKey(item) { 9 | return item.title[0]; 10 | } 11 | 12 | function groupData(item) { 13 | return { title: item.title[0] }; 14 | } 15 | 16 | module.exports = React.createClass({ 17 | itemRenderer: ReactWinJS.reactRenderer(function (item) { 18 | return
    {item.data.title}
    ; 19 | }), 20 | groupHeaderRenderer: ReactWinJS.reactRenderer(function (item) { 21 | return
    {item.data.title}
    ; 22 | }), 23 | handleToggleZoom: function (eventObject) { 24 | this.setState({ zoomedOut: !this.state.zoomedOut }); 25 | }, 26 | handleZoomChanged: function (eventObject) { 27 | this.setState({ zoomedOut: eventObject.detail }); 28 | }, 29 | getInitialState: function () { 30 | return { 31 | list: new WinJS.Binding.List([ 32 | { title: "Aaron" }, 33 | { title: "Adam" }, 34 | { title: "Allison" }, 35 | { title: "Barry" }, 36 | { title: "Bill" }, 37 | { title: "Brad" }, 38 | { title: "Bridget" }, 39 | { title: "Brett" }, 40 | { title: "Carly" }, 41 | { title: "Carol" }, 42 | { title: "Charles" }, 43 | { title: "Chris" }, 44 | { title: "Daisy" }, 45 | { title: "Dan" }, 46 | { title: "Denise" }, 47 | { title: "Derek" }, 48 | { title: "Earl" }, 49 | { title: "Emily" }, 50 | { title: "Emma" }, 51 | { title: "Erika" }, 52 | { title: "Ethan" }, 53 | { title: "Finley" }, 54 | { title: "Florence" }, 55 | { title: "Frank" }, 56 | { title: "Fred" } 57 | 58 | ]).createGrouped(groupKey, groupData), 59 | layout: { type: WinJS.UI.ListLayout }, 60 | zoomedOut: false 61 | }; 62 | }, 63 | render: function () { 64 | var zoomedInView = ; 73 | 74 | var zoomedOutView = ; 79 | 80 | return ( 81 |
    82 | 85 | 92 |
    93 | ); 94 | 95 | } 96 | }); -------------------------------------------------------------------------------- /examples/showcase/examples/AppBar.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | handleUpdateResult: function (result) { 8 | this.setState({ result: result }); 9 | }, 10 | handleToggleMe: function (eventObject) { 11 | var toggleCommand = eventObject.currentTarget.winControl; 12 | this.setState({ toggleSelected: toggleCommand.selected }); 13 | }, 14 | getInitialState: function () { 15 | return { 16 | toolBarIsSmall: false, 17 | result: null, 18 | toggleSelected: true 19 | }; 20 | }, 21 | render: function () { 22 | var subMenu = ( 23 | 24 | 28 | 32 | 33 | ); 34 | 35 | var appBar = ( 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | 52 | 53 | 59 | 60 | 65 | 66 | 71 | 72 | 77 | 78 | 79 | ); 80 | 81 | return ( 82 |
    83 |

    This AppBar renders at the bottom of the screen.

    84 |

    Resize your window. Notice how the AppBar puts commands into an 85 | overflow menu when it can't fit them in the primary area. You can 86 | control what gets overflowed first using the priority prop

    87 | 90 |

    Clicked command: {this.state.result || ""}

    91 |

    Toggle selected: {this.state.toggleSelected.toString()}

    92 | {this.props.appBarShown ? appBar : null} 93 |
    94 | ); 95 | } 96 | }); -------------------------------------------------------------------------------- /examples/address-book/main.css: -------------------------------------------------------------------------------- 1 | #app { 2 | height: 100%; 3 | } 4 | 5 | /* 6 | * BackButton 7 | * Ensure it's the same size as the SplitViewPaneToggle 8 | */ 9 | #app .win-backbutton { 10 | height: 48px; 11 | width: 48px; 12 | font-size: inherit; 13 | line-height: inherit; 14 | box-sizing: border-box; 15 | } 16 | #app .win-backbutton::before { 17 | font-size: 24px; 18 | line-height: 1.333; 19 | vertical-align: baseline; 20 | } 21 | 22 | /* 23 | * People Search Pane 24 | */ 25 | 26 | #app .peopleSearchPane .peopleListView .win-surface { 27 | margin-top: 0; 28 | } 29 | 30 | #app .peopleSearchPane .peopleListView .win-container { 31 | margin: 0; 32 | height: 48px; 33 | } 34 | 35 | #app .peopleSearchPane .peopleListView .win-item { 36 | height: 40px; 37 | padding-top: 8px; 38 | width: calc(100% - 12px); 39 | padding-left: 12px; 40 | } 41 | 42 | #app .peopleSearchPane .peopleListView .win-item .profilePicture { 43 | width: 34px; 44 | height: 34px; 45 | background-size: cover; 46 | -webkit-border-radius: 34px; 47 | -moz-border-radius: 34px; 48 | border-radius: 34px; 49 | display: inline-block; 50 | vertical-align: middle; 51 | margin-right: 5px; 52 | } 53 | 54 | #app .peopleSearchPane .peopleListView .win-item .name { 55 | vertical-align: middle; 56 | } 57 | 58 | #app .peopleSearchPane .peopleListView .win-groupheadercontainer { 59 | width: 32px; 60 | height: 32px; 61 | margin-top: 7px; 62 | margin-bottom: 10px; 63 | margin-left: 12px; 64 | } 65 | #app .peopleSearchPane .peopleListView .win-groupheader { 66 | font-size: 15px; 67 | width: 32px; 68 | height: 32px; 69 | padding: 0; 70 | background-color: rgb(1, 121, 216); 71 | color: white; 72 | text-align: center; 73 | line-height: 32px; 74 | } 75 | 76 | .peopleToolBar .win-toolbar-actionarea { 77 | background-color: white; 78 | } 79 | 80 | /* 81 | * Profile Pane 82 | */ 83 | 84 | #app .profilePane { 85 | box-sizing: border-box; 86 | padding-left: 10px; 87 | padding-right: 10px; 88 | } 89 | 90 | #app .profilePane .profileHeader { 91 | padding-bottom: 10px; 92 | } 93 | 94 | #app .profilePane .profileHeader .name { 95 | vertical-align: top; 96 | display: inline-block; 97 | font-size: 25px; 98 | padding-top: 6px; 99 | } 100 | 101 | .profileHeader .personInfo { 102 | display: flex; 103 | } 104 | 105 | #app .profilePane .profileHeader .profilePicture { 106 | width: 100px; 107 | height: 100px; 108 | background-size: cover; 109 | display: inline-block; 110 | border-radius: 100px; 111 | -webkit-border-radius: 100px; 112 | -moz-border-radius: 100px; 113 | margin-left: 25px; 114 | margin-top:10px; 115 | } 116 | 117 | .profileHeader .personInfo .profileStatus { 118 | width: 240px; 119 | margin-left:20px; 120 | margin-top:10px; 121 | } 122 | 123 | .profileHeader .personInfo .profileStatus .message { 124 | /*color: #ffffff;*/ 125 | font-size: 14px; 126 | } 127 | .profileHeader .personInfo .profileStatus .source { 128 | display:block; 129 | color: grey; 130 | font-size: 13px; 131 | } 132 | 133 | .profileContent ul { 134 | list-style:none; 135 | padding-left: 25px; 136 | padding-right: 25px; 137 | } 138 | 139 | .profileContent ul li { 140 | margin-bottom:25px; 141 | } 142 | 143 | .separator { 144 | width:100%; 145 | height:2px; 146 | background-color:rgb(205, 205, 205); 147 | } 148 | 149 | .profileContent .phoneIcon, 150 | .profileContent .callContent { 151 | display: inline-block; 152 | vertical-align: top; 153 | } 154 | 155 | .profileContent .videoCallIcon, 156 | .profileContent .emailIcon, 157 | .profileContent .messageIcon, 158 | .profileContent .phoneIcon, 159 | .profileContent .mapIcon { 160 | margin-right: 8px; 161 | } 162 | 163 | .profileContent .videoCallIcon::before { 164 | content: "\E13B"; 165 | } 166 | 167 | .profileContent .emailIcon::before { 168 | content: "\E119"; 169 | } 170 | 171 | .profileContent .messageIcon::before { 172 | content: "\E15F"; 173 | } 174 | 175 | .profileContent .phoneIcon::before { 176 | content: "\E13A"; 177 | } 178 | 179 | .profileContent .mapIcon::before { 180 | content: "\E139"; 181 | } 182 | -------------------------------------------------------------------------------- /examples/showcase/examples/ToolBar.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | 6 | module.exports = React.createClass({ 7 | handleToggleToolBarSize: function () { 8 | this.setState({ toolBarIsSmall: !this.state.toolBarIsSmall }); 9 | }, 10 | handleUpdateResult: function (result) { 11 | this.setState({ result: result }); 12 | }, 13 | handleToggleMe: function (eventObject) { 14 | var toggleCommand = eventObject.currentTarget.winControl; 15 | this.setState({ toggleSelected: toggleCommand.selected }); 16 | }, 17 | getInitialState: function () { 18 | return { 19 | toolBarIsSmall: false, 20 | result: null, 21 | toggleSelected: true 22 | }; 23 | }, 24 | componentDidUpdate: function (prevProps, prevState) { 25 | if (this.state.toolBarIsSmall !== prevState.toolBarIsSmall) { 26 | // Notify the ToolBar that is has been resized. 27 | this.refs.toolBar.winControl.forceLayout(); 28 | } 29 | }, 30 | render: function () { 31 | var subMenu = ( 32 | 33 | 37 | 41 | 42 | ); 43 | 44 | return ( 45 |
    46 |

    Notice how the ToolBar puts commands into an overflow menu when it can't fit 47 | them in the primary area. You can control what gets overflowed first using 48 | the priority prop

    49 | 52 |

    Clicked command: {this.state.result || ""}

    53 |

    Toggle selected: {this.state.toggleSelected.toString()}

    54 | 55 | 58 | 59 | 63 | 64 | 65 | 66 | 67 | 68 | 73 | 74 | 80 | 81 | 86 | 87 | 92 | 93 | 98 | 99 | 100 |
    101 | ); 102 | } 103 | }); -------------------------------------------------------------------------------- /examples/movies/SearchPage.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | var Score = require('./Score.jsx'); 6 | var formattedScore = Score.formattedScore; 7 | var textColoredForScore = Score.textColoredForScore; 8 | 9 | // For best performance, .win-container should be sized in CSS. 10 | // See index.html for its style. 11 | var styles = { 12 | item: { 13 | root: { 14 | height: "100%", 15 | display: "flex" 16 | }, 17 | poster: { 18 | marginRight: "10px", 19 | flex: "none" 20 | }, 21 | info: { 22 | root: { 23 | flex: "1 1", 24 | display: "flex", 25 | flexDirection: "column" 26 | }, 27 | title: { 28 | flex: "0 1 auto", 29 | overflow: "hidden" 30 | }, 31 | yearAndScore: { 32 | flex: "none" 33 | } 34 | } 35 | }, 36 | root: { 37 | height: "100%" 38 | }, 39 | listView: { 40 | height: "calc(100% - 50px)" 41 | }, 42 | footer: { 43 | height: "91px", 44 | lineHeight: "91px", 45 | textAlign: "center" 46 | } 47 | }; 48 | 49 | module.exports = React.createClass({ 50 | itemRenderer: ReactWinJS.reactRenderer(function (item) { 51 | var score = item.data.ratings.critics_score; 52 | var scoreComponent = textColoredForScore("Critics " + formattedScore(score), score); 53 | return ( 54 |
    55 | 56 |
    57 |

    {item.data.title}

    58 |
    59 | {item.data.year} {"\u2022"} {scoreComponent} 60 |
    61 |
    62 |
    63 | ); 64 | }), 65 | handleQueryChange: function (eventObject) { 66 | var queryText = eventObject.currentTarget.value; 67 | this.setState({ queryText: queryText }); 68 | 69 | this.pendingQueryId && clearTimeout(this.pendingQueryId); 70 | this.pendingQueryId = setTimeout(function () { 71 | this.props.onFetchFirstPage(queryText); 72 | }.bind(this), 300); 73 | }, 74 | handleMovieSelected: function (eventObject) { 75 | WinJS.Navigation.navigate("/movie", { 76 | movie: this.props.movies.getAt(eventObject.detail.itemIndex) 77 | }); 78 | }, 79 | handleFooterVisibilityChanged: function (eventObject) { 80 | if (eventObject.detail.visible) { 81 | this.props.onFetchNextPage(); 82 | } 83 | }, 84 | getInitialState: function () { 85 | return { 86 | queryText: this.props.queryText, 87 | layout: { type: WinJS.UI.ListLayout } 88 | }; 89 | }, 90 | render: function() { 91 | var resultsComponent; 92 | if (!this.props.movies) { 93 | resultsComponent =
    Loading...
    ; 94 | } else if (this.props.movies.length === 0) { 95 | resultsComponent =
    No movies found for "{this.props.queryText}".
    ; 96 | } else { 97 | var footerComponent = !this.props.hasMore ? null : ( 98 |
    99 | Loading... 100 |
    101 | ); 102 | 103 | resultsComponent = ( 104 | 113 | ); 114 | } 115 | 116 | return ( 117 |
    118 |
    119 | {resultsComponent} 120 |
    121 | ); 122 | } 123 | }); -------------------------------------------------------------------------------- /examples/showcase/index.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactDOM = require('react-dom'); 5 | var ReactWinJS = require('react-winjs'); 6 | 7 | // title is: 8 | // - Displayed as the title of the sample 9 | // - Used as the anchor ID of the sample 10 | // - Used to find the path to the source code of the sample. Specifically: 11 | // './examples/.jsx' 12 | var examples = [ 13 | { title: "AppBar", componenent: require('./examples/AppBar.jsx') }, 14 | { title: "AutoSuggestBox", componenent: require('./examples/AutoSuggestBox.jsx') }, 15 | { title: "BackButton", componenent: require('./examples/BackButton.jsx') }, 16 | { title: "ContentDialog", componenent: require('./examples/ContentDialog.jsx') }, 17 | { title: "DatePicker", componenent: require('./examples/DatePicker.jsx') }, 18 | { title: "FlipView", componenent: require('./examples/FlipView.jsx') }, 19 | { title: "Flyout", componenent: require('./examples/Flyout.jsx') }, 20 | { title: "Hub", componenent: require('./examples/Hub.jsx') }, 21 | { title: "ItemContainer", componenent: require('./examples/ItemContainer.jsx') }, 22 | { title: "ListView", componenent: require('./examples/ListView.jsx') }, 23 | { title: "Menu", componenent: require('./examples/Menu.jsx') }, 24 | { title: "Pivot", componenent: require('./examples/Pivot.jsx') }, 25 | { title: "Rating", componenent: require('./examples/Rating.jsx') }, 26 | { title: "SemanticZoom", componenent: require('./examples/SemanticZoom.jsx') }, 27 | { title: "SplitView", componenent: require('./examples/SplitView.jsx') }, 28 | { title: "TimePicker", componenent: require('./examples/TimePicker.jsx') }, 29 | { title: "ToggleSwitch", componenent: require('./examples/ToggleSwitch.jsx') }, 30 | { title: "ToolBar", componenent: require('./examples/ToolBar.jsx') }, 31 | { title: "Tooltip", componenent: require('./examples/Tooltip.jsx') } 32 | ]; 33 | 34 | var baseSourceUrl = "https://github.com/winjs/react-winjs/tree/master/examples/" + 35 | "showcase/examples/"; 36 | var styles = { 37 | viewport: { height: "100%", overflow: "auto" }, 38 | surface: { paddingBottom: 96 + 10 }, // Leave room for bottom AppBar 39 | example: { paddingBottom: 30 }, 40 | exampleTitle: { paddingBottom: 10 }, 41 | sourceLink: { paddingLeft: 5 } 42 | }; 43 | 44 | var App = React.createClass({ 45 | handleToggleAppBar: function (exampleTitle) { 46 | this.setState({ 47 | exampleWithAppBar: this.state.exampleWithAppBar === exampleTitle ? null : exampleTitle 48 | }); 49 | }, 50 | getInitialState: function () { 51 | return { 52 | // To prevent AppBars from occluding each other, only one example 53 | // should show an AppBar at a time. 54 | exampleWithAppBar: null 55 | }; 56 | }, 57 | render: function() { 58 | var tableOfContents = examples.map(function (example) { 59 | return <li key={example.title}><a className="win-link" href={"#" + example.title}>{example.title}</a></li>; 60 | }); 61 | 62 | var exampleMarkup = examples.map(function (example) { 63 | var sourceUrl = baseSourceUrl + example.title + ".jsx"; 64 | 65 | return ( 66 | <div style={styles.example} id={example.title} key={example.title} className="example"> 67 | <h3 className="win-h3" style={styles.exampleTitle}> 68 | {example.title} 69 | <a 70 | style={styles.sourceLink} 71 | href={sourceUrl} 72 | target="_blank" 73 | className="win-link win-type-base"> 74 | (view source) 75 | </a> 76 | </h3> 77 | <example.componenent 78 | appBarShown={this.state.exampleWithAppBar === example.title} 79 | onToggleAppBar={this.handleToggleAppBar.bind(null, example.title)} /> 80 | </div> 81 | ); 82 | }, this); 83 | 84 | return ( 85 | <div className="viewport" style={styles.viewport}> 86 | <div className="surface" style={styles.surface}> 87 | <h1 className="win-h1"><a className="win-link" href="https://github.com/winjs/react-winjs">react-winjs</a> Control Showcase</h1> 88 | 89 | <h3 className="win-h3">Table of Contents</h3> 90 | <ul>{tableOfContents}</ul> 91 | 92 | {exampleMarkup} 93 | </div> 94 | </div> 95 | ); 96 | } 97 | }); 98 | 99 | ReactDOM.render(<App />, document.getElementById("app")); 100 | -------------------------------------------------------------------------------- /examples/movies/index.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactDOM = require('react-dom'); 5 | var ReactWinJS = require('react-winjs'); 6 | var FakeData = require('./FakeData'); 7 | var Data = FakeData; 8 | var SearchPage = require('./SearchPage.jsx'); 9 | var DetailPage = require('./DetailPage.jsx'); 10 | 11 | // TODO: Preserve the scroll position when navigating back to search page. 12 | // TODO: Use a loading spinner instead of the text "Loading..." 13 | 14 | // Manages fetching of movie data. *fetchFirst* and *fetchNext* start fetches and the when the 15 | // resuts are ready, *dataHandler* is called. 16 | var MovieFetcher = function (dataHandler) { 17 | this._dataHandler = dataHandler; 18 | this._outstandingFetch = null; 19 | 20 | this.queryText = ""; 21 | this.nextPage = 1; 22 | this.receivedCount = 0; 23 | this.totalCount = 0; 24 | 25 | }; 26 | // Fetches the first page of results of *queryText*. If that fetch is already in 27 | // progress, this function is a no op. 28 | MovieFetcher.prototype.fetchFirst = function (queryText) { 29 | this.queryText = queryText; 30 | this.nextPage = 1; 31 | this.receivedCount = 0; 32 | this.totalCount = 0; 33 | 34 | this._doFetch(); 35 | }; 36 | // Fetches the next page of results for whatever *queryText* was passed to the most 37 | // recent call to *fetchFirst*. If a fetch is already in progress, this function is a no op. 38 | MovieFetcher.prototype.fetchNext = function () { 39 | this._doFetch(); 40 | }; 41 | // Cancels whatever fetch is currently in progress. 42 | MovieFetcher.prototype.stop = function () { 43 | var currFetch = this._outstandingFetch; 44 | this._outstandingFetch = null; 45 | currFetch && currFetch.promise.cancel(); 46 | }; 47 | MovieFetcher.prototype._doFetch = function () { 48 | var handleResults = function (response) { 49 | this._outstandingFetch = null; 50 | 51 | this.nextPage++; 52 | this.receivedCount += response.movies.length; 53 | this.totalCount = response.total; 54 | 55 | this._dataHandler({ 56 | queryText: queryText, 57 | page: page, 58 | movies: response.movies, 59 | hasMore: this.receivedCount < this.totalCount 60 | 61 | }); 62 | }.bind(this); 63 | 64 | var queryText = this.queryText; 65 | var page = this.nextPage; 66 | 67 | var hasMore = page === 1 || this.receivedCount < this.totalCount; 68 | var currFetch = this._outstandingFetch; 69 | if (!hasMore || currFetch && currFetch.queryText === queryText && currFetch.page === page) { 70 | // No more data or the fetch is already in progress. 71 | return; 72 | } 73 | 74 | this._outstandingFetch = null; 75 | currFetch && currFetch.promise.cancel(); 76 | 77 | var fetchPromise = queryText ? Data.getSearchResults(queryText, page) : Data.getInTheaters(page); 78 | fetchPromise = fetchPromise.then( 79 | handleResults, 80 | function (error) { 81 | console.log("Query error: "); 82 | console.log(" Query: '" + queryText + "'"); 83 | console.log(" Page: " + page); 84 | console.log(" Error: " + JSON.stringify(error)); 85 | } 86 | ); 87 | 88 | this._outstandingFetch = { 89 | promise: fetchPromise, 90 | queryText: queryText, 91 | page: page 92 | }; 93 | }; 94 | 95 | var App = React.createClass({ 96 | handleFetchResults: function (results) { 97 | var newMovies = results.movies; 98 | var currentMovies; 99 | if (results.page === 1) { 100 | currentMovies = new WinJS.Binding.List(newMovies); 101 | } else { 102 | currentMovies = this.state.movies; 103 | currentMovies.push.apply(currentMovies, newMovies); 104 | } 105 | 106 | this.setState({ 107 | movies: currentMovies, 108 | queryText: results.queryText, 109 | hasMore: results.hasMore 110 | }); 111 | }, 112 | handleFetchFirstPage: function (queryText) { 113 | this.fetcher.fetchFirst(queryText); 114 | }, 115 | handleFetchNextPage: function () { 116 | this.fetcher.fetchNext(); 117 | }, 118 | handleNavigated: function (eventObject) { 119 | this.setState({ 120 | nav: { 121 | location: eventObject.detail.location, 122 | state: eventObject.detail.state 123 | } 124 | }); 125 | }, 126 | getInitialState: function () { 127 | return { 128 | movies: null, 129 | queryText: "", 130 | hasMore: true, 131 | nav: { 132 | location: WinJS.Navigation.location, 133 | state: WinJS.Navigation.state 134 | } 135 | }; 136 | }, 137 | componentWillMount: function () { 138 | this.fetcher = new MovieFetcher(this.handleFetchResults); 139 | WinJS.Navigation.addEventListener("navigated", this.handleNavigated); 140 | WinJS.Navigation.navigate("/"); 141 | this.handleFetchNextPage(); 142 | }, 143 | componentWillUnmount: function () { 144 | WinJS.Navigation.removeEventListener("navigated", this.handleNavigated); 145 | this.fetcher.stop(); 146 | }, 147 | render: function () { 148 | var nav = this.state.nav; 149 | if (nav.location === "/movie") { 150 | return ( 151 | <DetailPage 152 | movie={nav.state.movie} /> 153 | ); 154 | } else { 155 | return ( 156 | <SearchPage 157 | movies={this.state.movies} 158 | queryText={this.state.queryText} 159 | hasMore={this.state.hasMore} 160 | onFetchFirstPage={this.handleFetchFirstPage} 161 | onFetchNextPage={this.handleFetchNextPage} /> 162 | ); 163 | } 164 | } 165 | }); 166 | 167 | ReactDOM.render(<App />, document.getElementById("app")); 168 | -------------------------------------------------------------------------------- /examples/address-book/index.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactDOM = require('react-dom'); 5 | var ReactWinJS = require('react-winjs'); 6 | var PeoplePage = require('./PeoplePage.jsx'); 7 | var OtherPage = require('./OtherPage.jsx'); 8 | var ProfilePicture = require('./ProfilePicture.jsx'); 9 | var Data = require('./FakeData.js'); 10 | 11 | var splitViewId = "rootSplitView"; 12 | 13 | var splitViewConfigs = { 14 | small: { 15 | closedDisplayMode: "none", 16 | openedDisplayMode: "overlay" 17 | }, 18 | medium: { 19 | closedDisplayMode: "inline", 20 | openedDisplayMode: "overlay" 21 | }, 22 | large: { 23 | closedDisplayMode: "inline", 24 | openedDisplayMode: "inline" 25 | } 26 | }; 27 | 28 | function merge(/* objs */) { 29 | var result = {}; 30 | for (var i = 0, len = arguments.length; i < len; i++) { 31 | var obj = arguments[i]; 32 | if (obj) { 33 | for (k in obj) { result[k] = obj[k]; } 34 | } 35 | } 36 | return result; 37 | } 38 | 39 | function getMode() { 40 | return ( 41 | window.innerWidth >= 1366 ? "large" : 42 | window.innerWidth >= 800 ? "medium" : 43 | "small" 44 | ); 45 | } 46 | 47 | var App = React.createClass({ 48 | getSplitViewConfig: function () { 49 | return splitViewConfigs[this.state.mode]; 50 | }, 51 | handlePeopleChanged: function (newPeople) { 52 | this.setState({ 53 | people: newPeople 54 | }); 55 | }, 56 | handleNavigation: function (newLocation) { 57 | this.setState({ 58 | location: newLocation 59 | }); 60 | }, 61 | handleBack: function () { 62 | var location = this.state.location; 63 | location.pop(); 64 | this.handleNavigation(location); 65 | }, 66 | handleResize: function () { 67 | var prevMode = this.state.mode; 68 | var nextMode = getMode(); 69 | 70 | if (prevMode !== nextMode) { 71 | this.setState({ mode: nextMode }); 72 | } 73 | }, 74 | handleCommandInvoked: function (newLocation) { 75 | this.setState({ 76 | location: newLocation, 77 | paneOpened: this.getSplitViewConfig().openedDisplayMode === "overlay" ? false : this.state.paneOpened 78 | }); 79 | }, 80 | handleTogglePane: function () { 81 | this.setState({ paneOpened: !this.state.paneOpened }); 82 | }, 83 | handleAfterClose: function () { 84 | this.setState({ paneOpened: false }); 85 | }, 86 | getInitialState: function () { 87 | var mode = getMode(); 88 | 89 | var groupKey = function (data) { 90 | return data.name[0].toUpperCase(); 91 | }; 92 | 93 | var groupData = function (data) { 94 | return { title: groupKey(data) }; 95 | }; 96 | 97 | var sorter = function (a, b) { 98 | if (a.name < b.name) { 99 | return -1; 100 | } else if (a.name > b.name) { 101 | return 1; 102 | } else { 103 | return 0; 104 | } 105 | }; 106 | 107 | var data = new WinJS.Binding.List(Data.people) 108 | .createSorted(sorter) 109 | .createGrouped(groupKey, groupData); 110 | 111 | return { 112 | people: data, 113 | mode: mode, 114 | location: ["people"] 115 | }; 116 | }, 117 | componentWillMount: function () { 118 | window.addEventListener("resize", this.handleResize); 119 | }, 120 | componentWillUnmount: function () { 121 | window.removeEventListener("resize", this.handleResize); 122 | }, 123 | renderPeoplePage: function () { 124 | return ( 125 | <PeoplePage 126 | mode={this.state.mode} 127 | people={this.state.people} 128 | location={this.state.location} 129 | onNavigate={this.handleNavigation} 130 | onPeopleChanged={this.handlePeopleChanged} /> 131 | ); 132 | }, 133 | renderOtherPage: function () { 134 | return <OtherPage location={this.state.location} /> 135 | }, 136 | renderContent: function () { 137 | if (this.state.location.length === 0 || this.state.location[0] === "people") { 138 | return this.renderPeoplePage(); 139 | } else { 140 | return this.renderOtherPage(); 141 | } 142 | }, 143 | renderBackButton: function () { 144 | var canGoBack = this.state.location.length > 1; 145 | var shouldShowBackButton = canGoBack && this.state.mode === "small"; 146 | return shouldShowBackButton ? 147 | <button style={{display: "inline-block"}} className="win-backbutton" onClick={this.handleBack} /> : 148 | null; 149 | }, 150 | render: function () { 151 | var paneComponent = ( 152 | <div> 153 | <ReactWinJS.SplitView.Command 154 | label="People" 155 | icon="contact" 156 | onInvoked={this.handleCommandInvoked.bind(null, ["people"])} /> 157 | <ReactWinJS.SplitView.Command 158 | label="What's New" 159 | icon="comment" 160 | onInvoked={this.handleCommandInvoked.bind(null, ["new"])} /> 161 | <ReactWinJS.SplitView.Command 162 | label="Groups" 163 | icon="people" 164 | onInvoked={this.handleCommandInvoked.bind(null, ["groups"])} /> 165 | 166 | <ReactWinJS.SplitView.Command 167 | style={{position: "absolute", bottom: 0, width: "100%"}} 168 | label="Settings" 169 | icon="settings" 170 | onInvoked={this.handleCommandInvoked.bind(null, ["settings"])} /> 171 | </div> 172 | ); 173 | 174 | var contentComponent = this.renderContent(); 175 | 176 | return ( 177 | <div style={{height: "100%"}}> 178 | <div style={{height: 48, backgroundColor: "rgb(1, 121, 216)"}} className="win-ui-dark"> 179 | <ReactWinJS.SplitViewPaneToggle 180 | aria-controls={splitViewId} 181 | style={{display:'inline-block'}} 182 | paneOpened={this.state.paneOpened} 183 | onInvoked={this.handleTogglePane} /> 184 | {this.renderBackButton()} 185 | <h3 className="win-h3" style={{display: "inline-block", marginLeft: 5}}>Address Book</h3> 186 | </div> 187 | <ReactWinJS.SplitView 188 | id={splitViewId} 189 | style={{height: "calc(100% - 48px)"}} 190 | paneComponent={paneComponent} 191 | contentComponent={contentComponent} 192 | onAfterClose={this.handleAfterClose} 193 | paneOpened={this.state.paneOpened} 194 | {...this.getSplitViewConfig()} /> 195 | </div> 196 | ); 197 | } 198 | }); 199 | 200 | ReactDOM.render(<App />, document.getElementById("app")); 201 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corp. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. 2 | 3 | "use strict"; 4 | 5 | var exec = require('child_process').exec; 6 | var execSync = require('child_process').execSync; 7 | 8 | module.exports = function(grunt) { 9 | 10 | var publishRoot = 'dist/'; 11 | var npmPublishRoot = publishRoot + 'npm/'; 12 | 13 | // All version number information is derived from package.json. This includes the version 14 | // info used with npm, bower, NuGet, and the GitHub release. 15 | var pkg = grunt.file.readJSON('package.json'); 16 | var fullWinjsVersion = pkg.peerDependencies.winjs; 17 | if (!fullWinjsVersion) { 18 | grunt.fail.fatal('Unable to determine WinJS version required by react-winjs'); 19 | } 20 | // package.json version contains <major>.<minor>.<patch>. We just want <major>.<minor> 21 | var winjsVersion = fullWinjsVersion.split(".").slice(0, 2).join("."); 22 | 23 | var currentGitCommitHash = execSync('git rev-parse HEAD').toString().trim(); 24 | 25 | var bomGlob = "**/*.+(js|css|htm|html)"; 26 | 27 | // Project configuration. 28 | grunt.initConfig({ 29 | pkg: pkg, 30 | 31 | clean: { 32 | publish: [publishRoot] 33 | }, 34 | 35 | copy: { 36 | publish: { 37 | files: [{ 38 | expand: true, 39 | src: [ 40 | 'react-winjs.js', 41 | 'LICENSE.txt', 42 | 'package.json', 43 | 'README.md' 44 | ], 45 | dest: npmPublishRoot 46 | }] 47 | } 48 | }, 49 | 50 | compress: { 51 | publish: { 52 | options: { 53 | archive: publishRoot + 'react-winjs.zip' 54 | }, 55 | files: [{ 56 | expand: true, 57 | cwd: npmPublishRoot, 58 | src: ["**"] 59 | }] 60 | } 61 | }, 62 | 63 | "check-bom": { 64 | publish: { 65 | files: [{ 66 | src: "react-winjs.js", 67 | expand: true, 68 | nocase: true 69 | }, { 70 | cwd: publishRoot, 71 | src: bomGlob, 72 | expand: true, 73 | nocase: true 74 | }] 75 | } 76 | }, 77 | 78 | nugetpack: { 79 | publish: { 80 | src: 'React.WinJS.nuspec', 81 | dest: publishRoot, 82 | options: { 83 | version: '<%= pkg.version %>' 84 | } 85 | } 86 | }, 87 | 88 | // Publishes nuget package 89 | nugetpush: { 90 | // Requires NuGet API key to be set. You can do this with: 91 | // grunt nugetkey --key=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 92 | publish: { 93 | src: publishRoot + '*.nupkg', 94 | } 95 | }, 96 | 97 | // Publishes GitHub release and bower package (bower consumes GitHub tags/releases) 98 | 'github-release': { 99 | // Requires this environment variable to be set: GITHUB_ACCESS_TOKEN 100 | // GITHUB_ACCESS_TOKEN can be generated from https://help.github.com/articles/creating-an-access-token-for-command-line-use/ 101 | publish: { 102 | options: { 103 | repository: 'winjs/react-winjs', 104 | auth: { 105 | user: process.env.GITHUB_ACCESS_TOKEN 106 | }, 107 | release: { 108 | tag_name: 'v<%= pkg.version %>', // Must follow semver syntax in order for bower to pick it up 109 | target_commitish: currentGitCommitHash, 110 | name: '<%= pkg.version %>', 111 | body: 112 | 'Release of react-winjs <%= pkg.version %>.\n' + 113 | '\n' + 114 | 'Compatible with WinJS ' + winjsVersion + '.\n' 115 | } 116 | }, 117 | files: { 118 | src: [publishRoot + 'react-winjs.zip'] 119 | } 120 | } 121 | } 122 | }); 123 | 124 | grunt.loadTasks('tasks/'); 125 | 126 | var plugins = [ 127 | 'grunt-contrib-clean', 128 | 'grunt-contrib-compress', 129 | 'grunt-contrib-copy', 130 | 'grunt-nuget', 131 | 'grunt-github-releaser' 132 | ]; 133 | plugins.forEach(function (plugin) { 134 | grunt.loadNpmTasks(plugin); 135 | }); 136 | 137 | // Publishes npm package 138 | grunt.registerTask('npm-release', function (mode) { 139 | var done = this.async(); 140 | var cmd = 'npm publish ' + npmPublishRoot; 141 | 142 | exec(cmd, function (err, stdout) { 143 | if (err) { 144 | grunt.fatal('npm publish failed using command: ' + cmd); 145 | } 146 | done(); 147 | }); 148 | }); 149 | 150 | // Sets up all of the state necessary to do a publish but doesn't actually publish 151 | // to any of the package managers. 152 | grunt.registerTask('prepare-publish', [ 153 | 'clean:publish', 154 | 'copy:publish', 155 | 'compress:publish', 156 | 'nugetpack:publish', 157 | 'check-bom:publish', 158 | ]); 159 | 160 | grunt.registerTask('finished-publish', function (mode) { 161 | grunt.log.writeln(''); 162 | grunt.log.writeln('Publish complete. Hand tweak the GitHub release description if necessary (https://github.com/winjs/react-winjs/releases)'); 163 | grunt.log.writeln(''); 164 | }); 165 | 166 | // 167 | // Public tasks designed to be run from the command line 168 | // 169 | 170 | // Populates the 'dist' folder and then uses it to: 171 | // - Create a GitHub release 172 | // - Publish to npm 173 | // - Publish to bower 174 | // - Publish to NuGet 175 | // When debugging publish, it's helpful to run just the 'prepare-publish' 176 | // task which puts all of the publication data into the 'dist' folder but 177 | // doesn't actually send the data to the package managers. 178 | grunt.registerTask('publish', function (mode) { 179 | if (!process.env.GITHUB_ACCESS_TOKEN) { 180 | grunt.fail.fatal('The GITHUB_ACCESS_TOKEN environment variable must be set in order to create GitHub releases'); 181 | } 182 | 183 | if (!mode) { 184 | grunt.log.writeln(''); 185 | grunt.log.writeln('Will publish version ' + pkg.version + ' of react-winjs to npm, NuGet, etc. Double check that:'); 186 | grunt.log.writeln(' * You are on the branch you\'d like to publish'); 187 | grunt.log.writeln(' * The branch has been pushed to GitHub'); 188 | grunt.log.writeln(' * You don\'t have any local edits'); 189 | grunt.log.writeln(''); 190 | grunt.log.writeln('If everything is in order, run "grunt publish:force" to proceed'); 191 | } else if (mode === 'force') { 192 | grunt.task.run([ 193 | 'prepare-publish', 194 | 'nugetpush:publish', 195 | 'github-release:publish', 196 | 'npm-release', 197 | 'finished-publish', 198 | ]); 199 | } 200 | }); 201 | }; 202 | -------------------------------------------------------------------------------- /examples/movies/FakeData.js: -------------------------------------------------------------------------------- 1 | var nextMovieId = 0; 2 | var ratings = ["Unrated", "G", "PG", "PG-13", "R"]; 3 | var firstNames = ["Charlsie", "Ofelia", "Jerome", "Leana", "Harlan", "Annalisa", "Leida", "Dessie", "Valrie", "Sharen", "Sergio", "Mitzie", "Celia", "Debbra", "Florida", "Kara", "Jacquie", "Sherley", "Carson", "Staci", "Paula", "Dann", "Linette", "Meri", "Almeta", "Detra", "Lupe", "Neville", "Marivel", "Carmine", "Carina", "Laureen", "Lourdes", "Laverne", "Verona", "Gertha", "Jene", "Joslyn", "Jone", "Latoya", "Margurite", "Emmett", "Wallace", "Elana", "Xiomara", "Sabra", "Ouida", "Kenton", "Norene", "Raul"]; 4 | var lastNames = ["Avey", "Crofoot", "Flor", "Barletta", "Zoller", "Rosson", "Coomes", "Wilken", "Withey", "Ojeda", "Mennella", "Gauer", "Puccio", "Zimmerer", "Cottrell", "Bridgman", "Gershman", "Tinoco", "Ayoub", "Fournier", "Marcella", "Melrose", "Lafontaine", "Cathcart", "Cioffi", "Sands", "Lei", "Cardoso", "Dela", "Metcalfe", "Ethridge", "Fryer", "Warden", "Madson", "Gonsales", "Tobey", "Knecht", "Gallion", "Thibault", "Brockington", "Baney", "Haddox", "Kang", "Galyean", "Riccio", "Lake", "Mirabella", "Frechette", "Rearick", "Carmouche"]; 5 | var loremIpsum = [ 6 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum", 7 | "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur", 8 | "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?", 9 | "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.", 10 | "On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.", 11 | ]; 12 | var words = loremIpsum.join(" ").replace(/,|!|\?|\./g, "").replace(/-/g, " ").split(" "); 13 | 14 | var posterWidth = 153; 15 | var posterHeight = 243; 16 | var _canvas; 17 | function makePoster(color) { 18 | if (!_canvas) { 19 | _canvas = document.createElement("canvas"); 20 | _canvas.width = posterWidth; 21 | _canvas.height = posterHeight; 22 | } 23 | var ctxt = _canvas.getContext("2d"); 24 | ctxt.fillStyle = color; 25 | ctxt.fillRect(0, 0, posterWidth, posterHeight); 26 | return _canvas.toDataURL(); 27 | } 28 | 29 | var posterColors = [ 30 | [68, 34, 87], [100, 66, 119], [132, 98, 151], 31 | [164, 162, 165], [196, 194, 197], [228, 226, 229] 32 | ]; 33 | var posters = posterColors.map(function (color) { 34 | return makePoster("rgb(" + color.join(", ") + ")"); 35 | }); 36 | 37 | function randomInt(first, last) { 38 | return Math.round(Math.random() * (last - first)) + first; 39 | } 40 | 41 | function randomElement(array) { 42 | return array[randomInt(0, array.length - 1)]; 43 | } 44 | 45 | function genArray(minLength, maxLength, genElement) { 46 | var len = randomInt(minLength, maxLength); 47 | var result = new Array(len); 48 | for (var i = 0; i < len; i++) { 49 | result[i] = genElement(); 50 | } 51 | return result; 52 | } 53 | 54 | function genActor() { 55 | return { name: randomElement(firstNames) + " " + randomElement(lastNames) }; 56 | } 57 | 58 | function genTitleWord() { 59 | var word = randomElement(words).toLowerCase(); 60 | return word[0].toUpperCase() + word.substr(1); 61 | } 62 | 63 | function genMovie() { 64 | return { 65 | id: nextMovieId++, 66 | title: (nextMovieId - 1 + " ") + genArray(1, 5, genTitleWord).join(" "), 67 | year: randomInt(1950, 2015), 68 | mpaa_rating: randomElement(ratings), 69 | synopsis: randomElement(loremIpsum), 70 | posters: { 71 | detailed: randomElement(posters) 72 | }, 73 | ratings: { 74 | critics_score: randomInt(0, 100), 75 | audience_score: randomInt(0, 100) 76 | }, 77 | abridged_cast: genArray(1, 8, genActor) 78 | }; 79 | } 80 | 81 | var movieCount = 1000; 82 | var movies = genArray(movieCount, movieCount, genMovie); 83 | var moviesPerPage = 20; 84 | var fetchDelay = 500; 85 | 86 | function getSearchResults(query, page) { 87 | query = query.toLowerCase(); 88 | var start = (page - 1) * moviesPerPage; 89 | var results = movies.filter(function (m) { 90 | return m.title.toLowerCase().indexOf(query) !== -1; 91 | }); 92 | return WinJS.Promise.timeout(fetchDelay).then(function () { 93 | return { 94 | movies: results.slice(start, start + moviesPerPage), 95 | total: results.length 96 | }; 97 | }); 98 | } 99 | 100 | function getInTheaters(page) { 101 | var start = (page - 1) * moviesPerPage; 102 | return WinJS.Promise.timeout(fetchDelay).then(function () { 103 | return { 104 | movies: movies.slice(start, start + moviesPerPage), 105 | total: movies.length 106 | }; 107 | }); 108 | } 109 | 110 | module.exports = { 111 | getSearchResults: getSearchResults, 112 | getInTheaters: getInTheaters 113 | }; -------------------------------------------------------------------------------- /examples/address-book/FakeData.js: -------------------------------------------------------------------------------- 1 | var nextPersonId = 0; 2 | var firstNames = ["Aaliyah","Aaron","Abigail","Adam","Addison","Adrian","Aiden","Alexa","Alexandra","Alexis","Alice","Allison","Alyssa","Amelia","Andrew","Angel","Anna","Annabelle","Anthony","Aria","Ariana","Arianna","Asher","Ashley","Aubree","Aubrey","Audrey","Austin","Autumn","Ava","Avery","Ayden","Bella","Benjamin","Bentley","Blake","Brandon","Brayden","Brianna","Brody","Brooklyn","Caleb","Camden","Cameron","Camila","Caroline","Carson","Carter","Charles","Charlotte","Chase","Chloe","Christian","Christopher","Claire","Colton","Connor","Cooper","Daniel","David","Dominic","Dylan","Easton","Eleanor","Eli","Elijah","Elizabeth","Ella","Ellie","Emily","Emma","Eva","Evan","Evelyn","Faith","Gabriel","Gabriella","Gavin","Genesis","Gianna","Grace","Grayson","Hadley","Hailey","Hannah","Harper","Henry","Hudson","Hunter","Ian","Isaac","Isabella","Isabelle","Isaiah","Jace","Jack","Jackson","Jasmine","Jason","Jaxon","Jaxson","Jayden","Jeremiah","John","Jonathan","Jordan","Jose","Joseph","Joshua","Josiah","Juan","Julia","Julian","Justin","Katherine","Kayden","Kaylee","Kennedy","Kevin","Khloe","Kylie","Landon","Lauren","Layla","Leah","Leo","Levi","Lillian","Lily","Lincoln","Logan","London","Lucas","Lucy","Luis","Luke","Lydia","Mackenzie","Madeline","Madelyn","Madison","Matthew","Maya","Melanie","Mia","Mila","Naomi","Natalie","Nathan","Nathaniel","Nevaeh","Nicholas","Nolan","Nora","Oliver","Olivia","Owen","Paisley","Parker","Penelope","Peyton","Piper","Riley","Robert","Ruby","Ryan","Ryder","Sadie","Samantha","Samuel","Sarah","Savannah","Scarlett","Sebastian","Serenity","Skylar","Sofia","Sophia","Sophie","Stella","Taylor","Thomas","Tristan","Tyler","Victoria","Violet","Vivian","Wyatt","Xavier","Zachary","Zoe","Zoey"]; 3 | var lastNames = ["Avey", "Crofoot", "Flor", "Barletta", "Zoller", "Rosson", "Coomes", "Wilken", "Withey", "Ojeda", "Mennella", "Gauer", "Puccio", "Zimmerer", "Cottrell", "Bridgman", "Gershman", "Tinoco", "Ayoub", "Fournier", "Marcella", "Melrose", "Lafontaine", "Cathcart", "Cioffi", "Sands", "Lei", "Cardoso", "Dela", "Metcalfe", "Ethridge", "Fryer", "Warden", "Madson", "Gonsales", "Tobey", "Knecht", "Gallion", "Thibault", "Brockington", "Baney", "Haddox", "Kang", "Galyean", "Riccio", "Lake", "Mirabella", "Frechette", "Rearick", "Carmouche"]; 4 | var loremIpsum = [ 5 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum", 6 | "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur", 7 | "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?", 8 | "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.", 9 | "On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.", 10 | ]; 11 | var statuses = [ 12 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim.", 13 | "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo.", 14 | "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account.", 15 | "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas.", 16 | "On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the." 17 | ]; 18 | 19 | var posterWidth = 400; 20 | var posterHeight = 400; 21 | var _canvas; 22 | function makePoster(color) { 23 | if (!_canvas) { 24 | _canvas = document.createElement("canvas"); 25 | _canvas.width = posterWidth; 26 | _canvas.height = posterHeight; 27 | } 28 | var ctxt = _canvas.getContext("2d"); 29 | ctxt.fillStyle = color; 30 | ctxt.fillRect(0, 0, posterWidth, posterHeight); 31 | return _canvas.toDataURL(); 32 | } 33 | 34 | var posterColors = [ 35 | [68, 34, 87], [100, 66, 119], [132, 98, 151], 36 | [164, 162, 165], [196, 194, 197], [228, 226, 229], 37 | [220, 77, 6], [252, 109, 38], [255, 141, 70] 38 | ]; 39 | var posters = posterColors.map(function (color) { 40 | return makePoster("rgb(" + color.join(", ") + ")"); 41 | }); 42 | 43 | function randomInt(first, last) { 44 | return Math.round(Math.random() * (last - first)) + first; 45 | } 46 | 47 | function randomElement(array) { 48 | return array[randomInt(0, array.length - 1)]; 49 | } 50 | 51 | function genArray(minLength, maxLength, genElement) { 52 | var len = randomInt(minLength, maxLength); 53 | var result = new Array(len); 54 | for (var i = 0; i < len; i++) { 55 | result[i] = genElement(); 56 | } 57 | return result; 58 | } 59 | 60 | function genName() { 61 | return randomElement(firstNames) + " " + randomElement(lastNames); 62 | } 63 | 64 | function genPhoneNumber() { 65 | return "555-0" + randomInt(100, 199); 66 | } 67 | 68 | function genPerson() { 69 | return { 70 | id: nextPersonId++, 71 | name: genName(), 72 | status: randomElement(statuses), 73 | statusHoursAgo: randomElement([2, 3, 4, 5, 6, 7, 8, 9]), 74 | picture: randomElement(posters), 75 | mobilePhone: genPhoneNumber(), 76 | workPhone: genPhoneNumber() 77 | }; 78 | } 79 | 80 | var personCount = 50; 81 | var people = genArray(personCount, personCount, genPerson); 82 | 83 | module.exports = { 84 | people: people 85 | }; -------------------------------------------------------------------------------- /examples/address-book/PeoplePage.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require('react'); 4 | var ReactWinJS = require('react-winjs'); 5 | var ProfilePicture = require('./ProfilePicture.jsx'); 6 | 7 | function calc100PercentMinus(n) { 8 | return n === 0 ? 9 | "100%" : 10 | "calc(100% - " + (n + "px") + ")"; 11 | } 12 | 13 | var PeoplePage = React.createClass({ 14 | handleToggleSelectionMode: function () { 15 | this.setState({ 16 | selectionMode: !this.state.selectionMode 17 | }); 18 | this.props.onNavigate(["people"]); 19 | this.refs.listView.winControl.selection.clear(); 20 | }, 21 | handleSelectionChanged: function (eventObject) { 22 | var listView = eventObject.currentTarget.winControl; 23 | var indices = listView.selection.getIndices(); 24 | // Post to avoid navigating while in the middle of the event handler 25 | setTimeout(function () { 26 | this.setState({ selectedPeople: indices }); 27 | this.props.onNavigate(indices.length === 1 && !this.state.selectionMode ? ["people", indices[0]] : ["people"]); 28 | }.bind(this), 0); 29 | }, 30 | handleDelete: function () { 31 | var people = this.props.people; 32 | var indices = this.state.selectedPeople; 33 | indices.sort(); 34 | indices.reverse(); 35 | indices.forEach(function (i) { 36 | people.splice(i, 1); 37 | }); 38 | this.setState({ 39 | selectedPeople: [], 40 | selectionMode: false 41 | }); 42 | this.props.onPeopleChanged(people); 43 | }, 44 | handleContentAnimating: function (eventObject) { 45 | // Disable ListView's entrance animation 46 | if (eventObject.detail.type === "entrance") { 47 | eventObject.preventDefault(); 48 | } 49 | }, 50 | personRenderer: ReactWinJS.reactRenderer(function (person) { 51 | return ( 52 | <div> 53 | <ProfilePicture backgroundUrl={person.data.picture} size={34} /> 54 | <span className="name">{person.data.name}</span> 55 | </div> 56 | ); 57 | }), 58 | groupHeaderRenderer: ReactWinJS.reactRenderer(function (item) { 59 | return ( 60 | <div>{item.data.title}</div> 61 | ); 62 | }), 63 | renderPeoplePane: function (peoplePaneWidth) { 64 | var deleteCommand = ( 65 | <ReactWinJS.ToolBar.Button 66 | key="delete" 67 | icon="delete" 68 | priority={0} 69 | disabled={this.state.selectedPeople.length === 0} 70 | onClick={this.handleDelete} /> 71 | ); 72 | 73 | return ( 74 | <div className="peopleSearchPane" style={{height: "100%", width: peoplePaneWidth, display: "inline-block", verticalAlign:"top"}}> 75 | <ReactWinJS.ToolBar className="peopleToolBar"> 76 | <ReactWinJS.ToolBar.Button 77 | key="edit" 78 | icon="edit" 79 | label="Edit" 80 | priority={4} /> 81 | <ReactWinJS.ToolBar.Button 82 | key="favorite" 83 | icon="favorite" 84 | label="Favorite" 85 | priority={3} /> 86 | <ReactWinJS.ToolBar.Button 87 | key="link" 88 | icon="link" 89 | label="Link" 90 | priority={2} /> 91 | <ReactWinJS.ToolBar.Button 92 | key="refresh" 93 | icon="refresh" 94 | label="Refresh" 95 | priority={1} /> 96 | 97 | <ReactWinJS.ToolBar.Button 98 | key="add" 99 | icon="add" 100 | label="Add" 101 | priority={0} /> 102 | {this.state.selectionMode ? deleteCommand : null} 103 | <ReactWinJS.ToolBar.Toggle 104 | key="select" 105 | icon="bullets" 106 | label="Select" 107 | priority={0} 108 | selected={this.state.selectionMode} 109 | onClick={this.handleToggleSelectionMode} /> 110 | </ReactWinJS.ToolBar> 111 | 112 | <ReactWinJS.ListView 113 | ref="listView" 114 | className="peopleListView win-selectionstylefilled" 115 | style={{height: "calc(100% - 48px)"}} 116 | itemDataSource={this.props.people.dataSource} 117 | itemTemplate={this.personRenderer} 118 | groupDataSource={this.props.people.groups.dataSource} 119 | groupHeaderTemplate={this.groupHeaderRenderer} 120 | layout={this.state.layout} 121 | selectionMode={this.state.selectionMode ? "multi" : "single"} 122 | tapBehavior={this.state.selectionMode ? "toggleSelect" : "directSelect"} 123 | onSelectionChanged={this.handleSelectionChanged} 124 | onContentAnimating={this.handleContentAnimating} /> 125 | </div> 126 | ); 127 | }, 128 | renderProfilePane: function (selectedIndex, peoplePaneWidth) { 129 | if (selectedIndex === null) { 130 | return ( 131 | <div className="profilePane" style={{height: "100%", width: calc100PercentMinus(peoplePaneWidth), display: "inline-block",verticalAlign:"top"}}> 132 | <div style={{display: "flex", height: "100%", justifyContent: "center", alignItems: "center", flexDirection: "column"}}> 133 | <h1 className="win-h1" style={{color: "grey"}}>No Selection</h1> 134 | </div> 135 | </div> 136 | ); 137 | } else { 138 | var selectedPerson = this.props.people.getAt(selectedIndex); 139 | return ( 140 | <div className="profilePane" style={{height: "100%", width: calc100PercentMinus(peoplePaneWidth), display: "inline-block",verticalAlign:"top"}}> 141 | <div className="profileHeader"> 142 | <div className="name">{selectedPerson.name}</div> 143 | <div className="personInfo"> 144 | <ProfilePicture backgroundUrl={selectedPerson.picture} size={100} /> 145 | <div className="profileStatus"> 146 | <span className="message"> 147 | {selectedPerson.status} 148 | </span> 149 | <span className="source">{selectedPerson.statusHoursAgo} hours ago</span> 150 | </div> 151 | </div> 152 | </div> 153 | <div className="separator" /> 154 | <div className="profileContent"> 155 | <ul> 156 | <li><span className="messageIcon" />Message</li> 157 | <li> 158 | <span className="phoneIcon" /> 159 | <div className="callContent"> 160 | <a href="call:5550100">Call Mobile</a> 161 | <div className="number">{selectedPerson.mobilePhone}</div> 162 | </div> 163 | </li> 164 | <li> 165 | <span className="phoneIcon" /> 166 | <div className="callContent"> 167 | <a href="call:5550100">Call Work</a> 168 | <div className="number">{selectedPerson.workPhone}</div> 169 | </div> 170 | </li> 171 | <li><span className="phoneIcon" />Call using an app</li> 172 | <li><span className="videoCallIcon" />Video call</li> 173 | <li><span className="emailIcon" />Email work</li> 174 | <li><span className="mapIcon" />Map home</li> 175 | </ul> 176 | </div> 177 | </div> 178 | ); 179 | } 180 | }, 181 | propTypes: { 182 | mode: React.PropTypes.oneOf(["small", "medium", "large"]).isRequired, 183 | people: React.PropTypes.object.isRequired, 184 | location: React.PropTypes.array.isRequired, 185 | onNavigate: React.PropTypes.func.isRequired, 186 | onPeopleChanged: React.PropTypes.func.isRequired 187 | }, 188 | getInitialState: function () { 189 | return { 190 | layout: { type: WinJS.UI.ListLayout }, 191 | selectedPeople: [], 192 | selectionMode: false 193 | }; 194 | }, 195 | render: function () { 196 | var selectedIndex = this.props.location.length >= 2 ? this.props.location[1] : null; 197 | 198 | if (this.props.mode === "small") { 199 | if (selectedIndex === null) { 200 | return this.renderPeoplePane("100%"); 201 | } else { 202 | return this.renderProfilePane(selectedIndex, 0); 203 | } 204 | } else { 205 | var peoplePaneWidth = 320; 206 | return ( 207 | <div style={{height: "100%"}}> 208 | {this.renderPeoplePane(peoplePaneWidth)} 209 | {this.renderProfilePane(selectedIndex, peoplePaneWidth)} 210 | </div> 211 | ); 212 | } 213 | } 214 | }); 215 | 216 | module.exports = PeoplePage; 217 | -------------------------------------------------------------------------------- /react-winjs.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ReactDOM = require('react-dom'); 3 | var ReactDOMServer = require('react-dom/server'); 4 | var WinJS = require("winjs"); 5 | 6 | // 7 | // Implementation Overview 8 | // 9 | // react-winjs is organized around the concept of prop handlers. A prop handler describes 10 | // how a react-winjs prop affects the underlying WinJS control. Prop handlers come from 3 sources: 11 | // 1. Handwritten common to all controls. These are defined in a variable called 12 | // *defaultPropHandlers*. These are prop handlers which appear on every react-winjs component. 13 | // 2. WinJS's d.ts file. Most of the prop handlers for each react-winjs component are 14 | // automatically generated from WinJS's d.ts file. Information from the d.ts file is stored in 15 | // a variable called *RawControlApis*. *RawControlApis* is generated by running the d.ts file 16 | // through this tool: https://github.com/winjs/winjs-control-apis 17 | // 3. Handwritten control-specific. These are defined in a variable called *ControlApis*. When 18 | // a react-winjs component has a prop that needs to be defined manually, this is where it 19 | // should be defined. There are a number of types of builtin prop handlers defined in the 20 | // *PropHandlers* variable which can be used here. 21 | // 22 | // When react-winjs builds the list of prop handlers for a component, it gathers them from the above 23 | // list of sources. If multiple sources define a prop handler with the same name, the one from the 24 | // source which appears closest to the bottom of the list above wins. 25 | // 26 | // There's a particular category of WinJS controls that host content that proved to be particularly 27 | // challenging to wrap as React components. More specifically, the controls in this category: 28 | // - host content 29 | // - move the hosted content around in the DOM 30 | // - store the hosted content in a Binding.List 31 | // 32 | // Controls that fall into this category of hosting content include: 33 | // - AppBar/ToolBar 34 | // - Hub 35 | // - Pivot 36 | // 37 | // And the controls that get hosted include (these are the ones that end up being created 38 | // through WinJSChildComponent, described below): 39 | // - AppBarCommand 40 | // - HubSection 41 | // - PivotItem 42 | // 43 | // What made this challenging to solve is that there are some features that aren't achievable in 44 | // a straight forward way through the React APIs. To solve this, you want to be able to: 45 | // - Render a React component *onto* an existing element. ReactDOM.render can only render *into* an 46 | // existing element. For example, when creating a HubSection you want to be able to control 47 | // attributes of the win-hub-section element such as its *class* and *style*. With ReactDOM.render, 48 | // you'd only be able to render into the win-hub-section element so the React component wouldn't 49 | // be able to control any attributes of the win-hub-section element. 50 | // - Hold onto a rendered component and inspect its *type* and *key* prop later. This information 51 | // comes in handy when diffing an array of components from a previous render pass with an array 52 | // of components for the current render pass. The diff information is used to mutate a 53 | // Binding.List to get it into the desired state. 54 | // 55 | // The WinJSChildComponent class provides a convenient API for solving both of these problems. When the 56 | // constructor receives a component, it creates the element (e.g. win-hub-section) that the component 57 | // is rendered *onto*. The owning control (e.g. WinJS.UI.Hub) is free to manage that element and move 58 | // it around the DOM. WinJSChildComponent provides the following APIs: 59 | // - update: Updates the element based on the component description that is passed in. 60 | // - dispose: Disposes the react-winjs component and its associated WinJS control. 61 | // - key: Returns the React key associated with the component. 62 | // - type: Returns the React type associated with the component. 63 | // 64 | 65 | // Generated from https://github.com/winjs/winjs-control-apis 66 | var RawControlApis = { 67 | AppBar: { 68 | closedDisplayMode: { 69 | type: "enum", 70 | values: [ 71 | "compact", 72 | "full", 73 | "minimal", 74 | "none" 75 | ] 76 | }, 77 | data: { 78 | name: "WinJS.Binding.List", 79 | type: "reference", 80 | typeArguments: [ 81 | { 82 | name: "WinJS.UI.ICommand", 83 | type: "reference", 84 | typeArguments: [] 85 | } 86 | ] 87 | }, 88 | element: { 89 | name: "HTMLElement", 90 | type: "reference", 91 | typeArguments: [] 92 | }, 93 | onAfterClose: { 94 | name: "Function", 95 | type: "reference", 96 | typeArguments: [] 97 | }, 98 | onAfterOpen: { 99 | name: "Function", 100 | type: "reference", 101 | typeArguments: [] 102 | }, 103 | onBeforeClose: { 104 | name: "Function", 105 | type: "reference", 106 | typeArguments: [] 107 | }, 108 | onBeforeOpen: { 109 | name: "Function", 110 | type: "reference", 111 | typeArguments: [] 112 | }, 113 | opened: { 114 | type: "boolean" 115 | }, 116 | placement: { 117 | type: "enum", 118 | values: [ 119 | "bottom", 120 | "top" 121 | ] 122 | } 123 | }, 124 | AppBarCommand: { 125 | disabled: { 126 | type: "boolean" 127 | }, 128 | element: { 129 | name: "HTMLElement", 130 | type: "reference", 131 | typeArguments: [] 132 | }, 133 | extraClass: { 134 | type: "string" 135 | }, 136 | firstElementFocus: { 137 | name: "HTMLElement", 138 | type: "reference", 139 | typeArguments: [] 140 | }, 141 | flyout: { 142 | name: "WinJS.UI.Flyout", 143 | type: "reference", 144 | typeArguments: [] 145 | }, 146 | hidden: { 147 | type: "boolean" 148 | }, 149 | icon: { 150 | type: "string" 151 | }, 152 | id: { 153 | type: "string" 154 | }, 155 | label: { 156 | type: "string" 157 | }, 158 | lastElementFocus: { 159 | name: "HTMLElement", 160 | type: "reference", 161 | typeArguments: [] 162 | }, 163 | onClick: { 164 | name: "Function", 165 | type: "reference", 166 | typeArguments: [] 167 | }, 168 | priority: { 169 | type: "number" 170 | }, 171 | section: { 172 | type: "string" 173 | }, 174 | selected: { 175 | type: "boolean" 176 | }, 177 | tooltip: { 178 | type: "string" 179 | }, 180 | type: { 181 | type: "string" 182 | } 183 | }, 184 | AutoSuggestBox: { 185 | chooseSuggestionOnEnter: { 186 | type: "boolean" 187 | }, 188 | disabled: { 189 | type: "boolean" 190 | }, 191 | element: { 192 | name: "HTMLElement", 193 | type: "reference", 194 | typeArguments: [] 195 | }, 196 | onQueryChanged: { 197 | name: "Function", 198 | type: "reference", 199 | typeArguments: [] 200 | }, 201 | onQuerySubmitted: { 202 | name: "Function", 203 | type: "reference", 204 | typeArguments: [] 205 | }, 206 | onResultSuggestionChosen: { 207 | name: "Function", 208 | type: "reference", 209 | typeArguments: [] 210 | }, 211 | onSuggestionsRequested: { 212 | name: "Function", 213 | type: "reference", 214 | typeArguments: [] 215 | }, 216 | placeholderText: { 217 | type: "string" 218 | }, 219 | queryText: { 220 | type: "string" 221 | }, 222 | searchHistoryContext: { 223 | type: "string" 224 | }, 225 | searchHistoryDisabled: { 226 | type: "boolean" 227 | } 228 | }, 229 | BackButton: { 230 | element: { 231 | name: "HTMLElement", 232 | type: "reference", 233 | typeArguments: [] 234 | } 235 | }, 236 | CellSpanningLayout: { 237 | groupHeaderPosition: { 238 | type: "enum", 239 | values: [ 240 | "left", 241 | "top" 242 | ] 243 | }, 244 | groupInfo: { 245 | name: "Function", 246 | type: "reference", 247 | typeArguments: [] 248 | }, 249 | itemInfo: { 250 | name: "Function", 251 | type: "reference", 252 | typeArguments: [] 253 | }, 254 | maximumRowsOrColumns: { 255 | type: "number" 256 | }, 257 | numberOfItemsPerItemsBlock: { 258 | type: "any" 259 | }, 260 | orientation: { 261 | type: "enum", 262 | values: [ 263 | "horizontal", 264 | "vertical" 265 | ] 266 | } 267 | }, 268 | Command: { 269 | disabled: { 270 | type: "boolean" 271 | }, 272 | element: { 273 | name: "HTMLElement", 274 | type: "reference", 275 | typeArguments: [] 276 | }, 277 | extraClass: { 278 | type: "string" 279 | }, 280 | firstElementFocus: { 281 | name: "HTMLElement", 282 | type: "reference", 283 | typeArguments: [] 284 | }, 285 | flyout: { 286 | name: "WinJS.UI.Flyout", 287 | type: "reference", 288 | typeArguments: [] 289 | }, 290 | hidden: { 291 | type: "boolean" 292 | }, 293 | icon: { 294 | type: "string" 295 | }, 296 | id: { 297 | type: "string" 298 | }, 299 | label: { 300 | type: "string" 301 | }, 302 | lastElementFocus: { 303 | name: "HTMLElement", 304 | type: "reference", 305 | typeArguments: [] 306 | }, 307 | onClick: { 308 | name: "Function", 309 | type: "reference", 310 | typeArguments: [] 311 | }, 312 | priority: { 313 | type: "number" 314 | }, 315 | section: { 316 | type: "string" 317 | }, 318 | selected: { 319 | type: "boolean" 320 | }, 321 | tooltip: { 322 | type: "string" 323 | }, 324 | type: { 325 | type: "string" 326 | } 327 | }, 328 | ContentDialog: { 329 | element: { 330 | name: "HTMLElement", 331 | type: "reference", 332 | typeArguments: [] 333 | }, 334 | hidden: { 335 | type: "boolean" 336 | }, 337 | onAfterHide: { 338 | name: "Function", 339 | type: "reference", 340 | typeArguments: [] 341 | }, 342 | onAfterShow: { 343 | name: "Function", 344 | type: "reference", 345 | typeArguments: [] 346 | }, 347 | onBeforeHide: { 348 | name: "Function", 349 | type: "reference", 350 | typeArguments: [] 351 | }, 352 | onBeforeShow: { 353 | name: "Function", 354 | type: "reference", 355 | typeArguments: [] 356 | }, 357 | primaryCommandDisabled: { 358 | type: "boolean" 359 | }, 360 | primaryCommandText: { 361 | type: "string" 362 | }, 363 | secondaryCommandDisabled: { 364 | type: "boolean" 365 | }, 366 | secondaryCommandText: { 367 | type: "string" 368 | }, 369 | title: { 370 | type: "string" 371 | } 372 | }, 373 | DatePicker: { 374 | calendar: { 375 | type: "string" 376 | }, 377 | current: { 378 | name: "Date", 379 | type: "reference", 380 | typeArguments: [] 381 | }, 382 | datePattern: { 383 | type: "string" 384 | }, 385 | disabled: { 386 | type: "boolean" 387 | }, 388 | element: { 389 | name: "HTMLElement", 390 | type: "reference", 391 | typeArguments: [] 392 | }, 393 | maxYear: { 394 | type: "number" 395 | }, 396 | minYear: { 397 | type: "number" 398 | }, 399 | monthPattern: { 400 | type: "string" 401 | }, 402 | onChange: { 403 | name: "Function", 404 | type: "reference", 405 | typeArguments: [] 406 | }, 407 | yearPattern: { 408 | type: "string" 409 | } 410 | }, 411 | FlipView: { 412 | currentPage: { 413 | type: "number" 414 | }, 415 | element: { 416 | name: "HTMLElement", 417 | type: "reference", 418 | typeArguments: [] 419 | }, 420 | itemDataSource: { 421 | name: "WinJS.UI.IListDataSource", 422 | type: "reference", 423 | typeArguments: [ 424 | { 425 | name: "T", 426 | type: "type-param" 427 | } 428 | ] 429 | }, 430 | itemSpacing: { 431 | type: "number" 432 | }, 433 | itemTemplate: { 434 | type: "any" 435 | }, 436 | onDataSourceCountChanged: { 437 | name: "Function", 438 | type: "reference", 439 | typeArguments: [] 440 | }, 441 | onPageCompleted: { 442 | name: "Function", 443 | type: "reference", 444 | typeArguments: [] 445 | }, 446 | onPageSelected: { 447 | name: "Function", 448 | type: "reference", 449 | typeArguments: [] 450 | }, 451 | onPageVisibilityChanged: { 452 | name: "Function", 453 | type: "reference", 454 | typeArguments: [] 455 | }, 456 | orientation: { 457 | type: "string" 458 | } 459 | }, 460 | Flyout: { 461 | alignment: { 462 | type: "string" 463 | }, 464 | anchor: { 465 | name: "HTMLElement", 466 | type: "reference", 467 | typeArguments: [] 468 | }, 469 | disabled: { 470 | type: "boolean" 471 | }, 472 | element: { 473 | name: "HTMLElement", 474 | type: "reference", 475 | typeArguments: [] 476 | }, 477 | hidden: { 478 | type: "boolean" 479 | }, 480 | onAfterHide: { 481 | name: "Function", 482 | type: "reference", 483 | typeArguments: [] 484 | }, 485 | onAfterShow: { 486 | name: "Function", 487 | type: "reference", 488 | typeArguments: [] 489 | }, 490 | onBeforeHide: { 491 | name: "Function", 492 | type: "reference", 493 | typeArguments: [] 494 | }, 495 | onBeforeShow: { 496 | name: "Function", 497 | type: "reference", 498 | typeArguments: [] 499 | }, 500 | placement: { 501 | type: "string" 502 | } 503 | }, 504 | GridLayout: { 505 | backdropColor: { 506 | type: "string" 507 | }, 508 | disableBackdrop: { 509 | type: "boolean" 510 | }, 511 | groupHeaderPosition: { 512 | type: "enum", 513 | values: [ 514 | "left", 515 | "top" 516 | ] 517 | }, 518 | groupInfo: { 519 | name: "Function", 520 | type: "reference", 521 | typeArguments: [] 522 | }, 523 | itemInfo: { 524 | name: "Function", 525 | type: "reference", 526 | typeArguments: [] 527 | }, 528 | maxRows: { 529 | type: "number" 530 | }, 531 | maximumRowsOrColumns: { 532 | type: "number" 533 | }, 534 | numberOfItemsPerItemsBlock: { 535 | type: "any" 536 | }, 537 | orientation: { 538 | type: "enum", 539 | values: [ 540 | "horizontal", 541 | "vertical" 542 | ] 543 | } 544 | }, 545 | Hub: { 546 | element: { 547 | name: "HTMLElement", 548 | type: "reference", 549 | typeArguments: [] 550 | }, 551 | headerTemplate: { 552 | type: "any" 553 | }, 554 | indexOfFirstVisible: { 555 | type: "number" 556 | }, 557 | indexOfLastVisible: { 558 | type: "number" 559 | }, 560 | loadingState: { 561 | type: "enum", 562 | values: [ 563 | "complete", 564 | "loading" 565 | ] 566 | }, 567 | onContentAnimating: { 568 | name: "Function", 569 | type: "reference", 570 | typeArguments: [] 571 | }, 572 | onHeaderInvoked: { 573 | name: "Function", 574 | type: "reference", 575 | typeArguments: [] 576 | }, 577 | onLoadingStateChanged: { 578 | name: "Function", 579 | type: "reference", 580 | typeArguments: [] 581 | }, 582 | orientation: { 583 | type: "enum", 584 | values: [ 585 | "horizontal", 586 | "vertical" 587 | ] 588 | }, 589 | scrollPosition: { 590 | type: "number" 591 | }, 592 | sectionOnScreen: { 593 | type: "number" 594 | }, 595 | sections: { 596 | name: "WinJS.Binding.List", 597 | type: "reference", 598 | typeArguments: [ 599 | { 600 | name: "WinJS.UI.HubSection", 601 | type: "reference", 602 | typeArguments: [] 603 | } 604 | ] 605 | }, 606 | zoomableView: { 607 | name: "WinJS.UI.IZoomableView", 608 | type: "reference", 609 | typeArguments: [ 610 | { 611 | type: "any" 612 | } 613 | ] 614 | } 615 | }, 616 | HubSection: { 617 | contentElement: { 618 | name: "HTMLElement", 619 | type: "reference", 620 | typeArguments: [] 621 | }, 622 | element: { 623 | name: "HTMLElement", 624 | type: "reference", 625 | typeArguments: [] 626 | }, 627 | header: { 628 | type: "string" 629 | }, 630 | isHeaderStatic: { 631 | type: "boolean" 632 | } 633 | }, 634 | ItemContainer: { 635 | draggable: { 636 | type: "boolean" 637 | }, 638 | element: { 639 | name: "HTMLElement", 640 | type: "reference", 641 | typeArguments: [] 642 | }, 643 | onInvoked: { 644 | name: "Function", 645 | type: "reference", 646 | typeArguments: [] 647 | }, 648 | onSelectionChanged: { 649 | name: "Function", 650 | type: "reference", 651 | typeArguments: [] 652 | }, 653 | onSelectionChanging: { 654 | name: "Function", 655 | type: "reference", 656 | typeArguments: [] 657 | }, 658 | selected: { 659 | type: "boolean" 660 | }, 661 | selectionDisabled: { 662 | type: "boolean" 663 | }, 664 | swipeBehavior: { 665 | type: "enum", 666 | values: [ 667 | "none", 668 | "select" 669 | ] 670 | }, 671 | swipeOrientation: { 672 | type: "enum", 673 | values: [ 674 | "horizontal", 675 | "vertical" 676 | ] 677 | }, 678 | tapBehavior: { 679 | type: "enum", 680 | values: [ 681 | "directSelect", 682 | "invokeOnly", 683 | "none", 684 | "toggleSelect" 685 | ] 686 | } 687 | }, 688 | ListLayout: { 689 | backdropColor: { 690 | type: "string" 691 | }, 692 | disableBackdrop: { 693 | type: "boolean" 694 | }, 695 | groupHeaderPosition: { 696 | type: "enum", 697 | values: [ 698 | "left", 699 | "top" 700 | ] 701 | }, 702 | numberOfItemsPerItemsBlock: { 703 | type: "any" 704 | }, 705 | orientation: { 706 | type: "enum", 707 | values: [ 708 | "horizontal", 709 | "vertical" 710 | ] 711 | } 712 | }, 713 | ListView: { 714 | automaticallyLoadPages: { 715 | type: "boolean" 716 | }, 717 | currentItem: { 718 | name: "WinJS.UI.IListViewItem", 719 | type: "reference", 720 | typeArguments: [] 721 | }, 722 | element: { 723 | name: "HTMLElement", 724 | type: "reference", 725 | typeArguments: [] 726 | }, 727 | footer: { 728 | name: "HTMLElement", 729 | type: "reference", 730 | typeArguments: [] 731 | }, 732 | groupDataSource: { 733 | name: "WinJS.UI.IListDataSource", 734 | type: "reference", 735 | typeArguments: [ 736 | { 737 | name: "T", 738 | type: "type-param" 739 | } 740 | ] 741 | }, 742 | groupHeaderTapBehavior: { 743 | type: "enum", 744 | values: [ 745 | "invoke", 746 | "none" 747 | ] 748 | }, 749 | groupHeaderTemplate: { 750 | type: "any" 751 | }, 752 | header: { 753 | name: "HTMLElement", 754 | type: "reference", 755 | typeArguments: [] 756 | }, 757 | indexOfFirstVisible: { 758 | type: "number" 759 | }, 760 | indexOfLastVisible: { 761 | type: "number" 762 | }, 763 | itemDataSource: { 764 | name: "WinJS.UI.IListDataSource", 765 | type: "reference", 766 | typeArguments: [ 767 | { 768 | name: "T", 769 | type: "type-param" 770 | } 771 | ] 772 | }, 773 | itemTemplate: { 774 | type: "any" 775 | }, 776 | itemsDraggable: { 777 | type: "boolean" 778 | }, 779 | itemsReorderable: { 780 | type: "boolean" 781 | }, 782 | layout: { 783 | name: "WinJS.UI.ILayout2", 784 | type: "reference", 785 | typeArguments: [] 786 | }, 787 | loadingBehavior: { 788 | type: "string" 789 | }, 790 | loadingState: { 791 | type: "string" 792 | }, 793 | maxDeferredItemCleanup: { 794 | type: "number" 795 | }, 796 | maxLeadingPages: { 797 | type: "number" 798 | }, 799 | maxTrailingPages: { 800 | type: "number" 801 | }, 802 | onAccessibilityAnnotationComplete: { 803 | name: "Function", 804 | type: "reference", 805 | typeArguments: [] 806 | }, 807 | onContentAnimating: { 808 | name: "Function", 809 | type: "reference", 810 | typeArguments: [] 811 | }, 812 | onFooterVisibilityChanged: { 813 | name: "Function", 814 | type: "reference", 815 | typeArguments: [] 816 | }, 817 | onGroupHeaderInvoked: { 818 | name: "Function", 819 | type: "reference", 820 | typeArguments: [] 821 | }, 822 | onHeaderVisibilityChanged: { 823 | name: "Function", 824 | type: "reference", 825 | typeArguments: [] 826 | }, 827 | onItemDragBetween: { 828 | name: "Function", 829 | type: "reference", 830 | typeArguments: [] 831 | }, 832 | onItemDragChanged: { 833 | name: "Function", 834 | type: "reference", 835 | typeArguments: [] 836 | }, 837 | onItemDragDrop: { 838 | name: "Function", 839 | type: "reference", 840 | typeArguments: [] 841 | }, 842 | onItemDragEnd: { 843 | name: "Function", 844 | type: "reference", 845 | typeArguments: [] 846 | }, 847 | onItemDragEnter: { 848 | name: "Function", 849 | type: "reference", 850 | typeArguments: [] 851 | }, 852 | onItemDragLeave: { 853 | name: "Function", 854 | type: "reference", 855 | typeArguments: [] 856 | }, 857 | onItemDragStart: { 858 | name: "Function", 859 | type: "reference", 860 | typeArguments: [] 861 | }, 862 | onItemInvoked: { 863 | name: "Function", 864 | type: "reference", 865 | typeArguments: [] 866 | }, 867 | onKeyboardNavigating: { 868 | name: "Function", 869 | type: "reference", 870 | typeArguments: [] 871 | }, 872 | onLoadingStateChanged: { 873 | name: "Function", 874 | type: "reference", 875 | typeArguments: [] 876 | }, 877 | onSelectionChanged: { 878 | name: "Function", 879 | type: "reference", 880 | typeArguments: [] 881 | }, 882 | onSelectionChanging: { 883 | name: "Function", 884 | type: "reference", 885 | typeArguments: [] 886 | }, 887 | pagesToLoad: { 888 | type: "number" 889 | }, 890 | pagesToLoadThreshold: { 891 | type: "number" 892 | }, 893 | scrollPosition: { 894 | type: "number" 895 | }, 896 | selection: { 897 | name: "WinJS.UI.ISelection", 898 | type: "reference", 899 | typeArguments: [ 900 | { 901 | name: "T", 902 | type: "type-param" 903 | } 904 | ] 905 | }, 906 | selectionMode: { 907 | type: "enum", 908 | values: [ 909 | "multi", 910 | "none", 911 | "single" 912 | ] 913 | }, 914 | swipeBehavior: { 915 | type: "enum", 916 | values: [ 917 | "none", 918 | "select" 919 | ] 920 | }, 921 | tapBehavior: { 922 | type: "enum", 923 | values: [ 924 | "directSelect", 925 | "invokeOnly", 926 | "none", 927 | "toggleSelect" 928 | ] 929 | }, 930 | zoomableView: { 931 | name: "WinJS.UI.IZoomableView", 932 | type: "reference", 933 | typeArguments: [ 934 | { 935 | name: "WinJS.UI.ListView", 936 | type: "reference", 937 | typeArguments: [ 938 | { 939 | name: "T", 940 | type: "type-param" 941 | } 942 | ] 943 | } 944 | ] 945 | } 946 | }, 947 | Menu: { 948 | alignment: { 949 | type: "string" 950 | }, 951 | anchor: { 952 | name: "HTMLElement", 953 | type: "reference", 954 | typeArguments: [] 955 | }, 956 | commands: { 957 | name: "Array", 958 | type: "reference", 959 | typeArguments: [ 960 | { 961 | name: "WinJS.UI.MenuCommand", 962 | type: "reference", 963 | typeArguments: [] 964 | } 965 | ] 966 | }, 967 | disabled: { 968 | type: "boolean" 969 | }, 970 | element: { 971 | name: "HTMLElement", 972 | type: "reference", 973 | typeArguments: [] 974 | }, 975 | hidden: { 976 | type: "boolean" 977 | }, 978 | onAfterHide: { 979 | name: "Function", 980 | type: "reference", 981 | typeArguments: [] 982 | }, 983 | onAfterShow: { 984 | name: "Function", 985 | type: "reference", 986 | typeArguments: [] 987 | }, 988 | onBeforeHide: { 989 | name: "Function", 990 | type: "reference", 991 | typeArguments: [] 992 | }, 993 | onBeforeShow: { 994 | name: "Function", 995 | type: "reference", 996 | typeArguments: [] 997 | }, 998 | placement: { 999 | type: "string" 1000 | } 1001 | }, 1002 | MenuCommand: { 1003 | disabled: { 1004 | type: "boolean" 1005 | }, 1006 | element: { 1007 | name: "HTMLElement", 1008 | type: "reference", 1009 | typeArguments: [] 1010 | }, 1011 | extraClass: { 1012 | type: "string" 1013 | }, 1014 | flyout: { 1015 | name: "WinJS.UI.Flyout", 1016 | type: "reference", 1017 | typeArguments: [] 1018 | }, 1019 | hidden: { 1020 | type: "boolean" 1021 | }, 1022 | id: { 1023 | type: "string" 1024 | }, 1025 | label: { 1026 | type: "string" 1027 | }, 1028 | onClick: { 1029 | name: "Function", 1030 | type: "reference", 1031 | typeArguments: [] 1032 | }, 1033 | selected: { 1034 | type: "boolean" 1035 | }, 1036 | type: { 1037 | type: "string" 1038 | } 1039 | }, 1040 | NavBar: { 1041 | closedDisplayMode: { 1042 | type: "string" 1043 | }, 1044 | commands: { 1045 | name: "WinJS.UI.AppBarCommand", 1046 | type: "reference", 1047 | typeArguments: [] 1048 | }, 1049 | element: { 1050 | name: "HTMLElement", 1051 | type: "reference", 1052 | typeArguments: [] 1053 | }, 1054 | hidden: { 1055 | type: "boolean" 1056 | }, 1057 | onAfterClose: { 1058 | name: "Function", 1059 | type: "reference", 1060 | typeArguments: [] 1061 | }, 1062 | onAfterOpen: { 1063 | name: "Function", 1064 | type: "reference", 1065 | typeArguments: [] 1066 | }, 1067 | onBeforeClose: { 1068 | name: "Function", 1069 | type: "reference", 1070 | typeArguments: [] 1071 | }, 1072 | onBeforeOpen: { 1073 | name: "Function", 1074 | type: "reference", 1075 | typeArguments: [] 1076 | }, 1077 | onChildrenProcessed: { 1078 | name: "Function", 1079 | type: "reference", 1080 | typeArguments: [] 1081 | }, 1082 | opened: { 1083 | type: "boolean" 1084 | }, 1085 | placement: { 1086 | type: "string" 1087 | } 1088 | }, 1089 | NavBarCommand: { 1090 | element: { 1091 | name: "HTMLElement", 1092 | type: "reference", 1093 | typeArguments: [] 1094 | }, 1095 | icon: { 1096 | type: "string" 1097 | }, 1098 | label: { 1099 | type: "string" 1100 | }, 1101 | location: { 1102 | type: "any" 1103 | }, 1104 | onInvoked: { 1105 | name: "Function", 1106 | type: "reference", 1107 | typeArguments: [] 1108 | }, 1109 | splitButton: { 1110 | type: "boolean" 1111 | }, 1112 | splitOpened: { 1113 | type: "boolean" 1114 | }, 1115 | state: { 1116 | type: "any" 1117 | }, 1118 | tooltip: { 1119 | type: "string" 1120 | } 1121 | }, 1122 | NavBarContainer: { 1123 | currentIndex: { 1124 | type: "number" 1125 | }, 1126 | data: { 1127 | name: "WinJS.Binding.List", 1128 | type: "reference", 1129 | typeArguments: [ 1130 | { 1131 | name: "WinJS.UI.NavBarCommand", 1132 | type: "reference", 1133 | typeArguments: [] 1134 | } 1135 | ] 1136 | }, 1137 | element: { 1138 | name: "HTMLElement", 1139 | type: "reference", 1140 | typeArguments: [] 1141 | }, 1142 | fixedSize: { 1143 | type: "boolean" 1144 | }, 1145 | layout: { 1146 | type: "enum", 1147 | values: [ 1148 | "horizontal", 1149 | "vertical" 1150 | ] 1151 | }, 1152 | maxRows: { 1153 | type: "number" 1154 | }, 1155 | onInvoked: { 1156 | name: "Function", 1157 | type: "reference", 1158 | typeArguments: [] 1159 | }, 1160 | onSplitToggle: { 1161 | name: "Function", 1162 | type: "reference", 1163 | typeArguments: [] 1164 | }, 1165 | template: { 1166 | name: "WinJS.Binding.Template", 1167 | type: "reference", 1168 | typeArguments: [] 1169 | } 1170 | }, 1171 | Pivot: { 1172 | customLeftHeader: { 1173 | name: "HTMLElement", 1174 | type: "reference", 1175 | typeArguments: [] 1176 | }, 1177 | customRightHeader: { 1178 | name: "HTMLElement", 1179 | type: "reference", 1180 | typeArguments: [] 1181 | }, 1182 | element: { 1183 | name: "HTMLElement", 1184 | type: "reference", 1185 | typeArguments: [] 1186 | }, 1187 | items: { 1188 | name: "WinJS.Binding.List", 1189 | type: "reference", 1190 | typeArguments: [ 1191 | { 1192 | name: "WinJS.UI.PivotItem", 1193 | type: "reference", 1194 | typeArguments: [] 1195 | } 1196 | ] 1197 | }, 1198 | locked: { 1199 | type: "boolean" 1200 | }, 1201 | onItemAnimationEnd: { 1202 | name: "Function", 1203 | type: "reference", 1204 | typeArguments: [] 1205 | }, 1206 | onItemAnimationStart: { 1207 | name: "Function", 1208 | type: "reference", 1209 | typeArguments: [] 1210 | }, 1211 | onSelectionChanged: { 1212 | name: "Function", 1213 | type: "reference", 1214 | typeArguments: [] 1215 | }, 1216 | selectedIndex: { 1217 | type: "number" 1218 | }, 1219 | selectedItem: { 1220 | name: "WinJS.UI.PivotItem", 1221 | type: "reference", 1222 | typeArguments: [] 1223 | }, 1224 | title: { 1225 | type: "string" 1226 | } 1227 | }, 1228 | PivotItem: { 1229 | contentElement: { 1230 | name: "HTMLElement", 1231 | type: "reference", 1232 | typeArguments: [] 1233 | }, 1234 | element: { 1235 | name: "HTMLElement", 1236 | type: "reference", 1237 | typeArguments: [] 1238 | }, 1239 | header: { 1240 | type: "string" 1241 | } 1242 | }, 1243 | Rating: { 1244 | averageRating: { 1245 | type: "number" 1246 | }, 1247 | disabled: { 1248 | type: "boolean" 1249 | }, 1250 | element: { 1251 | name: "HTMLElement", 1252 | type: "reference", 1253 | typeArguments: [] 1254 | }, 1255 | enableClear: { 1256 | type: "boolean" 1257 | }, 1258 | maxRating: { 1259 | type: "number" 1260 | }, 1261 | onCancel: { 1262 | name: "Function", 1263 | type: "reference", 1264 | typeArguments: [] 1265 | }, 1266 | onChange: { 1267 | name: "Function", 1268 | type: "reference", 1269 | typeArguments: [] 1270 | }, 1271 | onPreviewChange: { 1272 | name: "Function", 1273 | type: "reference", 1274 | typeArguments: [] 1275 | }, 1276 | tooltipStrings: { 1277 | name: "Array", 1278 | type: "reference", 1279 | typeArguments: [ 1280 | { 1281 | type: "string" 1282 | } 1283 | ] 1284 | }, 1285 | userRating: { 1286 | type: "number" 1287 | } 1288 | }, 1289 | SearchBox: { 1290 | chooseSuggestionOnEnter: { 1291 | type: "boolean" 1292 | }, 1293 | disabled: { 1294 | type: "boolean" 1295 | }, 1296 | element: { 1297 | name: "HTMLElement", 1298 | type: "reference", 1299 | typeArguments: [] 1300 | }, 1301 | focusOnKeyboardInput: { 1302 | type: "boolean" 1303 | }, 1304 | onQueryChanged: { 1305 | name: "Function", 1306 | type: "reference", 1307 | typeArguments: [] 1308 | }, 1309 | onQuerySubmitted: { 1310 | name: "Function", 1311 | type: "reference", 1312 | typeArguments: [] 1313 | }, 1314 | onResultSuggestionChosen: { 1315 | name: "Function", 1316 | type: "reference", 1317 | typeArguments: [] 1318 | }, 1319 | onSuggestionsRequested: { 1320 | name: "Function", 1321 | type: "reference", 1322 | typeArguments: [] 1323 | }, 1324 | placeholderText: { 1325 | type: "string" 1326 | }, 1327 | queryText: { 1328 | type: "string" 1329 | }, 1330 | searchHistoryContext: { 1331 | type: "string" 1332 | }, 1333 | searchHistoryDisabled: { 1334 | type: "boolean" 1335 | } 1336 | }, 1337 | SemanticZoom: { 1338 | element: { 1339 | name: "HTMLElement", 1340 | type: "reference", 1341 | typeArguments: [] 1342 | }, 1343 | enableButton: { 1344 | type: "boolean" 1345 | }, 1346 | locked: { 1347 | type: "boolean" 1348 | }, 1349 | onZoomChanged: { 1350 | name: "Function", 1351 | type: "reference", 1352 | typeArguments: [] 1353 | }, 1354 | zoomFactor: { 1355 | type: "number" 1356 | }, 1357 | zoomedOut: { 1358 | type: "boolean" 1359 | } 1360 | }, 1361 | SplitView: { 1362 | closedDisplayMode: { 1363 | type: "enum", 1364 | values: [ 1365 | "inline", 1366 | "none" 1367 | ] 1368 | }, 1369 | contentElement: { 1370 | name: "HTMLElement", 1371 | type: "reference", 1372 | typeArguments: [] 1373 | }, 1374 | element: { 1375 | name: "HTMLElement", 1376 | type: "reference", 1377 | typeArguments: [] 1378 | }, 1379 | onAfterClose: { 1380 | name: "Function", 1381 | type: "reference", 1382 | typeArguments: [] 1383 | }, 1384 | onAfterOpen: { 1385 | name: "Function", 1386 | type: "reference", 1387 | typeArguments: [] 1388 | }, 1389 | onBeforeClose: { 1390 | name: "Function", 1391 | type: "reference", 1392 | typeArguments: [] 1393 | }, 1394 | onBeforeOpen: { 1395 | name: "Function", 1396 | type: "reference", 1397 | typeArguments: [] 1398 | }, 1399 | openedDisplayMode: { 1400 | type: "enum", 1401 | values: [ 1402 | "inline", 1403 | "overlay" 1404 | ] 1405 | }, 1406 | paneElement: { 1407 | name: "HTMLElement", 1408 | type: "reference", 1409 | typeArguments: [] 1410 | }, 1411 | paneOpened: { 1412 | type: "boolean" 1413 | }, 1414 | panePlacement: { 1415 | type: "enum", 1416 | values: [ 1417 | "bottom", 1418 | "left", 1419 | "right", 1420 | "top" 1421 | ] 1422 | } 1423 | }, 1424 | SplitViewCommand: { 1425 | element: { 1426 | name: "HTMLElement", 1427 | type: "reference", 1428 | typeArguments: [] 1429 | }, 1430 | icon: { 1431 | type: "string" 1432 | }, 1433 | label: { 1434 | type: "string" 1435 | }, 1436 | onInvoked: { 1437 | name: "Function", 1438 | type: "reference", 1439 | typeArguments: [] 1440 | }, 1441 | tooltip: { 1442 | type: "string" 1443 | } 1444 | }, 1445 | SplitViewPaneToggle: { 1446 | element: { 1447 | name: "HTMLButtonElement", 1448 | type: "reference", 1449 | typeArguments: [] 1450 | }, 1451 | onInvoked: { 1452 | name: "Function", 1453 | type: "reference", 1454 | typeArguments: [] 1455 | }, 1456 | splitView: { 1457 | name: "HTMLElement", 1458 | type: "reference", 1459 | typeArguments: [] 1460 | } 1461 | }, 1462 | TimePicker: { 1463 | clock: { 1464 | type: "string" 1465 | }, 1466 | current: { 1467 | name: "Date", 1468 | type: "reference", 1469 | typeArguments: [] 1470 | }, 1471 | disabled: { 1472 | type: "boolean" 1473 | }, 1474 | element: { 1475 | name: "HTMLElement", 1476 | type: "reference", 1477 | typeArguments: [] 1478 | }, 1479 | hourPattern: { 1480 | type: "string" 1481 | }, 1482 | minuteIncrement: { 1483 | type: "number" 1484 | }, 1485 | minutePattern: { 1486 | type: "string" 1487 | }, 1488 | onChange: { 1489 | name: "Function", 1490 | type: "reference", 1491 | typeArguments: [] 1492 | }, 1493 | periodPattern: { 1494 | type: "string" 1495 | } 1496 | }, 1497 | ToggleSwitch: { 1498 | checked: { 1499 | type: "boolean" 1500 | }, 1501 | disabled: { 1502 | type: "boolean" 1503 | }, 1504 | element: { 1505 | name: "HTMLElement", 1506 | type: "reference", 1507 | typeArguments: [] 1508 | }, 1509 | labelOff: { 1510 | type: "string" 1511 | }, 1512 | labelOn: { 1513 | type: "string" 1514 | }, 1515 | onChange: { 1516 | name: "Function", 1517 | type: "reference", 1518 | typeArguments: [] 1519 | }, 1520 | title: { 1521 | type: "string" 1522 | } 1523 | }, 1524 | ToolBar: { 1525 | closedDisplayMode: { 1526 | type: "enum", 1527 | values: [ 1528 | "compact", 1529 | "full" 1530 | ] 1531 | }, 1532 | data: { 1533 | name: "WinJS.Binding.List", 1534 | type: "reference", 1535 | typeArguments: [ 1536 | { 1537 | name: "WinJS.UI.ICommand", 1538 | type: "reference", 1539 | typeArguments: [] 1540 | } 1541 | ] 1542 | }, 1543 | element: { 1544 | name: "HTMLElement", 1545 | type: "reference", 1546 | typeArguments: [] 1547 | }, 1548 | onAfterClose: { 1549 | name: "Function", 1550 | type: "reference", 1551 | typeArguments: [] 1552 | }, 1553 | onAfterOpen: { 1554 | name: "Function", 1555 | type: "reference", 1556 | typeArguments: [] 1557 | }, 1558 | onBeforeClose: { 1559 | name: "Function", 1560 | type: "reference", 1561 | typeArguments: [] 1562 | }, 1563 | onBeforeOpen: { 1564 | name: "Function", 1565 | type: "reference", 1566 | typeArguments: [] 1567 | }, 1568 | opened: { 1569 | type: "boolean" 1570 | } 1571 | }, 1572 | Tooltip: { 1573 | contentElement: { 1574 | name: "HTMLElement", 1575 | type: "reference", 1576 | typeArguments: [] 1577 | }, 1578 | element: { 1579 | name: "HTMLElement", 1580 | type: "reference", 1581 | typeArguments: [] 1582 | }, 1583 | extraClass: { 1584 | type: "string" 1585 | }, 1586 | infotip: { 1587 | type: "boolean" 1588 | }, 1589 | innerHTML: { 1590 | type: "string" 1591 | }, 1592 | onBeforeClose: { 1593 | name: "Function", 1594 | type: "reference", 1595 | typeArguments: [] 1596 | }, 1597 | onBeforeOpen: { 1598 | name: "Function", 1599 | type: "reference", 1600 | typeArguments: [] 1601 | }, 1602 | onClosed: { 1603 | name: "Function", 1604 | type: "reference", 1605 | typeArguments: [] 1606 | }, 1607 | onOpened: { 1608 | name: "Function", 1609 | type: "reference", 1610 | typeArguments: [] 1611 | }, 1612 | placement: { 1613 | type: "string" 1614 | } 1615 | } 1616 | }; 1617 | 1618 | var setImmediate; 1619 | var clearImmediate; 1620 | if (window.setImmediate && window.clearImmediate) { 1621 | setImmediate = window.setImmediate; 1622 | clearImmediate = window.clearImmediate; 1623 | } else { 1624 | setImmediate = function (callback) { 1625 | return setTimeout(callback, 0); 1626 | }; 1627 | clearImmediate = window.clearTimeout; 1628 | } 1629 | 1630 | function isEvent(propName) { 1631 | return propName[0] === "o" && propName[1] === "n"; 1632 | } 1633 | 1634 | function mapObject(obj, callback) { 1635 | var result = {}; 1636 | Object.keys(obj).forEach(function (key) { 1637 | var value = callback(key, obj[key]); 1638 | if (value !== undefined) { 1639 | result[key] = value; 1640 | } 1641 | }); 1642 | return result; 1643 | } 1644 | 1645 | function cloneObject(obj) { 1646 | var result = {}; 1647 | for (var k in obj) { result[k] = obj[k]; } 1648 | return result; 1649 | } 1650 | 1651 | function merge(/* objs */) { 1652 | var result = {}; 1653 | for (var i = 0, len = arguments.length; i < len; i++) { 1654 | var obj = arguments[i]; 1655 | if (obj) { 1656 | for (var k in obj) { result[k] = obj[k]; } 1657 | } 1658 | } 1659 | return result; 1660 | } 1661 | 1662 | function endsWith(s, suffix) { 1663 | return s.length >= suffix.length && s.substr(-suffix.length) === suffix; 1664 | } 1665 | 1666 | function arraysShallowEqual(a, b) { 1667 | if (a === b) { 1668 | return true; 1669 | } else if (a.length !== b.length) { 1670 | return false; 1671 | } else { 1672 | for (var i = 0, len = a.length; i < len; i++) { 1673 | if (a[i] !== b[i]) { 1674 | return false; 1675 | } 1676 | } 1677 | return true; 1678 | } 1679 | } 1680 | 1681 | function nestedSet(obj, path, value) { 1682 | var parts = path.split("."); 1683 | var allButLast = parts.slice(0, parts.length - 1); 1684 | var last = parts[parts.length - 1]; 1685 | var finalObj = allButLast.reduce(function (current, key) { 1686 | return current[key]; 1687 | }, obj); 1688 | finalObj[last] = value; 1689 | } 1690 | 1691 | function deparent(element) { 1692 | var parent = element.parentNode; 1693 | parent && parent.removeChild(element); 1694 | } 1695 | 1696 | function fireEvent(element, eventName) { 1697 | var eventObject = document.createEvent("CustomEvent"); 1698 | eventObject.initCustomEvent( 1699 | eventName, 1700 | true, // bubbles 1701 | false, // cancelable 1702 | null // detail 1703 | ); 1704 | element.dispatchEvent(eventObject); 1705 | } 1706 | 1707 | function makeClassSet(className) { 1708 | var classSet = {}; 1709 | className && className.split(" ").forEach(function (aClass) { 1710 | if (aClass) { 1711 | classSet[aClass] = true; 1712 | } 1713 | }); 1714 | return classSet; 1715 | } 1716 | 1717 | function getIn(object, path) { 1718 | var parts = path.split("."); 1719 | return parts.reduce(function (current, name) { 1720 | return current && current[name]; 1721 | }, object); 1722 | } 1723 | 1724 | // Given a type from RawControlApis returns a React propType. 1725 | function typeToPropType(typeInfo) { 1726 | if (typeInfo.type === "string") { 1727 | return React.PropTypes.string; 1728 | } else if (typeInfo.type === "boolean") { 1729 | return React.PropTypes.bool; 1730 | } else if (typeInfo.type === "number") { 1731 | return React.PropTypes.number; 1732 | } else if (typeInfo.type === "enum") { 1733 | return React.PropTypes.oneOf(typeInfo.values); 1734 | } else if (typeInfo.type === "any") { 1735 | return React.PropTypes.any; 1736 | } else if (typeInfo.type === "reference") { 1737 | if (typeInfo.name === "Function") { 1738 | return React.PropTypes.func; 1739 | } else if (typeInfo.name === "Array") { 1740 | var itemPropType = typeToPropType(typeInfo.typeArguments[0]); 1741 | return itemPropType ? React.PropTypes.arrayOf(itemPropType) : React.PropTypes.array; 1742 | } else if (getIn(window, typeInfo.name)) { 1743 | var instance = getIn(window, typeInfo.name); 1744 | return React.PropTypes.instanceOf(instance); 1745 | } 1746 | } else { 1747 | console.warn("react-winjs typeToPropType: unable to find propType for type: " + JSON.stringify(typeInfo, null, 2)); 1748 | } 1749 | } 1750 | 1751 | // TODO: Revisit all of this diffing stuff: 1752 | // - Make it more efficient 1753 | // - It's currently hard to understand because it makes aggressive 1754 | // assumptions (e.g. each item has a key and each item has a winControl) 1755 | // - Is it correct? 1756 | // - Should we just sync an array with a binding list instead of computing 1757 | // edits based on 2 arrays and then applying them to a binding list? 1758 | function buildIndex(array) { 1759 | var index = {}; 1760 | array.forEach(function (item, i) { 1761 | index[item.key] = i; 1762 | }); 1763 | return index; 1764 | } 1765 | function indexOfKey(array, key) { 1766 | for (var i = 0; i < array.length; i++) { 1767 | if (array[i].key === key) { 1768 | return i; 1769 | } 1770 | } 1771 | return -1; 1772 | } 1773 | function diffArraysByKey(old, latest) { 1774 | old = old.slice(0); 1775 | var oldIndex = buildIndex(old); 1776 | var latestIndex = buildIndex(latest); 1777 | var edits = []; 1778 | 1779 | // Handle removals 1780 | for (var i = old.length - 1; i >= 0; i--) { 1781 | var item = old[i]; 1782 | if (!latestIndex.hasOwnProperty(item.key)) { 1783 | edits.push({ type: "delete", index: i }); 1784 | old.splice(i, 1); 1785 | } 1786 | } 1787 | 1788 | // Handle insertions and moves 1789 | for (var i = 0; i < latest.length; i++) { 1790 | var item = latest[i]; 1791 | if (!oldIndex.hasOwnProperty(item.key)) { 1792 | // Insertion 1793 | edits.push({ type: "insert", index: i, value: item }); 1794 | old.splice(i, 0, item); 1795 | } else if (old[i].key !== item.key) { 1796 | // Move 1797 | //edits.push({ type: "move", from: oldIndex[item.key], to: i }); 1798 | //old.splice(oldIndex[item.key], 1); 1799 | 1800 | var fromIndex = indexOfKey(old, item.key); 1801 | edits.push({ type: "move", from: fromIndex, to: i }); 1802 | old.splice(fromIndex, 1); 1803 | old.splice(i, 0, item); 1804 | } 1805 | } 1806 | 1807 | return edits; 1808 | } 1809 | function applyEditsToBindingList(list, edits) { 1810 | edits.forEach(function (edit) { 1811 | if (edit.type === "delete") { 1812 | list.splice(edit.index, 1); 1813 | } else if (edit.type === "insert") { 1814 | list.splice(edit.index, 0, edit.value.winControl); 1815 | } else if (edit.type === "move") { 1816 | list.move(edit.from, edit.to); 1817 | } else { 1818 | throw "Unsupported edit type: " + edit.type; 1819 | } 1820 | }, this); 1821 | } 1822 | 1823 | // interface IWinJSComponent { 1824 | // winControl 1825 | // element 1826 | // data 1827 | // displayName 1828 | // } 1829 | 1830 | // interface IWinJSChildComponent extends IWinJSComponent { 1831 | // key 1832 | // type 1833 | // } 1834 | 1835 | // Returns a WinJSChildComponent for each component in *children*. Reuses 1836 | // WinJSChildComponents from *childComponentsMap* when possible. Disposes members of 1837 | // *childComponentsMap* if they are no longer needed. 1838 | function processChildren(componentDisplayName, children, childComponentsMap) { 1839 | var newChildComponents = []; 1840 | var newChildComponentsMap = {}; 1841 | 1842 | // A component's *key* represents its identity. If a component in *children* and a 1843 | // component in *childComponentsMap* have the same *key*, then they are assumed to 1844 | // represent the same component. 1845 | 1846 | React.Children.forEach(children, function (component) { 1847 | if (component) { 1848 | if (component.ref) { 1849 | console.warn( 1850 | "ref prop (" + component.ref + ") will not work on " + 1851 | component.type.displayName + " component because it is inside " + 1852 | "of a " + componentDisplayName + " component" 1853 | ); 1854 | } 1855 | 1856 | if (component.key === null) { 1857 | console.error( 1858 | component.type.displayName + " component requires a key " + 1859 | "when inside of a " + componentDisplayName + " component" 1860 | ); 1861 | } else { 1862 | var winjsChildComponent = childComponentsMap[component.key]; 1863 | if (winjsChildComponent) { 1864 | if (winjsChildComponent.type === component.type) { 1865 | winjsChildComponent.update(component); 1866 | } else { 1867 | // If a component's *type* has changed then the component must be 1868 | // recreated from scratch rather than updated. The reason is that 1869 | // the tagName of the underlying DOM element may have changed. The 1870 | // only way to change the tagName of the underlying DOM element is 1871 | // to instantiate a new react-winjs component. 1872 | winjsChildComponent.dispose(); 1873 | winjsChildComponent = new WinJSChildComponent(component); 1874 | } 1875 | } else { 1876 | winjsChildComponent = new WinJSChildComponent(component); 1877 | } 1878 | newChildComponents.push(winjsChildComponent); 1879 | newChildComponentsMap[component.key] = winjsChildComponent; 1880 | } 1881 | } 1882 | }); 1883 | 1884 | Object.keys(childComponentsMap).forEach(function (key) { 1885 | if (!newChildComponentsMap.hasOwnProperty(key)) { 1886 | childComponentsMap[key].dispose(); 1887 | } 1888 | }); 1889 | 1890 | return { 1891 | childComponents: newChildComponents, 1892 | childComponentsMap: newChildComponentsMap 1893 | }; 1894 | } 1895 | 1896 | function prefixedProperty(prefix, property) { 1897 | return prefix + property[0].toUpperCase() + property.substr(1); 1898 | } 1899 | 1900 | var isUnitlessProperty = { 1901 | flex: true, 1902 | flexGrow: true, 1903 | flexPositive: true, 1904 | flexShrink: true, 1905 | flexNegative: true, 1906 | fontWeight: true, 1907 | lineClamp: true, 1908 | lineHeight: true, 1909 | opacity: true, 1910 | order: true, 1911 | orphans: true, 1912 | widows: true, 1913 | zIndex: true, 1914 | zoom: true 1915 | }; 1916 | var vendorPrefixes = ["Moz", "ms", "Webkit"]; 1917 | Object.keys(isUnitlessProperty).forEach(function (property) { 1918 | vendorPrefixes.forEach(function (prefix) { 1919 | isUnitlessProperty[prefixedProperty(prefix, property)] = true; 1920 | }); 1921 | }); 1922 | 1923 | // Converts the value of a CSS attribute to a string. When certain attributes 1924 | // (e.g. width, height) are specified as numbers, this means adding "px" to the 1925 | // end of the string value. 1926 | function resolveStyleValue(cssProperty, value) { 1927 | if (typeof value === "number") { 1928 | return isUnitlessProperty[cssProperty] || value === 0 ? 1929 | ("" + value) : 1930 | (value + "px"); 1931 | } else { 1932 | return value ? ("" + value) : ""; 1933 | } 1934 | } 1935 | 1936 | var PropHandlers = { 1937 | // Maps to a property on the winControl. 1938 | property: function (propType) { 1939 | return { 1940 | propType: propType, 1941 | preCtorInit: function property_preCtorInit(element, options, data, displayName, propName, value) { 1942 | options[propName] = value; 1943 | }, 1944 | update: function property_update(winjsComponent, propName, oldValue, newValue) { 1945 | if (oldValue !== newValue) { 1946 | winjsComponent.winControl[propName] = newValue; 1947 | } 1948 | } 1949 | }; 1950 | }, 1951 | 1952 | // Maps to a property on the winControl which involves setting focus. Such properties 1953 | // are set outside of componentWillReceiveProps to prevent React from undoing the 1954 | // focus move. 1955 | focusProperty: function (propType) { 1956 | return { 1957 | propType: propType, 1958 | preCtorInit: function focusProperty_preCtorInit(element, options, data, displayName, propName, value) { 1959 | options[propName] = value; 1960 | }, 1961 | update: function focusProperty_update(winjsComponent, propName, oldValue, newValue) { 1962 | if (oldValue !== newValue) { 1963 | var asyncToken = winjsComponent.data[propName]; 1964 | asyncToken && clearImmediate(asyncToken); 1965 | asyncToken = setImmediate(function () { 1966 | winjsComponent.data[propName] = null; 1967 | winjsComponent.winControl[propName] = newValue; 1968 | }); 1969 | } 1970 | }, 1971 | dispose: function focusProperty_dispose(winjsComponent, propName) { 1972 | var asyncToken = winjsComponent.data[propName]; 1973 | asyncToken && clearImmediate(asyncToken); 1974 | } 1975 | }; 1976 | }, 1977 | 1978 | // Maps to a property on the winControl's element. 1979 | domProperty: function (propType) { 1980 | return { 1981 | propType: propType, 1982 | preCtorInit: function domProperty_preCtorInit(element, options, data, displayName, propName, value) { 1983 | element[propName] = value; 1984 | }, 1985 | update: function domProperty_update(winjsComponent, propName, oldValue, newValue) { 1986 | if (oldValue !== newValue) { 1987 | winjsComponent.element[propName] = newValue; 1988 | } 1989 | } 1990 | }; 1991 | }, 1992 | 1993 | // Maps to an attribute on the winControl's element. 1994 | domAttribute: function (propType) { 1995 | return { 1996 | propType: propType, 1997 | update: function domAttribute_update(winjsComponent, propName, oldValue, newValue) { 1998 | if (oldValue !== newValue) { 1999 | if (newValue !== null && newValue !== undefined) { 2000 | winjsComponent.element.setAttribute(propName, "" + newValue); 2001 | } else { 2002 | winjsComponent.element.removeAttribute(propName); 2003 | } 2004 | } 2005 | } 2006 | }; 2007 | }, 2008 | 2009 | // Maps to an event on the winControl. 2010 | event: { 2011 | propType: React.PropTypes.func, 2012 | // Can't set options in preCtorInit for events. The problem is WinJS control options 2013 | // use a different code path to hook up events than the event property setters. 2014 | // Consequently, setting an event property will not automatically unhook the event 2015 | // listener that was specified in the options during initialization. To avoid this 2016 | // problem, always go thru the event property setters. 2017 | update: function event_update(winjsComponent, propName, oldValue, newValue) { 2018 | if (oldValue !== newValue) { 2019 | winjsComponent.winControl[propName.toLowerCase()] = newValue; 2020 | } 2021 | } 2022 | }, 2023 | 2024 | // Maps to an event on the winControl's element. 2025 | domEvent: { 2026 | propType: React.PropTypes.func, 2027 | preCtorInit: function domEvent_preCtorInit(element, options, data, displayName, propName, value) { 2028 | element[propName.toLowerCase()] = value; 2029 | }, 2030 | update: function domEvent_update(winjsComponent, propName, oldValue, newValue) { 2031 | if (oldValue !== newValue) { 2032 | winjsComponent.element[propName.toLowerCase()] = newValue; 2033 | } 2034 | } 2035 | }, 2036 | 2037 | // Enable the addition and removal of CSS classes on the root of the winControl 2038 | // but don't clobber whatever CSS classes the underlying control may have added 2039 | // (e.g. don't clobber win-listview). 2040 | winControlClassName: { 2041 | propType: React.PropTypes.string, 2042 | preCtorInit: function winControlClassName_preCtorInit(element, options, data, displayName, propName, value) { 2043 | if (value) { 2044 | element.className = value; 2045 | } 2046 | data[propName] = makeClassSet(value); 2047 | }, 2048 | update: function winControlClassName_update(winjsComponent, propName, oldValue, newValue) { 2049 | if (oldValue !== newValue) { 2050 | var oldClassSet = winjsComponent.data[propName] || {}; 2051 | var newClassSet = makeClassSet(newValue); 2052 | var elementClassList = winjsComponent.winControl.element.classList; 2053 | for (var className in oldClassSet) { 2054 | if (!newClassSet[className]) { 2055 | elementClassList.remove(className); 2056 | } 2057 | } 2058 | for (var className in newClassSet) { 2059 | if (!oldClassSet[className]) { 2060 | elementClassList.add(className); 2061 | } 2062 | } 2063 | winjsComponent.data[propName] = newClassSet; 2064 | } 2065 | } 2066 | }, 2067 | 2068 | // Enable the addition and removal of inline styles on the root of the winControl 2069 | // but don't clobber whatever inline styles the underlying control may have added. 2070 | winControlStyle: { 2071 | propType: React.PropTypes.object, 2072 | preCtorInit: function winControlStyle_preCtorInit(element, options, data, displayName, propName, value) { 2073 | var elementStyle = element.style; 2074 | value = value || {}; 2075 | for (var cssProperty in value) { 2076 | elementStyle[cssProperty] = resolveStyleValue(cssProperty, value[cssProperty]); 2077 | } 2078 | }, 2079 | update: function winControlStyle_update(winjsComponent, propName, oldValue, newValue) { 2080 | if (oldValue !== newValue) { 2081 | oldValue = oldValue || {}; 2082 | newValue = newValue || {}; 2083 | if(winjsComponent.winControl && winjsComponent.winControl.element) { 2084 | var elementStyle = winjsComponent.winControl.element.style; 2085 | for (var cssProperty in oldValue) { 2086 | if (!newValue.hasOwnProperty(cssProperty)) { 2087 | elementStyle[cssProperty] = ""; 2088 | } 2089 | } 2090 | for (var cssProperty in newValue) { 2091 | if (oldValue[cssProperty] !== newValue[cssProperty]) { 2092 | elementStyle[cssProperty] = resolveStyleValue(cssProperty, newValue[cssProperty]); 2093 | } 2094 | } 2095 | } 2096 | } 2097 | } 2098 | }, 2099 | 2100 | // Emits a warning to the console whenever prop gets used. 2101 | warn: function PropHandlers_warn(warnMessage) { 2102 | return { 2103 | // Don't need preCtorInit because this prop handler doesn't have any side 2104 | // effects on the WinJS control. update also runs during initialization so 2105 | // update is just as good as preCtorInit for our use case. 2106 | update: function warn_update(winjsComponent, propName, oldValue, newValue) { 2107 | console.warn(winjsComponent.displayName + ": " + warnMessage); 2108 | } 2109 | }; 2110 | }, 2111 | 2112 | // Creates a DOM element and mounts a React component on it. Gives this DOM 2113 | // element to the *winControlProperty* property of the winControl. 2114 | propertyWithMount: function PropHandlers_propertyWithMount(winControlProperty) { 2115 | return { 2116 | propType: React.PropTypes.element, 2117 | preCtorInit: function propertyWithMount_preCtorInit(element, options, data, displayName, propName, value) { 2118 | if (value) { 2119 | data[propName] = document.createElement("div"); 2120 | ReactDOM.render(value, data[propName]); 2121 | options[winControlProperty] = data[propName]; 2122 | } 2123 | }, 2124 | update: function propertyWithMount_update(winjsComponent, propName, oldValue, newValue) { 2125 | var winControl = winjsComponent.winControl; 2126 | var element = winjsComponent.data[propName]; 2127 | if (newValue) { 2128 | if (!element) { 2129 | element = document.createElement("div"); 2130 | winjsComponent.data[propName] = element; 2131 | } 2132 | ReactDOM.render(newValue, element); 2133 | if (winControl[winControlProperty] !== element) { 2134 | winControl[winControlProperty] = element; 2135 | } 2136 | } else if (oldValue) { 2137 | element && ReactDOM.unmountComponentAtNode(element); 2138 | winControl[winControlProperty] = null; 2139 | } 2140 | }, 2141 | dispose: function propertyWithMount_dispose(winjsComponent, propName) { 2142 | var element = winjsComponent.data[propName]; 2143 | element && ReactDOM.unmountComponentAtNode(element); 2144 | } 2145 | }; 2146 | }, 2147 | 2148 | 2149 | // Mounts a React component on whatever element gets returned by getMountPoint. 2150 | mountTo: function PropHandlers_mountTo(getMountPoint) { 2151 | return { 2152 | propType: React.PropTypes.element, 2153 | // Can't use preCtorInit because the mount point may not exist until the 2154 | // constructor has run. 2155 | update: function mountTo_update(winjsComponent, propName, oldValue, newValue) { 2156 | var data = winjsComponent.data[propName] || {}; 2157 | var version = (data.version || 0) + 1; 2158 | winjsComponent.data[propName] = { 2159 | // *mountComponent* may run asynchronously and we may queue it multiple 2160 | // times before it runs. *version* allows us to ensure only the latest 2161 | // version runs and the others are no ops. 2162 | version: version, 2163 | // *element* is the element to which we last mounted the component. 2164 | element: data.element 2165 | }; 2166 | 2167 | var mountComponent = function () { 2168 | if (version === winjsComponent.data[propName].version) { 2169 | var oldElement = winjsComponent.data[propName].element; 2170 | 2171 | if (newValue) { 2172 | var newElement = getMountPoint(winjsComponent); 2173 | if (oldElement && oldElement !== newElement) { 2174 | ReactDOM.unmountComponentAtNode(oldElement); 2175 | } 2176 | 2177 | ReactDOM.render(newValue, newElement); 2178 | winjsComponent.data[propName].element = newElement; 2179 | } else if (oldValue) { 2180 | oldElement && ReactDOM.unmountComponentAtNode(oldElement); 2181 | winjsComponent.data[propName].element = null; 2182 | } 2183 | } 2184 | }; 2185 | 2186 | // *isDeclarativeControlContainer* is a hook some WinJS controls provide 2187 | // (e.g. HubSection, PivotItem) to ensure that processing runs on the 2188 | // control only when the control is ready for it. This enables lazy loading 2189 | // of HubSections/PivotItems (e.g. load off screen items asynchronously in 2190 | // batches). Additionally, doing processing thru this hook guarantees that 2191 | // the processing won't run until the control is in the DOM. 2192 | var winControl = winjsComponent.winControl; 2193 | var queueProcessing = winControl.constructor.isDeclarativeControlContainer; 2194 | if (queueProcessing && typeof queueProcessing === "function") { 2195 | queueProcessing(winControl, mountComponent); 2196 | } else { 2197 | mountComponent(); 2198 | } 2199 | }, 2200 | dispose: function mountTo_dispose(winjsComponent, propName) { 2201 | var data = winjsComponent.data[propName] || {}; 2202 | var element = data.element; 2203 | element && ReactDOM.unmountComponentAtNode(element); 2204 | } 2205 | }; 2206 | }, 2207 | 2208 | // Uses the Binding.List's editing APIs to make it match the children prop. Does this to 2209 | // the Binding.List stored in the winControl's property called bindingListName. 2210 | syncChildrenWithBindingList: function PropHandlers_syncChildrenWithBindingList(bindingListName) { 2211 | return { 2212 | preCtorInit: function syncChildrenWithBindingList_preCtorInit(element, options, data, displayName, propName, value) { 2213 | var latest = processChildren(displayName, value, {}); 2214 | data[propName] = { 2215 | winjsChildComponents: latest.childComponents, 2216 | winjsChildComponentsMap: latest.childComponentsMap 2217 | }; 2218 | 2219 | options[bindingListName] = new WinJS.Binding.List( 2220 | latest.childComponents.map(function (winjsChildComponent) { 2221 | return winjsChildComponent.winControl; 2222 | }) 2223 | ); 2224 | }, 2225 | update: function syncChildrenWithBindingList_update(winjsComponent, propName, oldValue, newValue) { 2226 | var data = winjsComponent.data[propName] || {}; 2227 | var oldChildComponents = data.winjsChildComponents || []; 2228 | var oldChildComponentsMap = data.winjsChildComponentsMap || {}; 2229 | var latest = processChildren(winjsComponent.displayName, newValue, oldChildComponentsMap); 2230 | 2231 | var bindingList = winjsComponent.winControl[bindingListName]; 2232 | if (bindingList) { 2233 | applyEditsToBindingList( 2234 | bindingList, 2235 | diffArraysByKey(oldChildComponents, latest.childComponents) 2236 | ); 2237 | } else { 2238 | winjsComponent.winControl[bindingListName] = new WinJS.Binding.List(latest.childComponents.map(function (winjsChildComponent) { 2239 | return winjsChildComponent.winControl; 2240 | })); 2241 | } 2242 | 2243 | winjsComponent.data[propName] = { 2244 | winjsChildComponents: latest.childComponents, 2245 | winjsChildComponentsMap: latest.childComponentsMap 2246 | }; 2247 | }, 2248 | dispose: function syncChildrenWithBindingList_dispose(winjsComponent, propName) { 2249 | var data = winjsComponent.data[propName] || {}; 2250 | var childComponents = data.winjsChildComponents || []; 2251 | childComponents.forEach(function (winjsChildComponent) { 2252 | winjsChildComponent.dispose(); 2253 | }); 2254 | } 2255 | } 2256 | } 2257 | }; 2258 | 2259 | function defineControl(options) { 2260 | // Required 2261 | var winjsControl = options.winjsControl; 2262 | 2263 | // Optional 2264 | var winControlOptions = options.winControlOptions || {}; 2265 | var preCtorInit = options.preCtorInit || function () { }; 2266 | var propHandlers = options.propHandlers || {}; 2267 | var render = options.render || function (component) { 2268 | return React.DOM.div(); 2269 | }; 2270 | var displayName = options.displayName; 2271 | 2272 | function initWinJSComponent(winjsComponent, element, props) { 2273 | winjsComponent.data = {}; 2274 | winjsComponent.displayName = displayName; 2275 | winjsComponent.element = element; 2276 | 2277 | // Give propHandlers that implement preCtorInit the opportunity to run before 2278 | // instantiating the winControl. 2279 | var options = cloneObject(winControlOptions); 2280 | preCtorInit(element, options, winjsComponent.data, displayName); 2281 | Object.keys(props).forEach(function (propName) { 2282 | var handler = propHandlers[propName]; 2283 | if (handler && handler.preCtorInit) { 2284 | handler.preCtorInit(element, options, winjsComponent.data, displayName, propName, props[propName]); 2285 | } 2286 | }); 2287 | winjsComponent.winControl = new winjsControl(element, options); 2288 | 2289 | // Process propHandlers that don't implement preCtorInit. 2290 | Object.keys(props).forEach(function (propName) { 2291 | var handler = propHandlers[propName]; 2292 | if (handler && !handler.preCtorInit) { 2293 | handler.update(winjsComponent, propName, undefined, props[propName]); 2294 | } 2295 | }); 2296 | } 2297 | 2298 | function updateWinJSComponent(winjsComponent, prevProps, nextProps) { 2299 | // Handle props that were added or changed 2300 | Object.keys(nextProps).forEach(function (propName) { 2301 | var handler = propHandlers[propName]; 2302 | if (handler) { 2303 | handler.update(winjsComponent, propName, prevProps[propName], nextProps[propName]); 2304 | } 2305 | }); 2306 | 2307 | // Handle props that were removed 2308 | Object.keys(prevProps).forEach(function (propName) { 2309 | if (!nextProps.hasOwnProperty(propName)) { 2310 | var handler = propHandlers[propName]; 2311 | if (handler) { 2312 | handler.update(winjsComponent, propName, prevProps[propName], undefined); 2313 | } 2314 | } 2315 | }); 2316 | } 2317 | 2318 | function disposeWinJSComponent(winjsComponent) { 2319 | winjsComponent.winControl.dispose && winjsComponent.winControl.dispose(); 2320 | Object.keys(propHandlers).forEach(function (propName) { 2321 | var handler = propHandlers[propName]; 2322 | handler.dispose && handler.dispose(winjsComponent, propName); 2323 | }) 2324 | } 2325 | 2326 | return React.createClass({ 2327 | displayName: displayName, 2328 | statics: { 2329 | initWinJSComponent: initWinJSComponent, 2330 | updateWinJSComponent: updateWinJSComponent, 2331 | disposeWinJSComponent: disposeWinJSComponent 2332 | }, 2333 | propTypes: mapObject(propHandlers, function (propName, propHandler) { 2334 | return propHandler.propType; 2335 | }), 2336 | shouldComponentUpdate: function () { 2337 | return false; 2338 | }, 2339 | // If choosing to implement componentWillMount, be aware that componentWillMount 2340 | // will run when WinJSChildComponent renders the component to a string via 2341 | // renderRootlessComponent. 2342 | componentDidMount: function () { 2343 | initWinJSComponent(this, ReactDOM.findDOMNode(this), this.props); 2344 | }, 2345 | componentWillUnmount: function () { 2346 | disposeWinJSComponent(this); 2347 | }, 2348 | componentWillReceiveProps: function (nextProps) { 2349 | updateWinJSComponent(this, this.props, nextProps); 2350 | }, 2351 | render: function() { 2352 | return render(this); 2353 | } 2354 | }); 2355 | } 2356 | 2357 | var hostEl = document.createElement("div"); 2358 | function renderRootlessComponent(component) { 2359 | var html = ReactDOMServer.renderToStaticMarkup(component); 2360 | hostEl.innerHTML = html; 2361 | var element = hostEl.firstElementChild; 2362 | hostEl.removeChild(element); 2363 | return element; 2364 | } 2365 | 2366 | // TODO: Is there a better way to solve this problem that WinJSChildComponent solves? 2367 | // TODO: Because we're not going thru React's lifecycle, we're missing out on 2368 | // validation of propTypes. 2369 | // TODO: ref doesn't work on WinJSChildComponents. The reason is that during updates, we 2370 | // don't call ReactDOM.render. Because of this, refs would go stale and only reflect the 2371 | // state of the component after its first render. Consequently, we clone the component 2372 | // during its first render so it never shows up in refs. This should make it clearer 2373 | // that refs don't work than generating stale refs. 2374 | function WinJSChildComponent(component) { // implements IWinJSChildComponent 2375 | // Clone the component so a ref isn't generated. 2376 | var clonedComponent = React.cloneElement(component, { ref: null }); 2377 | var element = renderRootlessComponent(clonedComponent); 2378 | component.type.initWinJSComponent(this, element, component.props); 2379 | this.key = component.key; 2380 | this.type = component.type; 2381 | this._props = component.props; 2382 | this._disposeWinJSComponent = component.type.disposeWinJSComponent; 2383 | }; 2384 | WinJSChildComponent.prototype.update = function (component) { 2385 | component.type.updateWinJSComponent(this, this._props, component.props); 2386 | this._props = component.props; 2387 | }; 2388 | WinJSChildComponent.prototype.dispose = function () { 2389 | this._disposeWinJSComponent(this); 2390 | }; 2391 | 2392 | 2393 | // Prop handlers that are common to every WinJS control. 2394 | var defaultPropHandlers = { 2395 | className: PropHandlers.winControlClassName, 2396 | style: PropHandlers.winControlStyle, 2397 | 2398 | // TODO: Instead of special casing these, support DOM attributes 2399 | // more generically. 2400 | id: PropHandlers.domProperty(React.PropTypes.string), 2401 | "aria-controls": PropHandlers.domAttribute(React.PropTypes.any), 2402 | "aria-expanded": PropHandlers.domAttribute(React.PropTypes.any) 2403 | }; 2404 | 2405 | // Control-specific prop handlers derived from RawControlApis 2406 | var DefaultControlPropHandlers = (function processRawApis() { 2407 | var keepProperty = function keepProperty(propertyName) { 2408 | return !endsWith(propertyName.toLowerCase(), "element"); 2409 | }; 2410 | 2411 | return mapObject(RawControlApis, function (controlName, controlApis) { 2412 | var propHandlers = {}; 2413 | Object.keys(controlApis).forEach(function (propName) { 2414 | if (isEvent(propName)) { 2415 | propHandlers[propName] = PropHandlers.event; 2416 | } else if (keepProperty(propName)) { 2417 | var typeInfo = controlApis[propName]; 2418 | var propType = typeToPropType(typeInfo); 2419 | propHandlers[propName] = PropHandlers.property(propType); 2420 | } 2421 | }); 2422 | return propHandlers; 2423 | }); 2424 | })(); 2425 | 2426 | // Each entry in controlApis has the same format as the argument to defineControl except 2427 | // updateWithDefaults automatically provides: 2428 | // - winjsControl 2429 | // - displayName 2430 | // - propHandlers 2431 | // and updateWithDefaults implements an extra option: 2432 | // - underlyingControlName 2433 | // By default, winjsControl, displayName, and propHanders are inferred from the entry's key 2434 | // in controlApis. If underlyingControlName is provided, they will instead be inferred from 2435 | // that name. 2436 | function updateWithDefaults(controlApis) { 2437 | Object.keys(controlApis).forEach(function (controlName) { 2438 | var spec = controlApis[controlName]; 2439 | var winjsControlName = spec.underlyingControlName || controlName; 2440 | spec.winjsControl = spec.winjsControl || WinJS.UI[winjsControlName]; 2441 | spec.displayName = spec.displayName || winjsControlName; 2442 | spec.propHandlers = merge( 2443 | defaultPropHandlers, // Common to all WinJS controls 2444 | DefaultControlPropHandlers[winjsControlName], // Control-specific derived from RawControlApis 2445 | spec.propHandlers // Control-specific handwritten 2446 | ); 2447 | }); 2448 | return controlApis; 2449 | } 2450 | 2451 | var typeWarnPropHandler = PropHandlers.warn("Invalid prop 'type'. Instead, the command type is" + 2452 | " determined by the component: Button, Toggle, Separator, ContentCommand, FlyoutCommand."); 2453 | var CommandSpecs = { 2454 | Button: { 2455 | underlyingControlName: "AppBarCommand", 2456 | winControlOptions: { type: "button" }, 2457 | render: function (component) { 2458 | return React.DOM.button(); 2459 | }, 2460 | propHandlers: { 2461 | type: typeWarnPropHandler, 2462 | } 2463 | }, 2464 | Toggle: { 2465 | underlyingControlName: "AppBarCommand", 2466 | winControlOptions: { type: "toggle" }, 2467 | render: function (component) { 2468 | return React.DOM.button(); 2469 | }, 2470 | propHandlers: { 2471 | type: typeWarnPropHandler 2472 | } 2473 | }, 2474 | Separator: { 2475 | underlyingControlName: "AppBarCommand", 2476 | winControlOptions: { type: "separator" }, 2477 | render: function (component) { 2478 | return React.DOM.hr(); 2479 | }, 2480 | propHandlers: { 2481 | type: typeWarnPropHandler 2482 | } 2483 | }, 2484 | ContentCommand: { 2485 | underlyingControlName: "AppBarCommand", 2486 | winControlOptions: { type: "content" }, 2487 | propHandlers: { 2488 | type: typeWarnPropHandler, 2489 | children: PropHandlers.mountTo(function (winjsComponent) { 2490 | return winjsComponent.winControl.element; 2491 | }) 2492 | } 2493 | }, 2494 | FlyoutCommand: { 2495 | underlyingControlName: "AppBarCommand", 2496 | winControlOptions: { type: "flyout" }, 2497 | render: function (component) { 2498 | return React.DOM.button(); 2499 | }, 2500 | propHandlers: { 2501 | type: typeWarnPropHandler, 2502 | flyoutComponent: { 2503 | propType: React.PropTypes.element, 2504 | update: function FlyoutCommand_flyoutComponent_update(winjsComponent, propName, oldValue, newValue) { 2505 | var data = winjsComponent.data[propName]; 2506 | if (!data) { 2507 | var flyoutHost = document.createElement("div"); 2508 | flyoutHost.className = "win-react-flyout-host"; 2509 | document.body.appendChild(flyoutHost); 2510 | winjsComponent.data[propName] = data = { 2511 | flyoutHost: flyoutHost, 2512 | flyoutComponent: null 2513 | }; 2514 | } 2515 | var oldWinControl = data.flyoutComponent && data.flyoutComponent.winControl; 2516 | var instance = ReactDOM.render(newValue, data.flyoutHost); 2517 | if (oldWinControl !== instance.winControl) { 2518 | winjsComponent.winControl.flyout = instance.winControl; 2519 | } 2520 | winjsComponent.data[propName].flyoutComponent = instance; 2521 | }, 2522 | dispose: function FlyoutCommand_flyoutComponent_dispose(winjsComponent, propName) { 2523 | var data = winjsComponent.data[propName]; 2524 | if (data && data.flyoutHost) { 2525 | ReactDOM.unmountComponentAtNode(data.flyoutHost); 2526 | deparent(data.flyoutHost); 2527 | } 2528 | } 2529 | } 2530 | } 2531 | } 2532 | }; 2533 | 2534 | var ControlApis = updateWithDefaults({ 2535 | AppBar: { 2536 | propHandlers: { 2537 | opened: PropHandlers.focusProperty(React.PropTypes.bool), 2538 | children: PropHandlers.syncChildrenWithBindingList("data") 2539 | } 2540 | }, 2541 | "AppBar.Button": CommandSpecs.Button, 2542 | "AppBar.Toggle": CommandSpecs.Toggle, 2543 | "AppBar.Separator": CommandSpecs.Separator, 2544 | "AppBar.ContentCommand": CommandSpecs.ContentCommand, 2545 | "AppBar.FlyoutCommand": CommandSpecs.FlyoutCommand, 2546 | AutoSuggestBox: {}, 2547 | BackButton: { 2548 | preCtorInit: function (element, options, data, displayName) { 2549 | element.addEventListener("click", function (eventObject) { 2550 | // Prevent React from seeing the "click" event to workaround this React 2551 | // bug: https://github.com/facebook/react/issues/3790 2552 | eventObject.stopPropagation(); 2553 | }); 2554 | }, 2555 | render: function (component) { 2556 | return React.DOM.button(); 2557 | } 2558 | }, 2559 | // CellSpanningLayout: Not a component so just use off of WinJS.UI? 2560 | ContentDialog: { 2561 | propHandlers: { 2562 | hidden: PropHandlers.focusProperty(React.PropTypes.bool), 2563 | children: PropHandlers.mountTo(function (winjsComponent) { 2564 | return winjsComponent.winControl.element.querySelector(".win-contentdialog-content"); 2565 | }) 2566 | } 2567 | }, 2568 | DatePicker: {}, 2569 | FlipView: {}, 2570 | Flyout: { 2571 | // The WinJS Flyout control doesn't come with a good mount point. 2572 | // App content and control content are siblings in Flyout.element. 2573 | // Consequently, if React rendered to Flyout.element, it would destroy 2574 | // some of Flyout's elements. To fix this, we give Flyout a div 2575 | // (className="win-react-flyout-mount-point") which will contain only 2576 | // app content. The React component renders into this div so it doesn't 2577 | // destroy any control content. 2578 | render: function (component) { 2579 | return React.DOM.div(null, React.DOM.div({ className: "win-react-flyout-mount-point" })); 2580 | }, 2581 | propHandlers: { 2582 | hidden: PropHandlers.focusProperty(React.PropTypes.bool), 2583 | children: PropHandlers.mountTo(function (winjsComponent) { 2584 | return winjsComponent.winControl.element.querySelector(".win-react-flyout-mount-point"); 2585 | }) 2586 | } 2587 | }, 2588 | // GridLayout: Not a component so just use off of WinJS.UI? 2589 | Hub: { 2590 | propHandlers: { 2591 | children: PropHandlers.syncChildrenWithBindingList("sections") 2592 | } 2593 | }, 2594 | "Hub.Section": { 2595 | underlyingControlName: "HubSection", 2596 | propHandlers: { 2597 | children: PropHandlers.mountTo(function (winjsComponent) { 2598 | return winjsComponent.winControl.contentElement; 2599 | }) 2600 | } 2601 | }, 2602 | ItemContainer: { 2603 | propHandlers: { 2604 | children: PropHandlers.mountTo(function (winjsComponent) { 2605 | return winjsComponent.winControl.element.querySelector(".win-item"); 2606 | }) 2607 | } 2608 | }, 2609 | // ListLayout: Not a component so just use off of WinJS.UI? 2610 | ListView: { 2611 | propHandlers: { 2612 | currentItem: PropHandlers.focusProperty(React.PropTypes.any), 2613 | headerComponent: PropHandlers.propertyWithMount("header"), 2614 | footerComponent: PropHandlers.propertyWithMount("footer"), 2615 | } 2616 | }, 2617 | // TODO: Keyboarding doesn't work in Menu probably because MenuCommands are not direct 2618 | // children of the Menu. 2619 | Menu: { 2620 | propHandlers: { 2621 | hidden: PropHandlers.focusProperty(React.PropTypes.bool), 2622 | children: { 2623 | // children propHandler looks like this rather than using mountTo on 2624 | // winControl.element because this enables props.children to have 2625 | // multiple components whereas the other technique restricts it to one. 2626 | update: function (winjsComponent, propName, oldValue, newValue) { 2627 | // TODO: dispose 2628 | ReactDOM.render(React.DOM.div(null, newValue), winjsComponent.winControl.element); 2629 | } 2630 | } 2631 | } 2632 | }, 2633 | "Menu.Button": merge(CommandSpecs.Button, { 2634 | underlyingControlName: "MenuCommand" 2635 | }), 2636 | "Menu.Toggle": merge(CommandSpecs.Toggle, { 2637 | underlyingControlName: "MenuCommand" 2638 | }), 2639 | "Menu.Separator": merge(CommandSpecs.Separator, { 2640 | underlyingControlName: "MenuCommand" 2641 | }), 2642 | "Menu.FlyoutCommand": merge(CommandSpecs.FlyoutCommand, { 2643 | underlyingControlName: "MenuCommand" 2644 | }), 2645 | Pivot: { 2646 | propHandlers: { 2647 | children: PropHandlers.syncChildrenWithBindingList("items"), 2648 | customLeftHeaderComponent: PropHandlers.propertyWithMount("customLeftHeader"), 2649 | customRightHeaderComponent: PropHandlers.propertyWithMount("customRightHeader") 2650 | } 2651 | }, 2652 | "Pivot.Item": { 2653 | underlyingControlName: "PivotItem", 2654 | propHandlers: { 2655 | children: PropHandlers.mountTo(function (winjsComponent) { 2656 | return winjsComponent.winControl.contentElement; 2657 | }) 2658 | } 2659 | }, 2660 | Rating: {}, 2661 | SemanticZoom: { 2662 | propHandlers: { 2663 | zoomedInComponent: { 2664 | propType: React.PropTypes.element, 2665 | preCtorInit: function zoomedInComponent_preCtorInit(element, options, data, displayName, propName, value) { 2666 | var child = new WinJSChildComponent(value); 2667 | // Zoomed in component should be the first child. 2668 | element.insertBefore(child.winControl.element, element.firstElementChild); 2669 | data[propName] = child; 2670 | }, 2671 | update: function zoomedInComponent_update(winjsComponent, propName, oldValue, newValue) { 2672 | var child = winjsComponent.data[propName]; 2673 | if (child.type === newValue.type) { 2674 | child.update(newValue); 2675 | } else { 2676 | console.warn("SemanticZoom: zoomedInComponent's component type can't change"); 2677 | } 2678 | }, 2679 | dispose: function zoomedInComponent_dispose(winjsComponent, propName) { 2680 | var child = winjsComponent.data[propName]; 2681 | child && child.dispose(); 2682 | } 2683 | }, 2684 | zoomedOutComponent: { 2685 | propType: React.PropTypes.element, 2686 | preCtorInit: function zoomedOutComponent_preCtorInit(element, options, data, displayName, propName, value) { 2687 | var child = new WinJSChildComponent(value); 2688 | // Zoomed out component should be the second child. 2689 | element.appendChild(child.winControl.element); 2690 | data[propName] = child; 2691 | }, 2692 | update: function zoomedOutComponent_update(winjsComponent, propName, oldValue, newValue) { 2693 | var child = winjsComponent.data[propName]; 2694 | if (child.type === newValue.type) { 2695 | child.update(newValue); 2696 | } else { 2697 | console.warn("SemanticZoom: zoomedOutComponent's component type can't change"); 2698 | } 2699 | }, 2700 | dispose: function zoomedOutComponent_dispose(winjsComponent, propName) { 2701 | var child = winjsComponent.data[propName]; 2702 | child && child.dispose(); 2703 | } 2704 | } 2705 | } 2706 | }, 2707 | SplitView: { 2708 | propHandlers: { 2709 | paneOpened: PropHandlers.focusProperty(React.PropTypes.bool), 2710 | paneComponent: PropHandlers.mountTo(function (winjsComponent) { 2711 | return winjsComponent.winControl.paneElement; 2712 | }), 2713 | contentComponent: PropHandlers.mountTo(function (winjsComponent) { 2714 | return winjsComponent.winControl.contentElement; 2715 | }) 2716 | } 2717 | }, 2718 | "SplitView.Command": { 2719 | underlyingControlName: "SplitViewCommand", 2720 | }, 2721 | SplitViewPaneToggle: { 2722 | render: function (component) { 2723 | return React.DOM.button(); 2724 | }, 2725 | propHandlers: { 2726 | // paneOpened provides a React-friendly interface for making the SplitViewPaneToggle accessible. 2727 | // When paneOpened is specified, is not undefined, and is not null, it: 2728 | // - Sets SplitViewPaneToggle's aria-expanded attribute to match paneOpened 2729 | // - Fires SplitViewPaneToggle's "invoked" event when aria-expanded is mutated 2730 | paneOpened: { 2731 | propType: React.PropTypes.bool, 2732 | update: function paneOpened_update(winjsComponent, propName, oldValue, newValue) { 2733 | var data = winjsComponent.data[propName]; 2734 | if (!data) { 2735 | data = { 2736 | // WinJS.UI.SplitViewPaneToggle depends on WinJS.Utilities._MutationObserver so it 2737 | // is safe to use it here. 2738 | ariaExpandedMutationObserver: new WinJS.Utilities._MutationObserver(function () { 2739 | var element = winjsComponent.element; 2740 | var ariaExpanded = (element.getAttribute("aria-expanded") === "true"); 2741 | if (ariaExpanded !== winjsComponent.data[propName].value) { 2742 | fireEvent(element, "invoked"); // Fire WinJS.UI.SplitViewPaneToggle's invoked event 2743 | } 2744 | }), 2745 | observing: false, 2746 | value: newValue 2747 | }; 2748 | winjsComponent.data[propName] = data; 2749 | } 2750 | 2751 | if (oldValue !== newValue) { 2752 | if (newValue !== null && newValue !== undefined) { 2753 | winjsComponent.element.setAttribute("aria-expanded", newValue ? "true" : "false"); 2754 | if (!data.observing) { 2755 | data.observing = true; 2756 | data.ariaExpandedMutationObserver.observe(winjsComponent.element, { 2757 | attributes: true, 2758 | attributeFilter: ["aria-expanded"] 2759 | }); 2760 | } 2761 | } else { 2762 | winjsComponent.element.removeAttribute("aria-expanded"); 2763 | if (data.observing) { 2764 | data.observing = false; 2765 | data.ariaExpandedMutationObserver.disconnect(); 2766 | } 2767 | } 2768 | } 2769 | 2770 | data.value = newValue; 2771 | }, 2772 | dispose: function paneOpened_dispose(winjsComponent, propName) { 2773 | var data = winjsComponent.data[propName]; 2774 | if (data && data.observing) { 2775 | data.ariaExpandedMutationObserver.disconnect(); 2776 | } 2777 | } 2778 | } 2779 | } 2780 | }, 2781 | TimePicker: {}, 2782 | ToggleSwitch: {}, 2783 | ToolBar: { 2784 | propHandlers: { 2785 | opened: PropHandlers.focusProperty(React.PropTypes.bool), 2786 | children: PropHandlers.syncChildrenWithBindingList("data") 2787 | } 2788 | }, 2789 | "ToolBar.Button": CommandSpecs.Button, 2790 | "ToolBar.Toggle": CommandSpecs.Toggle, 2791 | "ToolBar.Separator": CommandSpecs.Separator, 2792 | "ToolBar.ContentCommand": CommandSpecs.ContentCommand, 2793 | "ToolBar.FlyoutCommand": CommandSpecs.FlyoutCommand, 2794 | Tooltip: { 2795 | propHandlers: { 2796 | children: PropHandlers.mountTo(function (winjsComponent) { 2797 | return winjsComponent.winControl.element; 2798 | }), 2799 | contentComponent: PropHandlers.propertyWithMount("contentElement") 2800 | } 2801 | } 2802 | }); 2803 | 2804 | // 2805 | // Publish 2806 | // 2807 | 2808 | var ReactWinJS = {}; 2809 | 2810 | // Controls 2811 | // 2812 | 2813 | // Sort to ensure that controls come before their subcontrols 2814 | // (e.g. AppBar comes before AppBar.Toggle). 2815 | Object.keys(ControlApis).sort().forEach(function (controlName) { 2816 | nestedSet(ReactWinJS, controlName, defineControl(ControlApis[controlName])); 2817 | }); 2818 | 2819 | // Utilites 2820 | // 2821 | 2822 | // Given a function that returns a React component, 2823 | // returns an item renderer function that can be used 2824 | // with WinJS controls. Useful for describing FlipView 2825 | // and ListView item templates as React components. 2826 | ReactWinJS.reactRenderer = function reactRenderer(componentFunction) { 2827 | var componentFunctionBound; 2828 | var renderItem = function renderItem(item) { 2829 | var element = document.createElement("div"); 2830 | element.className = "win-react-renderer-host"; 2831 | ReactDOM.render(componentFunctionBound(item), element); 2832 | WinJS.Utilities.markDisposable(element, function () { 2833 | ReactDOM.unmountComponentAtNode(element); 2834 | }); 2835 | return element; 2836 | }; 2837 | 2838 | return function itemRenderer(itemOrItemPromise) { 2839 | if (!componentFunctionBound) { 2840 | componentFunctionBound = componentFunction.bind(this); 2841 | } 2842 | 2843 | return WinJS.Promise.is(itemOrItemPromise) ? 2844 | itemOrItemPromise.then(renderItem) : 2845 | renderItem(itemOrItemPromise); 2846 | } 2847 | }; 2848 | 2849 | 2850 | // Low-level utilities for wrapping custom WinJS-style controls 2851 | // 2852 | 2853 | ReactWinJS.defineControl = defineControl; 2854 | ReactWinJS.PropHandlers = PropHandlers; 2855 | ReactWinJS.defaultPropHandlers = defaultPropHandlers; 2856 | 2857 | module.exports = ReactWinJS; 2858 | --------------------------------------------------------------------------------