* add samples/react-spfx-multilist-grid/

* images

* images

* Update README.md

* images
This commit is contained in:
Russell gove 2017-01-06 10:04:19 -05:00 committed by Vesa Juvonen
parent 7c44cf7f93
commit 692de70857
115 changed files with 35479 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,22 @@
{
// The number of spaces a tab is equal to.
"editor.tabSize": 2,
// When enabled, will trim trailing whitespace when you save a file.
"files.trimTrailingWhitespace": true,
// Controls if the editor should automatically close brackets after opening them
"editor.autoClosingBrackets": false,
// Configure glob patterns for excluding files and folders.
"search.exclude": {
"**/bower_components": true,
"**/node_modules": true,
"coverage": true,
"dist": true,
"lib-amd": true,
"lib": true,
"temp": true
}
,
"typescript.tsdk": "./node_modules/typescript/lib"
}

View File

@ -0,0 +1,34 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "0.1.0",
"command": "gulp",
"isShellCommand": true,
"showOutput": "always",
"args": [
"--no-color"
],
"tasks": [
{
"taskName": "bundle",
"isBuildCommand": true,
"problemMatcher": [
"$tsc"
]
},
{
"taskName": "test",
"isTestCommand": true,
"problemMatcher": [
"$tsc"
]
},
{
"taskName": "serve",
"isWatching": true,
"problemMatcher": [
"$tsc"
]
}
]
}

View File

@ -0,0 +1,7 @@
{
"@microsoft/generator-sharepoint": {
"libraryName": "spfxreactgrid",
"libraryId": "28bf799f-01fc-45e9-8997-f7982eaf8b53",
"framework": "react"
}
}

View File

@ -0,0 +1,66 @@
# SPFX React Grid
## Summary
An SPFX Webpart that uses React, Office-UI-Fabric, and Redux to let users edit list data from multiple Webs and Multiple Sites in a single grid.
![alt tag](/samples/react-spfx-multilist-grid/src/images/editListItems.PNG)
## Applies to
* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant)
## Prerequisites
> React, Redux Office-UI-Fabric
## Solution
Solution|Author(s)
--------|---------
react-spfx-multilist-grid | Russell Gove
## Version history
Version|Date|Comments
-------|----|--------
0.1|December 31, 2016|Initial version
## Disclaimer
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
---
## Minimal Path to Awesome
- Clone this repository
- in the command line run:
- `npm install`
- `tsd install`
- `gulp serve`
> Include any additional steps as needed.
## Features
An SPFX Webpart that uses React, Office-UI-Fabric, and Redux to let users edit list data from multiple Webs and Multiple Sites in a single grid.
Usage:
1. Define the columns to be shown in the grid
![alt tag](/samples/react-spfx-multilist-grid/src/images/columnDefinitions.PNG)
2. Define the lists to be edited
![alt tag](/samples/react-spfx-multilist-grid/src/images/ListDefinitions.PNG)
3. Edit the info
![alt tag](/samples/react-spfx-multilist-grid/src/images/editListItems.PNG)
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/readme-template" />

Binary file not shown.

View File

@ -0,0 +1,116 @@
'use strict';
var escapeStringRegexp = require('escape-string-regexp');
var ansiStyles = require('ansi-styles');
var stripAnsi = require('strip-ansi');
var hasAnsi = require('has-ansi');
var supportsColor = require('supports-color');
var defineProps = Object.defineProperties;
var isSimpleWindowsTerm = process.platform === 'win32' && !/^xterm/i.test(process.env.TERM);
function Chalk(options) {
// detect mode if not set manually
this.enabled = !options || options.enabled === undefined ? supportsColor : options.enabled;
}
// use bright blue on Windows as the normal blue color is illegible
if (isSimpleWindowsTerm) {
ansiStyles.blue.open = '\u001b[94m';
}
var styles = (function () {
var ret = {};
Object.keys(ansiStyles).forEach(function (key) {
ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g');
ret[key] = {
get: function () {
return build.call(this, this._styles.concat(key));
}
};
});
return ret;
})();
var proto = defineProps(function chalk() {}, styles);
function build(_styles) {
var builder = function () {
return applyStyle.apply(builder, arguments);
};
builder._styles = _styles;
builder.enabled = this.enabled;
// __proto__ is used because we must return a function, but there is
// no way to create a function with a different prototype.
/* eslint-disable no-proto */
builder.__proto__ = proto;
return builder;
}
function applyStyle() {
// support varags, but simply cast to string in case there's only one arg
var args = arguments;
var argsLen = args.length;
var str = argsLen !== 0 && String(arguments[0]);
if (argsLen > 1) {
// don't slice `arguments`, it prevents v8 optimizations
for (var a = 1; a < argsLen; a++) {
str += ' ' + args[a];
}
}
if (!this.enabled || !str) {
return str;
}
var nestedStyles = this._styles;
var i = nestedStyles.length;
// Turns out that on Windows dimmed gray text becomes invisible in cmd.exe,
// see https://github.com/chalk/chalk/issues/58
// If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop.
var originalDim = ansiStyles.dim.open;
if (isSimpleWindowsTerm && (nestedStyles.indexOf('gray') !== -1 || nestedStyles.indexOf('grey') !== -1)) {
ansiStyles.dim.open = '';
}
while (i--) {
var code = ansiStyles[nestedStyles[i]];
// Replace any instances already present with a re-opening code
// otherwise only the part of the string until said closing code
// will be colored, and the rest will simply be 'plain'.
str = code.open + str.replace(code.closeRe, code.open) + code.close;
}
// Reset the original 'dim' if we changed it to work around the Windows dimmed gray issue.
ansiStyles.dim.open = originalDim;
return str;
}
function init() {
var ret = {};
Object.keys(styles).forEach(function (name) {
ret[name] = {
get: function () {
return build.call(this, [name]);
}
};
});
return ret;
}
defineProps(Chalk.prototype, init());
module.exports = new Chalk();
module.exports.styles = ansiStyles;
module.exports.hasColor = hasAnsi;
module.exports.stripColor = stripAnsi;
module.exports.supportsColor = supportsColor;

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,149 @@
{
"_args": [
[
{
"name": "chalk",
"raw": "chalk@^1.1.1",
"rawSpec": "^1.1.1",
"scope": null,
"spec": ">=1.1.1 <2.0.0",
"type": "range"
},
"C:\\Users\\trwg1\\Documents\\GitHub\\spfxreactgrid\\node_modules\\har-validator"
]
],
"_from": "chalk@>=1.1.1 <2.0.0",
"_id": "chalk@1.1.3",
"_inCache": true,
"_installable": true,
"_location": "/chalk",
"_nodeVersion": "0.10.32",
"_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/chalk-1.1.3.tgz_1459210604109_0.3892582862172276"
},
"_npmUser": {
"email": "i.am.qix@gmail.com",
"name": "qix"
},
"_npmVersion": "2.14.2",
"_phantomChildren": {},
"_requested": {
"name": "chalk",
"raw": "chalk@^1.1.1",
"rawSpec": "^1.1.1",
"scope": null,
"spec": ">=1.1.1 <2.0.0",
"type": "range"
},
"_requiredBy": [
"/babel-code-frame",
"/clap",
"/fancy-log",
"/gulp",
"/gulp-util",
"/har-validator",
"/karma-mocha-clean-reporter",
"/log-symbols",
"/marked-terminal",
"/node-sass",
"/postcss"
],
"_resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"_shasum": "a8115c55e4a702fe4d150abd3872822a7e09fc98",
"_shrinkwrap": null,
"_spec": "chalk@^1.1.1",
"_where": "C:\\Users\\trwg1\\Documents\\GitHub\\spfxreactgrid\\node_modules\\har-validator",
"bugs": {
"url": "https://github.com/chalk/chalk/issues"
},
"dependencies": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
},
"description": "Terminal string styling done right. Much color.",
"devDependencies": {
"coveralls": "^2.11.2",
"matcha": "^0.6.0",
"mocha": "*",
"nyc": "^3.0.0",
"require-uncached": "^1.0.2",
"resolve-from": "^1.0.0",
"semver": "^4.3.3",
"xo": "*"
},
"directories": {},
"dist": {
"shasum": "a8115c55e4a702fe4d150abd3872822a7e09fc98",
"tarball": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz"
},
"engines": {
"node": ">=0.10.0"
},
"files": [
"index.js"
],
"gitHead": "0d8d8c204eb87a4038219131ad4d8369c9f59d24",
"homepage": "https://github.com/chalk/chalk#readme",
"keywords": [
"color",
"colour",
"colors",
"terminal",
"console",
"cli",
"string",
"str",
"ansi",
"style",
"styles",
"tty",
"formatting",
"rgb",
"256",
"shell",
"xterm",
"log",
"logging",
"command-line",
"text"
],
"license": "MIT",
"maintainers": [
{
"email": "i.am.qix@gmail.com",
"name": "qix"
},
{
"email": "sindresorhus@gmail.com",
"name": "sindresorhus"
},
{
"email": "sindresorhus+unicorn@gmail.com",
"name": "unicorn"
}
],
"name": "chalk",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/chalk/chalk.git"
},
"scripts": {
"bench": "matcha benchmark.js",
"coverage": "nyc npm test && nyc report",
"coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
"test": "xo && mocha"
},
"version": "1.1.3",
"xo": {
"envs": [
"node",
"mocha"
]
}
}

View File

@ -0,0 +1,213 @@
<h1 align="center">
<br>
<br>
<img width="360" src="https://cdn.rawgit.com/chalk/chalk/19935d6484811c5e468817f846b7b3d417d7bf4a/logo.svg" alt="chalk">
<br>
<br>
<br>
</h1>
> Terminal string styling done right
[![Build Status](https://travis-ci.org/chalk/chalk.svg?branch=master)](https://travis-ci.org/chalk/chalk)
[![Coverage Status](https://coveralls.io/repos/chalk/chalk/badge.svg?branch=master)](https://coveralls.io/r/chalk/chalk?branch=master)
[![](http://img.shields.io/badge/unicorn-approved-ff69b4.svg)](https://www.youtube.com/watch?v=9auOCbH5Ns4)
[colors.js](https://github.com/Marak/colors.js) used to be the most popular string styling module, but it has serious deficiencies like extending `String.prototype` which causes all kinds of [problems](https://github.com/yeoman/yo/issues/68). Although there are other ones, they either do too much or not enough.
**Chalk is a clean and focused alternative.**
![](https://github.com/chalk/ansi-styles/raw/master/screenshot.png)
## Why
- Highly performant
- Doesn't extend `String.prototype`
- Expressive API
- Ability to nest styles
- Clean and focused
- Auto-detects color support
- Actively maintained
- [Used by ~4500 modules](https://www.npmjs.com/browse/depended/chalk) as of July 15, 2015
## Install
```
$ npm install --save chalk
```
## Usage
Chalk comes with an easy to use composable API where you just chain and nest the styles you want.
```js
var chalk = require('chalk');
// style a string
chalk.blue('Hello world!');
// combine styled and normal strings
chalk.blue('Hello') + 'World' + chalk.red('!');
// compose multiple styles using the chainable API
chalk.blue.bgRed.bold('Hello world!');
// pass in multiple arguments
chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz');
// nest styles
chalk.red('Hello', chalk.underline.bgBlue('world') + '!');
// nest styles of the same type even (color, underline, background)
chalk.green(
'I am a green line ' +
chalk.blue.underline.bold('with a blue substring') +
' that becomes green again!'
);
```
Easily define your own themes.
```js
var chalk = require('chalk');
var error = chalk.bold.red;
console.log(error('Error!'));
```
Take advantage of console.log [string substitution](http://nodejs.org/docs/latest/api/console.html#console_console_log_data).
```js
var name = 'Sindre';
console.log(chalk.green('Hello %s'), name);
//=> Hello Sindre
```
## API
### chalk.`<style>[.<style>...](string, [string...])`
Example: `chalk.red.bold.underline('Hello', 'world');`
Chain [styles](#styles) and call the last one as a method with a string argument. Order doesn't matter, and later styles take precedent in case of a conflict. This simply means that `Chalk.red.yellow.green` is equivalent to `Chalk.green`.
Multiple arguments will be separated by space.
### chalk.enabled
Color support is automatically detected, but you can override it by setting the `enabled` property. You should however only do this in your own code as it applies globally to all chalk consumers.
If you need to change this in a reusable module create a new instance:
```js
var ctx = new chalk.constructor({enabled: false});
```
### chalk.supportsColor
Detect whether the terminal [supports color](https://github.com/chalk/supports-color). Used internally and handled for you, but exposed for convenience.
Can be overridden by the user with the flags `--color` and `--no-color`. For situations where using `--color` is not possible, add an environment variable `FORCE_COLOR` with any value to force color. Trumps `--no-color`.
### chalk.styles
Exposes the styles as [ANSI escape codes](https://github.com/chalk/ansi-styles).
Generally not useful, but you might need just the `.open` or `.close` escape code if you're mixing externally styled strings with your own.
```js
var chalk = require('chalk');
console.log(chalk.styles.red);
//=> {open: '\u001b[31m', close: '\u001b[39m'}
console.log(chalk.styles.red.open + 'Hello' + chalk.styles.red.close);
```
### chalk.hasColor(string)
Check whether a string [has color](https://github.com/chalk/has-ansi).
### chalk.stripColor(string)
[Strip color](https://github.com/chalk/strip-ansi) from a string.
Can be useful in combination with `.supportsColor` to strip color on externally styled text when it's not supported.
Example:
```js
var chalk = require('chalk');
var styledString = getText();
if (!chalk.supportsColor) {
styledString = chalk.stripColor(styledString);
}
```
## Styles
### Modifiers
- `reset`
- `bold`
- `dim`
- `italic` *(not widely supported)*
- `underline`
- `inverse`
- `hidden`
- `strikethrough` *(not widely supported)*
### Colors
- `black`
- `red`
- `green`
- `yellow`
- `blue` *(on Windows the bright version is used as normal blue is illegible)*
- `magenta`
- `cyan`
- `white`
- `gray`
### Background colors
- `bgBlack`
- `bgRed`
- `bgGreen`
- `bgYellow`
- `bgBlue`
- `bgMagenta`
- `bgCyan`
- `bgWhite`
## 256-colors
Chalk does not support anything other than the base eight colors, which guarantees it will work on all terminals and systems. Some terminals, specifically `xterm` compliant ones, will support the full range of 8-bit colors. For this the lower level [ansi-256-colors](https://github.com/jbnicolai/ansi-256-colors) package can be used.
## Windows
If you're on Windows, do yourself a favor and use [`cmder`](http://bliker.github.io/cmder/) instead of `cmd.exe`.
## Related
- [chalk-cli](https://github.com/chalk/chalk-cli) - CLI for this module
- [ansi-styles](https://github.com/chalk/ansi-styles/) - ANSI escape codes for styling strings in the terminal
- [supports-color](https://github.com/chalk/supports-color/) - Detect whether a terminal supports color
- [strip-ansi](https://github.com/chalk/strip-ansi) - Strip ANSI escape codes
- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes
- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes
- [wrap-ansi](https://github.com/chalk/wrap-ansi) - Wordwrap a string with ANSI escape codes
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)

View File

@ -0,0 +1,22 @@
{
"entries": [
{
"entry": "./lib/webparts/spfxReactGrid/SpfxReactGridWebPart.js",
"manifest": "./src/webparts/spfxReactGrid/SpfxReactGridWebPart.manifest.json",
"outputPath": "./dist/spfx-react-grid.bundle.js"
}
],
"externals": {
"@microsoft/sp-client-base": "node_modules/@microsoft/sp-client-base/dist/sp-client-base.js",
"@microsoft/sp-client-preview": "node_modules/@microsoft/sp-client-preview/dist/sp-client-preview.js",
"@microsoft/sp-webpart-base": "node_modules/@microsoft/sp-webpart-base/dist/sp-webpart-base.js",
"@microsoft/sp-lodash-subset": "node_modules/@microsoft/sp-lodash-subset/dist/sp-lodash-subset.js",
"office-ui-fabric-react": "node_modules/office-ui-fabric-react/dist/office-ui-fabric-react.js",
"react": "node_modules/react/dist/react.min.js",
"react-dom": "node_modules/react-dom/dist/react-dom.min.js",
"react-dom/server": "node_modules/react-dom/dist/react-dom-server.min.js"
},
"localizedResources": {
"spfxReactGridStrings": "webparts/spfxReactGrid/loc/{locale}.js"
}
}

View File

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

View File

@ -0,0 +1,10 @@
{
"solution": {
"name": "spfxreactgrid-client-side-solution",
"id": "28bf799f-01fc-45e9-8997-f7982eaf8b53",
"version": "1.0.0.0"
},
"paths": {
"zippedPackage": "solution/spfxreactgrid.spapp"
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
{
"cdnBasePath": "<!-- PATH TO CDN -->"
}

View File

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

View File

@ -0,0 +1,38 @@
{
"name": "spfxreactgrid",
"version": "0.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"dependencies": {
"@microsoft/sp-client-base": "~0.4.0",
"@microsoft/sp-client-preview": "~0.5.0",
"@microsoft/sp-webpart-base": "^0.1.0",
"office-ui-fabric-react": "^0.36.0",
"react": "0.14.8",
"react-data-grid": "^1.0.62",
"react-dom": "0.14.8",
"react-redux": "^4.4.6",
"react-router": "^2.8.1",
"react-router-redux": "^4.0.5",
"redux": "^3.4.0",
"redux-localstorage": "^0.4.1",
"redux-logger": "^2.6.0",
"redux-promise-middleware": "^4.1.0",
"redux-thunk": "^2.1.0",
"sp-pnp-js": "^1.0.6",
"underscore": "^1.8.3"
},
"devDependencies": {
"@microsoft/sp-build-web": "~0.7.0",
"@microsoft/sp-module-interfaces": "~0.4.0",
"@microsoft/sp-webpart-workbench": "~0.5.0",
"gulp": "~3.9.1"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp nuke",
"test": "gulp test"
}
}

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Gulp" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{28bf799f-01fc-45e9-8997-f7982eaf8b53}</ProjectGuid>
<ProjectHome />
<ProjectView>ProjectFiles</ProjectView>
<StartupFile>node_modules\gulp\bin\gulp.js</StartupFile>
<WorkingDirectory>.</WorkingDirectory>
<OutputPath>.</OutputPath>
<ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
<TypeScriptSourceMap>true</TypeScriptSourceMap>
<TypeScriptModuleKind>CommonJS</TypeScriptModuleKind>
<EnableTypeScript>false</EnableTypeScript>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ScriptArguments>serve</ScriptArguments>
<StartWebBrowser>True</StartWebBrowser>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'" />
<PropertyGroup Condition="'$(Configuration)' == 'Release'" />
<Target Name="Gulp">
<Message Text="Running gulp2vs.js" Importance="normal" />
<Exec Command="node.exe &quot;$(MSBuildThisFileDirectory)\node_modules\@microsoft\npmx-lib\lib\gulp2vs.js&quot;" />
</Target>
<ItemGroup>
<Content Include="*.js" />
<Content Include="*.json" />
<Content Include="*.md" />
<Content Include="config\**\*.json" />
<Content Include="docs\*.md" />
<Content Include="sharepoint\feature_xml\**\*.*" />
<Content Include="src\**\*.html" />
<Content Include="src\**\*.js" />
<Content Include="src\**\*.json" />
<Content Include="src\**\*.less" />
<Content Include="src\**\*.resx" />
<Content Include="src\**\*.scss" />
<Content Include="src\**\*.ts" />
<Content Include="src\**\*.tsx" />
<Content Include="typings\**\*.ts" />
</ItemGroup>
<ItemGroup>
<Folder Include="config\" />
<Folder Include="src\" />
<Folder Include="src\webparts\" />
<Folder Include="src\webparts\spfxReactGrid\" />
<Folder Include="src\webparts\spfxReactGrid\actions\" />
<Folder Include="src\webparts\spfxReactGrid\components\" />
<Folder Include="src\webparts\spfxReactGrid\containers\" />
<Folder Include="src\webparts\spfxReactGrid\loc\" />
<Folder Include="src\webparts\spfxReactGrid\model\" />
<Folder Include="src\webparts\spfxReactGrid\reducers\" />
<Folder Include="src\webparts\spfxReactGrid\store\" />
<Folder Include="src\webparts\spfxReactGrid\utils\" />
<Folder Include="typings\" />
<Folder Include="typings\%40ms\" />
<Folder Include="typings\assertion-error\" />
<Folder Include="typings\chai\" />
<Folder Include="typings\combokeys\" />
<Folder Include="typings\es6-collections\" />
<Folder Include="typings\es6-promise\" />
<Folder Include="typings\globals\" />
<Folder Include="typings\globals\react-data-grid\" />
<Folder Include="typings\globals\redux-promise-middleware\" />
<Folder Include="typings\globals\redux\" />
<Folder Include="typings\knockout\" />
<Folder Include="typings\lodash\" />
<Folder Include="typings\mocha\" />
<Folder Include="typings\modules\" />
<Folder Include="typings\modules\react-redux\" />
<Folder Include="typings\modules\react-router-redux\" />
<Folder Include="typings\modules\react-router\" />
<Folder Include="typings\modules\react-select\" />
<Folder Include="typings\modules\redux-thunk\" />
<Folder Include="typings\node\" />
<Folder Include="typings\react\" />
<Folder Include="typings\systemjs\" />
<Folder Include="typings\whatwg-fetch\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<!--Do not delete the following Import Project. While this appears to do nothing it is a marker for setting TypeScript properties before our import that depends on them.-->
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="False" />
<Import Project="$(VSToolsPath)\Node.js Tools\Microsoft.NodejsTools.targets" />
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>False</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>0</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:48022/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>True</UseCustomServer>
<CustomServerUrl>http://localhost:1337</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}" User="">
<WebProjectProperties>
<StartPageUrl>
</StartPageUrl>
<StartAction>CurrentPage</StartAction>
<AspNetDebugging>True</AspNetDebugging>
<SilverlightDebugging>False</SilverlightDebugging>
<NativeDebugging>False</NativeDebugging>
<SQLDebugging>False</SQLDebugging>
<ExternalProgram>
</ExternalProgram>
<StartExternalURL>
</StartExternalURL>
<StartCmdLineArguments>
</StartCmdLineArguments>
<StartWorkingDirectory>
</StartWorkingDirectory>
<EnableENC>False</EnableENC>
<AlwaysStartWebServerOnDebug>False</AlwaysStartWebServerOnDebug>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.25928.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "spfxreactgrid", "spfxreactgrid.njsproj", "{28BF799F-01FC-45E9-8997-F7982EAF8B53}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{28BF799F-01FC-45E9-8997-F7982EAF8B53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{28BF799F-01FC-45E9-8997-F7982EAF8B53}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28BF799F-01FC-45E9-8997-F7982EAF8B53}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28BF799F-01FC-45E9-8997-F7982EAF8B53}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

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

View File

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

View File

@ -0,0 +1,8 @@
import ListDefinition from "./model/ListDefinition";
import column from "./model/ColumnDefinition";
export interface ISpfxReactGridWebPartProps {
description: string;
lists: ListDefinition[];
columns: column[];
}

View File

@ -0,0 +1,3 @@
WorkBench-pageContent {
max-width: inherit;
}

View File

@ -0,0 +1,19 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"id": "72ac17a3-8735-43ff-9a3c-3be22ac2ca00",
"componentType": "WebPart",
"version": "0.0.1",
"manifestVersion": 2,
"preconfiguredEntries": [{
"groupId": "72ac17a3-8735-43ff-9a3c-3be22ac2ca00",
"group": { "default": "Under Development" },
"title": { "default": "SPFX React Grid" },
"description": { "default": "react grid sample" },
"officeFabricIconFontName": "Page",
"properties": {
"description": "SPFX React Grid"
}
}]
}

View File

@ -0,0 +1,85 @@
import * as React from "react";
import * as ReactDom from "react-dom";
import { Provider } from "react-redux";
// require("office-ui-fabric/dist/css/Fabric.css");
// require("office-ui-fabric/dist/css/Fabric.Components.css");
// require("office-ui-fabric/dist/components/CommandBar/CommandBar.css");
import configureStore from "./store/configure-store";
const { Router, createMemoryHistory } = require("react-router");
import { addLists } from "./actions/listActions";
import { addColumns } from "./actions/columnActions";
import { addPageContext } from "./actions/PageContextActions";
import {
BaseClientSideWebPart,
IPropertyPaneSettings, IWebPartData, IHtmlProperties,
IWebPartContext,
PropertyPaneTextField
} from "@microsoft/sp-webpart-base";
import { Log } from "@microsoft/sp-client-base";
import routes from "./store/routes";
import * as strings from "spfxReactGridStrings";
import { ISpfxReactGridWebPartProps } from "./ISpfxReactGridWebPartProps";
const store: Redux.Store<any> = configureStore({});
const history = createMemoryHistory(location);
const App: React.StatelessComponent<any> = () => (
<Provider store={store}>
<Router history={history}>
{routes}
</Router>
</Provider>
);
export default class SpfxReactGridWebPart extends BaseClientSideWebPart<ISpfxReactGridWebPartProps> {
public constructor(context: IWebPartContext) {
super(context);
Log.verbose("SpfxReactGridWebPart", "In constructor of SpfxReactGridWebPart");
}
public render(): void {
store.dispatch(addLists(this.properties.lists));
store.dispatch(addColumns(this.properties.columns));
store.dispatch(addPageContext(this.context.pageContext));
Log.verbose("SpfxReactGridWebPart", "In render of SpfxReactGridWebPart");
ReactDom.render(App(), this.domElement);
}
protected deserialize(data: IWebPartData): ISpfxReactGridWebPartProps {
const info = super.deserialize(data);
return info;
}
protected onBeforeSerialize(): IHtmlProperties {
// this.properties.columns = [];
// this.properties.lists =[];
this.properties.columns = store.getState().columns;
this.properties.lists = store.getState().lists;
return super.onBeforeSerialize();
}
protected get propertyPaneSettings(): IPropertyPaneSettings {
Log.verbose("SpfxReactGridWebPart", "In propertyPaneSettings of SpfxReactGridWebPart");
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField("description", {
label: strings.DescriptionFieldLabel
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,382 @@
import {
ADD_LISTITEM,
ADD_LISTITEMS,
REMOVE_LISTITEM,
GET_LISTITEMS,
GOT_LISTITEMS,
GET_LISTITEMSERROR,
GOT_LISTITEM,
GET_LISTITEMERROR,
CLEAR_LISTITEMS,
SAVE_LISTITEM,//save locally
UNDO_LISTITEMCHANGES,
UPDATE_LISTITEM,//save to sharepoint
UPDATE_LISTITEM_ERROR,
UPDATE_LISTITEM_SUCCESS,
ADDED_NEW_ITEM_TO_SHAREPOINT,
REMOVE_LISTITEM_SUCCESS,
REMOVE_LISTITEM_ERROR
} from "../constants";
import "whatwg-fetch";
import * as utils from "../utils/utils";
import { Web, TypedHash } from "sp-pnp-js";
import ListItem from "../model/ListItem";
import GridRowStatus from "../Model/GridRowStatus";
import ListDefinition from "../model/ListDefinition";
export function clearListItems() {
return {
type: CLEAR_LISTITEMS,
payload: {
}
};
}
export function addListItem(listItem: ListItem) {
return {
type: ADD_LISTITEM,
payload: {
listItem: listItem
}
};
}
export function removeListItem(dispatch: any, listItem: ListItem, listDefinition: ListDefinition): any {
const weburl = utils.ParseSPField(listDefinition.webLookup).id;
const listid = utils.ParseSPField(listDefinition.listLookup).id;
const web = new Web(weburl);
switch (listItem.__metadata__GridRowStatus) {
case GridRowStatus.modified:
case GridRowStatus.pristine:
web.lists.getById(listid).items.getById(listItem.ID).recycle()
.then((response) => {
// shouwld have an option to rfresh here in cas of calculated columns
const gotListItems = removeListItemSuccessAction(listItem);
dispatch(gotListItems); // need to ewname this one to be digfferent from the omported ome
})
.catch((error) => {
console.log(error);
dispatch(removeListItemErrorAction(error)); // need to ewname this one to be digfferent from the omported ome
});
return {
type: REMOVE_LISTITEM,
payload: {
listItem: listItem
}
};
}
}
export function removeListItemSuccessAction(listItem) {
return {
type: REMOVE_LISTITEM_SUCCESS,
payload: {
listItem: listItem
}
};
}
export function removeListItemErrorAction(listItem) {
return {
type: REMOVE_LISTITEM_ERROR,
payload: {
listItem: listItem
}
};
}
export function addListItems(listItems: ListItem[]) {
return {
type: ADD_LISTITEMS,
payload: {
listItems: listItems
}
};
}
export function listDefinitionIsValid(listDefinition: ListDefinition): boolean {
if (listDefinition.webLookup === null) {
return false;
}
if (listDefinition.listLookup === null) {
return false;
}
if (listDefinition.columnReferences === null) {
return false;
}
return true;
}
/**
* Action to update a listitem in sharepoint
*/
export function updateListItemAction(dispatch: any, listDefinition: ListDefinition, listItem: ListItem): any {
// listDefinition = this.getListDefinition(listItem.__metadata__ListDefinitionId);// The list Definition this item is associated with.
const weburl = utils.ParseSPField(listDefinition.webLookup).id;
const listid = utils.ParseSPField(listDefinition.listLookup).id;
const web = new Web(weburl);
let typedHash: TypedHash<string | number | boolean> = {};
for (const columnRef of listDefinition.columnReferences) {
let fieldName = utils.ParseSPField(columnRef.name).id;
switch (columnRef.fieldDefinition.TypeAsString) {
case "Lookup":
if (listItem[fieldName]) {// field may not be set
typedHash[fieldName + "Id"] = listItem[fieldName].Id;
}
break;
case "User":
if (listItem[fieldName]) {// field may not be set
typedHash[fieldName + "Id"] = listItem[fieldName].Id;
}
break;
default:
typedHash[fieldName] = listItem[fieldName];
}
}
switch (listItem.__metadata__GridRowStatus) {
case GridRowStatus.modified:
case GridRowStatus.pristine:// if user cjust chnage the listedef
const promise = web.lists.getById(listid).items.getById(listItem.ID).update(typedHash, listItem["odata.etag"])
.then((response) => {
getListItem(listDefinition, listItem.ID)
.then((r) => {
// srfresh here in cas of calculated columns
r.__metadata__ListDefinitionId = listDefinition.guid; // save my listdef, so i can get the columnReferences later
r.__metadata__GridRowStatus = GridRowStatus.pristine; // save my listdef, so i can get the columnReferences later
const gotListItems = updateListItemSuccessAction(r);
dispatch(gotListItems); // need to ewname this one to be digfferent from the omported ome
});
})
.catch((error) => {
console.log(error);
dispatch(updateListItemErrorAction(error)); // need to ewname this one to be digfferent from the omported ome
});
const action = {
type: UPDATE_LISTITEM,
payload: {
promise: promise
}
};
return action;
case GridRowStatus.new:
const mewpromise = web.lists.getById(listid).items.add(typedHash)
.then((response) => {//
const itemId = response.data.Id;
getListItem(listDefinition, itemId)
.then((r) => {
/**
* data recived after adding an item is NOT the same as we recive from a get
* need to fetch item and wap it in
*/
r.__metadata__ListDefinitionId = listDefinition.guid; // save my listdef, so i can get the columnReferences later
r.__metadata__GridRowStatus = GridRowStatus.pristine; // save my listdef, so i can get the columnReferences later
const actiom = addedNewItemInSharepouint(r, listItem);
dispatch(actiom); // need to ewname this one to be digfferent from the omported ome
})
.catch((error) => {
console.log(error);
dispatch(updateListItemErrorAction(error)); // need to ewname this one to be digfferent from the omported ome
});
});
return {
type: UPDATE_LISTITEM,
payload: {
promise: mewpromise
}
};
default:
return; // action called on item with ibalid state
}
}
export function updateListItemErrorAction(error) {
return {
type: UPDATE_LISTITEM_ERROR,
payload: {
error: error
}
};
}
/**
* called after an item was added to the local cache, updated, then sent to sharepoint.
* We need to replace our local copy, with the real valuse that we got back from sharepoint
*/
export function addedNewItemInSharepouint(listItem, localCopy) {
return {
type: ADDED_NEW_ITEM_TO_SHAREPOINT,
payload: {
listItem: listItem,
localCopy: localCopy
}
};
}
export function updateListItemSuccessAction(listItem) {
return {
type: UPDATE_LISTITEM_SUCCESS,
payload: {
listItem: listItem
}
};
}
export function getListItem(listDefinition: ListDefinition, itemId: number): Promise<any> {
let fieldnames = new Array<string>();
let expands = new Array<string>();
for (const columnreference of listDefinition.columnReferences) {
switch (columnreference.fieldDefinition.TypeAsString) {
case "Lookup":
expands.push(columnreference.fieldDefinition.InternalName);
fieldnames.push(columnreference.fieldDefinition.InternalName + "/" + columnreference.fieldDefinition.LookupField);
fieldnames.push(columnreference.fieldDefinition.InternalName + "/Id");
break;
case "User":
// url is ?$select=Author/Name,Author/Title&$expand=Author/Id
expands.push(columnreference.fieldDefinition.InternalName + "/Id");
fieldnames.push(columnreference.fieldDefinition.InternalName + "/Title");
fieldnames.push(columnreference.fieldDefinition.InternalName + "/Id");
fieldnames.push(columnreference.fieldDefinition.InternalName + "/Name");
break;
default:
const internalName = utils.ParseSPField(columnreference.name).id;
fieldnames.push(internalName); // need to split
}
}
const weburl = utils.ParseSPField(listDefinition.webLookup).id;
const listid = utils.ParseSPField(listDefinition.listLookup).id;
const web = new Web(weburl);
let promise: Promise<any> = web.lists.getById(listid).items.getById(itemId).select(fieldnames.concat("GUID").concat("Id").join(",")).expand(expands.join(",")).get();
return promise;
}
export function getListItemErrorAction(error) {
return {
type: GET_LISTITEMERROR,
payload: {
error: error
}
};
}
export function gotListItemAction(item) {
return {
type: GOT_LISTITEM,
payload: {
item: item
}
};
}
export function getListItemsAction(dispatch: any, listDefinitions: Array<ListDefinition>): any {
dispatch(clearListItems());
const promises: Array<Promise<any>> = new Array<Promise<any>>();
for (const listDefinition of listDefinitions) {
if (!listDefinitionIsValid(listDefinition)) {
break;
}
let fieldnames = new Array<string>();
let expands = new Array<string>();
for (const columnreference of listDefinition.columnReferences) {
switch (columnreference.fieldDefinition.TypeAsString) {
case "Lookup":
expands.push(columnreference.fieldDefinition.InternalName);
fieldnames.push(columnreference.fieldDefinition.InternalName + "/" + columnreference.fieldDefinition.LookupField);
fieldnames.push(columnreference.fieldDefinition.InternalName + "/Id");
break;
case "User":
// url is ?$select=Author/Name,Author/Title&$expand=Author/Id
expands.push(columnreference.fieldDefinition.InternalName + "/Id");
fieldnames.push(columnreference.fieldDefinition.InternalName + "/Title");
fieldnames.push(columnreference.fieldDefinition.InternalName + "/Id");
fieldnames.push(columnreference.fieldDefinition.InternalName + "/Name");
break;
default:
const internalName = utils.ParseSPField(columnreference.name).id;
fieldnames.push(internalName); // need to split
}
}
const weburl = utils.ParseSPField(listDefinition.webLookup).id;
const listid = utils.ParseSPField(listDefinition.listLookup).id;
const web = new Web(weburl);
const promise = web.lists.getById(listid).items.select(fieldnames.concat("GUID").concat("Id").join(",")).expand(expands.join(",")).get()
.then((response) => {
const data = _.map(response, (item: any) => {
item.__metadata__ListDefinitionId = listDefinition.guid; // save my listdef, so i can get the columnReferences later
item.__metadata__GridRowStatus = GridRowStatus.pristine;
return item;
});
console.log(data);
const gotListItems = gotListItemsAction(data);
dispatch(gotListItems); // need to ewname this one to be digfferent from the omported ome
})
.catch((error) => {
console.log(error);
dispatch(getListItemsErrorAction(error)); // need to ewname this one to be digfferent from the omported ome
});
promises.push(promise);
}
const action = {
type: GET_LISTITEMS,
payload: {
promise: Promise.all(promises)
}
};
return action;
}
export function getListItemsErrorAction(error) {
return {
type: GET_LISTITEMSERROR,
payload: {
error: error
}
};
}
export function gotListItemsAction(items) {
return {
type: GOT_LISTITEMS,
payload: {
items: items
}
};
}
export function saveListItemAction(listItem: ListItem) {
const action = {
type: SAVE_LISTITEM,
payload: {
listItem
}
};
return action;
}
export function undoListItemChangesAction(listItem: ListItem) {
const action = {
type: UNDO_LISTITEMCHANGES,
payload: {
listItem
}
};
return action;
}

View File

@ -0,0 +1,63 @@
import {
GET_LOOKUPOPTIONS,
GET_LOOKUPOPTIONS_SUCCESS,
GET_LOOKUPOPTIONS_ERROR,
} from "../constants";
import "whatwg-fetch";
import { Web } from "sp-pnp-js";
import { LookupOptions, LookupOption, LookupOptionStatus } from "../model/LookupOptions";
export function getLookupOptionAction(dispatch: any, lookupSite: string, lookupWebId: string, lookupListId: string, lookupField: string): any {
let lookupOptions = new LookupOptions(lookupSite, lookupWebId, lookupListId, lookupField);
// first add the empty header record to the store, then issue a request to get the details
const web = new Web(lookupWebId);
let fields = ["Id"];
fields.push(lookupField);
const promise = web.lists.getById(lookupListId).items.select(fields.join(",")).get()
.then((response) => {
const data: LookupOption[] = _.map(response, (item: any) => {
return new LookupOption(item.Id, item[lookupField]);
});
lookupOptions.lookupOption = data;
lookupOptions.status=LookupOptionStatus.fetched;
const gotLookupOptions = gotLookupOptionAction(lookupOptions);
dispatch(gotLookupOptions); // need to ewname this one to be digfferent from the omported ome
})
.catch((error) => {
console.log(error);
lookupOptions.status = LookupOptionStatus.error;
dispatch(getLookupOptionErrorAction(error, lookupOptions)); // need to ewname this one to be digfferent from the omported ome
});
const action = {
type: GET_LOOKUPOPTIONS,
payload: {
promise: promise,
lookupOptions: lookupOptions
}
};
return action;
}
function getLookupOptionErrorAction(error, lookupOptions: LookupOptions) {
return {
type: GET_LOOKUPOPTIONS_ERROR,
payload: {
error: error,
lookupOptions: LookupOption
}
};
}
function gotLookupOptionAction(lookupOptions: LookupOptions) {
return {
type: GET_LOOKUPOPTIONS_SUCCESS,
payload: {
lookupOptions: lookupOptions
}
};
}

View File

@ -0,0 +1,13 @@
import {
ADD_PAGECONTEXT
} from "../constants";
export function addPageContext(pageContext) {
return {
type: ADD_PAGECONTEXT,
payload: {
pageContext: pageContext
}
};
}

View File

@ -0,0 +1,145 @@
import {
GOT_WEBS,
GET_WEBS,
GET_WEBSERROR,
GET_LISTSFORWEB_SUCCESS,
GET_LISTSFORWEB_ERROR,
GET_FIELDSFORLIST_SUCCESS,
GET_FIELDSFORLIST_ERROR
} from "../constants";
import "whatwg-fetch";
import * as utils from "../utils/utils";
import { Web as SPWeb } from "sp-pnp-js";
import { Site as SPSite } from "sp-pnp-js";
import { Web, WebList, WebListField } from "../model/Site";
/**
* Action to get all the webs within a site.
* This cant currently be done with rest, so this returns all the subsites of the rootweb instead.
*/
export function getWebsAction(dispatch: any, siteUrl: string): any {
const site: SPSite = new SPSite(siteUrl);
const payload = site.rootWeb.webs.orderBy("Title").get()
.then((response) => {
const data = _.map(response, (item: any) => {
const web: Web = new Web(item.Id, item.Title, item.Url);
return web;
});
console.log(data);
const gotWebs = gotWebsAction(siteUrl, data);
dispatch(gotWebs); // need to ewname this one to be digfferent from the omported ome
})
.catch((error) => {
console.log(error);
dispatch(getWebsErrorAction(error)); // need to ewname this one to be digfferent from the omported ome
});
const action = {
type: GET_WEBS,
payload: {
promise: payload
}
};
return action;
}
export function gotWebsAction(siteUrl, items) {
return {
type: GOT_WEBS,
payload: {
siteUrl: siteUrl,
webs: items
}
};
}
export function getWebsErrorAction(error) {
return {
type: GET_WEBSERROR,
payload: {
error: error
}
};
}
export function getListsForWebAction(dispatch: any, webUrl: string): any {
const web = new SPWeb(webUrl);
const payload = web.lists.orderBy("Title").get()
.then((response) => {
const data = _.map(response, (item: any) => {
return new WebList(item.Id, item.Title, item.Url, );
});
const gotListsForWeb = gotListsForWebAction(webUrl, data);
dispatch(gotListsForWeb); // need to ewname this one to be digfferent from the omported ome
})
.catch((error) => {
console.log(error);
dispatch(getListsForWebActionError(error)); // need to ewname this one to be digfferent from the omported ome
});
const action = {
type: GET_WEBS,
payload: {
promise: payload
}
};
return action;
}
export function gotListsForWebAction(webUrl, lists) {
return {
type: GET_LISTSFORWEB_SUCCESS,
payload: {
webUrl: webUrl,
lists: lists
}
};
}
export function getListsForWebActionError(error) {
return {
type: GET_LISTSFORWEB_ERROR,
payload: {
error: error
}
};
}
export function getFieldsForListAction(dispatch: any, webUrl: string, listId: string): any {
const web = new SPWeb(webUrl);
const payload = web.lists.getById(listId).fields.filter("Hidden eq false").orderBy("Title").get()
.then((response) => {
const data = _.map(response, (item: any) => {
return new WebListField(item.id, new utils.ParsedSPField(item.InternalName, item.Title).toString(), item);
});
console.log(data);
const gotWebs = gotFieldsForListAction(webUrl, listId, data);
dispatch(gotWebs); // need to ewname this one to be digfferent from the omported ome
})
.catch((error) => {
console.log(error);
dispatch(getFieldsForListActionError(error, webUrl, listId)); // need to ewname this one to be digfferent from the omported ome
});
const action = {
type: GET_WEBS,
payload: {
promise: payload
}
};
return action;
}
export function gotFieldsForListAction(webUrl, listId, fields) {
return {
type: GET_FIELDSFORLIST_SUCCESS,
payload: {
webUrl: webUrl,
listId: listId,
fields: fields
}
};
}
export function getFieldsForListActionError(error, webUrl, listId) {
return {
type: GET_FIELDSFORLIST_ERROR,
payload: {
webUrl: webUrl,
listId: listId,
}
};
}

View File

@ -0,0 +1,55 @@
import {
GET_SITE_USERS,
GET_SITE_USERS_ERROR,
GET_SITE_USERS_SUCCESS
} from "../constants";
import "whatwg-fetch";
import { Site } from "sp-pnp-js";
import { SiteUser, SiteUsers, SiteUsersStatus } from "../model/SiteUsers";
export function getSiteUsersAction(dispatch: any, siteUrl: string): any {
let siteUsers = new SiteUsers(siteUrl);
// first add the empty header record to the store, then issue a request to get the details
const site = new Site(siteUrl);
const promise = site.rootWeb.siteUsers.get()
.then((response) => {
const data: SiteUser[] = _.map(response, (item: any) => {
return new SiteUser(item.Id, item.Title, item.LoginName);
});
siteUsers.siteUser = data;
siteUsers.status = SiteUsersStatus.fetched;
const action = gotSiteUsers(siteUsers);
dispatch(action);
})
.catch((error) => {
console.log(error);
siteUsers.status = SiteUsersStatus.error;
dispatch(getSiteUsersErrorAction(error, siteUsers)); // need to ewname this one to be digfferent from the omported ome
});
const action = {
type: GET_SITE_USERS,
payload: {
promise: promise,
siteUsers: siteUsers
}
};
return action;
}
function getSiteUsersErrorAction(error, siteUsers: SiteUsers) {
return {
type: GET_SITE_USERS_ERROR,
payload: {
error: error,
siteUsers: siteUsers
}
};
}
function gotSiteUsers(siteUsers: SiteUsers) {
return {
type: GET_SITE_USERS_SUCCESS,
payload: {
siteUsers: siteUsers
}
};
}

View File

@ -0,0 +1,11 @@
// http://www.mattgreer.org/articles/typescript-react-and-redux/
export interface Action<T> {
readonly type: string;
readonly payload: T;
error?: boolean;
meta?: any;
}
interface ActionCreator<T> {
readonly type: string;
(payload: T): Action<T>;
}

View File

@ -0,0 +1,70 @@
import {
ADD_COLUMN,
ADD_COLUMNS,
REMOVE_COLUMN,
REMOVE_ALLCOLUMNS,
MOVE_COLUMN_UP,
MOVE_COLUMN_DOWN,
SAVE_COLUMN
} from "../constants";
import ColumnDefinition from "../model/ColumnDefinition";
export function addColumn(column: ColumnDefinition) {
return {
type: ADD_COLUMN,
payload: {
column: column
}
};
}
export function removeColumn(column: ColumnDefinition) {
return {
type: REMOVE_COLUMN,
payload: {
column: column
}
};
}
export function moveCulumnUp(column: ColumnDefinition) {
return {
type: MOVE_COLUMN_UP,
payload: {
column: column
}
};
}
export function moveCulumnDown(column: ColumnDefinition) {
return {
type: MOVE_COLUMN_DOWN,
payload: {
column: column
}
};
}
export function removeAllColumns() {
return {
type: REMOVE_ALLCOLUMNS,
payload: {
}
};
}
export function addColumns(columns: ColumnDefinition[]) {
return {
type: ADD_COLUMNS,
payload: {
columns: columns
}
};
}
export function saveColumn(column: ColumnDefinition) {
return {
type: SAVE_COLUMN,
payload: {
column: column
}
};
}

View File

@ -0,0 +1,52 @@
import {
ADD_LIST,
SAVE_LIST,
ADD_LISTS,
REMOVE_LIST,REMOVE_ALLLISTS
} from "../constants";
import "whatwg-fetch";
import ListDefinition from "../model/ListDefinition";
export function addList(list: ListDefinition) {
return {
type: ADD_LIST,
payload: {
list: list
}
};
}
export function removeList(list: ListDefinition) {
return {
type: REMOVE_LIST,
payload: {
list: list
}
};
}
export function removeAllLists() {
return {
type: REMOVE_ALLLISTS,
payload: {
}
};
}
export function saveList(list: ListDefinition) {
const action = {
type: SAVE_LIST,
payload: {
list
}
};
return action;
}
export function addLists(lists: ListDefinition[]) {
return {
type: ADD_LISTS,
payload: {
lists: lists
}
};
}

View File

@ -0,0 +1,32 @@
import * as React from "react";
export interface ISelectChoices {
value: any;
name: string;
}
export interface IDropDownEditorProps extends React.Props<any> {
value: string;
onChange(event): void;
choices: Array<ISelectChoices>;
}
export class DropDownEditor extends React.Component<IDropDownEditorProps, void> {
constructor() {
super();
this.handleChange = this.handleChange.bind(this);
}
private handleChange(event) {
this.props.onChange(event);
}
public render() {
const { value} = this.props;
return (
<select value={value} onChange={this.handleChange} >
{this.props.choices.map((choice) => {
return (
<option key={choice.value} value={choice.value + "#;" + choice.name} >{choice.name}</option>
);
}, this)
}
</select >
);
};
}

View File

@ -0,0 +1,38 @@
import * as React from "react";
export interface KeyValue {
value: any;
displayName: string;
}
import { Dropdown, IDropdownOption } from "office-ui-fabric-react";
export interface IListEditorProps extends React.Props<any> {
selectedValue?: string;
onChange(event): void;
lists: Array<{ id: string, title: string }>;
}
export default class ListEditor extends React.Component<IListEditorProps, void> {
constructor() {
super();
this.handleChange = this.handleChange.bind(this);
}
private handleChange(selectedItem: IDropdownOption) {
this.props.onChange(selectedItem.key);
}
public render() {
const { selectedValue, lists} = this.props;
let options: Array<IDropdownOption> = lists.map((list) => {
return ({
key: list.id + "#;" + list.title,
text: list.title
});
});
options.unshift({ key: null, text: "..Select One" });
return (
<Dropdown label="" selectedKey={selectedValue} options={options} onChanged={this.handleChange} >
</Dropdown >
);
}
}

View File

@ -0,0 +1,21 @@
import * as React from "react";
export const SharePointLookupCellFormatter = React.createClass<{ value: string, onFocus: any }, void>({
render(): JSX.Element {
if (this.props.value == null) {
return (<a href="#" data-entityid={this.props.entityid} style={{ textDecoration: "none" }}
data-columnid={this.props.columnid} onFocus={this.props.onFocus} >
</a>);
}
if (this.props.value.indexOf) {
const displayValue = this.props.value.substring(this.props.value.indexOf("#;") + 2);
return (<a href="#" onFocus={this.props.onFocus} style={{ textDecoration: "none" }}>
{displayValue}
</a>);
}
else {
return (<div>Invalid Value passed to SharePointLookupCellFormatter</div>);
}
}
});

View File

@ -0,0 +1,39 @@
import * as React from "react";
import { Web } from "../model/Site";
export interface KeyValue {
value: any;
displayName: string;
}
import { Dropdown, IDropdownOption } from "office-ui-fabric-react";
export interface IWebEditorProps extends React.Props<any> {
selectedValue?: string;
onChange(event): void;
webs: Array<Web>;
}
export default class WebEditor extends React.Component<IWebEditorProps, void> {
constructor() {
super();
this.handleChange = this.handleChange.bind(this);
}
private handleChange(selectedItem:IDropdownOption) {
this.props.onChange(selectedItem.key);
}
public render() {
const { selectedValue, webs} = this.props;
let options: Array<IDropdownOption> = webs.map((web) => {
return ({
key: web.url + "#;" + web.title,
text: web.title
});
});
options.unshift({ key: null, text: "..Select One" });
return (
<Dropdown label="" selectedKey={selectedValue} options={options} onChanged={this.handleChange} >
</Dropdown >
);
}
}

View File

@ -0,0 +1,22 @@
import * as React from 'react';
export interface IContainerProps extends React.Props<any> {
size: number;
center: boolean;
testid?: string;
}
export default function Container({
size = 1,
center = false,
children = null,
testid = ''
}: IContainerProps) {
return (
<div data-testid={testid} >
{children}
</div>
);
}

View File

@ -0,0 +1,14 @@
import * as React from 'react';
export interface IContentProps extends React.Props<any> {
isVisible: boolean;
style?: Object;
}
export default function Content({ children = null, isVisible }: IContentProps) {
return (
<main>
{ isVisible ? children : null }
</main>
);
}

View File

@ -0,0 +1,75 @@
// see https://spin.atomicobject.com/2016/09/27/typed-redux-reducers-typescript-2-0/
export type ADD_LIST = 'App/ADD_LIST';
export const ADD_LIST: ADD_LIST = 'App/ADD_LIST';
export const SAVE_LIST = "App/SAVE_LIST";
export const ADD_LISTS = "App/ADD_LISTS";
export const REMOVE_LIST = "App/REMOVE_LIST";
export const REMOVE_ALLLISTS = "App/REMOVE_ALLLISTS";
export const ADD_LISTITEM = "App/ADD_LISTITEM";
export const ADD_LISTITEMS = "App/ADD_LISTITEMS";
export const GET_LISTITEMS = "App/GET_LISTITEMS";
export const GOT_LISTITEMS = "App/GOT_LISTITEMS";
export const GET_LISTITEM = "App/GET_LISTITEM";
export const GOT_LISTITEM = "App/GOT_LISTITEM";
export const GET_LISTITEMERROR = "App/GET_LISTITEM_ERROR";
export const CLEAR_LISTITEMS = "App/CLEAR_LISTITEMS";
export const GET_LISTITEMSERROR = "App/GET_LISTITEMS_ERROR";
export const REMOVE_LISTITEM_SUCCESS = "App/REMOVE_LISTITEM_SUCCESS";
export const REMOVE_LISTITEM_ERROR = "App/REMOVE_LISTITEM_ERROR";
export const REMOVE_LISTITEM = "App/REMOVE_LISTITEM";
/**Save listitem in store */
export const SAVE_LISTITEM="App/SAVE_LISTITEM";
export const UNDO_LISTITEMCHANGES="App/UNDO_LISTITEMCHANGES";
/** Updates item in shareepoint */
export const UPDATE_LISTITEM="App/UPDATE_LISTITEM";
export const UPDATE_LISTITEM_SUCCESS="App/UPDATE_LISTITEM_SUCCESS";
export const UPDATE_LISTITEM_ERROR="App/UPDATE_LISTITEM_ERROR";
export const ADDED_NEW_ITEM_TO_SHAREPOINT="App/ADDED_NEW_ITEM_TO_SHAREPOINT";
export const UPDATE_ALLLISTITEMS="App/UPDATE_ALLLISTITEMS";
export const ADD_COLUMN = "App/ADD_COLUMN";
export const ADD_COLUMNS = "App/ADD_COLUMNS";
export const REMOVE_COLUMN = "App/REMOVE_COLUMN";
export const REMOVE_ALLCOLUMNS = "App/REMOVE_ALLCOLUMNS";
export const SAVE_COLUMN = "App/SAVE_COLUMN";
export const MOVE_COLUMN_UP = "App/MOVE_COLUMN_UP";
export const MOVE_COLUMN_DOWN = "App/MOVE_COLUMN_DOWN";
export const GET_WEBS = "App/GET_WEBS";
export const GOT_WEBS = "App/GOT_WEBS";
export const GET_LISTSFORWEB = "App/GET_LISTSFORWEB";
export type GET_LISTSFORWEB_SUCCESS = "App/GET_LISTSFORWEB_SUCCESS";
export const GET_LISTSFORWEB_SUCCESS: GET_LISTSFORWEB_SUCCESS = "App/GET_LISTSFORWEB_SUCCESS";
export const GET_LISTSFORWEB_ERROR = "App/GET_LISTSFORWEB_ERROR";
export const GET_FIELDSFORLIST = "App/GET_FIELDSFORLIST";
export const GET_FIELDSFORLIST_SUCCESS = "App/GET_FIELDSFORLIST_SUCCESS";
export const GET_FIELDSFORLIST_ERROR = "App/GET_FIELDSFORLIST_ERROR";
export const ADD_WEBS = "App/ADD_WEBS";
export const GET_WEBSERROR = "App/GET_WEBSERROR";
export const ADD_PAGECONTEXT = "App/ADD_PAGECONTEXT";
export const ADD_WEBPARTPROPERTIES = "App/ADD_WEBPARTPROPERTIES";
export const GET_LOOKUPOPTIONS = "App/GET_LOOKUPOPTIONS";
export const GET_LOOKUPOPTIONS_SUCCESS = "App/GET_LOOKUPOPTIONS_SUCCESS";
export const GET_LOOKUPOPTIONS_ERROR = "App/GET_LOOKUPOPTIONS_ERROR";
export const ADD_LOOKUPOPTIONS_REQUEST_HEADER = "App/ADD_LOOKUPOPTIONS_REQUEST_HEADER";
export const GET_SITE_USERS = "App/GET_LOOKUPOPTIONS";
export const GET_SITE_USERS_SUCCESS = "App/GET_SITE_USERS_SUCCESS";
export const GET_SITE_USERS_ERROR = "App/GET_SITE_USERS_ERROR";

View File

@ -0,0 +1,344 @@
import * as React from "react";
import { SharePointLookupCellFormatter } from "../components/SharePointFormatters";
const connect = require("react-redux").connect;
import { addColumn, removeColumn, saveColumn, removeAllColumns, moveCulumnUp, moveCulumnDown } from "../actions/columnActions";
import ColumnDefinition from "../model/ColumnDefinition";
import { Button, ButtonType, TextField, CommandBar, Dropdown, IDropdownOption, Toggle } from "office-ui-fabric-react";
import Container from "../components/container";
import { Guid, Log } from "@microsoft/sp-client-base";
const fieldTypes: Array<IDropdownOption> = [
{ key: null, text: "(Selecte one)" },
{ key: "__LISTDEFINITIONTITLE__", text: "List Title" }, //used to display the ListDefinition Title in the grid, for when users add a new item
{ key: "Text", text: "Text" },
{ key: "Integer", text: "Integer" },
{ key: "Note", text: "Note" },
{ key: "DateTime", text: "DateTime" },
// { key: "Counter", text: "Counter" },
{ key: "Choice", text: "Choice" },
{ key: "Lookup", text: "Lookup" },
// { key: "Boolean", value: "Boolean" },
{ key: "Number", text: "Number" },
// { key: "Currency", value: "Currency" },
// { key: "URL", value: "URL" },
// { key: "Computed", value: "Computed" },
// { name: "Guid", value: "Guid" },
// { name: "MultiChoice", value: "MultiChoice" },
// { name: "Computed", value: "Computed" },
// { name: "Calculated", value: "Calculated" },
// { name: "Computed", value: "Computed" },
// { name: "File", value: "File" },
// { name: "Attachments", value: "Attachments" },
{ key: "User", text: "User" },
// { name: "ModStat", value: "ModStat" },
// { name: "ContentTypeId", value: "ContentTypeId" },
// { name: "WorkflowStatus", value: "WorkflowStatus" },
// { name: "WorkflowEventType", value: "WorkflowEventType" },
];
interface IColumnsPageProps extends React.Props<any> {
columns: Array<ColumnDefinition>;
addColumn: () => void;
removeAllColumns: () => void;
removeColumn: (column) => void;
saveColumn: (Column) => void;
moveColumnUp: (Column: ColumnDefinition) => void;
moveColumnDown: (Column: ColumnDefinition) => void;
}
interface IContextMenu extends React.Props<any> {
onRowDelete: AdazzleReactDataGrid.ColumnEventCallback;
}
function mapStateToProps(state) {
return {
columns: state.columns,
};
}
function mapDispatchToProps(dispatch) {
return {
addColumn: (): void => {
const id = Guid.newGuid();
const col: ColumnDefinition = new ColumnDefinition(id.toString(), "", 80, true);
dispatch(addColumn(col));
},
saveColumn: (updatedRowData): void => {
dispatch(saveColumn(updatedRowData));
},
removeColumn: (column): void => {
dispatch(removeColumn(column));
},
removeAllColumns: (column): void => {
dispatch(removeAllColumns());
},
moveColumnUp: (column): void => {
dispatch(moveCulumnUp(column));
},
moveColumnDown: (column): void => {
dispatch(moveCulumnDown(column));
},
};
}
export interface GridColumn {
id: string;
name: string;
editable: boolean;
width: number;
formatter?: string;
editor?: string;
}
interface IGridProps {
editing: {
entityid: string;
columnid: string;
};
}
class ColumnDefinitionContainer extends React.Component<IColumnsPageProps, IGridProps> {
public constructor() {
super();
this.CellContents = this.CellContents.bind(this);
this.TableDetail = this.TableDetail.bind(this);
this.TableRow = this.TableRow.bind(this);
this.TableRows = this.TableRows.bind(this);
this.toggleEditing = this.toggleEditing.bind(this);
this.handleCellUpdated = this.handleCellUpdated.bind(this);
this.handleCellUpdatedEvent = this.handleCellUpdatedEvent.bind(this);
this.handleRowdeleted = this.handleRowdeleted.bind(this);
this.moveColumnUp = this.moveColumnUp.bind(this);
this.moveColumnDown = this.moveColumnDown.bind(this);
}
public gridColulumns: Array<GridColumn> = [{
id: "guid",
name: "guid",
editable: true,
width: 250
},
{
id: "name",
name: "name",
editable: true,
width: 100
},
{
id: "type",
name: "type",
editable: true,
editor: "FieldTypesEditor",
formatter: "",
width: 80
},
{
id: "editable",
name: "editable",
editable: true,
editor: "BooleanEditor",
formatter: "BooleanFormatter",
width: 99
}];
private handleCellUpdatedEvent(event) { //native react uses a Synthetic event
this.handleCellUpdated(event.target.value);
}
private handleCellUpdated(value) { // Office UI Fabric does not use events. It just calls this method with the new value
let {entityid, columnid} = this.state.editing;
const entity: ColumnDefinition = this.props.columns.find((temp) => temp.guid === entityid);
const column = this.gridColulumns.find(temp => temp.id === columnid);
entity[column.name] = value;
this.props.saveColumn(entity);
}
private moveColumnUp(event) {
Log.verbose("list-Page", "Row changed-fired when row changed or leaving cell ");
const target = this.getParent(event.target, "TD");
const attributes: NamedNodeMap = target.attributes;
const entityId = attributes.getNamedItem("data-entityid").value;
const column: ColumnDefinition = this.props.columns.find(cd => cd.guid === entityId);
this.props.moveColumnUp(column);
return;
}
private moveColumnDown(event) {
Log.verbose("list-Page", "Row changed-fired when row changed or leaving cell ");
const target = this.getParent(event.target, "TD");
const attributes: NamedNodeMap = target.attributes;
const entityId = attributes.getNamedItem("data-entityid").value;
const column: ColumnDefinition = this.props.columns.find(cd => cd.guid === entityId);
this.props.moveColumnDown(column);
return;
}
private handleRowdeleted(event) {
Log.verbose("list-Page", "Row changed-fired when row changed or leaving cell ");
const target = this.getParent(event.target, "TD");
const attributes: NamedNodeMap = target.attributes;
const entityId = attributes.getNamedItem("data-entityid").value;
const column: ColumnDefinition = this.props.columns.find(cd => cd.guid === entityId);
this.props.removeColumn(column);
return;
}
public getParent(node: Node, type: string): Node {
while (node.nodeName !== "TD") {
node = node.parentNode;
}
return node;
}
public toggleEditing(event) {
Log.verbose("list-Page", "focus event fired editing when entering cell");
const target = this.getParent(event.target, "TD"); // walk up the Dom to the TD, thats where the IDs are stored
const attributes: NamedNodeMap = target.attributes;
const entityid = attributes.getNamedItem("data-entityid").value;
const columnid = attributes.getNamedItem("data-columnid").value;
this.setState({ "editing": { entityid: entityid, columnid: columnid } });
}
public CellContentsEditable(props: { entity: ColumnDefinition, gridColumn: GridColumn, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entity, gridColumn, cellUpdated, cellUpdatedEvent} = props;
if (!gridColumn.editable) {
return (<span>
{entity[gridColumn.name]}
</span>);
}
switch (gridColumn.editor) {
case "BooleanEditor":
return (
<Toggle label="" checked={entity[gridColumn.name]} onChanged={(val: boolean) => cellUpdated(val)} >
</Toggle >
);
case "FieldTypesEditor":
return (
<Dropdown label="" selectedKey={entity[gridColumn.name]} options={fieldTypes} onChanged={(selection: IDropdownOption) => cellUpdated(selection.key)} >
</Dropdown >
);
default:
return (
<TextField autoFocus width={gridColumn.width}
value={entity[gridColumn.name]}
onChanged={cellUpdated} // this does not use eventing. It just calls the method. onChanged NOT onChange
/>);
}
}
public CellContents(props: { entity: ColumnDefinition, gridColumn: GridColumn }): JSX.Element {
const {entity, gridColumn} = props;
if (!gridColumn.editable) {
return (<span>
{entity[gridColumn.name]}
</span>);
}
switch (gridColumn.formatter) {
case "BooleanFormatter":
// Does not worlk. Does not does not have onFocus
// return (
// <Toggle label="" checked={entity[gridColumn.name]} disabled={true} >
// </Toggle >
// );
let result = (entity[gridColumn.name]) ? (<div>Yes</div>) : (<div>No</div>);
return result;
case "SharePointLookupCellFormatter":
return (<SharePointLookupCellFormatter value={entity[gridColumn.name]} onFocus={this.toggleEditing} />);
default:
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }}>
{entity[gridColumn.name]}
</a>
);
}
}
public TableDetail(props): JSX.Element {
const {entity, column} = props;
if (this.state && this.state.editing && this.state.editing.entityid === entity.guid && this.state.editing.columnid === column.id && column.editable) {
return (<td data-entityid={entity.guid} data-columnid={column.id} style={{ width: column.width, border: "1px solid red", padding: "0px" }}>
<this.CellContentsEditable entity={entity} gridColumn={column} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
</td>
);
} else {
return (<td onClick={this.toggleEditing} data-entityid={entity.guid} data-columnid={column.id} style={{ width: column.width, border: "1px solid black", padding: "0px" }} >
<this.CellContents key={entity.id + column.id} entity={entity} gridColumn={column} />
</td>
);
}
}
public TableRow(props: { isFirst: boolean, isLast: boolean, entity: ColumnDefinition, columns: Array<GridColumn>, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entity, columns, cellUpdated, cellUpdatedEvent, isLast, isFirst} = props;
return (
<tr>
{
columns.map(function (column) {
return (
<this.TableDetail key={column.id} entity={entity} column={column} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
);
}, this)
}
<td data-entityid={entity.guid} data-columnid={""} onClick={this.toggleEditing}>
<Button
onClick={this.handleRowdeleted}
buttonType={ButtonType.icon}
icon="Delete" />
<Button
buttonType={ButtonType.icon}
icon="Up" disabled={isFirst}
onClick={this.moveColumnUp} />
<Button
buttonType={ButtonType.icon}
icon="Down" disabled={isLast}
onClick={this.moveColumnDown} />
</td>
</tr>);
};
public TableRows(props: { entities: Array<ColumnDefinition>, columns: Array<GridColumn>, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entities, columns, cellUpdated, cellUpdatedEvent} = props;
return (
<tbody>
{
entities.map(function (entity, index, all) {
return (
<this.TableRow isFirst={index === 0} isLast={index === all.length - 1} key={entity.guid} columns={columns} entity={entity} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
);
}, this)
}
</tbody>
);
}
public render() {
const { addColumn } = this.props;
return (
<Container testid="columns" size={2} center>
<h1>Column Definitions</h1>
<CommandBar items={[{
key: "AddColumns",
name: "Add a Column",
icon: "Add",
onClick: addColumn
},
{
key: "ClearAllColums",
name: "Remove All Columns",
canCheck: true,
icon: "Delete",
onClick: this.props.removeAllColumns
}]} />
<table style={{ borderColor: "#600", borderWidth: "0 0 0 0", borderStyle: "solid" }}>
<thead>
<tr>
{this.gridColulumns.map((column) => {
return <th key={column.name}>{column.name}</th>;
})}
</tr>
</thead>
{
<this.TableRows entities={this.props.columns} columns={this.gridColulumns} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
})}
</table>
</Container>
);
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ColumnDefinitionContainer);

View File

@ -0,0 +1,472 @@
import * as React from "react";
import * as utils from "../utils/utils";
const connect = require("react-redux").connect;
import { SharePointLookupCellFormatter } from "../components/SharePointFormatters";
import WebEditor from "../components/WebEditor";
import ListEditor from "../components/ListEditor";
import { addList, removeList, saveList, removeAllLists } from "../actions/listActions";
import { getWebsAction, getListsForWebAction, getFieldsForListAction } from "../actions/SiteActions";
import { Button, ButtonType, Dropdown, IDropdownOption, TextField, CommandBar } from "office-ui-fabric-react";
import ListDefinition from "../model/ListDefinition";
import { FieldDefinition } from "../model/ListDefinition";
import { ColumnReference } from "../model/ListDefinition";
import { Site, Web, WebList, WebListField } from "../model/Site";
import ColumnDefinition from "../model/ColumnDefinition";
import Container from "../components/container";
import { Guid, Log, PageContext } from "@microsoft/sp-client-base";
export class GridColumn {
constructor(
public id: string,
public name: string,
public title: string,
public editable: boolean,
public width: number,
public type: string,
public formatter: string = "",
public editor?: string) { }
}
interface IListViewPageProps extends React.Props<any> {
lists: Array<ListDefinition>;
columnRefs: Array<ColumnDefinition>;
sites: Array<Site>;
addList: (siteUrl: string) => void;
removeList: (List) => void;
removeAllLists: () => void;
saveList: (List) => void;
getWebs: (siteUrl) => Promise<any>;
getListsForWeb: (webUrl) => Promise<any>;
getFieldsForList: (webUrl, listId) => Promise<any>;
pageContext: PageContext;
}
function mapStateToProps(state) {
return {
lists: state.lists,
sites: state.sites,
columnRefs: state.columns,
pageContext: state.pageContext
};
}
function mapDispatchToProps(dispatch) {
return {
addList: (siteUrl: string): void => {
const id = Guid.newGuid();
const list: ListDefinition = new ListDefinition(id.toString(), null, null, siteUrl, null, null);
dispatch(addList(list));
},
removeList: (list: ListDefinition): void => {
dispatch(removeList(list));
},
removeAllLists: (): void => {
dispatch(removeAllLists());
},
getWebs: (siteUrl): Promise<any> => {
return dispatch(getWebsAction(dispatch, siteUrl));
},
getListsForWeb(webUrl): Promise<any> {
return dispatch(getListsForWebAction(dispatch, webUrl));
},
getFieldsForList(webUrl, listId): Promise<any> {
return dispatch(getFieldsForListAction(dispatch, webUrl, listId));
},
saveList: (list): void => {
const action = saveList(list);
dispatch(action);
},
};
}
interface IGridProps {
editing: {
entityid: string;
columnid: string;
};
}
class ListDefinitionContainer extends React.Component<IListViewPageProps, IGridProps> {
public defaultColumns: Array<GridColumn> = [
{
id: "rowGuid",
name: "guid",
title: "List Definition ID",
editable: false,
width: 250,
formatter: "",
type: "Text"
},
{
id: "SiteUrl",
name: "siteUrl", // the url to the site
title: "SiteUrl",
editable: true,
width: 359,
formatter: "",
type: "Text"
},
{
id: "listDefTitle",
name: "listDefTitle",
title: "List Definition Title",
editable: true,
width: 100,
formatter: "",
type: "Text"
},
{
id: "WebLookup",
name: "webLookup", // the name of the field in the model
title: "Web Containing List",
editable: true,
width: 300,
editor: "WebEditor",
formatter: "SharePointLookupCellFormatter",
type: "Lookup"
},
{
id: "listlookup",
width: 300,
name: "listLookup",
title: "List",
editable: true,
editor: "ListEditor",
formatter: "SharePointLookupCellFormatter",
type: "Lookup"
}];
public extendedColumns: Array<GridColumn> = [];
public constructor() {
super();
this.getWebsForSite = this.getWebsForSite.bind(this);
this.getListsForWeb = this.getListsForWeb.bind(this);
this.getFieldsForlist = this.getFieldsForlist.bind(this);
this.getFieldDefinition = this.getFieldDefinition.bind(this);
this.CellContentsEditable = this.CellContentsEditable.bind(this);
this.CellContents = this.CellContents.bind(this);
this.TableDetail = this.TableDetail.bind(this);
this.TableRow = this.TableRow.bind(this);
this.TableRows = this.TableRows.bind(this);
this.toggleEditing = this.toggleEditing.bind(this);
this.handleCellUpdated = this.handleCellUpdated.bind(this);
this.handleCellUpdatedEvent = this.handleCellUpdatedEvent.bind(this);
this.deleteList = this.deleteList.bind(this);
this.addList = this.addList.bind(this);
}
public componentWillMount(): void {
if (this.props.sites.length === 0) {
// prload current site, assuming user wants lists from current site
// this.props.getWebs(this.props.pageContext.site.absoluteUrl);
}
this.extendedColumns = _.clone(this.defaultColumns);
for (const columnRef of this.props.columnRefs) {
const newCol = new GridColumn(columnRef.guid, columnRef.name, columnRef.name, columnRef.editable, columnRef.width, columnRef.type, "FieldFormatter", "FieldEditor");
this.extendedColumns.push(newCol);
}
}
private isdeafaultColumn(columnid): boolean {
for (const col of this.defaultColumns) {
if (col.id === columnid) return true;
}
return false;
}
private updateExtendedColumn(entity: ListDefinition, columnid: string, value: any) {
const internalName = utils.ParseSPField(value).id;
const fieldDefinition: FieldDefinition = this.getFieldDefinition(entity, internalName); // values is the fueld just selected.... get the definition for it
for (const col of entity.columnReferences) {
if (col.columnDefinitionId === columnid) {
col.name = value;
col.fieldDefinition = fieldDefinition;
return;
}
}
const x = new ColumnReference(columnid, value, fieldDefinition);
entity.columnReferences.push(x);
}
public getFieldDefinition(listdef: ListDefinition, internalName: string): FieldDefinition {
const field = this.getFieldInList(listdef, internalName);
return field.fieldDefinition;
}
private handleCellUpdatedEvent(event) { //native react uses a Synthetic event
this.handleCellUpdated(event.target.value);
}
private handleCellUpdated(value) { // Office UI Fabric does not use events. It just calls this method with the new value
const {entityid, columnid} = this.state.editing;
const entity: ListDefinition = this.props.lists.find((temp) => temp.guid === entityid);
const column = this.extendedColumns.find(temp => temp.id === columnid);
// if it is a default column, just set its value , otheriwse update it in the list of extended columns (i.e. sharepoint columns)
if (this.isdeafaultColumn(columnid)) {
/** need to save the web url if the web column was updated
* Sharepoint rest wont let me go from an SPSite to an SPWeb using just the id. Need tis
* I need the url to the Web.
* hmmmm... can i construct it (dont store the Id of the we, store the path instead?)
* need this for lookup columns.. they only stote a weid and list id...ohhhh noooo
*/
entity[column.name] = value;
}
else {
this.updateExtendedColumn(entity, columnid, value);
}
this.props.saveList(entity);
}
public addList(event): any {
this.props.addList(this.props.pageContext.site.absoluteUrl);
return;
}
public deleteList(event) {
Log.verbose("list-Page", "Row changed-fired when row changed or leaving cell ");
const target = this.getParent(event.target, "TD");
const attributes: NamedNodeMap = target.attributes;
const entity = attributes.getNamedItem("data-entityid").value;
const list: ListDefinition = this.props.lists.find(temp => temp.guid === entity);
this.props.removeList(list);
return;
}
public getParent(node: Node, type: string): Node {
while (node.nodeName !== "TD") {
node = node.parentNode;
}
return node;
}
public getWebsForSite(listDef: ListDefinition): Array<Web> {
for (const site of this.props.sites) {
if (site.url === listDef.siteUrl) {
return site.webs;
}
}
// not in our cache/ go get it
this.props.getWebs(listDef.siteUrl);
return [];
}
public getListsForWeb(listDef: ListDefinition): Array<WebList> {
const webs = this.getWebsForSite(listDef);
for (const web of webs) {
if (web.url === utils.ParseSPField(listDef.webLookup).id) {
if (web.listsFetched) {
return web.lists;
}
else {
this.props.getListsForWeb(utils.ParseSPField(listDef.webLookup).id);
return [];
}
}
}
return []; // havent fetched parent yet,
}
public getFieldsForlist(listDef: ListDefinition, colType?: string): Array<WebListField> {
const lists = this.getListsForWeb(listDef);
for (const list of lists) {
if (list.id === utils.ParseSPField(listDef.listLookup).id) {
if (list.fieldsFetched) {
if (colType === undefined || colType === null) {
return list.fields;
} else {
return _.filter(list.fields, (f) => f.fieldDefinition.TypeAsString === colType);
}
}
else {
this.props.getFieldsForList(utils.ParseSPField(listDef.webLookup).id, utils.ParseSPField(listDef.listLookup).id);
return [];
}
}
}
return [];// havent fetched parent yet,
}
/** This method is called just before we ara going to save a field in our listdef. It gets the Field Deefinition from sharepoint. */
public getFieldInList(listDef: ListDefinition, internalName): WebListField {
const fields = this.getFieldsForlist(listDef);
for (const field of fields) {
if (utils.ParseSPField(field.name).id === internalName) {
return field;
}
}
}
public GetColumnReferenence(listDefinition: ListDefinition, columnDefinitionId: string): ColumnReference {
for (const columnref of listDefinition.columnReferences) {
if (columnref.columnDefinitionId === columnDefinitionId) {
return columnref;
}
}
}
public toggleEditing(event) {
Log.verbose("list-Page", "focus event fired editing when entering cell");
const target = this.getParent(event.target, "TD"); // walk up the Dom to the TD, thats where the IDs are stored
const attributes: NamedNodeMap = target.attributes;
const entityid = attributes.getNamedItem("data-entityid").value;
const columnid = attributes.getNamedItem("data-columnid").value;
this.setState({ "editing": { entityid: entityid, columnid: columnid } });
}
public CellContentsEditable(props: { entity: ListDefinition, column: GridColumn, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entity, column, cellUpdated, cellUpdatedEvent} = props;
let columnValue;
if (this.isdeafaultColumn(column.id)) {
columnValue = entity[column.name];
}
else {
const colRef: ColumnReference = this.GetColumnReferenence(entity, column.id);
if (colRef) {
columnValue = this.GetColumnReferenence(entity, column.id).name;
}
}
switch (column.editor) {
case "WebEditor":
let webs = this.getWebsForSite(entity);
return (<WebEditor webs={webs} selectedValue={columnValue} onChange={cellUpdated} />);
case "ListEditor":
let lists = this.getListsForWeb(entity);// the Id portion of the WebLookup is the URL
return (<ListEditor selectedValue={columnValue} onChange={cellUpdated} lists={lists} />);
case "FieldEditor":
const colType = column.type;
let fields: Array<IDropdownOption> = this.getFieldsForlist(entity, colType).map(fld => {
return { key: fld.name, text: utils.ParseSPField(fld.name).value };
});
fields.unshift({ key: null, text: "(Select one)" });
return (<Dropdown options={fields} label="" selectedKey={columnValue} onChanged={(selection: IDropdownOption) => cellUpdated(selection.key)} />);
default:
return (
<TextField autoFocus width={column.width}
value={entity[column.name]}
onChanged={cellUpdated} />);
}
}
public CellContents(props: { entity: ListDefinition, column: GridColumn }): JSX.Element {
const {entity, column} = props;
switch (column.formatter) {
case "SharePointLookupCellFormatter":
return (<SharePointLookupCellFormatter value={entity[column.name]} onFocus={this.toggleEditing} />);
default:
if (this.isdeafaultColumn(column.id)) {
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }}>
{entity[column.name]}
</a>
);
}
else {
const colref = entity.columnReferences.find(cr => cr.columnDefinitionId === column.id);
let displaytext = "";
if (colref != null) {
displaytext = utils.ParseSPField(colref.name).value;
}
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }}>
{displaytext}
</a>
);
}
}
}
public TableDetail(props: { entity: ListDefinition, column: GridColumn, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entity, column, cellUpdated, cellUpdatedEvent} = props;
if (this.state && this.state.editing && this.state.editing.entityid === entity.guid && this.state.editing.columnid === column.id) {
return (<td data-entityid={entity.guid} data-columnid={column.id} style={{ width: column.width, border: "1px solid red", padding: "0px" }}>
<this.CellContentsEditable entity={entity} column={column} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
</td>
);
} else {
return (<td data-entityid={entity.guid} data-columnid={column.id} style={{ width: column.width, border: "1px solid black", padding: "0px" }} onClick={this.toggleEditing} >
<this.CellContents entity={entity} column={column} />
</td>
);
}
}
public TableRow(props: { entity: ListDefinition, columns: Array<GridColumn>, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entity, columns, cellUpdated, cellUpdatedEvent} = props;
debugger;
return (
<tr>
{
columns.filter(c => c.type !== "__LISTDEFINITIONTITLE__").map(function (column) {
return (
<this.TableDetail key={column.id} entity={entity} column={column} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
);
}, this)
}
<td data-entityid={entity.guid} data-columnid={""}>
<Button
onClick={this.deleteList}
buttonType={ButtonType.icon}
icon="Delete" />
</td>
</tr>);
};
public TableRows(props: { entities: Array<ListDefinition>, columns: Array<GridColumn>, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entities, columns, cellUpdated, cellUpdatedEvent} = props;
return (
<tbody>
{
entities.map(function (list) {
return (
<this.TableRow key={list.guid} entity={list} columns={columns} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
);
}, this)
}
</tbody>
);
}
public render() {
debugger;
return (
<Container testid="columns" size={2} center>
<h1>List Definitions</h1>
<CommandBar items={[{
key: "Add LIST",
name: "Add a List",
icon: "Add",
onClick: this.addList
},
{
key: "Clear All Lists",
name: "Remove All Lists",
icon: "Delete",
onClick: this.props.removeAllLists
},
{
key: "Allow All Types ",
name: "Allow All Types ",
canCheck: true,
isChecked: true,
icon: "ClearFilter"
}]} />
<table border="1">
<thead>
<tr>
{this.extendedColumns.filter(c => c.type !== "__LISTDEFINITIONTITLE__").map((column) => {
return <th key={column.name}>{column.title}</th>;
})}
</tr>
</thead>
{
<this.TableRows entities={this.props.lists} columns={this.extendedColumns} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
})}
</table>
</Container>
);
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ListDefinitionContainer);

View File

@ -0,0 +1,826 @@
import * as utils from "../utils/utils";
import * as React from "react";
const connect = require("react-redux").connect;
import {
addListItem, removeListItem, getListItemsAction, saveListItemAction,
undoListItemChangesAction, updateListItemAction,
} from "../actions/listItemActions";
import { getLookupOptionAction } from "../actions/lookupOptionsActions";
import { getSiteUsersAction } from "../actions/siteUsersActions";
import ListItem from "../model/ListItem";
import ColumnDefinition from "../model/ColumnDefinition";
import { LookupOptions, LookupOptionStatus } from "../model/LookupOptions";
import { SiteUsers, SiteUsersStatus } from "../model/SiteUsers";
import GridRowStatus from "../model/GridRowStatus";
import ListDefinition from "../model/ListDefinition";
import { Button, ButtonType, TextField, IDropdownOption, Dropdown, Spinner, SpinnerType } from "office-ui-fabric-react";
import { CommandBar } from "office-ui-fabric-react/lib/CommandBar";
import { DatePicker, IDatePickerStrings } from "office-ui-fabric-react/lib/DatePicker";
import Container from "../components/container";
import { Log } from "@microsoft/sp-client-base";
interface IListViewPageProps extends React.Props<any> {
/** An array of ListItems fetched from sharepoint */
siteUsers: Array<SiteUsers>;
/** An array of ListItems fetched from sharepoint */
listItems: Array<ListItem>;
/** An array of LookupOptions. One for each Lookup Column */
lookupOptions: Array<LookupOptions>;
/** An array of columns to be displayed on the grid */
columns: Array<ColumnDefinition>;
/** The listDefinitions. Says which lists to pull data from */
listDefinitions: Array<ListDefinition>;
/** Redux Action to add a new listitem */
addListItem: (ListItem) => void;
/** Redux Action to add a new remove a list item */
removeListItem: (l: ListItem, ListDef: ListDefinition) => void;
/** Redux Action to get listitems from a specific list */
getListItems: (listDefinitions: Array<ListDefinition>) => void;
/** Redux Action to update a listitem in sharepoint */
updateListItem: (ListItem: ListItem, ListDef: ListDefinition) => Promise<any>;
/** Redux Action to get the lookup options for a specific field */
getLookupOptionAction: (lookupSite, lookupWebId, lookupListId, lookupField) => void;
/** Redux Action to get the lookup options for a specific field */
getSiteUsersAction: (site) => Promise<any>;
/** Redux Action to undo changes made to the listitem */
undoItemChanges: (ListItem) => void;
/** Redux Action to save the listitem in the store (NOT to sharepoint*/
saveListItem: (ListItem) => void;
}
function mapStateToProps(state) {
return {
listItems: state.items,
columns: state.columns,
listDefinitions: state.lists,
systemStatus: state.systemStatus,
lookupOptions: state.lookupOptions,
siteUsers: state.siteUsers
};
}
export class GridColumn {
constructor(
public id: string,
public name: string,
public editable: boolean,
public width: number,
public formatter: string = "",
public editor?: string) { }
}
function mapDispatchToProps(dispatch) {
return {
addListItem: (listItem: ListItem): void => {
dispatch(addListItem(listItem));
},
removeListItem: (listItem: ListItem, listDef: ListDefinition): void => {
dispatch(removeListItem(dispatch, listItem, listDef));
},
updateListItem: (listItem: ListItem, listDef: ListDefinition): Promise<any> => {
const action = updateListItemAction(dispatch, listDef, listItem);
dispatch(action); // need to ewname this one to be digfferent from the omported ome
return action.payload.promise;
},
saveListItem: (listItem: ListItem): void => {
dispatch(saveListItemAction(listItem));
},
undoItemChanges: (listItem: ListItem): void => {
dispatch(undoListItemChangesAction(listItem));
},
getListItems: (listDefinitions: Array<ListDefinition>): void => {
dispatch(getListItemsAction(dispatch, listDefinitions));
},
getLookupOptionAction: (lookupSite, lookupWebId, lookupListId, lookupField): void => {
dispatch(getLookupOptionAction(dispatch, lookupSite, lookupWebId, lookupListId, lookupField));
},
getSiteUsersAction: (site): void => {
const action = getSiteUsersAction(dispatch, site);
dispatch(action);
return action.payload.promise;
},
};
}
/**
*
*/
interface IGridState {
editing: {
/**The Sharepoint GUID of the listitem being edited */
entityid: string;
/**The id of the column being edited */
columnid: string;
};
}
/**
* This component is the Grid for editing listitems.
*/
class ListItemContainer extends React.Component<IListViewPageProps, IGridState> {
public constructor() {
super();
this.CellContentsEditable = this.CellContentsEditable.bind(this);
this.CellContents = this.CellContents.bind(this);
this.TableDetail = this.TableDetail.bind(this);
this.TableRow = this.TableRow.bind(this);
this.TableRows = this.TableRows.bind(this);
this.toggleEditing = this.toggleEditing.bind(this);
this.addListItem = this.addListItem.bind(this);
this.removeListItem = this.removeListItem.bind(this);
this.handleCellUpdated = this.handleCellUpdated.bind(this);
this.handleCellUpdatedEvent = this.handleCellUpdatedEvent.bind(this);
this.undoItemChanges = this.undoItemChanges.bind(this);
this.updateListItem = this.updateListItem.bind(this);
this.getLookupOptions = this.getLookupOptions.bind(this);
}
private addListItem(): void {
let listItem = new ListItem();
for (const column of this.props.columns) {
listItem[column.name] = null;
}
if (this.props.listDefinitions.length === 1) {
listItem.__metadata__ListDefinitionId = this.props.listDefinitions[0].guid;
} else {
listItem.__metadata__ListDefinitionId = null;
}
this.props.addListItem(listItem);
}
private removeListItem(event): void {
const parentTD = this.getParent(event.target, "TD");
const attributes: NamedNodeMap = parentTD.attributes;
const entityid = attributes.getNamedItem("data-entityid").value; // theid of the SPListItem
const listItem: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid); // the listItemItself
const listDef = this.getListDefinition(listItem.__metadata__ListDefinitionId);// The list Definition this item is associated with.
this.props.removeListItem(listItem, listDef);
}
/**
* When the component Mounts, call an action to get the listitems for all the listdefinitions
*/
public componentWillMount() {
this.props.getListItems(this.props.listDefinitions);
}
/**
* Method to get the parent TD of any cell,
* The listItemId and columnID are stored as attributes of the cells parent TD.
*/
public getParent(node: Node, type: string): Node {
while (node.nodeName !== "TD") {
node = node.parentNode;
}
return node;
}
/**
* This event gets fired whenever a cell on the grid recieves focus.
* The "editing" propery of this component determines which cell is being edited.
* This method gets the clicked on (the entityid-- the id of the SPLIstItem) and the columnId (the id of the ColumnDefinition)
* and sets them in the "editing"property of state.
* When Component then redraws, it draws that cell as an editable (See the TableDetail method).
*
* If the rendering of that column in edit mode requires additional Info, dispatch a redux action to get the data.
* (Dispatching the action from within the render mehod itself cused infinite loop)
*
*/
public toggleEditing(event) {
Log.verbose("list-Page", "focus event fired editing when entering cell");
const target = this.getParent(event.target, "TD"); // walk up the Dom to the TD, thats where the IDs are stored
const attributes: NamedNodeMap = target.attributes;
const entityid = attributes.getNamedItem("data-entityid").value;
const columnid = attributes.getNamedItem("data-columnid").value;
if (columnid != "") { //user clicked on a column, not a button( Buttons are in a td with am column id of ""
/**
* Need to fire events here to get data needed for the rerender
*/
const listitem = this.props.listItems.find(li => li.GUID === entityid);
const listDef = this.getListDefinition(listitem.__metadata__ListDefinitionId);
if (listDef) {// if user just added an item we may not hava a lisdef yest
const colref = listDef.columnReferences.find(cr => cr.columnDefinitionId === columnid);
if (colref) {// Listname does not have a columnReference
switch (colref.fieldDefinition.TypeAsString) {
case "Lookup":
let lookupField = colref.fieldDefinition.LookupField;
let lookupListId = colref.fieldDefinition.LookupList;
let lookupWebId = colref.fieldDefinition.LookupWebId;
/**
* We are assuming here that the lookup listy is in the same web.
*
*/
lookupWebId = utils.ParseSPField(listDef.webLookup).id; // temp fix. Need to use graph to get the web by id in the site
let lookupSite = listDef.siteUrl;
this.ensureLookupOptions(lookupSite, lookupWebId, lookupListId, lookupField);
break;
case "User":
lookupWebId = utils.ParseSPField(listDef.webLookup).id; // temp fix. Need to use graph to get the web by id in the site
let site = listDef.siteUrl;
this.ensureSiteUsers(site);
break;
default:
break;
}
}
}
}
this.setState({ "editing": { entityid: entityid, columnid: columnid } });
}
/**
* This event gets fired to revert any changes made to the ListItem.
*/
public undoItemChanges(event): void {
const parentTD = this.getParent(event.target, "TD"); // the listitemId and the column ID are always stored as attributes of the parent TD.
const attributes: NamedNodeMap = parentTD.attributes;
const entityitem = attributes.getNamedItem("data-entityid");
const entityid = entityitem.value;
const entity: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid);
this.props.undoItemChanges(entity);
}
/**
* This event gets fired, to save the item back to SharePoint.
*/
public updateListItem(event): void {
const parentTD = this.getParent(event.target, "TD");
const attributes: NamedNodeMap = parentTD.attributes;
const entityid = attributes.getNamedItem("data-entityid").value; // theid of the SPListItem
const entity: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid);
const listDef: ListDefinition = this.getListDefinition(entity.__metadata__ListDefinitionId);
if (entity.__metadata__ListDefinitionId === entity.__metadata__OriginalValues.__metadata__ListDefinitionId
|| entity.__metadata__GridRowStatus === GridRowStatus.new) {// List not changed
this.props.updateListItem(entity, listDef);
}
else {// list changed, add to new, delete from old (will need to do some fiorld mapping in here
entity.__metadata__GridRowStatus = GridRowStatus.new;
this.props.updateListItem(entity, listDef).then(response => {
const oldListDef: ListDefinition = this.getListDefinition(entity.__metadata__OriginalValues.__metadata__ListDefinitionId);
this.props.removeListItem(entity.__metadata__OriginalValues, oldListDef);
});
}
}
/**
* This method gets called when react events are used to update a cell in the grid.
* It just gets the value and passes it to handleCellUpdated.
*/
private handleCellUpdatedEvent(event) { //native react uses a Synthetic event
this.handleCellUpdated(event.target.value);
}
/**
* This method gets called when user changes the listdefinition for an item.
* the old fields are moved to the corresponing new fields and translated as needed
*/
private mapOldListFieldsToNewListFields(listItem: ListItem) {
const newListDef = this.getListDefinition(listItem.__metadata__ListDefinitionId);
const oldListDef = this.getListDefinition(listItem.__metadata__OriginalValues.__metadata__ListDefinitionId);
for (const newColRef of newListDef.columnReferences) {
// find the old columnReference
const oldColRef = oldListDef.columnReferences.find(cr => cr.columnDefinitionId === newColRef.columnDefinitionId);
const newFieldName = utils.ParseSPField(newColRef.name).id;
const oldFieldName = utils.ParseSPField(oldColRef.name).id;
switch (newColRef.fieldDefinition.TypeAsString) {
case "User":
// should male a local copy befor i start messing with these.// fieldd names may overlap on old and new
// const name = listItem.__metadata__OriginalValues[oldFieldName].Name;// the user login name
const name = listItem[oldFieldName].Name;// the user login name
const siteUsersOnNewSite = this.props.siteUsers.find(su => su.siteUrl === newListDef.siteUrl);
const newUser = siteUsersOnNewSite.siteUser.find(user => user.loginName === name);
if (newUser) {
listItem[newFieldName].Id = newUser.id;
listItem[newFieldName].Name = newUser.loginName;
listItem[newFieldName].Title = newUser.value;
}
else {
delete listItem[newFieldName];
}
break;
default:
listItem[newFieldName] = listItem[oldFieldName];
}
}
}
/**
* This method gets called when react cells in the gid get updated.
* Office UI Fabric does not use events. It just calls this method with the new value.
* It reformats the data to fit the format we recievbed from SP in the first place ,
* and dispatches an action to save the data in the store.
*
* Also, it saves the original version of the record, so we can undo later.
*/
private handleCellUpdated(value) {
const {entityid, columnid} = this.state.editing;
const entity: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid);
const listDef = this.getListDefinition(entity.__metadata__ListDefinitionId);
const titlecolumnid = this.props.columns.find(c => { return c.type === "__LISTDEFINITIONTITLE__"; }).guid;
if (columnid === titlecolumnid) { // user just changed the listDef,
if (entity.__metadata__GridRowStatus === GridRowStatus.pristine) {
if (!entity.__metadata__OriginalValues) { //SAVE orgininal values so we can undo;
entity.__metadata__OriginalValues = _.cloneDeep(entity); // need deep if we have lookup values
}
}
entity.__metadata__ListDefinitionId = value.key; // value is a DropDDownOptions
if (entity.__metadata__GridRowStatus !== GridRowStatus.new) {
const newListDef = this.getListDefinition(value.key);
this.props.getSiteUsersAction(newListDef.siteUrl).then(r => {
this.mapOldListFieldsToNewListFields(entity);
this.props.saveListItem(entity);
});
} else {
this.props.saveListItem(entity);
}
return;
}
const columnReference = listDef.columnReferences.find(cr => cr.columnDefinitionId === columnid);
const internalName = utils.ParseSPField(columnReference.name).id;
if (!entity.__metadata__OriginalValues) { //SAVE orgininal values so we can undo;
entity.__metadata__OriginalValues = _.cloneDeep(entity); // need deep if we have lookup values
}
if (entity.__metadata__GridRowStatus === GridRowStatus.pristine) {
entity.__metadata__GridRowStatus = GridRowStatus.modified;
}
switch (columnReference.fieldDefinition.TypeAsString) {
case "User":
if (!entity[internalName]) {// if value was not previously set , then this is undefined//
entity[internalName] = {};// set new value to an empty objecte
}
entity[internalName].Id = value.key;//and then fill in the values
entity[internalName].Title = value.text;
break;
case "Choice":
entity[internalName] = value.text;
break;
case "DateTime":
entity[internalName] = (value.getFullYear().toString()) + "-" + (value.getMonth() + 1).toString() + "-" + value.getDate().toString() + "T00:00:00Z";
break;
case "Lookup":
if (!entity[internalName]) {// if value was not previously set , then this is undefined//
entity[internalName] = {};// set new value to an empty objecte
}
entity[internalName]["Id"] = value.key;//and then fill in the values
entity[internalName][columnReference.fieldDefinition.LookupField] = value.text;
break;
default:
entity[internalName] = value;
}
this.props.saveListItem(entity);
}
/**
* If the the options for a lookup list are not in the cache, fire an event to get them
* This method is called when a lookup column receives focus.
*/
public ensureSiteUsers(siteUrl: string): SiteUsers {
// see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
const siteUsers = this.props.siteUsers.find(x => {
return (x.siteUrl === siteUrl);
});
if (siteUsers === undefined) {
this.props.getSiteUsersAction(siteUrl);
}
return siteUsers;
}
/**
* Gets the options to display for a lookupField
* This method is called when a lookup column gets rendered... we fire the event to get the data when its focused,
* then we use the data when it gets renderd
*/
public getSiteUsers(siteUrl: string): SiteUsers {
// see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
const siteUsers = this.props.siteUsers.find(x => {
return (x.siteUrl === siteUrl);
});
return siteUsers;
}
/**
* If the the options for a lookup list are not in the cache, fire an event to get them
* This method is called when a lookup column receives focus.
*/
public ensureLookupOptions(lookupSite: string, lookupWebId: string, lookupListId: string, lookupField: string): LookupOptions {
// see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
const lookupoptions = this.props.lookupOptions.find(x => {
return (x.lookupField === lookupField) &&
(x.lookupListId === lookupListId) &&
(x.lookupSite === lookupSite) &&
(x.lookupWebId === lookupWebId);
});
if (lookupoptions === undefined) {
this.props.getLookupOptionAction(lookupSite, lookupWebId, lookupListId, lookupField);
}
return lookupoptions;
}
/**
* Gets the options to display for a lookupField
* This method is called when a lookup column gets rendered... we fire the event to get the data when its focused,
* then we use the data when it gets renderd
*/
public getLookupOptions(lookupSite: string, lookupWebId: string, lookupListId: string, lookupField: string): LookupOptions {
// see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
let lookupoptions = this.props.lookupOptions.find(x => {
return (x.lookupField === lookupField) &&
(x.lookupListId === lookupListId) &&
(x.lookupSite === lookupSite) &&
(x.lookupWebId === lookupWebId);
});
return lookupoptions;
}
/**
* Returns the ListDefinition for the given ListDefinionId
*
*/
public getListDefinition(
/** The id of the list definition to be retrieved */
listdefid: string
): ListDefinition {
return this.props.listDefinitions.find(ld => ld.guid === listdefid);
}
/**
* This method renders the contents of an individual cell in an editable format.
*/
public CellContentsEditable(props: { entity: ListItem, column: ColumnDefinition, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entity, column, cellUpdated, cellUpdatedEvent} = props;
if (column.type === "__LISTDEFINITIONTITLE__") {
const opts: Array<IDropdownOption> = this.props.listDefinitions.map(ld => {
return { key: ld.guid, text: ld.listDefTitle };
});
// if (!entity.__metadata__ListDefinitionId) {
// opts.unshift({ key: null, text: "Select one" });
// }
// should I have a different handler for this?
return (
<Dropdown options={opts} selectedKey={entity.__metadata__ListDefinitionId} label=""
onChanged={(selection: IDropdownOption) => { cellUpdated(selection); } } />
);
}
const listDef = this.getListDefinition(entity.__metadata__ListDefinitionId);
const colref = listDef.columnReferences.find(cr => cr.columnDefinitionId === column.guid);
const internalName = utils.ParseSPField(colref.name).id;
const columnValue = entity[internalName];
switch (colref.fieldDefinition.TypeAsString) {
case "User":
let siteUrl = listDef.siteUrl;
let siteUsers = this.getSiteUsers(siteUrl);
if (siteUsers) {
switch (siteUsers.status) {
case SiteUsersStatus.fetched:
let options: IDropdownOption[] = siteUsers.siteUser.map((opt, index, options) => {
return { key: opt.id, text: opt.value };
});
const selectedKey = columnValue ? columnValue.Id : null;
return (
<Dropdown label="" options={options} selectedKey={selectedKey} onChanged={(selection: IDropdownOption) => { cellUpdated(selection); } } >
</Dropdown >
);
case SiteUsersStatus.fetching:
return (
<Spinner type={SpinnerType.normal} />
);
case SiteUsersStatus.error:
return (
<Spinner label="Error" type={SpinnerType.normal} />
);
default:
return (
<Spinner type={SpinnerType.normal} />
);
}
} else {
return (
<Spinner type={SpinnerType.normal} />
);
}
/* falls through */
case "Lookup":
let lookupField = colref.fieldDefinition.LookupField;
let lookupListId = colref.fieldDefinition.LookupList;
let lookupWebId = colref.fieldDefinition.LookupWebId;
/**
* We are assuming here that the lookup listy is in the same web.
*
*/
lookupWebId = utils.ParseSPField(listDef.webLookup).id; // temp fix. Need to use graph to get the web by id in the site
let lookupSite = listDef.siteUrl;
let lookupOptions = this.getLookupOptions(lookupSite, lookupWebId, lookupListId, lookupField);
if (lookupOptions) {
switch (lookupOptions.status) {
case LookupOptionStatus.fetched:
let options: IDropdownOption[] = lookupOptions.lookupOption.map((opt, index, options) => {
return { key: opt.id, text: opt.value };
});
return (
<Dropdown label="" options={options} selectedKey={(columnValue ? columnValue.Id : null)} onChanged={(selection: IDropdownOption) => { cellUpdated(selection); } } >
</Dropdown >
);
case LookupOptionStatus.fetching:
return (
<Spinner type={SpinnerType.normal} />
);
case LookupOptionStatus.error:
return (
<Spinner label="Error" type={SpinnerType.normal} />
);
default:
return (
<Spinner type={SpinnerType.normal} />
);
}
} else {
return (
<Spinner type={SpinnerType.normal} />
);
}
/* falls through */
case "Choice":
const choices = colref.fieldDefinition.Choices.map((c, i) => {
let opt: IDropdownOption = {
index: i,
key: i,
text: c,
isSelected: (c === columnValue)
};
return opt;
});
return (
<Dropdown label="" selectedKey={entity[columnValue]} options={choices} onChanged={(selection: IDropdownOption) => cellUpdated(selection)} >
</Dropdown >
);
case "Text":
return (
<input autoFocus type="text"
value={columnValue}
onChange={cellUpdatedEvent} />);
case "Note":
return (
<TextField autoFocus
value={columnValue}
onChanged={cellUpdated} />);
case "DateTime":
const datpickerStrings: IDatePickerStrings = {
"months": [""],
"shortMonths": [""],
"days": [""],
"shortDays": [""],
goToToday: "yes"
};
let date = null;
if (columnValue !== null) {
const year = parseInt(columnValue.substring(0, 34));
const month = parseInt(columnValue.substring(5, 7)) - 1;
const day = parseInt(columnValue.substring(8, 10));
date = new Date(year, month, day);
}
return (
<DatePicker strings={datpickerStrings} onSelectDate={cellUpdated} value={date}
allowTextInput={true} isRequired={colref.fieldDefinition.Required}
/>);
default:
return (
<input autoFocus type="text"
value={columnValue}
onChange={cellUpdatedEvent} />);
}
}
/**
* This method renders the contents of an individual cell in a non-editable format.
*/
public CellContents(props: { entity: ListItem, column: ColumnDefinition }): JSX.Element {
const {entity, column} = props;
if (!entity.__metadata__ListDefinitionId) { // item is new and list not yet selected
}
const listDef = this.getListDefinition(entity.__metadata__ListDefinitionId);
if (column.type === "__LISTDEFINITIONTITLE__") {// this type is sued to show the listdefinition name
if (listDef != null) {//listdef has been selected
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
{listDef.listDefTitle}
</a>);
}
else {//listdef not yet selected
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
Select a list
</a>);
}
}
if (!listDef) { // cant edit columns til we select a listdef, not NO onFocus={this.toggleEditing}
return (<a href="#" style={{ textDecoration: "none" }} >
select a list first
</a>
);
}
const colref = listDef.columnReferences.find(cr => cr.columnDefinitionId === column.guid);
if (colref === undefined) { //Column has not been configured for this list
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
'Column Not Defined'
</a>
);
}
const internalName = utils.ParseSPField(colref.name).id;
switch (colref.fieldDefinition.TypeAsString) {
case "User":
if (entity[internalName] === undefined) { // value not set
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
</a>
);
} else {
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
{entity[internalName]["Title"]}
</a>
);
}
/* falls through */
case "Lookup":
if (entity[internalName] === undefined) { // value not set
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
</a>
);
} else {
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
{entity[internalName][colref.fieldDefinition.LookupField]}
</a>
);
}
/* falls through */
case "Text":
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
{entity[internalName]}
</a>
);
/* falls through */
case "Note":
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} dangerouslySetInnerHTML={{ __html: entity[internalName] }} >
</a>
);
/* falls through */
case "DateTime":
let value: string;
if (entity[internalName] === null) {
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
</a>);
}
if (colref.fieldDefinition.EntityPropertyName === "DateOnly") {
value = entity[internalName].split("T")[0];
}
else {
value = entity[internalName];
}
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
{value}
</a>
);
/* falls through */
default:
return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
{entity[internalName]}
</a>
);
}
}
/**
* This method renders the A TD for an individual Cell. The TD contains the listItemID and the ColumnID as attributes.
* It calls CellContentsEditable or CellContents based on whether the cell is being edited.
* It determines if the cell is being edited by looking at this,props.editing(which got set by ToggleEditing).
*/
public TableDetail(props: { entity: ListItem, column: ColumnDefinition, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entity, column, cellUpdated, cellUpdatedEvent} = props;
if (this.state && this.state.editing && this.state.editing.entityid === entity.GUID && this.state.editing.columnid === column.guid && column.editable) {
return (<td key={entity.GUID + column.guid} data-entityid={entity.GUID} data-columnid={column.guid} style={{ border: "2px solid black", padding: "0px" }}>
<this.CellContentsEditable entity={entity} column={column} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
</td>
);
} else {
return (<td key={entity.GUID + column.guid} data-entityid={entity.GUID} data-columnid={column.guid} style={{ border: "1px solid black", padding: "0px" }} onClick={this.toggleEditing} >
<this.CellContents entity={entity} column={column} />
</td>
);
}
}
/**
* This method renders a tableRow for an individual listitem
*/
public TableRow(props: { entity: ListItem, columns: Array<ColumnDefinition>, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entity, columns, cellUpdated, cellUpdatedEvent} = props;
return (
<tr>
{
columns.map(function (column) {
return (
<this.TableDetail key={column.guid} entity={entity} column={column} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
);
}, this)
}
<td data-entityid={entity.GUID} data-columnid={""} width="200" onClick={this.toggleEditing} >
<div>
<Button width="20" style={{ padding: 0 }}
onClick={this.updateListItem} alt="Save to Sharepoint"
buttonType={ButtonType.icon}
icon="Save" disabled={!(entity.__metadata__OriginalValues)} />
<Button width="20" style={{ padding: 0 }}
onClick={this.removeListItem}
buttonType={ButtonType.icon}
icon="Delete" />
<Button width="20" style={{ padding: 0 }}
// onClick={this.deleteList}
buttonType={ButtonType.icon}
disabled={!(entity.__metadata__OriginalValues)}
onClick={this.undoItemChanges}
icon="Undo" />
</div>
</td>
</tr>);
};
/**
* Render rows for the listItems
*/
public TableRows(props: { entities: Array<ListItem>, columns: Array<ColumnDefinition>, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
const {entities, columns, cellUpdated, cellUpdatedEvent} = props;
return (
<tbody>
{
entities.map(function (list) {
return (
<this.TableRow key={list.GUID} entity={list} columns={columns} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
);
}, this)
}
</tbody>
);
}
public render() {
const { listItems } = this.props;
Log.info("ListItemContainer", "In Render");
return (
<Container testid="columns" size={2} center>
<CommandBar items={[{
key: "AddItem",
name: "Add an Item",
icon: "Add",
onClick: this.addListItem
},
{
key: "DleteAll",
name: "DeleteAll",
icon: "Delete"
},
{
key: "Undo All changes",
name: "UndoAll",
icon: "Undo"
},
{
key: "Save All ",
name: "Save To SharePoint",
icon: "Save"
}]} />
<table border="1">
<thead>
<tr>
{this.props.columns.map((column) => {
return <th key={column.name}>{column.name}</th>;
})}
</tr>
</thead>
{
<this.TableRows entities={listItems} columns={this.props.columns} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
})}
</table>
</Container>
);
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ListItemContainer);

View File

@ -0,0 +1,761 @@
// import * as utils from "../utils/utils";
// import * as React from "react";
// const connect = require("react-redux").connect;
// import {
// addListItem, removeListItem, getListItemsAction, saveListItemAction,
// undoListItemChangesAction, updateListItemAction,
// } from "../actions/listItemActions";
// import { getLookupOptionAction } from "../actions/lookupOptionsActions";
// import { getSiteUsersAction } from "../actions/siteUsersActions";
// import ListItem from "../model/ListItem";
// import ColumnDefinition from "../model/ColumnDefinition";
// import { LookupOptions, LookupOptionStatus } from "../model/LookupOptions";
// import { SiteUsers, SiteUsersStatus } from "../model/SiteUsers";
// import GridRowStatus from "../model/GridRowStatus";
// import ListDefinition from "../model/ListDefinition";
// import { FocusZone,Button, ButtonType, TextField, IDropdownOption, Dropdown, Spinner, SpinnerType, DetailsList, IColumn } from "office-ui-fabric-react";
// import { CommandBar } from "office-ui-fabric-react/lib/CommandBar";
// import { DatePicker, IDatePickerStrings } from "office-ui-fabric-react/lib/DatePicker";
// import Container from "../components/container";
// import { Log } from "@microsoft/sp-client-base";
// interface SPColumn extends IColumn {
// editable: boolean;
// type: string;
// }
// interface IListViewPageProps extends React.Props<any> {
// /** An array of ListItems fetched from sharepoint */
// siteUsers: Array<SiteUsers>;
// /** An array of ListItems fetched from sharepoint */
// listItems: Array<ListItem>;
// /** An array of LookupOptions. One for each Lookup Column */
// lookupOptions: Array<LookupOptions>;
// /** An array of columns to be displayed on the grid */
// columns: Array<ColumnDefinition>;
// /** The listDefinitions. Says which lists to pull data from */
// listDefinitions: Array<ListDefinition>;
// /** Redux Action to add a new listitem */
// addListItem: (ListItem) => void;
// /** Redux Action to add a new remove a list item */
// removeListItem: (l: ListItem, ListDef: ListDefinition) => void;
// /** Redux Action to get listitems from a specific list */
// getListItems: (listDefinitions: Array<ListDefinition>) => void;
// /** Redux Action to update a listitem in sharepoint */
// updateListItem: (ListItem: ListItem, ListDef: ListDefinition) => Promise<any>;
// /** Redux Action to get the lookup options for a specific field */
// getLookupOptionAction: (lookupSite, lookupWebId, lookupListId, lookupField) => void;
// /** Redux Action to get the lookup options for a specific field */
// getSiteUsersAction: (site) => Promise<any>;
// /** Redux Action to undo changes made to the listitem */
// undoItemChanges: (ListItem) => void;
// /** Redux Action to save the listitem in the store (NOT to sharepoint*/
// saveListItem: (ListItem) => void;
// }
// function mapStateToProps(state) {
// return {
// listItems: state.items,
// columns: state.columns,
// listDefinitions: state.lists,
// systemStatus: state.systemStatus,
// lookupOptions: state.lookupOptions,
// siteUsers: state.siteUsers
// };
// }
// export class GridColumn {
// constructor(
// public id: string,
// public name: string,
// public editable: boolean,
// public width: number,
// public formatter: string = "",
// public editor?: string) { }
// }
// function mapDispatchToProps(dispatch) {
// return {
// addListItem: (listItem: ListItem): void => {
// dispatch(addListItem(listItem));
// },
// removeListItem: (listItem: ListItem, listDef: ListDefinition): void => {
// dispatch(removeListItem(dispatch, listItem, listDef));
// },
// updateListItem: (listItem: ListItem, listDef: ListDefinition): Promise<any> => {
// const action = updateListItemAction(dispatch, listDef, listItem);
// dispatch(action); // need to ewname this one to be digfferent from the omported ome
// return action.payload.promise;
// },
// saveListItem: (listItem: ListItem): void => {
// dispatch(saveListItemAction(listItem));
// },
// undoItemChanges: (listItem: ListItem): void => {
// dispatch(undoListItemChangesAction(listItem));
// },
// getListItems: (listDefinitions: Array<ListDefinition>): void => {
// dispatch(getListItemsAction(dispatch, listDefinitions));
// },
// getLookupOptionAction: (lookupSite, lookupWebId, lookupListId, lookupField): void => {
// dispatch(getLookupOptionAction(dispatch, lookupSite, lookupWebId, lookupListId, lookupField));
// },
// getSiteUsersAction: (site): void => {
// const action = getSiteUsersAction(dispatch, site);
// dispatch(action);
// return action.payload.promise;
// },
// };
// }
// /**
// *
// */
// interface IGridState {
// editing: {
// /**The Sharepoint GUID of the listitem being edited */
// entityid: string;
// /**The id of the column being edited */
// columnid: string;
// };
// }
// /**
// * This component is the Grid for editing listitems.
// */
// class ListItemContainerUIF extends React.Component<IListViewPageProps, IGridState> {
// public constructor() {
// super();
// this.CellContentsEditable = this.CellContentsEditable.bind(this);
// this.CellContents = this.CellContents.bind(this);
// this.TableDetail = this.TableDetail.bind(this);
// this.toggleEditing = this.toggleEditing.bind(this);
// this.addListItem = this.addListItem.bind(this);
// this.removeListItem = this.removeListItem.bind(this);
// this.handleCellUpdated = this.handleCellUpdated.bind(this);
// this.handleCellUpdatedEvent = this.handleCellUpdatedEvent.bind(this);
// this.undoItemChanges = this.undoItemChanges.bind(this);
// this.updateListItem = this.updateListItem.bind(this);
// this.getLookupOptions = this.getLookupOptions.bind(this);
// this.onRenderItemColumn = this.onRenderItemColumn.bind(this);
// }
// private addListItem(): void {
// let listItem = new ListItem();
// for (const column of this.props.columns) {
// listItem[column.name] === null;
// }
// if (this.props.listDefinitions.length === 1) {
// listItem.__metadata__ListDefinitionId = this.props.listDefinitions[0].guid;
// } else {
// listItem.__metadata__ListDefinitionId = null;
// }
// this.props.addListItem(listItem);
// }
// private removeListItem(event): void {
// const parentTD = this.getParent(event.target, "DIV", "spCell");
// const attributes: NamedNodeMap = parentTD.attributes;
// const entityid = attributes.getNamedItem("data-entityid").value; // theid of the SPListItem
// const listItem: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid); // the listItemItself
// const listDef = this.getListDefinition(listItem.__metadata__ListDefinitionId);// The list Definition this item is associated with.
// this.props.removeListItem(listItem, listDef);
// }
// /**
// * When the component Mounts, call an action to get the listitems for all the listdefinitions
// */
// public componentWillMount() {
// this.props.getListItems(this.props.listDefinitions);
// }
// /**
// * Method to get the parent span of any cell whose classnAME IS spcell
// * The listItemId and columnID are stored as attributes of THAT SPAN
// */
// public getParent(node: Node, type: string, className: string): Node {
// while (node.nodeName !== type && node["className"] !== className) {
// node = node.parentNode;
// }
// return node;
// }
// /**
// * This event gets fired whenever a cell on the grid recieves focus.
// * The "editing" propery of this component determines which cell is being edited.
// * This method gets the clicked on (the entityid-- the id of the SPLIstItem) and the columnId (the id of the ColumnDefinition)
// * and sets them in the "editing"property of state.
// * When Component then redraws, it draws that cell as an editable (See the TableDetail method).
// *
// * If the rendering of that column in edit mode requires additional Info, dispatch a redux action to get the data.
// * (Dispatching the action from within the render mehod itself cused infinite loop)
// *
// */
// public toggleEditing(event) {
// Log.verbose("list-Page", "focus event fired editing when entering cell");
// const target = this.getParent(event.target, "DIV", "spCell"); // walk up the Dom to the TD, thats where the IDs are stored
// const attributes: NamedNodeMap = target.attributes;
// const entityid = attributes.getNamedItem("data-entityid").value;
// const columnid = attributes.getNamedItem("data-columnid").value;
// if (columnid != "") { //user clicked on a column, not a button( Buttons are in a td with am column id of ""
// /**
// * Need to fire events here to get data needed for the rerender
// */
// const listitem = this.props.listItems.find(li => li.GUID === entityid);
// const listDef = this.getListDefinition(listitem.__metadata__ListDefinitionId);
// if (listDef) {// if user just added an item we may not hava a lisdef yest
// const colref = listDef.columnReferences.find(cr => cr.columnDefinitionId === columnid);
// if (colref) {// Listname does not have a columnReference
// switch (colref.fieldDefinition.TypeAsString) {
// case "Lookup":
// let lookupField = colref.fieldDefinition.LookupField;
// let lookupListId = colref.fieldDefinition.LookupList;
// let lookupWebId = colref.fieldDefinition.LookupWebId;
// /**
// * We are assuming here that the lookup listy is in the same web.
// *
// */
// lookupWebId = utils.ParseSPField(listDef.webLookup).id; // temp fix. Need to use graph to get the web by id in the site
// let lookupSite = listDef.siteUrl;
// this.ensureLookupOptions(lookupSite, lookupWebId, lookupListId, lookupField);
// break;
// case "User":
// lookupWebId = utils.ParseSPField(listDef.webLookup).id; // temp fix. Need to use graph to get the web by id in the site
// let site = listDef.siteUrl;
// this.ensureSiteUsers(site);
// break;
// default:
// break;
// }
// }
// }
// }
// this.setState({ "editing": { entityid: entityid, columnid: columnid } });
// }
// /**
// * This event gets fired to revert any changes made to the ListItem.
// */
// public undoItemChanges(event): void {
// const parentTD = this.getParent(event.target, "DIV", "spCell"); // the listitemId and the column ID are always stored as attributes of the parent TD.
// const attributes: NamedNodeMap = parentTD.attributes;
// const entityitem = attributes.getNamedItem("data-entityid");
// const entityid = entityitem.value;
// const entity: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid);
// this.props.undoItemChanges(entity);
// }
// /**
// * This event gets fired, to save the item back to SharePoint.
// */
// public updateListItem(event): void {
// const parentTD = this.getParent(event.target, "DIV", "spCell");
// const attributes: NamedNodeMap = parentTD.attributes;
// const entityid = attributes.getNamedItem("data-entityid").value; // theid of the SPListItem
// const entity: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid);
// const listDef: ListDefinition = this.getListDefinition(entity.__metadata__ListDefinitionId);
// if (entity.__metadata__ListDefinitionId === entity.__metadata__OriginalValues.__metadata__ListDefinitionId
// || entity.__metadata__GridRowStatus === GridRowStatus.new) {// List not changed
// this.props.updateListItem(entity, listDef);
// }
// else {// list changed, add to new, delete from old (will need to do some fiorld mapping in here
// entity.__metadata__GridRowStatus = GridRowStatus.new;
// this.props.updateListItem(entity, listDef).then(response => {
// const oldListDef: ListDefinition = this.getListDefinition(entity.__metadata__OriginalValues.__metadata__ListDefinitionId);
// this.props.removeListItem(entity.__metadata__OriginalValues, oldListDef);
// });
// }
// }
// /**
// * This method gets called when react events are used to update a cell in the grid.
// * It just gets the value and passes it to handleCellUpdated.
// */
// private handleCellUpdatedEvent(event) { //native react uses a Synthetic event
// this.handleCellUpdated(event.target.value);
// }
// /**
// * This method gets called when user changes the listdefinition for an item.
// * the old fields are moved to the corresponing new fields and translated as needed
// */
// private mapOldListFieldsToNewListFields(listItem: ListItem) {
// const newListDef = this.getListDefinition(listItem.__metadata__ListDefinitionId);
// const oldListDef = this.getListDefinition(listItem.__metadata__OriginalValues.__metadata__ListDefinitionId);
// for (const newColRef of newListDef.columnReferences) {
// // find the old columnReference
// const oldColRef = oldListDef.columnReferences.find(cr => cr.columnDefinitionId === newColRef.columnDefinitionId);
// const newFieldName = utils.ParseSPField(newColRef.name).id;
// const oldFieldName = utils.ParseSPField(oldColRef.name).id;
// switch (newColRef.fieldDefinition.TypeAsString) {
// case "User":
// // should male a local copy befor i start messing with these.// fieldd names may overlap on old and new
// // const name = listItem.__metadata__OriginalValues[oldFieldName].Name;// the user login name
// const name = listItem[oldFieldName].Name;// the user login name
// const siteUsersOnNewSite = this.props.siteUsers.find(su => su.siteUrl === newListDef.siteUrl);
// const newUser = siteUsersOnNewSite.siteUser.find(user => user.loginName === name);
// listItem[newFieldName].Id = newUser.id;
// listItem[newFieldName].Name = newUser.loginName;
// listItem[newFieldName].Title = newUser.value;
// break;
// default:
// listItem[newFieldName] = listItem[oldFieldName];
// }
// }
// }
// /**
// * This method gets called when react cells in the gid get updated.
// * Office UI Fabric does not use events. It just calls this method with the new value.
// * It reformats the data to fit the format we recievbed from SP in the first place ,
// * and dispatches an action to save the data in the store.
// *
// * Also, it saves the original version of the record, so we can undo later.
// */
// private handleCellUpdated(value) {
// const {entityid, columnid} = this.state.editing;
// const entity: ListItem = this.props.listItems.find((temp) => temp.GUID === entityid);
// const listDef = this.getListDefinition(entity.__metadata__ListDefinitionId);
// const titlecolumnid = this.props.columns.find(c => { return c.type === "__LISTDEFINITIONTITLE__" }).guid
// if (columnid === titlecolumnid) { // user just changed the listDef,
// if (entity.__metadata__GridRowStatus === GridRowStatus.pristine) {
// if (!entity.__metadata__OriginalValues) { //SAVE orgininal values so we can undo;
// entity.__metadata__OriginalValues = _.cloneDeep(entity); // need deep if we have lookup values
// }
// }
// entity.__metadata__ListDefinitionId = value.key; // value is a DropDDownOptions
// if (entity.__metadata__GridRowStatus !== GridRowStatus.new) {
// const listDef = this.getListDefinition(value.key);
// this.props.getSiteUsersAction(listDef.siteUrl).then(r => {
// this.mapOldListFieldsToNewListFields(entity);
// this.props.saveListItem(entity);
// });
// } else {
// this.props.saveListItem(entity);
// }
// return;
// }
// const columnReference = listDef.columnReferences.find(cr => cr.columnDefinitionId === columnid);
// const internalName = utils.ParseSPField(columnReference.name).id;
// if (!entity.__metadata__OriginalValues) { //SAVE orgininal values so we can undo;
// entity.__metadata__OriginalValues = _.cloneDeep(entity); // need deep if we have lookup values
// }
// if (entity.__metadata__GridRowStatus === GridRowStatus.pristine) {
// entity.__metadata__GridRowStatus = GridRowStatus.modified;
// }
// switch (columnReference.fieldDefinition.TypeAsString) {
// case "User":
// if (!entity[internalName]) {// if value was not previously set , then this is undefined//
// entity[internalName] = {};// set new value to an empty objecte
// }
// entity[internalName].Id = value.key;//and then fill in the values
// entity[internalName].Title = value.text;
// break;
// case "DateTime":
// const year = value.getFullYear().toString();
// entity[internalName] = (value.getFullYear().toString()) + "-" + (value.getMonth() + 1).toString() + "-" + value.getDate().toString() + "T00:00:00Z";
// break;
// case "Lookup":
// if (!entity[internalName]) {// if value was not previously set , then this is undefined//
// entity[internalName] = {};// set new value to an empty objecte
// }
// entity[internalName]["Id"] = value.key;//and then fill in the values
// entity[internalName][columnReference.fieldDefinition.LookupField] = value.text;
// break;
// default:
// entity[internalName] = value;
// }
// this.props.saveListItem(entity);
// }
// /**
// * If the the options for a lookup list are not in the cache, fire an event to get them
// * This method is called when a lookup column receives focus.
// */
// public ensureSiteUsers(siteUrl: string): SiteUsers {
// // see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
// const siteUsers = this.props.siteUsers.find(x => {
// return (x.siteUrl === siteUrl);
// });
// if (siteUsers === undefined) {
// this.props.getSiteUsersAction(siteUrl);
// }
// return siteUsers;
// }
// /**
// * Gets the options to display for a lookupField
// * This method is called when a lookup column gets rendered... we fire the event to get the data when its focused,
// * then we use the data when it gets renderd
// */
// public getSiteUsers(siteUrl: string): SiteUsers {
// // see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
// const siteUsers = this.props.siteUsers.find(x => {
// return (x.siteUrl === siteUrl);
// });
// return siteUsers;
// }
// /**
// * If the the options for a lookup list are not in the cache, fire an event to get them
// * This method is called when a lookup column receives focus.
// */
// public ensureLookupOptions(lookupSite: string, lookupWebId: string, lookupListId: string, lookupField: string): LookupOptions {
// // see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
// const lookupoptions = this.props.lookupOptions.find(x => {
// return (x.lookupField === lookupField) &&
// (x.lookupListId === lookupListId) &&
// (x.lookupSite === lookupSite) &&
// (x.lookupWebId === lookupWebId);
// });
// if (lookupoptions === undefined) {
// this.props.getLookupOptionAction(lookupSite, lookupWebId, lookupListId, lookupField);
// }
// return lookupoptions;
// }
// /**
// * Gets the options to display for a lookupField
// * This method is called when a lookup column gets rendered... we fire the event to get the data when its focused,
// * then we use the data when it gets renderd
// */
// public getLookupOptions(lookupSite: string, lookupWebId: string, lookupListId: string, lookupField: string): LookupOptions {
// // see if the options are in the store, if so, return them, otherwoise dispatch an action to get them
// let lookupoptions = this.props.lookupOptions.find(x => {
// return (x.lookupField === lookupField) &&
// (x.lookupListId === lookupListId) &&
// (x.lookupSite === lookupSite) &&
// (x.lookupWebId === lookupWebId);
// });
// return lookupoptions;
// }
// /**
// * Returns the ListDefinition for the given ListDefinionId
// *
// */
// public getListDefinition(
// /** The id of the list definition to be retrieved */
// listdefid: string
// ): ListDefinition {
// return this.props.listDefinitions.find(ld => ld.guid === listdefid);
// }
// /**
// * This method renders the contents of an individual cell in an editable format.
// */
// public CellContentsEditable(props: { entity: ListItem, column: SPColumn, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
// const {entity, column, cellUpdated, cellUpdatedEvent} = props;
// if (column.type === "__LISTDEFINITIONTITLE__") {
// entity.__metadata__ListDefinitionId
// const opts: Array<IDropdownOption> = this.props.listDefinitions.map(ld => {
// return { key: ld.guid, text: ld.title };
// });
// // if (!entity.__metadata__ListDefinitionId) {
// // opts.unshift({ key: null, text: "Select one" });
// // }
// // should I have a different handler for this?
// return (
// <Dropdown options={opts} selectedKey={entity.__metadata__ListDefinitionId} label=""
// onChanged={(selection: IDropdownOption) => { cellUpdated(selection); } } />
// );
// }
// const listDef = this.getListDefinition(entity.__metadata__ListDefinitionId);
// const colref = listDef.columnReferences.find(cr => cr.columnDefinitionId === column.key);
// const internalName = utils.ParseSPField(colref.name).id;
// const columnValue = entity[internalName];
// switch (colref.fieldDefinition.TypeAsString) {
// case "User":
// let siteUrl = listDef.siteUrl;
// let siteUsers = this.getSiteUsers(siteUrl);
// if (siteUsers) {
// switch (siteUsers.status) {
// case SiteUsersStatus.fetched:
// let options: IDropdownOption[] = siteUsers.siteUser.map((opt, index, options) => {
// return { key: opt.id, text: opt.value };
// });
// const selectedKey = columnValue ? columnValue.Id : null;
// return (
// <Dropdown label="" options={options} selectedKey={selectedKey} onChanged={(selection: IDropdownOption) => { cellUpdated(selection); } } >
// </Dropdown >
// );
// case SiteUsersStatus.fetching:
// return (
// <Spinner type={SpinnerType.normal} />
// );
// case SiteUsersStatus.error:
// return (
// <Spinner label="Error" type={SpinnerType.normal} />
// );
// default:
// return (
// <Spinner type={SpinnerType.normal} />
// );
// }
// } else {
// return (
// <Spinner type={SpinnerType.normal} />
// );
// }
// case "Lookup":
// let lookupField = colref.fieldDefinition.LookupField;
// let lookupListId = colref.fieldDefinition.LookupList;
// let lookupWebId = colref.fieldDefinition.LookupWebId;
// /**
// * We are assuming here that the lookup listy is in the same web.
// *
// */
// lookupWebId = utils.ParseSPField(listDef.webLookup).id; // temp fix. Need to use graph to get the web by id in the site
// let lookupSite = listDef.siteUrl;
// let lookupOptions = this.getLookupOptions(lookupSite, lookupWebId, lookupListId, lookupField);
// if (lookupOptions) {
// switch (lookupOptions.status) {
// case LookupOptionStatus.fetched:
// let options: IDropdownOption[] = lookupOptions.lookupOption.map((opt, index, options) => {
// return { key: opt.id, text: opt.value };
// });
// return (
// <Dropdown label="" options={options} selectedKey={(columnValue ? columnValue.Id : null)} onChanged={(selection: IDropdownOption) => { cellUpdated(selection); } } >
// </Dropdown >
// );
// case LookupOptionStatus.fetching:
// return (
// <Spinner type={SpinnerType.normal} />
// );
// case LookupOptionStatus.error:
// return (
// <Spinner label="Error" type={SpinnerType.normal} />
// );
// default:
// return (
// <Spinner type={SpinnerType.normal} />
// );
// }
// } else {
// return (
// <Spinner type={SpinnerType.normal} />
// );
// }
// case "Choice":
// const choices = colref.fieldDefinition.Choices.map((c, i) => {
// let opt: IDropdownOption = {
// index: i,
// key: i,
// text: c,
// isSelected: (c === columnValue)
// };
// return opt;
// });
// return (
// <Dropdown label="" selectedKey={entity[columnValue]} options={choices} onChanged={(selection: IDropdownOption) => cellUpdated(selection)} >
// </Dropdown >
// );
// case "Text":
// return (
// <input autoFocus type="text"
// value={columnValue}
// onChange={cellUpdatedEvent} />);
// case "Note":
// return (
// <TextField autoFocus
// value={columnValue}
// onChanged={cellUpdated} />);
// case "DateTime":
// const datpickerStrings: IDatePickerStrings = {
// "months": [""],
// "shortMonths": [""],
// "days": [""],
// "shortDays": [""],
// goToToday: "yes"
// };
// const year = parseInt(columnValue.substring(0, 34));
// const month = parseInt(columnValue.substring(5, 7)) - 1;
// const day = parseInt(columnValue.substring(8, 10));
// const date = new Date(year, month, day);
// return (
// <DatePicker strings={datpickerStrings} onSelectDate={cellUpdated} value={date}
// allowTextInput={true} isRequired={colref.fieldDefinition.Required}
// />);
// default:
// return (
// <input autoFocus type="text"
// value={columnValue}
// onChange={cellUpdatedEvent} />);
// }
// }
// /**
// * This method renders the contents of an individual cell in a non-editable format.
// */
// public CellContents(props: { entity: ListItem, column: SPColumn }): JSX.Element {
// const {entity, column} = props;
// if (!entity.__metadata__ListDefinitionId) { // item is new and list not yet selected
// }
// const listDef = this.getListDefinition(entity.__metadata__ListDefinitionId);
// if (column.type === "__LISTDEFINITIONTITLE__") {// this type is sued to show the listdefinition name
// if (listDef != null) {//listdef has been selected
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {listDef.title}
// </a>);
// }
// else {//listdef not yet selected
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// Select a list
// </a>);
// }
// }
// if (!listDef) { // cant edit columns til we select a listdef, not NO onFocus={this.toggleEditing}
// return (<a href="#" style={{ textDecoration: "none" }} >
// select a list first
// </a>
// );
// }
// const colref = listDef.columnReferences.find(cr => cr.columnDefinitionId === column.key);
// if (colref === undefined) { //Column has not been configured for this list
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// 'Column Not Defined'
// </a>
// );
// }
// const internalName = utils.ParseSPField(colref.name).id;
// switch (colref.fieldDefinition.TypeAsString) {
// case "User":
// if (entity[internalName] === undefined) { // value not set
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// </a>
// );
// } else {
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {entity[internalName]["Title"]}
// </a>
// );
// }
// case "Lookup":
// if (entity[internalName] === undefined) { // value not set
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// </a>
// );
// } else {
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {entity[internalName][colref.fieldDefinition.LookupField]}
// </a>
// );
// }
// case "Text":
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {entity[internalName]}
// </a>
// );
// case "Note":
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} dangerouslySetInnerHTML={{ __html: entity[internalName] }} >
// </a>
// );
// case "DateTime":
// let value: string;
// if (entity[internalName] === null) {
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// </a>);
// }
// if (colref.fieldDefinition.EntityPropertyName === "DateOnly") {
// value = entity[internalName].split("T")[0];
// }
// else {
// value = entity[internalName];
// }
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {value}
// </a>
// );
// default:
// return (<a href="#" onFocus={this.toggleEditing} style={{ textDecoration: "none" }} >
// {entity[internalName]}
// </a>
// );
// }
// }
// /**
// * This method renders the A TD for an individual Cell. The TD contains the listItemID and the ColumnID as attributes.
// * It calls CellContentsEditable or CellContents based on whether the cell is being edited.
// * It determines if the cell is being edited by looking at this,props.editing(which got set by ToggleEditing).
// */
// public TableDetail(props: { entity: ListItem, column: SPColumn, cellUpdated: (newValue) => void, cellUpdatedEvent: (event: React.SyntheticEvent) => void; }): JSX.Element {
// const {entity, column, cellUpdated, cellUpdatedEvent} = props;
// if (this.state && this.state.editing && this.state.editing.entityid === entity.GUID && this.state.editing.columnid === column.key) {
// return (<div width={column.minWidth} className="spCell" key={entity.GUID + column.key} data-entityid={entity.GUID} data-columnid={column.key} style={{ border: "2px solid black", padding: "0px" }}>
// <this.CellContentsEditable entity={entity} column={column} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
// </div>
// );
// } else {
// return (<div width={column.minWidth} className="spCell" key={entity.GUID + column.key} data-entityid={entity.GUID} data-columnid={column.key} style={{ border: "1px solid black", padding: "0px" }} onClick={this.toggleEditing} >
// <this.CellContents entity={entity} column={column} />
// </div>
// );
// }
// }
// public onRenderItemColumn(item: any, index: number, column: SPColumn): any {
// const listDef = this.getListDefinition(item.__metadata__ListDefinitionId);
// const colRef = listDef.columnReferences.find(cr => cr.columnDefinitionId === column.key);
// const internalName = utils.ParseSPField(colRef.name).id;
// return (
// <FocusZone>
// <this.TableDetail entity={item} column={column} cellUpdated={this.handleCellUpdated} cellUpdatedEvent={this.handleCellUpdatedEvent} />
// </FocusZone>
// );
// }
// public render() {
// const { listItems } = this.props;
// Log.info("ListItemContainer", "In Render");
// const uColumns: SPColumn[] = this.props.columns.map((c, i, a) => {
// return {
// key: c.guid,
// name: c.name,
// fieldName: c.name,
// minWidth: 80,
// maxWidth: 80,
// width: 80,
// type: c.type,
// editable: c.editable,
// };
// });
// return (
// <Container testid="columns" size={2} center>
// <DetailsList columns={uColumns} items={listItems} onRenderItemColumn={this.onRenderItemColumn}>
// </DetailsList>
// </Container>
// );
// }
// }
// export default connect(
// mapStateToProps,
// mapDispatchToProps
// )(ListItemContainerUIF);

View File

@ -0,0 +1,56 @@
import * as React from "react";
import {
Button,
MessageBar,
MessageBarType
} from "office-ui-fabric-react";
const connect = require("react-redux").connect;
import SystemStatus from "../model/SystemStatus";
const Link = require("react-router").Link;
import Content from "../components/content";
interface IAppProps extends React.Props<any> {
systemStatus: SystemStatus;
}
function mapStateToProps(state) {
return {
systemStatus: state.systemStatus,
};
}
function mapDispatchToProps(dispatch) {
return {
};
}
class App extends React.Component<IAppProps, void> {
private messageBar(error: string): JSX.Element {
if (error === "") {
return (<div />);
}
else {
return (
<MessageBar messageBarType={MessageBarType.error}>{error} </MessageBar>
);
}
}
public render() {
const { children} = this.props;
return (
<div>
<Button> <Link to="/lists">List Definitions</Link></Button>
<Button> <Link to="/columns">Column Definitions</Link></Button>
<Button> <Link to="/">List Items</Link></Button>
<div>
{this.messageBar(this.props.systemStatus.fetchStatus)}
<div>{this.props.systemStatus.currentAction}
</div>
</div>
<Content isVisible={true}>
{children}
</Content>
</div >
);
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);

View File

@ -0,0 +1,7 @@
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field"
}
});

View File

@ -0,0 +1,10 @@
declare interface ISpfxReactGridStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
}
declare module 'spfxReactGridStrings' {
const strings: ISpfxReactGridStrings;
export = strings;
}

View File

@ -0,0 +1,10 @@
export default class ColumnDefinition {
public constructor(
public guid: string,
public name: string,
public width: number,
public editable: boolean = true,
public type: string = "Text",
)
{ }
}

View File

@ -0,0 +1,59 @@
// for eac of the columns to be displayed, which field in this list should we use
import { Guid } from '@microsoft/sp-client-base';
export class FieldDefinition {
/** The type of field to dispay */
public TypeAsString: string;
/** The Internal Name of the firld */
public InternalName: string;
/** for Lookup Fields, the field iin the foreign list to be displayed*/
public LookupField: string;
/** for Lookup Fields, the Id of the web containing the foreign list*/
public LookupWebId: string;
/** for Lookup Fields, the Id of the foreign list*/
public LookupList: string;
/** for Lookup Fields, the Id of the foreign list*/
public Choices: Array<string>;
/** for Lookup Fields, the Id of the foreign list*/
public Required: boolean;
/** for Date Fiels. Determines if it is Date Only EntityPropertyName === "DateOnly" */
public EntityPropertyName: string;
}
export class ColumnReference {
public constructor(
/** Tghe ID of this ColmnReference */
public columnDefinitionId: string,
/** The field in the Sharepoint list. Stored as 'internalname#;displayname')*/
public name: string,
/** The FULL field Definistion from sharepoint.... need to shave tos donw a bit
* the Definition of the field iin sharepoint.... Should narrrow trhis down later I need EntityPropertyName and TypeAsString. Required,lookup info. etc...*/
public fieldDefinition: FieldDefinition
) { }
}
export default class ListDefinition {
/** The columns in the Sharepoint list we ewant to edit*/
public columnReferences: Array<ColumnReference>;
/** The Guid of this list Definition. The id field is the id of the list in sharepoint. The guid is the ID of this listdefinition */
public guid: string;
public constructor(
/** the id of the list we are pointing to in Sharepoint. Stored as id#;name */
public id: string,
/** the web the list is contained in. Stored as webUrl#;Title */
public webLookup: string,
/** the list to be disoplayed. Stored as listid#;Title */
public listLookup: string,
/** the url of the site holding the list. (only used during setup) */
public siteUrl: string,
/** the url of the list. (not used. to be deleted) */
public url: string,
/** A name for the listDefinition. When users add a new item, they must select the list nby this name */
public listDefTitle: string) {
this.columnReferences = new Array<ColumnReference>();
this.guid = Guid.newGuid().toString();
}
}

View File

@ -0,0 +1,25 @@
export enum LookupOptionStatus {
fetching,
fetched,
error
}
export class LookupOption {
constructor(public id: number, public value: string) { }
}
export class LookupOptions {
public status: LookupOptionStatus;
public lookupOption:Array<LookupOption>;
constructor(
/** Th eurl opf the site that contains the lookup list */
public lookupSite: string,
/** The id of the web that contains the lookup list NOTY WORKINMG: Cannot get web by id */
public lookupWebId: string,
/** The id of the Liist that contains the lookup info */
public lookupListId: string,
/** The Internal name of the field that is being looked up */
public lookupField: string) {
this.status = LookupOptionStatus.fetching;
this.lookupOption= new Array<LookupOption>();
}
}

View File

@ -0,0 +1,51 @@
//Webs is only needed when we ara ading a list, dont pupulate it until then
export class WebListField {
public constructor(public id: string,
/** the name of the field in the list stored as internalName#;name */
public name: string,
/** the Definition of the field iin sharepoint.... Should narrrow trhis down later I need EntityPropertyName and TypeAsString. lookup info. etc...*/
public fieldDefinition:any,
) {
}
}
export class WebList {
/** An array of fieldss that are contained in the list */
public fields: Array<WebListField>;
/** Indicates if fieldss have been fetched for this list */
public fieldsFetched;
public constructor(
public id: string,
public title: string,
public url: string) {
this.fieldsFetched = false;
this.fields = new Array<WebListField>();
}
}
export class Web {
/** An array of lists that are contained in the web */
public lists: Array<WebList>;
/** Indicates if lists have been fetched for this web */
public listsFetched: boolean;
public constructor(
/** The ID of the SPWebObject as stored in Sharepoint. PRObably can be deleted */
public id: string,
/** The Title of the SPWebObject as stored in Sharepoint. for display puroses */
public title: string,
/** The Url the SPWeb*/
public url: string) {
this.listsFetched = false;
this.lists = new Array<WebList>();
}
}
export class Site {
/** An array of webs, contains currently contains only webs under the rootweb. Need to change pnp to get allwebs */
public webs: Array<Web>;
public constructor(
/** The url of a SiteCOllection we want to get data from */
public url: string) {
this.webs = new Array<Web>();
}
}

View File

@ -0,0 +1,31 @@
export enum SiteUsersStatus {
fetching,
fetched,
error
}
export class SiteUser {
/** The integer id for the user within a site */
public id: number;
/** The display name for the user */
public value: string;
/** The loginID for a user, userd when moving items from one site to annother*/
public loginName: string;
constructor(id: number, value: string,loginName:string) {
this.id=id;
this.value=value;
this.loginName=loginName;
}
}
export class SiteUsers {
public status: SiteUsersStatus;
public siteUser: Array<SiteUser>;
constructor(
/** Th eurl opf the site that contains the lookup list */
public siteUrl: string,
) {
this.status = SiteUsersStatus.fetching;
this.siteUser = new Array<SiteUser>();
}
}

View File

@ -0,0 +1,4 @@
export default class {
public currentAction:string;
public fetchStatus:string;
}

View File

@ -0,0 +1,10 @@
/** if status is null row is fresh from server */
enum GridRowStatus{
new,
pristine,
modified,
toBeDeleted
}
/**Status of row )mew */
export default GridRowStatus;

View File

@ -0,0 +1,22 @@
import GridRowStatus from "./GridRowStatus";
import { Guid } from '@microsoft/sp-client-base';
export default class ListItem {
/** The listDefinition this item came from. Need this to get to columnMappings */
public __metadata__ListDefinitionId;
/** The Status of this . Need this to get to columnMappings */
public __metadata__GridRowStatus: GridRowStatus;
/** The Original Values of this item . Need this to revert changes */
public __metadata__OriginalValues: ListItem;
/** the internal ID for tthe listitem */
public GUID: string;
public ID: number;
/**
* When a new Item is added, its status is set to new and its given a Guid.
* wjen we save it to sharepoint we need to refresh and get the actual goid assigned by sharepoint.
*/
public constructor(
) {
this.__metadata__GridRowStatus = GridRowStatus.new;
this.GUID = Guid.newGuid().toString();
}
}

View File

@ -0,0 +1,57 @@
import ColumnDefinition from "../Model/ColumnDefinition";
import * as _ from "lodash";
import {
ADD_COLUMN,
ADD_COLUMNS,
REMOVE_COLUMN,
SAVE_COLUMN, REMOVE_ALLCOLUMNS,
MOVE_COLUMN_UP,
MOVE_COLUMN_DOWN
} from "../constants";
function moveColumnUp(state: Array<ColumnDefinition>, action) {
let newstate = _.clone(state);
const index = _.findIndex<ColumnDefinition>(newstate, c => c.guid === action.payload.column.guid);
newstate[index] = newstate.splice(index - 1, 1, newstate[index])[0];
return newstate;
}
function moveColumnDown(state: Array<ColumnDefinition>, action) {
let newstate = _.clone(state);
let index = _.findIndex<ColumnDefinition>(newstate, c => c.guid === action.payload.column.guid);
newstate[index] = newstate.splice(index+1, 1, newstate[index])[0];
return newstate;
}
const INITIAL_STATE = new Array<ColumnDefinition>();
function listItemReducer(state = INITIAL_STATE, action: any = { type: "" }) {
switch (action.type) {
case MOVE_COLUMN_UP:
return moveColumnUp(state, action);
case MOVE_COLUMN_DOWN:
return moveColumnDown(state, action);
case ADD_COLUMN:
let newarray = _.clone(state);
newarray.push(action.payload.column);
return newarray;
case SAVE_COLUMN:
let newarray2 = _.clone(state);
let item = newarray2.find(item => item.guid === action.payload.column.guid);
item = action.payload.column;
return newarray2;
case REMOVE_COLUMN:
let newArr = _.filter(state, (o) => { return o.guid !== action.payload.column.guid; });
return newArr;
case REMOVE_ALLCOLUMNS:
return [];
case ADD_COLUMNS:
return _.union(state, action.payload.columns);
default:
return state;
}
}
export default listItemReducer;

View File

@ -0,0 +1,100 @@
import ListItem from "../Model/ListItem";
import * as _ from "lodash";
import {
ADD_LISTITEM,
REMOVE_LISTITEM_SUCCESS,
GOT_LISTITEMS,
SAVE_LISTITEM,
UNDO_LISTITEMCHANGES,
UPDATE_LISTITEM_SUCCESS,
ADDED_NEW_ITEM_TO_SHAREPOINT,
CLEAR_LISTITEMS
} from "../constants";
const INITIAL_STATE = [];
/**
* deletes the originalvalues of a listitem, after itt has been saved to sharepoint,. This in effec disables the undo button,
*/
function updateListItemSuccess(state: Array<ListItem>, action: { payload: { listItem: ListItem } }) {
let newState = _.cloneDeep(state);
let index = _.findIndex(newState, { GUID: action.payload.listItem.GUID });
// if (newState[index].__metadata__OriginalValues) {
// delete newState[index].__metadata__OriginalValues;
// }
newState[index] = action.payload.listItem;
return newState;
}
/**
* reverts a listimes values to thos originally retrived from sharepoint
*/
function undoListItemChanges(state: Array<ListItem>, action: { payload: { listItem: ListItem } }) {
let newarray3 = _.cloneDeep(state);
let index = _.findIndex(newarray3, { GUID: action.payload.listItem.GUID });
if (newarray3[index].__metadata__OriginalValues) {
newarray3[index] = newarray3[index].__metadata__OriginalValues;
}
return newarray3;
}
/**
* Adds a new listitem to the store
*/
function addListItem(state: Array<ListItem>, action: { payload: { listItem: ListItem } }) {
let newarray = _.cloneDeep(state);
newarray.unshift(action.payload.listItem);
return newarray;
}
/**
* removes a listitem from the store
*/
function removeListItem(state: Array<ListItem>, action: { payload: { listItem: ListItem } }) {
// REMOVES ITEM FROM STORE. Called after item is deleted from sharepoint
let newArr = _.filter(state, (o) => { return o.GUID !== action.payload.listItem.GUID; });
return newArr;
}
/**
* After adding a new item to the store, updatiing it, then sending to SP ,
* we need to replace the local copy with the values we fot back from sharepoint
*/
function relpaceItemInStore(state: Array<ListItem>, action: { payload: { listItem: ListItem, localCopy: ListItem } }) {
let newState = _.cloneDeep(state);
const idx = _.findIndex(newState, { GUID: action.payload.localCopy.GUID });
newState[idx] = action.payload.listItem;
return newState;
}
/**
* updates a Listitem in the store
*/
function saveListItem(state: Array<ListItem>, action: { payload: { listItem: ListItem } }) {
let newarray2 = _.cloneDeep(state);
let item = newarray2.find(i => i.GUID === action.payload.listItem.GUID);
item = action.payload.listItem;
if (!item.__metadata__OriginalValues) {
item.__metadata__OriginalValues = state.find(i => i.GUID === action.payload.listItem.GUID);
}
return newarray2;
}
function listItemReducer(state = INITIAL_STATE, action: any = { type: "" }) {
switch (action.type) {
case ADD_LISTITEM:
return addListItem(state, action);
case REMOVE_LISTITEM_SUCCESS:
return removeListItem(state, action);
case SAVE_LISTITEM:
return saveListItem(state, action);
case UPDATE_LISTITEM_SUCCESS:
return updateListItemSuccess(state, action);
case ADDED_NEW_ITEM_TO_SHAREPOINT:
return relpaceItemInStore(state, action);
case UNDO_LISTITEMCHANGES:
return undoListItemChanges(state, action);
case GOT_LISTITEMS:
return _.union(state, action.payload.items);
case CLEAR_LISTITEMS:
return [];
default:
return state;
}
}
export default listItemReducer;

View File

@ -0,0 +1,49 @@
import {
ADD_LIST,
REMOVE_LIST,
REMOVE_ALLLISTS,
ADD_LISTS,
SAVE_LIST,
REMOVE_COLUMN // remove the dolumn from all lists
} from "../constants";
import * as _ from "lodash";
import ListDefinition from "../model/ListDefinition";
const INITIAL_STATE = [];
function listReducer(state: Array<ListDefinition> = INITIAL_STATE, action: any = { type: "" }): Array<ListDefinition> {
switch (action.type) {
case ADD_LIST:
//https://spin.atomicobject.com/2016/09/27/typed-redux-reducers-typescript-2-0/
let newarray = _.clone(state);
newarray.push(action.payload.list);
return newarray;
case SAVE_LIST:
let newarray2 = _.clone(state);
let item = newarray2.find(item => item.guid === action.payload.list.guid);
item = action.payload.list;
return newarray2;
case REMOVE_LIST:
let newArr = _.filter(state, (o) => { return o.guid !== action.payload.list.guid; });
return newArr;
case REMOVE_ALLLISTS:
return [];
case ADD_LISTS:
return _.union(state, action.payload.lists);
case REMOVE_COLUMN:
let listrefs = _.clone(state);
for (const listref of listrefs) {
for (let i = listref.columnReferences.length - 1; i >= 0; i--) {
if (listref.columnReferences[i].columnDefinitionId === action.payload.column.guid) {
listref.columnReferences.splice(i, 1);
}
}
}
return listrefs;
default:
return state;
}
}
export default listReducer;

View File

@ -0,0 +1,54 @@
import { LookupOptions } from "../Model/LookupOptions";
import { Log } from "@microsoft/sp-client-base";
import * as _ from "lodash";
import {
GET_LOOKUPOPTIONS,
GET_LOOKUPOPTIONS_SUCCESS,
GET_LOOKUPOPTIONS_ERROR
} from "../constants";
function getLookupOptions(state: Array<LookupOptions>, action) {
let newstate = _.clone(state);
newstate.push(action.payload.lookupOptions);
Log.info("getLookupOptions", "Added Header Record");
return newstate;
}
function updateLookupOption(state: Array<LookupOptions>, action: { payload: { lookupOptions: LookupOptions } }) {
let newstate = _.clone(state);
let index = _.findIndex<LookupOptions>(newstate, x =>
(x.lookupField === action.payload.lookupOptions.lookupField) &&
(x.lookupListId === action.payload.lookupOptions.lookupListId) &&
(x.lookupSite === action.payload.lookupOptions.lookupSite) &&
(x.lookupWebId === action.payload.lookupOptions.lookupWebId));
if (index !== -1) {
newstate[index] = action.payload.lookupOptions;
}
else {
newstate.push(action.payload.lookupOptions);
}
Log.info("getLookupOptions", "Updated Header Record");
return newstate;
}
const INITIAL_STATE = [];
function lookupOptionReducer(state = INITIAL_STATE, action: any = { type: "" }) {
switch (action.type) {
case GET_LOOKUPOPTIONS:
Log.verbose("getLookupOptions", "In getLookupOptions GET_LOOKUPOPTIONS listItemReducer ActionType is " + action.type);
return getLookupOptions(state, action);
case GET_LOOKUPOPTIONS_SUCCESS:
Log.verbose("getLookupOptions", "In getLookupOptions GET_LOOKUPOPTIONS_SUCCESSof listItemReducer ActionType is " + action.type);
return updateLookupOption(state, action);
case GET_LOOKUPOPTIONS_ERROR:
/** The ActionCreator has set the state to error , so i just update the item */
Log.verbose("getLookupOptions", "In getLookupOptions GET_LOOKUPOPTIONS_ERRORof listItemReducer ActionType is " + action.type);
return updateLookupOption(state, action);
default:
return state;
}
}
export default lookupOptionReducer;

View File

@ -0,0 +1,12 @@
import {
ADD_PAGECONTEXT
} from "../constants";
export default function PageContextReducer(state = {}, action: any = { type: "" }) {
switch (action.type) {
case ADD_PAGECONTEXT:
return action.payload.pageContext;
default:
return state;
}
}

View File

@ -0,0 +1,27 @@
import { combineReducers } from "redux";
import ListReducer from "./ListReducer";
import listItemReducer from "./listItemReducer";
import ColumnReducer from "./ColumnReducer";
import PageContextReducer from "./PageContextReducer";
import LookupOptionsReducer from "./LookupOptionsReducer";
import SiteReducer from "./SiteReducer";
import SiteUserReducer from "./SiteUsersReducer";
import SystemStatus from "./SystemStatus";
const { routerReducer } = require("react-router-redux");
export function RootReducer(state, action) {
const combinedReducers = combineReducers(
{
items: listItemReducer,
lists: ListReducer,
columns: ColumnReducer,
sites: SiteReducer,
pageContext: PageContextReducer,
routing: routerReducer,
systemStatus:SystemStatus,
lookupOptions:LookupOptionsReducer,
siteUsers:SiteUserReducer
}
);
return combinedReducers(state, action);
}

View File

@ -0,0 +1,56 @@
import {
GOT_WEBS,
GET_LISTSFORWEB_SUCCESS,
GET_FIELDSFORLIST_SUCCESS
} from "../constants";
import * as _ from "lodash";
import { Site } from "../model/Site";
const INITIAL_STATE: Array<Site> = [];
function gotWebs(state: Array<Site> = INITIAL_STATE, action: any = { type: "" }): Array<Site> {
let site: Site = new Site(action.payload.siteUrl);
site.webs = action.payload.webs;
let result: Array<Site> = _.union(state, new Array<Site>(site));
return result;
}
function siteReducer(state: Array<Site> = INITIAL_STATE, action: any = { type: "" }): Array<Site> {
switch (action.type) {
case GOT_WEBS:
return gotWebs(state, action);
case GET_LISTSFORWEB_SUCCESS:
let newState = _.clone(state);
//find the site and add the lists to it
for (const site of newState) {
for (const web of site.webs) {
if (web.url === action.payload.webUrl) {
web.lists = action.payload.lists;
web.listsFetched = true;
}
}
}
return newState;
case GET_FIELDSFORLIST_SUCCESS:
let newState2 = _.clone(state);
//find the site and add the lists to it
for (const site of newState2) {
for (const web of site.webs) {
if (web.url === action.payload.webUrl) {
for (const list of web.lists) {
if (list.id === action.payload.listId) {
list.fields = action.payload.fields;
list.fieldsFetched = true;
}
}
}
}
}
return newState2;
default:
return state;
}
}
export default siteReducer;

View File

@ -0,0 +1,51 @@
import { SiteUsers } from "../Model/SiteUsers";
import { Log } from "@microsoft/sp-client-base";
import * as _ from "lodash";
import {
GET_SITE_USERS,
GET_SITE_USERS_SUCCESS,
GET_SITE_USERS_ERROR
} from "../constants";
function getSiteUsers(state: Array<SiteUsers>, action) {
let newstate = _.clone(state);
newstate.push(action.payload.lookupOptions);
Log.info("getLookupOptions", "Added Header Record");
return newstate;
}
function updateSiteUser(state: Array<SiteUsers>, action: { payload: { siteUsers: SiteUsers } }) {
let newstate = _.clone(state);
let index = _.findIndex<SiteUsers>(newstate, x =>
(x.siteUrl === action.payload.siteUsers.siteUrl) );
if (index !== -1) {
newstate[index] = action.payload.siteUsers;
}
else {
newstate.push(action.payload.siteUsers);
}
Log.info("GetSiteYsers", "Updated Header Record");
return newstate;
}
const INITIAL_STATE = [];
function siteUsersReducer (state = INITIAL_STATE, action: any = { type: "" }) {
switch (action.type) {
case GET_SITE_USERS:
Log.verbose("getLookupOptions", "In getLookupOptions GET_SITE_USERS listItemReducer ActionType is " + action.type);
return getSiteUsers(state, action);
case GET_SITE_USERS_SUCCESS:
Log.verbose("getLookupOptions", "In getLookupOptions GET_SITE_USERS_SUCCESS listItemReducer ActionType is " + action.type);
return updateSiteUser(state, action);
case GET_SITE_USERS_ERROR:
/** The ActionCreator has set the state to error , so i just update the item */
Log.verbose("getLookupOptions", "In getLookupOptions GET_SITE_USERS_ERROR listItemReducer ActionType is " + action.type);
return updateSiteUser(state, action);
default:
return state;
}
}
export default siteUsersReducer;

View File

@ -0,0 +1,31 @@

import SystemStatus from "../Model/SystemStatus";
const defaultStatus: SystemStatus = {
currentAction: "",
fetchStatus: ""
};
import { Log } from "@microsoft/sp-client-base";
export default function SystemStatusReducer(state: SystemStatus = defaultStatus, action: any = { type: "" }) {
/**
*
*
* DO NOT UNCOMMENTS
* CAUSING INFINITE LOOP
*
*
*
*
*/
const newstate = _.clone(state);
if (action.type.startsWith("App/")) {
newstate.currentAction = action.type;
}
if (action.type.endsWith("_ERROR")) {
Log.info("SystemStatusReducer", "canged sfetch Status to action.payload.error.message");
newstate.fetchStatus = action.payload.error.message;
}
return newstate;
//return state;
}

View File

@ -0,0 +1,71 @@
import {
createStore,
applyMiddleware,
compose,
Middleware
} from "redux";
import { fromJS } from "immutable";
import { createMemoryHistory } from "react-router";
import { routerMiddleware } from "react-router-redux";
import thunk from "redux-thunk";
import promiseMiddleware from "redux-promise-middleware";
import logger from "./logger";
import { RootReducer } from "../reducers/rootReducer";
const __DEV__: boolean = true; // from webpack
function configureStore(initialState) {
const store = createStore(
RootReducer,
initialState,
compose(
applyMiddleware(..._getMiddleware()),
///// persistState("session", _getStorageConfig()),
__DEV__ && environment.devToolsExtension ?
environment.devToolsExtension() :
f => f));
// _enableHotLoader(store);
return store;
}
function _getMiddleware(): Middleware[] {
const history = createMemoryHistory();
let middleware = [
routerMiddleware(history),
promiseMiddleware(),
thunk,
];
if (__DEV__) {
middleware = [...middleware, logger];
}
return middleware;
}
const environment: any = window || this;
// function _enableHotLoader(store) {
// if (!__DEV__) {
// return;
// }
// const { hot } = module as any;
// if (hot) {
// hot.accept("../reducers", () => {
// const nextRootReducer = require("../reducers");
// store.replaceReducer(nextRootReducer);
// });
// }
// }
function _getStorageConfig() {
return {
key: "typescript-react-redux-seed",
serialize: (store) => {
return store && store.session ?
JSON.stringify(store.session.toJS()) : store;
},
deserialize: (state) => ({
session: state ? fromJS(JSON.parse(state)) : fromJS({}),
}),
};
}
export default configureStore;

View File

@ -0,0 +1,19 @@
const createLogger = require('redux-logger');
import immutableToJS from '../utils/immutable-to-js';
const logger = createLogger({
collapsed: true,
stateTransformer: (state) => {
return immutableToJS(state);
},
predicate: (getState, { type }) => {
return type !== 'redux-form/BLUR' &&
type !== 'redux-form/CHANGE' &&
type !== 'redux-form/FOCUS' &&
type !== 'redux-form/TOUCH';
},
});
export default logger;

View File

@ -0,0 +1,16 @@
import * as React from "react";
const { IndexRoute, Route } = require("react-router");
import App from "../containers/app";
import ListDefinitionContainer from "../containers/ListDefinitionContainer";
import ColumnDefinitionContainer from "../containers/ColumnDefinitionContainer";
import ListItemContainer from "../containers/ListItemContainer";
export default (
<Route path="/" component={App}>
<IndexRoute component={ListItemContainer} />
<Route path="/lists" component={ListDefinitionContainer}>
</Route>
<Route path="/columns" component={ColumnDefinitionContainer}>
</Route>
</Route>
);

View File

@ -0,0 +1,18 @@
import { Iterable } from 'immutable';
/**
* [immutableToJS
* converts properties of the provided [state] object from immutable
* data structures to regular JavaScript data structures - used with
* redux-logger
*
* @param {object} state [state reference]
* @return {object} [transformed state]
*/
export default function immutableToJS(state) {
return Object.keys(state).reduce((newState, key) => {
const val = state[key];
newState[key] = Iterable.isIterable(val) ? val.toJS() : val;
return newState;
}, {});
}

View File

@ -0,0 +1,25 @@
import { Log } from "@microsoft/sp-client-base";
export class ParsedSPField {
constructor(public id: string, public value: string
) { }
public toString(): string {
return this.id + "#;" + this.value;
}
}
/** Parses a string that is deleimetted with '#;'. Returns a ParsedSPField where the id is the part befor the '#;' and the value is the part after the '#;'.
* if thi input parameter is null, the id and the string of the returned ParsedSPField object will be null.
*/
export function ParseSPField(fieldValue: string): ParsedSPField {
if (fieldValue === null) {
return new ParsedSPField(null, null);
}
if (fieldValue.indexOf("#;") === -1) {
Log.info("ParseSPField", "Parse SP field called with a field value that has no '#;' delimeter. field valud is " + fieldValue);
return new ParsedSPField(null, null);
}
const parts = fieldValue.split("#;");
return new ParsedSPField(parts[0], parts[1]);
}

View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true
}
}

View File

@ -0,0 +1,15 @@
{
"globalDependencies": {
"react-data-grid": "registry:dt/react-data-grid#1.0.4+20161116100048",
"redux": "registry:dt/redux#3.5.2+20160703092728",
"redux-promise-middleware": "registry:dt/redux-promise-middleware#0.0.0+20160108032528"
},
"dependencies": {
"react-router": "registry:npm/react-router#2.4.0+20160628165748",
"react-router-redux": "registry:npm/react-router-redux#4.0.0+20160602212457",
"redux-thunk": "registry:npm/redux-thunk#2.0.0+20160525185520"
},
"devDependencies": {
"react-redux": "registry:npm/react-redux#4.4.0+20160614222153"
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,388 @@
// Type definitions for chai 3.2.0
// Project: http://chaijs.com/
// Definitions by: Jed Mao <https://github.com/jedmao/>,
// Bart van der Schoor <https://github.com/Bartvds>,
// Andrew Brown <https://github.com/AGBrown>,
// Olivier Chevet <https://github.com/olivr70>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
// <reference path="../assertion-error/assertion-error.d.ts"/>
declare module Chai {
interface ChaiStatic {
expect: ExpectStatic;
should(): Should;
/**
* Provides a way to extend the internals of Chai
*/
use(fn: (chai: any, utils: any) => void): any;
assert: AssertStatic;
config: Config;
AssertionError: AssertionError;
}
export interface ExpectStatic extends AssertionStatic {
fail(actual?: any, expected?: any, message?: string, operator?: string): void;
}
export interface AssertStatic extends Assert {
}
export interface AssertionStatic {
(target: any, message?: string): Assertion;
}
interface ShouldAssertion {
equal(value1: any, value2: any, message?: string): void;
Throw: ShouldThrow;
throw: ShouldThrow;
exist(value: any, message?: string): void;
}
interface Should extends ShouldAssertion {
not: ShouldAssertion;
fail(actual: any, expected: any, message?: string, operator?: string): void;
}
interface ShouldThrow {
(actual: Function): void;
(actual: Function, expected: string|RegExp, message?: string): void;
(actual: Function, constructor: Error|Function, expected?: string|RegExp, message?: string): void;
}
interface Assertion extends LanguageChains, NumericComparison, TypeComparison {
not: Assertion;
deep: Deep;
any: KeyFilter;
all: KeyFilter;
a: TypeComparison;
an: TypeComparison;
include: Include;
includes: Include;
contain: Include;
contains: Include;
ok: Assertion;
true: Assertion;
false: Assertion;
null: Assertion;
undefined: Assertion;
NaN: Assertion;
exist: Assertion;
empty: Assertion;
arguments: Assertion;
Arguments: Assertion;
equal: Equal;
equals: Equal;
eq: Equal;
eql: Equal;
eqls: Equal;
property: Property;
ownProperty: OwnProperty;
haveOwnProperty: OwnProperty;
ownPropertyDescriptor: OwnPropertyDescriptor;
haveOwnPropertyDescriptor: OwnPropertyDescriptor;
length: Length;
lengthOf: Length;
match: Match;
matches: Match;
string(string: string, message?: string): Assertion;
keys: Keys;
key(string: string): Assertion;
throw: Throw;
throws: Throw;
Throw: Throw;
respondTo: RespondTo;
respondsTo: RespondTo;
itself: Assertion;
satisfy: Satisfy;
satisfies: Satisfy;
closeTo(expected: number, delta: number, message?: string): Assertion;
members: Members;
increase: PropertyChange;
increases: PropertyChange;
decrease: PropertyChange;
decreases: PropertyChange;
change: PropertyChange;
changes: PropertyChange;
extensible: Assertion;
sealed: Assertion;
frozen: Assertion;
}
interface LanguageChains {
to: Assertion;
be: Assertion;
been: Assertion;
is: Assertion;
that: Assertion;
which: Assertion;
and: Assertion;
has: Assertion;
have: Assertion;
with: Assertion;
at: Assertion;
of: Assertion;
same: Assertion;
}
interface NumericComparison {
above: NumberComparer;
gt: NumberComparer;
greaterThan: NumberComparer;
least: NumberComparer;
gte: NumberComparer;
below: NumberComparer;
lt: NumberComparer;
lessThan: NumberComparer;
most: NumberComparer;
lte: NumberComparer;
within(start: number, finish: number, message?: string): Assertion;
}
interface NumberComparer {
(value: number, message?: string): Assertion;
}
interface TypeComparison {
(type: string, message?: string): Assertion;
instanceof: InstanceOf;
instanceOf: InstanceOf;
}
interface InstanceOf {
(constructor: Object, message?: string): Assertion;
}
interface Deep {
equal: Equal;
include: Include;
property: Property;
members: Members;
}
interface KeyFilter {
keys: Keys;
}
interface Equal {
(value: any, message?: string): Assertion;
}
interface Property {
(name: string, value?: any, message?: string): Assertion;
}
interface OwnProperty {
(name: string, message?: string): Assertion;
}
interface OwnPropertyDescriptor {
(name: string, descriptor: PropertyDescriptor, message?: string): Assertion;
(name: string, message?: string): Assertion;
}
interface Length extends LanguageChains, NumericComparison {
(length: number, message?: string): Assertion;
}
interface Include {
(value: Object, message?: string): Assertion;
(value: string, message?: string): Assertion;
(value: number, message?: string): Assertion;
keys: Keys;
members: Members;
any: KeyFilter;
all: KeyFilter;
}
interface Match {
(regexp: RegExp|string, message?: string): Assertion;
}
interface Keys {
(...keys: string[]): Assertion;
(keys: any[]): Assertion;
(keys: Object): Assertion;
}
interface Throw {
(): Assertion;
(expected: string, message?: string): Assertion;
(expected: RegExp, message?: string): Assertion;
(constructor: Error, expected?: string, message?: string): Assertion;
(constructor: Error, expected?: RegExp, message?: string): Assertion;
(constructor: Function, expected?: string, message?: string): Assertion;
(constructor: Function, expected?: RegExp, message?: string): Assertion;
}
interface RespondTo {
(method: string, message?: string): Assertion;
}
interface Satisfy {
(matcher: Function, message?: string): Assertion;
}
interface Members {
(set: any[], message?: string): Assertion;
}
interface PropertyChange {
(object: Object, prop: string, msg?: string): Assertion;
}
export interface Assert {
/**
* @param expression Expression to test for truthiness.
* @param message Message to display on error.
*/
(expression: any, message?: string): void;
fail(actual?: any, expected?: any, msg?: string, operator?: string): void;
ok(val: any, msg?: string): void;
isOk(val: any, msg?: string): void;
notOk(val: any, msg?: string): void;
isNotOk(val: any, msg?: string): void;
equal(act: any, exp: any, msg?: string): void;
notEqual(act: any, exp: any, msg?: string): void;
strictEqual(act: any, exp: any, msg?: string): void;
notStrictEqual(act: any, exp: any, msg?: string): void;
deepEqual(act: any, exp: any, msg?: string): void;
notDeepEqual(act: any, exp: any, msg?: string): void;
isTrue(val: any, msg?: string): void;
isFalse(val: any, msg?: string): void;
isNull(val: any, msg?: string): void;
isNotNull(val: any, msg?: string): void;
isUndefined(val: any, msg?: string): void;
isDefined(val: any, msg?: string): void;
isNaN(val: any, msg?: string): void;
isNotNaN(val: any, msg?: string): void;
isAbove(val: number, abv: number, msg?: string): void;
isBelow(val: number, blw: number, msg?: string): void;
isFunction(val: any, msg?: string): void;
isNotFunction(val: any, msg?: string): void;
isObject(val: any, msg?: string): void;
isNotObject(val: any, msg?: string): void;
isArray(val: any, msg?: string): void;
isNotArray(val: any, msg?: string): void;
isString(val: any, msg?: string): void;
isNotString(val: any, msg?: string): void;
isNumber(val: any, msg?: string): void;
isNotNumber(val: any, msg?: string): void;
isBoolean(val: any, msg?: string): void;
isNotBoolean(val: any, msg?: string): void;
typeOf(val: any, type: string, msg?: string): void;
notTypeOf(val: any, type: string, msg?: string): void;
instanceOf(val: any, type: Function, msg?: string): void;
notInstanceOf(val: any, type: Function, msg?: string): void;
include(exp: string, inc: any, msg?: string): void;
include(exp: any[], inc: any, msg?: string): void;
notInclude(exp: string, inc: any, msg?: string): void;
notInclude(exp: any[], inc: any, msg?: string): void;
match(exp: any, re: RegExp, msg?: string): void;
notMatch(exp: any, re: RegExp, msg?: string): void;
property(obj: Object, prop: string, msg?: string): void;
notProperty(obj: Object, prop: string, msg?: string): void;
deepProperty(obj: Object, prop: string, msg?: string): void;
notDeepProperty(obj: Object, prop: string, msg?: string): void;
propertyVal(obj: Object, prop: string, val: any, msg?: string): void;
propertyNotVal(obj: Object, prop: string, val: any, msg?: string): void;
deepPropertyVal(obj: Object, prop: string, val: any, msg?: string): void;
deepPropertyNotVal(obj: Object, prop: string, val: any, msg?: string): void;
lengthOf(exp: any, len: number, msg?: string): void;
//alias frenzy
throw(fn: Function, msg?: string): void;
throw(fn: Function, regExp: RegExp): void;
throw(fn: Function, errType: Function, msg?: string): void;
throw(fn: Function, errType: Function, regExp: RegExp): void;
throws(fn: Function, msg?: string): void;
throws(fn: Function, regExp: RegExp): void;
throws(fn: Function, errType: Function, msg?: string): void;
throws(fn: Function, errType: Function, regExp: RegExp): void;
Throw(fn: Function, msg?: string): void;
Throw(fn: Function, regExp: RegExp): void;
Throw(fn: Function, errType: Function, msg?: string): void;
Throw(fn: Function, errType: Function, regExp: RegExp): void;
doesNotThrow(fn: Function, msg?: string): void;
doesNotThrow(fn: Function, regExp: RegExp): void;
doesNotThrow(fn: Function, errType: Function, msg?: string): void;
doesNotThrow(fn: Function, errType: Function, regExp: RegExp): void;
operator(val: any, operator: string, val2: any, msg?: string): void;
closeTo(act: number, exp: number, delta: number, msg?: string): void;
sameMembers(set1: any[], set2: any[], msg?: string): void;
sameDeepMembers(set1: any[], set2: any[], msg?: string): void;
includeMembers(superset: any[], subset: any[], msg?: string): void;
ifError(val: any, msg?: string): void;
isExtensible(obj: {}, msg?: string): void;
extensible(obj: {}, msg?: string): void;
isNotExtensible(obj: {}, msg?: string): void;
notExtensible(obj: {}, msg?: string): void;
isSealed(obj: {}, msg?: string): void;
sealed(obj: {}, msg?: string): void;
isNotSealed(obj: {}, msg?: string): void;
notSealed(obj: {}, msg?: string): void;
isFrozen(obj: Object, msg?: string): void;
frozen(obj: Object, msg?: string): void;
isNotFrozen(obj: Object, msg?: string): void;
notFrozen(obj: Object, msg?: string): void;
}
export interface Config {
includeStack: boolean;
}
export class AssertionError {
constructor(message: string, _props?: any, ssf?: Function);
name: string;
message: string;
showDiff: boolean;
stack: string;
}
}
declare var chai: Chai.ChaiStatic;
declare module "chai" {
export = chai;
}
interface Object {
should: Chai.Assertion;
}

View File

@ -0,0 +1,107 @@
// Type definitions for Combokeys v2.4.6
// Project: https://github.com/PolicyStat/combokeys
// Definitions by: Ian Clanton-Thuon <https://github.com/iclanton>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
declare namespace Combokeys {
interface CombokeysStatic {
new (element: Element): Combokeys;
/**
* all instances of Combokeys
*/
instances: Combokeys[];
/**
* reset all instances
*/
reset(): void;
}
interface Combokeys {
element: Element;
/**
* binds an event to Combokeys
*
* can be a single key, a combination of keys separated with +,
* an array of keys, or a sequence of keys separated by spaces
*
* be sure to list the modifier keys first to make sure that the
* correct key ends up getting bound (the last key in the pattern)
*
* @param {keys} key combination or combinations
* @param {callback} callback function
* @param {handler} optional - one of "keypress", "keydown", or "keyup"
* @returns void
*/
bind(keys: string | string[], callback: () => void, action?: string): void;
/**
* binds multiple combinations to the same callback
*
* @param {keys} key combinations
* @param {callback} callback function
* @param {handler} optional - one of "keypress", "keydown", or "keyup"
* @returns void
*/
bindMultiple(keys: string[], callback: () => void, action?: string): void;
/**
* unbinds an event to Combokeys
*
* the unbinding sets the callback function of the specified key combo
* to an empty function and deletes the corresponding key in the
* directMap dict.
*
* the keycombo+action has to be exactly the same as
* it was defined in the bind method
*
* @param {keys} key combination or combinations
* @param {action} optional - one of "keypress", "keydown", or "keyup"
* @returns void
*/
unbind(keys: string | string[], action?: string): void;
/**
* triggers an event that has already been bound
*
* @param {keys} key combination
* @param {action} optional - one of "keypress", "keydown", or "keyup"
* @returns void
*/
trigger(keys: string, action?: string): void;
/**
* resets the library back to its initial state. This is useful
* if you want to clear out the current keyboard shortcuts and bind
* new ones - for example if you switch to another page
*
* @returns void
*/
reset(): void;
/**
* should we stop this event before firing off callbacks
*
* @param {e} event
* @param {element} bound element
* @return {boolean}
*/
stopCallback(e: Event, element: Element): boolean;
/**
* detach all listners from the bound element
*
* @return {void}
*/
detach(): void;
}
}
declare var combokeys: Combokeys.CombokeysStatic;
declare module "combokeys" {
export = combokeys;
}

View File

@ -0,0 +1,113 @@
// Type definitions for es6-collections v0.5.1
// Project: https://github.com/WebReflection/es6-collections/
// Definitions by: Ron Buckton <http://github.com/rbuckton>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
/* *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
interface IteratorResult<T> {
done: boolean;
value?: T;
}
interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
throw?(e?: any): IteratorResult<T>;
}
interface ForEachable<T> {
forEach(callbackfn: (value: T) => void): void;
}
interface Map<K, V> {
clear(): void;
delete(key: K): boolean;
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void;
get(key: K): V;
has(key: K): boolean;
set(key: K, value?: V): Map<K, V>;
entries(): Iterator<[K, V]>;
keys(): Iterator<K>;
values(): Iterator<V>;
size: number;
}
interface MapConstructor {
new <K, V>(): Map<K, V>;
new <K, V>(iterable: ForEachable<[K, V]>): Map<K, V>;
prototype: Map<any, any>;
}
declare var Map: MapConstructor;
interface Set<T> {
add(value: T): Set<T>;
clear(): void;
delete(value: T): boolean;
forEach(callbackfn: (value: T, index: T, set: Set<T>) => void, thisArg?: any): void;
has(value: T): boolean;
entries(): Iterator<[T, T]>;
keys(): Iterator<T>;
values(): Iterator<T>;
size: number;
}
interface SetConstructor {
new <T>(): Set<T>;
new <T>(iterable: ForEachable<T>): Set<T>;
prototype: Set<any>;
}
declare var Set: SetConstructor;
interface WeakMap<K, V> {
delete(key: K): boolean;
clear(): void;
get(key: K): V;
has(key: K): boolean;
set(key: K, value?: V): WeakMap<K, V>;
}
interface WeakMapConstructor {
new <K, V>(): WeakMap<K, V>;
new <K, V>(iterable: ForEachable<[K, V]>): WeakMap<K, V>;
prototype: WeakMap<any, any>;
}
declare var WeakMap: WeakMapConstructor;
interface WeakSet<T> {
delete(value: T): boolean;
clear(): void;
add(value: T): WeakSet<T>;
has(value: T): boolean;
}
interface WeakSetConstructor {
new <T>(): WeakSet<T>;
new <T>(iterable: ForEachable<T>): WeakSet<T>;
prototype: WeakSet<any>;
}
declare var WeakSet: WeakSetConstructor;
declare module "es6-collections" {
var Map: MapConstructor;
var Set: SetConstructor;
var WeakMap: WeakMapConstructor;
var WeakSet: WeakSetConstructor;
}

View File

@ -0,0 +1,74 @@
// Type definitions for es6-promise
// Project: https://github.com/jakearchibald/ES6-Promise
// Definitions by: François de Campredon <https://github.com/fdecampredon/>, vvakame <https://github.com/vvakame>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
interface Thenable<R> {
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Thenable<U>;
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => void): Thenable<U>;
catch<U>(onRejected?: (error: any) => U | Thenable<U>): Thenable<U>;
}
declare class Promise<R> implements Thenable<R> {
/**
* If you call resolve in the body of the callback passed to the constructor,
* your promise is fulfilled with result object passed to resolve.
* If you call reject your promise is rejected with the object passed to reject.
* For consistency and debugging (eg stack traces), obj should be an instanceof Error.
* Any errors thrown in the constructor callback will be implicitly passed to reject().
*/
constructor(callback: (resolve : (value?: R | Thenable<R>) => void, reject: (error?: any) => void) => void);
/**
* onFulfilled is called when/if "promise" resolves. onRejected is called when/if "promise" rejects.
* Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called.
* Both callbacks have a single parameter , the fulfillment value or rejection reason.
* "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve.
* If an error is thrown in the callback, the returned promise rejects with that error.
*
* @param onFulfilled called when/if "promise" resolves
* @param onRejected called when/if "promise" rejects
*/
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Promise<U>;
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => void): Promise<U>;
/**
* Sugar for promise.then(undefined, onRejected)
*
* @param onRejected called when/if "promise" rejects
*/
catch<U>(onRejected?: (error: any) => U | Thenable<U>): Promise<U>;
}
declare module Promise {
/**
* Make a new promise from the thenable.
* A thenable is promise-like in as far as it has a "then" method.
*/
function resolve<R>(value?: R | Thenable<R>): Promise<R>;
/**
* Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error
*/
function reject(error: any): Promise<any>;
/**
* Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects.
* the array passed to all can be a mixture of promise-like objects and other objects.
* The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value.
*/
function all<R>(promises: (R | Thenable<R>)[]): Promise<R[]>;
/**
* Make a Promise that fulfills when any item fulfills, and rejects if any item rejects.
*/
function race<R>(promises: (R | Thenable<R>)[]): Promise<R>;
}
declare module 'es6-promise' {
var foo: typeof Promise; // Temp variable to reference Promise in local context
module rsvp {
export var Promise: typeof foo;
}
export = rsvp;
}

View File

@ -0,0 +1,498 @@
// Generated by typings
// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/896d675b6d9dae75656e499a80ae733d904350bb/react-data-grid/react-data-grid.d.ts
declare namespace AdazzleReactDataGrid {
import React = __React;
interface SelectionParams {
rowIdx: number,
row: any
}
interface GridProps {
/**
* Gets the data to render in each row. Required.
* Can be an array or a function that takes an index and returns an object.
*/
rowGetter: Array<Object> | ((rowIdx: number) => Object)
/**
* The total number of rows to render. Required.
*/
rowsCount: number
/**
* The columns to render.
*/
columns?: Array<Column>
/**
* Invoked when the user changes the value of a single cell.
* Should update that cell's value.
* @param e Information about the event
*/
onRowUpdated?: (e: RowUpdateEvent) => void
/**
* Invoked when the user pulls down the drag handle of an editable cell.
* Should update the values of the selected cells.
* @param e Information about the event
*/
onCellsDragged?: (e: CellDragEvent) => void
/**
* Invoked when the user double clicks on the drag handle of an editable cell.
* Should update the values of the cells beneath the selected cell.
* @param e Information about the event
*/
onDragHandleDoubleClick?: (e: DragHandleDoubleClickEvent) => void
/**
* Invoked when the user copies a value from one cell and pastes it into another (in the same column).
* Should update the value of the cell in row e.toRow.
* @param e Information about the event
*/
onCellCopyPaste?: (e: CellCopyPasteEvent) => void
/**
* Invoked after the user updates the grid rows in any way.
* @param e Information about the event
*/
onGridRowsUpdated?: (e: GridRowsUpdatedEvent) => void
/**
* A toolbar to display above the grid.
* Consider using the toolbar included in "react-data-grid/addons".
*/
toolbar?: React.ReactElement<any>
/**
* A context menu to disiplay when the user right-clicks a cell.
* Consider using "react-contextmenu", included in "react-data-grid/addons".
*/
contextMenu?: React.ReactElement<any>
/**
* A react component to customize how rows are rendered.
* If you want to define your own, consider extending ReactDataGrid.Row.
*/
rowRenderer?: React.ReactElement<any> | React.ComponentClass<any> | React.StatelessComponent<any>
/**
* A component to display when there are no rows to render.
*/
emptyRowsView?: React.ComponentClass<any> | React.StatelessComponent<any>
/**
* The minimum width of the entire grid in pixels.
*/
minWidth?: number
/**
* The minimum height of the entire grid in pixels.
* @default 350
*/
minHeight?: number
/**
* The height of each individual row in pixels.
* @default 35
*/
rowHeight?: number
/**
* The height of the header row in pixels.
* @default rowHeight
*/
headerRowHeight?: number
/**
* The minimum width of each column in pixels.
* @default 80
*/
minColumnWidth?: number
/**
* Invoked when a column has been resized.
* @param index The index of the column
* @param width The new width of the column
*/
onColumnResize?: (index: number, width: number) => void
/**
* Controls what happens when the user navigates beyond the first or last cells.
* 'loopOverRow' will navigate to the beginning/end of the current row.
* 'changeRow' will navigate to the beginning of the next row or the end of the last.
* 'none' will do nothing.
* @default none
*/
cellNavigationMode?: 'none' | 'loopOverRow' | 'changeRow'
/**
* Called when the user sorts the grid by some column.
* Should update the order of the rows returned by rowGetter.
* @param sortColumn The name of the column being sorted by
* @param sortDirection The direction to sort ('ASC'/'DESC'/'NONE')
*/
onGridSort?: (sortColumn: string, sortDirection: 'ASC' | 'DESC' | 'NONE') => void
/**
* Called when the user filters a column by some value.
* Should restrict the rows in rowGetter to only things that match the filter.
* @param filter The filter being added
*/
onAddFilter?: (filter: Filter) => void
/**
* Called when the user clears all filters.
* Should restore the rows in rowGetter to their original state.
*/
onClearFilters?: () => void
/**
* When set to true or 'multi', enables multiple row select.
* When set to 'single', enables single row select.
* When set to false or not set, disables row select.
* @default false
*/
enableRowSelect?: boolean | 'single' | 'multi'
/**
* Called when a row is selected.
* @param rows The (complete) current selection of rows.
*/
onRowSelect?: (rows: Array<Object>) => void
/**
* A property that's unique to every row.
* This property is required to enable row selection.
* @default 'id'
*/
rowKey?: string
/**
* Enables cells to be selected when clicked.
* @default false
*/
enableCellSelect?: boolean
/**
* Called when a cell is selected.
* @param coordinates The row and column indices of the selected cell.
*/
onCellSelected?: (coordinates: {rowIdx: number, idx: number}) => void
/**
* Called when a cell is deselected.
* @param coordinates The row and column indices of the deselected cell.
*/
onCellDeSelected?: (coordinates: {rowIdx: number, idx: number}) => void
/**
* How long to wait before rendering a new row while scrolling in milliseconds.
* @default 0
*/
rowScrollTimeout?: number
/**
* Options object for selecting rows
*/
rowSelection?: {
showCheckbox?: boolean
enableShiftSelect?: boolean
onRowsSelected?: (rows: Array<SelectionParams>) => void,
onRowsDeselected?: (rows: Array<SelectionParams>) => void,
selectBy?: {
indexes?: Array<number>;
keys?: { rowKey: string, values: Array<any> };
isSelectedKey?: string;
}
}
}
/**
* Information about a specific column to be rendered.
*/
interface Column {
/**
* A unique key for this column. Required.
* Each row should have a property with this name, which contains this column's value.
*/
key: string
/**
* This column's display name. Required.
*/
name: string
/**
* A custom width for this specific column.
* @default minColumnWidth from the ReactDataGrid
*/
width?: number
/**
* Whether this column can be resized by the user.
* @default false
*/
resizable?: boolean
/**
* Whether this column should stay fixed on the left as the user scrolls horizontally.
* @default false
*/
locked?: boolean
/**
* Whether this column can be edited.
* @default false
*/
editable?: boolean
/**
* Whether the rows in the grid can be sorted by this column.
* @default false
*/
sortable?: boolean
/**
* Whether the rows in the grid can be filtered by this column.
* @default false
*/
filterable?: boolean
/**
* The editor for this column. Several editors are available in "react-data-grid/addons".
* @default A simple text editor
*/
editor?: React.ReactElement<any>
/**
* A custom read-only formatter for this column. An image formatter is available in "react-data-grid/addons".
*/
formatter?: React.ReactElement<any> | React.ComponentClass<any> | React.StatelessComponent<any>
/**
* A custom formatter for this column's header.
*/
headerRenderer?: React.ReactElement<any> | React.ComponentClass<any> | React.StatelessComponent<any>
/**
* Events to be bound to the cells in this specific column.
* Each event must respect this standard in order to work correctly:
* @example
* function onXxx(ev :SyntheticEvent, (rowIdx, idx, name): args)
*/
events?: {
[name: string]: ColumnEventCallback
}
}
interface ColumnEventCallback {
/**
* A callback for a native react event on a specific cell.
* @param ev The react event
* @param args The row and column coordinates of the cell, and the name of the event.
*/
(ev: React.SyntheticEvent, args: {rowIdx: number, idx: number, name: string}): void
}
/**
* Information about a row update
*/
interface RowUpdateEvent {
/**
* The index of the updated row.
*/
rowIdx: number
/**
* The columns that were updated and their values.
*/
updated: Object
/**
* The name of the column that was updated.
*/
cellKey: string
/**
* The name of the key pressed to trigger the event ('Tab', 'Enter', etc.).
*/
key: string
}
/**
* Information about a cell drag
*/
interface CellDragEvent {
/**
* The name of the column that was dragged.
*/
cellKey: string
/**
* The row where the drag began.
*/
fromRow: number
/**
* The row where the drag ended.
*/
toRow: number
/**
* The value of the cell that was dragged.
*/
value: any
}
/**
* Information about a drag handle double click
*/
interface DragHandleDoubleClickEvent {
/**
* The row where the double click occurred.
*/
rowIdx: number
/**
* The column where the double click occurred.
*/
idx: number
/**
* The values of the row.
*/
rowData: Object
/**
* The double click event.
*/
e: React.SyntheticEvent
}
/**
* Information about a copy paste
*/
interface CellCopyPasteEvent {
/**
* The row that was pasted to.
*/
rowIdx: number
/**
* The value that was pasted.
*/
value: any
/**
* The row that was copied from.
*/
fromRow: number
/**
* The row that was pasted to.
*/
toRow: number
/**
* The key of the column where the copy paste occurred.
*/
cellKey: string
}
/**
* Information about some update to the grid's contents
*/
interface GridRowsUpdatedEvent {
/**
* The key of the column where the event occurred.
*/
cellKey: string
/**
* The top row affected by the event.
*/
fromRow: number
/**
* The bottom row affected by the event.
*/
toRow: number
/**
* The columns that were updated and their values.
*/
updated: Object
/**
* The action that occurred to trigger this event.
* One of 'cellUpdate', 'cellDrag', 'columnFill', or 'copyPaste'.
*/
action: 'cellUpdate' | 'cellDrag' | 'columnFill' | 'copyPaste'
}
/**
* Some filter to be applied to the grid's contents
*/
interface Filter {
/**
* The key of the column being filtered.
*/
columnKey: string
/**
* The term to filter by.
*/
filterTerm: string
}
/**
* Excel-like grid component built with React, with editors, keyboard navigation, copy & paste, and the like
* http://adazzle.github.io/react-data-grid/
*/
export class ReactDataGrid extends React.Component<GridProps, {}> { }
export namespace ReactDataGrid {
// Useful types
export import Column = AdazzleReactDataGrid.Column;
export import Filter = AdazzleReactDataGrid.Filter;
// Various events
export import RowUpdateEvent = AdazzleReactDataGrid.RowUpdateEvent;
export import SelectionParams = AdazzleReactDataGrid.SelectionParams;
export import CellDragEvent = AdazzleReactDataGrid.CellDragEvent;
export import DragHandleDoubleClickEvent = AdazzleReactDataGrid.DragHandleDoubleClickEvent;
export import CellCopyPasteEvent = AdazzleReactDataGrid.CellCopyPasteEvent;
export import GridRowsUpdatedEvent = AdazzleReactDataGrid.GridRowsUpdatedEvent;
// Actual classes exposed on module.exports
/**
* A react component that renders a row of the grid
*/
export class Row extends React.Component<any, any> { }
/**
* A react coponent that renders a cell of the grid
*/
export class Cell extends React.Component<any, any> { }
}
}
declare namespace AdazzleReactDataGridPlugins {
import React = __React;
// TODO: refine types for these addons
export namespace Editors {
export class AutoComplete extends React.Component<any, {}> { }
export class DropDownEditor extends React.Component<any, {}> { }
export class SimpleTextEditor extends React.Component<any, {}> { }
export class CheckboxEditor extends React.Component<any, {}> { }
}
export namespace Formatters {
export class ImageFormatter extends React.Component<any, {}> { }
export class DropDownFormatter extends React.Component<any, {}> { }
}
export class Toolbar extends React.Component<any, any> {}
// TODO: re-export the react-contextmenu typings once those exist
// https://github.com/vkbansal/react-contextmenu/issues/10
export namespace Menu {
export class ContextMenu extends React.Component<any, {}> { }
export class MenuHeader extends React.Component<any, {}> { }
export class MenuItem extends React.Component<any, {}> { }
export class SubMenu extends React.Component<any, {}> { }
export const monitor: {
getItem(): any
getPosition(): any
hideMenu(): void
};
export function connect(Menu: any): any;
export function ContextMenuLayer(
identifier: any,
configure?: (props: any) => any
): (Component: any) => any
}
}
declare module "react-data-grid" {
import ReactDataGrid = AdazzleReactDataGrid.ReactDataGrid;
// commonjs export
export = ReactDataGrid;
}
declare module "react-data-grid/addons" {
import Plugins = AdazzleReactDataGridPlugins;
import Editors = Plugins.Editors;
import Formatters = Plugins.Formatters;
import Toolbar = Plugins.Toolbar;
import Menu = Plugins.Menu;
// ES6 named exports
export {
Editors,
Formatters,
Toolbar,
Menu
}
// attach to window
global {
interface Window {
ReactDataGridPlugins: {
Editors: typeof Editors
Formatters: typeof Formatters
Toolbar: typeof Toolbar
Menu: typeof Menu
}
}
}
}

View File

@ -0,0 +1,8 @@
{
"resolution": "main",
"tree": {
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/896d675b6d9dae75656e499a80ae733d904350bb/react-data-grid/react-data-grid.d.ts",
"raw": "registry:dt/react-data-grid#1.0.4+20161116100048",
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/896d675b6d9dae75656e499a80ae733d904350bb/react-data-grid/react-data-grid.d.ts"
}
}

View File

@ -0,0 +1,7 @@
// Generated by typings
// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/e7df72296ea2a55f57ece35e28bb930ae93b19b5/redux-promise-middleware/redux-promise-middleware.d.ts
declare module "redux-promise-middleware" {
function promiseMiddleware(config?: { promiseTypeSuffixes: string[] }): Redux.Middleware;
export default promiseMiddleware;
}

View File

@ -0,0 +1,8 @@
{
"resolution": "main",
"tree": {
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/e7df72296ea2a55f57ece35e28bb930ae93b19b5/redux-promise-middleware/redux-promise-middleware.d.ts",
"raw": "registry:dt/redux-promise-middleware#0.0.0+20160108032528",
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/e7df72296ea2a55f57ece35e28bb930ae93b19b5/redux-promise-middleware/redux-promise-middleware.d.ts"
}
}

View File

@ -0,0 +1,401 @@
// Generated by typings
// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/15ddcf312782faf9e7fdfe724a3a29382a5825d7/redux/redux.d.ts
declare namespace Redux {
/**
* An *action* is a plain object that represents an intention to change the
* state. Actions are the only way to get data into the store. Any data,
* whether from UI events, network callbacks, or other sources such as
* WebSockets needs to eventually be dispatched as actions.
*
* Actions must have a `type` field that indicates the type of action being
* performed. Types can be defined as constants and imported from another
* module. Its better to use strings for `type` than Symbols because strings
* are serializable.
*
* Other than `type`, the structure of an action object is really up to you.
* If youre interested, check out Flux Standard Action for recommendations on
* how actions should be constructed.
*/
interface Action {
type: any;
}
/* reducers */
/**
* A *reducer* (also called a *reducing function*) is a function that accepts
* an accumulation and a value and returns a new accumulation. They are used
* to reduce a collection of values down to a single value
*
* Reducers are not unique to Reduxthey are a fundamental concept in
* functional programming. Even most non-functional languages, like
* JavaScript, have a built-in API for reducing. In JavaScript, it's
* `Array.prototype.reduce()`.
*
* In Redux, the accumulated value is the state object, and the values being
* accumulated are actions. Reducers calculate a new state given the previous
* state and an action. They must be *pure functions*functions that return
* the exact same output for given inputs. They should also be free of
* side-effects. This is what enables exciting features like hot reloading and
* time travel.
*
* Reducers are the most important concept in Redux.
*
* *Do not put API calls into reducers.*
*
* @template S State object type.
*/
type Reducer<S> = <A extends Action>(state: S, action: A) => S;
/**
* Object whose values correspond to different reducer functions.
*/
interface ReducersMapObject {
[key: string]: Reducer<any>;
}
/**
* Turns an object whose values are different reducer functions, into a single
* reducer function. It will call every child reducer, and gather their results
* into a single state object, whose keys correspond to the keys of the passed
* reducer functions.
*
* @template S Combined state object type.
*
* @param reducers An object whose values correspond to different reducer
* functions that need to be combined into one. One handy way to obtain it
* is to use ES6 `import * as reducers` syntax. The reducers may never
* return undefined for any action. Instead, they should return their
* initial state if the state passed to them was undefined, and the current
* state for any unrecognized action.
*
* @returns A reducer function that invokes every reducer inside the passed
* object, and builds a state object with the same shape.
*/
function combineReducers<S>(reducers: ReducersMapObject): Reducer<S>;
/* store */
/**
* A *dispatching function* (or simply *dispatch function*) is a function that
* accepts an action or an async action; it then may or may not dispatch one
* or more actions to the store.
*
* We must distinguish between dispatching functions in general and the base
* `dispatch` function provided by the store instance without any middleware.
*
* The base dispatch function *always* synchronously sends an action to the
* stores reducer, along with the previous state returned by the store, to
* calculate a new state. It expects actions to be plain objects ready to be
* consumed by the reducer.
*
* Middleware wraps the base dispatch function. It allows the dispatch
* function to handle async actions in addition to actions. Middleware may
* transform, delay, ignore, or otherwise interpret actions or async actions
* before passing them to the next middleware.
*/
interface Dispatch<S> {
<A extends Action>(action: A): A;
}
/**
* Function to remove listener added by `Store.subscribe()`.
*/
interface Unsubscribe {
(): void;
}
/**
* A store is an object that holds the applications state tree.
* There should only be a single store in a Redux app, as the composition
* happens on the reducer level.
*
* @template S State object type.
*/
interface Store<S> {
/**
* Dispatches an action. It is the only way to trigger a state change.
*
* The `reducer` function, used to create the store, will be called with the
* current state tree and the given `action`. Its return value will be
* considered the **next** state of the tree, and the change listeners will
* be notified.
*
* The base implementation only supports plain object actions. If you want
* to dispatch a Promise, an Observable, a thunk, or something else, you
* need to wrap your store creating function into the corresponding
* middleware. For example, see the documentation for the `redux-thunk`
* package. Even the middleware will eventually dispatch plain object
* actions using this method.
*
* @param action A plain object representing what changed. It is a good
* idea to keep actions serializable so you can record and replay user
* sessions, or use the time travelling `redux-devtools`. An action must
* have a `type` property which may not be `undefined`. It is a good idea
* to use string constants for action types.
*
* @returns For convenience, the same action object you dispatched.
*
* Note that, if you use a custom middleware, it may wrap `dispatch()` to
* return something else (for example, a Promise you can await).
*/
dispatch: Dispatch<S>;
/**
* Reads the state tree managed by the store.
*
* @returns The current state tree of your application.
*/
getState(): S;
/**
* Adds a change listener. It will be called any time an action is
* dispatched, and some part of the state tree may potentially have changed.
* You may then call `getState()` to read the current state tree inside the
* callback.
*
* You may call `dispatch()` from a change listener, with the following
* caveats:
*
* 1. The subscriptions are snapshotted just before every `dispatch()` call.
* If you subscribe or unsubscribe while the listeners are being invoked,
* this will not have any effect on the `dispatch()` that is currently in
* progress. However, the next `dispatch()` call, whether nested or not,
* will use a more recent snapshot of the subscription list.
*
* 2. The listener should not expect to see all states changes, as the state
* might have been updated multiple times during a nested `dispatch()` before
* the listener is called. It is, however, guaranteed that all subscribers
* registered before the `dispatch()` started will be called with the latest
* state by the time it exits.
*
* @param listener A callback to be invoked on every dispatch.
* @returns A function to remove this change listener.
*/
subscribe(listener: () => void): Unsubscribe;
/**
* Replaces the reducer currently used by the store to calculate the state.
*
* You might need this if your app implements code splitting and you want to
* load some of the reducers dynamically. You might also need this if you
* implement a hot reloading mechanism for Redux.
*
* @param nextReducer The reducer for the store to use instead.
*/
replaceReducer(nextReducer: Reducer<S>): void;
}
/**
* A store creator is a function that creates a Redux store. Like with
* dispatching function, we must distinguish the base store creator,
* `createStore(reducer, preloadedState)` exported from the Redux package, from
* store creators that are returned from the store enhancers.
*
* @template S State object type.
*/
interface StoreCreator {
<S>(reducer: Reducer<S>, enhancer?: StoreEnhancer<S>): Store<S>;
<S>(reducer: Reducer<S>, preloadedState: S, enhancer?: StoreEnhancer<S>): Store<S>;
}
/**
* A store enhancer is a higher-order function that composes a store creator
* to return a new, enhanced store creator. This is similar to middleware in
* that it allows you to alter the store interface in a composable way.
*
* Store enhancers are much the same concept as higher-order components in
* React, which are also occasionally called component enhancers.
*
* Because a store is not an instance, but rather a plain-object collection of
* functions, copies can be easily created and modified without mutating the
* original store. There is an example in `compose` documentation
* demonstrating that.
*
* Most likely youll never write a store enhancer, but you may use the one
* provided by the developer tools. It is what makes time travel possible
* without the app being aware it is happening. Amusingly, the Redux
* middleware implementation is itself a store enhancer.
*/
type StoreEnhancer<S> = (next: StoreEnhancerStoreCreator<S>) => StoreEnhancerStoreCreator<S>;
type GenericStoreEnhancer = <S>(next: StoreEnhancerStoreCreator<S>) => StoreEnhancerStoreCreator<S>;
type StoreEnhancerStoreCreator<S> = (reducer: Reducer<S>, preloadedState?: S) => Store<S>;
/**
* Creates a Redux store that holds the state tree.
* The only way to change the data in the store is to call `dispatch()` on it.
*
* There should only be a single store in your app. To specify how different
* parts of the state tree respond to actions, you may combine several
* reducers
* into a single reducer function by using `combineReducers`.
*
* @template S State object type.
*
* @param reducer A function that returns the next state tree, given the
* current state tree and the action to handle.
*
* @param [preloadedState] The initial state. You may optionally specify it to
* hydrate the state from the server in universal apps, or to restore a
* previously serialized user session. If you use `combineReducers` to
* produce the root reducer function, this must be an object with the same
* shape as `combineReducers` keys.
*
* @param [enhancer] The store enhancer. You may optionally specify it to
* enhance the store with third-party capabilities such as middleware, time
* travel, persistence, etc. The only store enhancer that ships with Redux
* is `applyMiddleware()`.
*
* @returns A Redux store that lets you read the state, dispatch actions and
* subscribe to changes.
*/
const createStore: StoreCreator;
/* middleware */
interface MiddlewareAPI<S> {
dispatch: Dispatch<S>;
getState(): S;
}
/**
* A middleware is a higher-order function that composes a dispatch function
* to return a new dispatch function. It often turns async actions into
* actions.
*
* Middleware is composable using function composition. It is useful for
* logging actions, performing side effects like routing, or turning an
* asynchronous API call into a series of synchronous actions.
*/
interface Middleware {
<S>(api: MiddlewareAPI<S>): (next: Dispatch<S>) => Dispatch<S>;
}
/**
* Creates a store enhancer that applies middleware to the dispatch method
* of the Redux store. This is handy for a variety of tasks, such as
* expressing asynchronous actions in a concise manner, or logging every
* action payload.
*
* See `redux-thunk` package as an example of the Redux middleware.
*
* Because middleware is potentially asynchronous, this should be the first
* store enhancer in the composition chain.
*
* Note that each middleware will be given the `dispatch` and `getState`
* functions as named arguments.
*
* @param middlewares The middleware chain to be applied.
* @returns A store enhancer applying the middleware.
*/
function applyMiddleware(...middlewares: Middleware[]): GenericStoreEnhancer;
/* action creators */
/**
* An *action creator* is, quite simply, a function that creates an action. Do
* not confuse the two termsagain, an action is a payload of information, and
* an action creator is a factory that creates an action.
*
* Calling an action creator only produces an action, but does not dispatch
* it. You need to call the stores `dispatch` function to actually cause the
* mutation. Sometimes we say *bound action creators* to mean functions that
* call an action creator and immediately dispatch its result to a specific
* store instance.
*
* If an action creator needs to read the current state, perform an API call,
* or cause a side effect, like a routing transition, it should return an
* async action instead of an action.
*
* @template A Returned action type.
*/
interface ActionCreator<A> {
(...args: any[]): A;
}
/**
* Object whose values are action creator functions.
*/
interface ActionCreatorsMapObject {
[key: string]: ActionCreator<any>;
}
/**
* Turns an object whose values are action creators, into an object with the
* same keys, but with every function wrapped into a `dispatch` call so they
* may be invoked directly. This is just a convenience method, as you can call
* `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
*
* For convenience, you can also pass a single function as the first argument,
* and get a function in return.
*
* @param actionCreator An object whose values are action creator functions.
* One handy way to obtain it is to use ES6 `import * as` syntax. You may
* also pass a single function.
*
* @param dispatch The `dispatch` function available on your Redux store.
*
* @returns The object mimicking the original object, but with every action
* creator wrapped into the `dispatch` call. If you passed a function as
* `actionCreator`, the return value will also be a single function.
*/
function bindActionCreators<A extends ActionCreator<any>>(actionCreator: A, dispatch: Dispatch<any>): A;
function bindActionCreators<
A extends ActionCreator<any>,
B extends ActionCreator<any>
>(actionCreator: A, dispatch: Dispatch<any>): B;
function bindActionCreators<M extends ActionCreatorsMapObject>(actionCreators: M, dispatch: Dispatch<any>): M;
function bindActionCreators<
M extends ActionCreatorsMapObject,
N extends ActionCreatorsMapObject
>(actionCreators: M, dispatch: Dispatch<any>): N;
/* compose */
/**
* Composes single-argument functions from right to left. The rightmost
* function can take multiple arguments as it provides the signature for the
* resulting composite function.
*
* @param funcs The functions to compose.
* @returns R function obtained by composing the argument functions from right
* to left. For example, `compose(f, g, h)` is identical to doing
* `(...args) => f(g(h(...args)))`.
*/
function compose(): <R>(a: R, ...args: any[]) => R;
function compose<A, R>(
f1: (b: A) => R,
f2: (...args: any[]) => A
): (...args: any[]) => R;
function compose<A, B, R>(
f1: (b: B) => R,
f2: (a: A) => B,
f3: (...args: any[]) => A
): (...args: any[]) => R;
function compose<A, B, C, R>(
f1: (b: C) => R,
f2: (a: B) => C,
f3: (a: A) => B,
f4: (...args: any[]) => A
): (...args: any[]) => R;
function compose<R>(
f1: (a: any) => R,
...funcs: Function[]
): (...args: any[]) => R;
}
declare module "redux" {
export = Redux;
}

View File

@ -0,0 +1,8 @@
{
"resolution": "main",
"tree": {
"src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/15ddcf312782faf9e7fdfe724a3a29382a5825d7/redux/redux.d.ts",
"raw": "registry:dt/redux#3.5.2+20160703092728",
"typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/15ddcf312782faf9e7fdfe724a3a29382a5825d7/redux/redux.d.ts"
}
}

View File

@ -0,0 +1,8 @@
/// <reference path="globals/react-data-grid/index.d.ts" />
/// <reference path="globals/redux-promise-middleware/index.d.ts" />
/// <reference path="globals/redux/index.d.ts" />
/// <reference path="modules/react-redux/index.d.ts" />
/// <reference path="modules/react-router-redux/index.d.ts" />
/// <reference path="modules/react-router/index.d.ts" />
/// <reference path="modules/react-select/index.d.ts" />
/// <reference path="modules/redux-thunk/index.d.ts" />

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,214 @@
// Type definitions for mocha 2.2.5
// Project: http://mochajs.org/
// Definitions by: Kazi Manzur Rashid <https://github.com/kazimanzurrashid/>, otiai10 <https://github.com/otiai10>, jt000 <https://github.com/jt000>, Vadim Macagon <https://github.com/enlight>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
interface MochaSetupOptions {
//milliseconds to wait before considering a test slow
slow?: number;
// timeout in milliseconds
timeout?: number;
// ui name "bdd", "tdd", "exports" etc
ui?: string;
//array of accepted globals
globals?: any[];
// reporter instance (function or string), defaults to `mocha.reporters.Spec`
reporter?: any;
// bail on the first test failure
bail?: boolean;
// ignore global leaks
ignoreLeaks?: boolean;
// grep string or regexp to filter tests with
grep?: any;
}
interface MochaDone {
(error?: Error): void;
}
declare var mocha: Mocha;
declare var describe: Mocha.IContextDefinition;
declare var xdescribe: Mocha.IContextDefinition;
// alias for `describe`
declare var context: Mocha.IContextDefinition;
// alias for `describe`
declare var suite: Mocha.IContextDefinition;
declare var it: Mocha.ITestDefinition;
declare var xit: Mocha.ITestDefinition;
// alias for `it`
declare var test: Mocha.ITestDefinition;
declare function before(action: () => void): void;
declare function before(action: (done: MochaDone) => void): void;
declare function setup(action: () => void): void;
declare function setup(action: (done: MochaDone) => void): void;
declare function after(action: () => void): void;
declare function after(action: (done: MochaDone) => void): void;
declare function teardown(action: () => void): void;
declare function teardown(action: (done: MochaDone) => void): void;
declare function beforeEach(action: () => void): void;
declare function beforeEach(action: (done: MochaDone) => void): void;
declare function suiteSetup(action: () => void): void;
declare function suiteSetup(action: (done: MochaDone) => void): void;
declare function afterEach(action: () => void): void;
declare function afterEach(action: (done: MochaDone) => void): void;
declare function suiteTeardown(action: () => void): void;
declare function suiteTeardown(action: (done: MochaDone) => void): void;
declare class Mocha {
constructor(options?: {
grep?: RegExp;
ui?: string;
reporter?: string;
timeout?: number;
bail?: boolean;
});
/** Setup mocha with the given options. */
setup(options: MochaSetupOptions): Mocha;
bail(value?: boolean): Mocha;
addFile(file: string): Mocha;
/** Sets reporter by name, defaults to "spec". */
reporter(name: string): Mocha;
/** Sets reporter constructor, defaults to mocha.reporters.Spec. */
reporter(reporter: (runner: Mocha.IRunner, options: any) => any): Mocha;
ui(value: string): Mocha;
grep(value: string): Mocha;
grep(value: RegExp): Mocha;
invert(): Mocha;
ignoreLeaks(value: boolean): Mocha;
checkLeaks(): Mocha;
/** Enables growl support. */
growl(): Mocha;
globals(value: string): Mocha;
globals(values: string[]): Mocha;
useColors(value: boolean): Mocha;
useInlineDiffs(value: boolean): Mocha;
timeout(value: number): Mocha;
slow(value: number): Mocha;
enableTimeouts(value: boolean): Mocha;
asyncOnly(value: boolean): Mocha;
noHighlighting(value: boolean): Mocha;
/** Runs tests and invokes `onComplete()` when finished. */
run(onComplete?: (failures: number) => void): Mocha.IRunner;
}
// merge the Mocha class declaration with a module
declare module Mocha {
/** Partial interface for Mocha's `Runnable` class. */
interface IRunnable {
title: string;
fn: Function;
async: boolean;
sync: boolean;
timedOut: boolean;
}
/** Partial interface for Mocha's `Suite` class. */
interface ISuite {
parent: ISuite;
title: string;
fullTitle(): string;
}
/** Partial interface for Mocha's `Test` class. */
interface ITest extends IRunnable {
parent: ISuite;
pending: boolean;
fullTitle(): string;
}
/** Partial interface for Mocha's `Runner` class. */
interface IRunner {}
interface IContextDefinition {
(description: string, spec: () => void): ISuite;
only(description: string, spec: () => void): ISuite;
skip(description: string, spec: () => void): void;
timeout(ms: number): void;
}
interface ITestDefinition {
(expectation: string, assertion?: () => void): ITest;
(expectation: string, assertion?: (done: MochaDone) => void): ITest;
only(expectation: string, assertion?: () => void): ITest;
only(expectation: string, assertion?: (done: MochaDone) => void): ITest;
skip(expectation: string, assertion?: () => void): void;
skip(expectation: string, assertion?: (done: MochaDone) => void): void;
timeout(ms: number): void;
}
export module reporters {
export class Base {
stats: {
suites: number;
tests: number;
passes: number;
pending: number;
failures: number;
};
constructor(runner: IRunner);
}
export class Doc extends Base {}
export class Dot extends Base {}
export class HTML extends Base {}
export class HTMLCov extends Base {}
export class JSON extends Base {}
export class JSONCov extends Base {}
export class JSONStream extends Base {}
export class Landing extends Base {}
export class List extends Base {}
export class Markdown extends Base {}
export class Min extends Base {}
export class Nyan extends Base {}
export class Progress extends Base {
/**
* @param options.open String used to indicate the start of the progress bar.
* @param options.complete String used to indicate a complete test on the progress bar.
* @param options.incomplete String used to indicate an incomplete test on the progress bar.
* @param options.close String used to indicate the end of the progress bar.
*/
constructor(runner: IRunner, options?: {
open?: string;
complete?: string;
incomplete?: string;
close?: string;
});
}
export class Spec extends Base {}
export class TAP extends Base {}
export class XUnit extends Base {
constructor(runner: IRunner, options?: any);
}
}
}
declare module "mocha" {
export = Mocha;
}

View File

@ -0,0 +1,107 @@
// Generated by typings
// Source: https://raw.githubusercontent.com/andrew-w-ross/typings-redux/master/redux.d.ts
declare module '~react-redux~redux' {
module redux {
//This should be extended
export interface IAction {
type: string | number;
}
export interface IActionGeneric<TPayload> extends IAction {
payload?: TPayload;
error?: Error;
meta?: any;
}
export interface IReducer<TState> {
(state: TState, action: IAction): TState;
}
export interface IReducerMap {
[key: string]: IReducerMap | IReducer<any>
}
export interface IDispatch {
(action: IAction): IAction;
}
export interface IMiddlewareStore<TState> {
getState(): TState;
dispatch: IDispatch;
}
export interface IStore<TState> extends IMiddlewareStore<TState> {
subscribe(listener: (state: TState) => any): () => void;
replaceReducer(nextReducer: IReducer<TState>): void;
}
export interface IMiddleware<State> {
(middlewareStore: IMiddlewareStore<State>): (next: IDispatch) => IDispatch;
}
export interface ICreateStoreGeneric<TState> {
(reducer: IReducer<TState>, initialState?: TState): IStore<TState>;
}
export interface IStoreEnhancerGeneric<TState> {
(createStore: ICreateStoreGeneric<TState>): ICreateStoreGeneric<TState>;
}
export function createStore<TState>(reducer: IReducer<TState>, initialState?: TState): IStore<TState>;
export function combineReducers(reducers: IReducerMap): IReducer<any>;
export function combineReducers<TState>(reducers: IReducerMap): IReducer<TState>;
export function applyMiddleware<TState>(...middlewares: IMiddleware<TState>[]): IStoreEnhancerGeneric<TState>;
export function bindActionCreators<TActionCreator extends Function | { [key: string]: Function }>(actionCreators: TActionCreator, dispatch: IDispatch): TActionCreator;
export function compose<TArg>(...functions: { (arg: TArg): TArg }[]): (arg: TArg) => TArg;
export function compose(...functions: { (arg: any): any }[]): (arg: any) => any;
}
export = redux;
}
// Generated by typings
// Source: https://raw.githubusercontent.com/andrew-w-ross/typings-react-redux/21202533f75a73d4fa4c50e0357aaf23739fcabb/react-redux.d.ts
declare module 'react-redux' {
import * as React from 'react';
import {IStore, IDispatch} from '~react-redux~redux';
module ReactRedux {
export class Provider extends React.Component<{store:IStore<any>},{}>{}
export interface IMapStateToProps {
(state: any, ownProps?: any): Object;
}
export interface IMapDispatchToProps {
(dispatch: IDispatch, ownProps?: any): Object;
}
export interface IConnectOptions {
pure?: boolean;
withRef?: boolean;
}
type ComponentConstructor<P> = React.ComponentClass<P> | React.StatelessComponent<P>
function wrapWithConnect<T extends ComponentConstructor<any>>(
component: T
): T
export function connect(
mapStateToProps?: IMapStateToProps,
mapDispatchToProps?: IMapDispatchToProps | Object,
mergeProps?: (stateProps: Object, dispatchProps: Object, ownProps: Object) => Object,
options?: IConnectOptions
): typeof wrapWithConnect;
}
export = ReactRedux;
}

View File

@ -0,0 +1,19 @@
{
"resolution": "main",
"tree": {
"src": "https://raw.githubusercontent.com/andrew-w-ross/typings-react-redux/21202533f75a73d4fa4c50e0357aaf23739fcabb/typings.json",
"raw": "registry:npm/react-redux#4.4.0+20160614222153",
"main": "react-redux.d.ts",
"dependencies": {
"redux": {
"src": "https://raw.githubusercontent.com/andrew-w-ross/typings-redux/master/typings.json",
"raw": "github:andrew-w-ross/typings-redux",
"main": "redux.d.ts",
"name": "redux",
"type": "typings"
}
},
"name": "react-redux",
"type": "typings"
}
}

View File

@ -0,0 +1,334 @@
// Generated by typings
// Source: https://raw.githubusercontent.com/andrew-w-ross/typings-history/master/history.d.ts
declare module '~react-router-redux~history' {
module otherHistory {
type Action = 'PUSH' | 'REPLACE' | 'POP';
type BeforeUnloadHook = () => string | void;
type CreateLocation = (path: string, locationObj: Object) => Location;
type CreateHistoryDefault = CreateHistory<HistoryOptions, History>;
type CreateHistory<TOptions extends HistoryOptions, TResult extends History> = (options?: TOptions) => TResult;
type Hash = string;
interface History {
listenBefore(hook: TransitionHook): Function;
listen(listener: LocationListener): Function;
transitionTo(location: Location): void;
push(location: LocationDescriptor): void;
replace(location: LocationDescriptor): void;
go(n: number): void;
goBack(): void;
goForward(): void;
createKey(): LocationKey;
createLocation: CreateLocation;
createPath(location: LocationDescriptor): Path;
createHref(location: LocationDescriptor): Href;
}
interface HistoryOptions {
getUserConfirmation?(message: string, callback: (confirmed: boolean) => void): void;
queryKey?: boolean | string;
}
interface BeforeUnload {
listenBeforeUnload?(callBack: () => string | boolean | void): void;
}
interface QueryOptions {
parseQueryString?(queryString: string): any;
stringifyQuery?(query: Object): string;
}
interface BasenameOptions {
basename?: string;
}
type Href = string;
interface Location {
pathname?: Pathname;
search?: Search;
query?: Query;
state?: LocationState;
action?: Action;
key?: LocationKey;
}
interface LocationDescriptorObject {
pathname?: Pathname;
search?: Search;
query?: Query;
state?: LocationState;
}
type LocationDescriptor = LocationDescriptorObject | Path;
type LocationKey = string;
type LocationListener = (location: Location) => void;
type LocationState = Object;
type Path = string;
type Pathname = string;
type Query = Object;
type Search = string;
type TransitionHook = (location: Location, callback?: Function) => any;
export const createLocation: CreateLocation;
export const createHistory: CreateHistoryDefault;
export const createHashHistory: CreateHistoryDefault;
export const createMemoryHistory: CreateHistoryDefault;
export function useBeforeUnload<TArguments, TResult extends History>(createHistory: CreateHistory<TArguments, TResult>): CreateHistory<TArguments, TResult & BeforeUnload>;
export function useQueries<TArguments, TResult extends History>(createHistory: CreateHistory<TArguments, TResult>): CreateHistory<TArguments & QueryOptions, TResult>;
export function useBasename<TArguments, TResult extends History>(createHistory: CreateHistory<TArguments, TResult>): CreateHistory<TArguments & BasenameOptions, TResult>;
}
export = otherHistory;
}
// Generated by typings
// Source: https://raw.githubusercontent.com/andrew-w-ross/typings-redux/master/redux.d.ts
declare module '~react-router-redux~redux' {
module redux {
//This should be extended
export interface IAction {
type: string | number;
}
export interface IActionGeneric<TPayload> extends IAction {
payload?: TPayload;
error?: Error;
meta?: any;
}
export interface IReducer<TState> {
(state: TState, action: IAction): TState;
}
export interface IReducerMap {
[key: string]: IReducerMap | IReducer<any>
}
export interface IDispatch {
(action: IAction): IAction;
}
export interface IMiddlewareStore<TState> {
getState(): TState;
dispatch: IDispatch;
}
export interface IStore<TState> extends IMiddlewareStore<TState> {
subscribe(listener: (state: TState) => any): () => void;
replaceReducer(nextReducer: IReducer<TState>): void;
}
export interface IMiddleware<State> {
(middlewareStore: IMiddlewareStore<State>): (next: IDispatch) => IDispatch;
}
export interface ICreateStoreGeneric<TState> {
(reducer: IReducer<TState>, initialState?: TState): IStore<TState>;
}
export interface IStoreEnhancerGeneric<TState> {
(createStore: ICreateStoreGeneric<TState>): ICreateStoreGeneric<TState>;
}
export function createStore<TState>(reducer: IReducer<TState>, initialState?: TState): IStore<TState>;
export function combineReducers(reducers: IReducerMap): IReducer<any>;
export function combineReducers<TState>(reducers: IReducerMap): IReducer<TState>;
export function applyMiddleware<TState>(...middlewares: IMiddleware<TState>[]): IStoreEnhancerGeneric<TState>;
export function bindActionCreators<TActionCreator extends Function | { [key: string]: Function }>(actionCreators: TActionCreator, dispatch: IDispatch): TActionCreator;
export function compose<TArg>(...functions: { (arg: TArg): TArg }[]): (arg: TArg) => TArg;
export function compose(...functions: { (arg: any): any }[]): (arg: any) => any;
}
export = redux;
}
// Generated by typings
// Source: https://raw.githubusercontent.com/andrew-w-ross/typings-react-router-redux/caf792cb79efbf54ce328366809dc52821192a73/react-router-redux.d.ts
declare module 'react-router-redux' {
import {History, Location, LocationDescriptor} from '~react-router-redux~history';
import {IMiddleware, IStore, IAction, IReducer} from '~react-router-redux~redux';
module reactRouterRedux {
/**
* An action type that you can listen for in your reducers to be notified of route updates. Fires after any changes to history.
*/
export const LOCATION_CHANGE: string;
/**
* Router action
*/
export interface RouterAction extends IAction {
/**
* New location
*/
payload: Location;
}
/**
* Redux router internal state. Useful if you're overriding own reducer
*/
export interface IRouterState {
locationBeforeTransitions: Location;
}
/**
* History syncing options
*/
interface ISyncHistoryOptions<TState> {
/**
* When false, the URL will not be kept in sync during time travel.
* This is useful when using persistState from Redux DevTools and not wanting to maintain the URL state when restoring state.
* @default true
*/
adjustUrlOnReplay?: boolean;
/**
* A selector function to obtain the history state from your store.
* Useful when not using the provided routerReducer to store history state. Allows you to use wrappers, such as Immutable.js.
* @default state => state.routing
*/
selectLocationState?: (state: TState) => IRouterState;
}
/**
* @export
* @template TState
* @param {History} history History singleton from react-router
* @param {IStore<TState>} store Application store
* @param {ISyncHistoryOptions<TState>} options Syncing options
*/
export function syncHistoryWithStore<TState>(history: History, store: IStore<TState>, options?: ISyncHistoryOptions<TState>): History;
/**
* A reducer function that keeps track of the router state. You must add this reducer to your app reducers when creating the store.
* It will return a location property in state. If you use combineReducers, it will be nested under wherever property you add it to (state.routing in the example above).
* Warning: It is a bad pattern to use react-redux's connect decorator to map the state from this reducer to props on your Route components. This can lead to infinite loops and performance problems. react-router already provides this for you via this.props.location.
*
* @export
* @param {Location} state (description)
* @param {IAction} action (description)
* @returns {Location} (description)
*/
export function routerReducer(state: IRouterState, action: RouterAction): IRouterState;
/**
* A middleware you can apply to your Redux store to capture dispatched actions created by the action creators.
* It will redirect those actions to the provided history instance.
*/
export function routerMiddleware<TState>(history: History): IMiddleware<TState>;
// Action types interfaces. These are deprecated
/**
* @deprecated
*/
interface IRoutePushAction {
(nextLocation: LocationDescriptor): IAction;
}
/**
* @deprecated
*/
interface IRouteReplaceAction {
(nextLocation: LocationDescriptor): IAction;
}
/**
* @deprecated
*/
interface IRouteGoAction {
(n: number): IAction;
}
/**
* @deprecated
*/
interface IRouteGoForwardAction {
(): IAction;
}
/**
* @deprecated
*/
interface IRouteGoBackAction {
(): IAction;
}
/**
* @deprecated
*/
export interface IRouteActions {
push: IRoutePushAction;
replace: IRouteReplaceAction;
go: IRouteGoAction;
goForward: IRouteGoForwardAction;
goBack: IRouteGoBackAction;
}
/**
* An object that contains all the actions creators you can use to manipulate history:
* @deprecated
* @export
*/
export const routerActions: IRouteActions;
/**
* This action type will be dispatched by the history actions below.
* If you're writing a middleware to watch for navigation events, be sure to
* look for actions of this type.
* @export
*/
export const CALL_HISTORY_METHOD: string;
// separate actions
/**
* @deprecated
*/
export const push: IRoutePushAction;
/**
* @deprecated
*/
export const replace: IRouteReplaceAction;
/**
* @deprecated
*/
export const go: IRouteGoAction;
/**
* @deprecated
*/
export const goForward: IRouteGoForwardAction;
/**
* @deprecated
*/
export const goBack: IRouteGoBackAction;
}
export = reactRouterRedux;
}

View File

@ -0,0 +1,26 @@
{
"resolution": "main",
"tree": {
"src": "https://raw.githubusercontent.com/andrew-w-ross/typings-react-router-redux/caf792cb79efbf54ce328366809dc52821192a73/typings.json",
"raw": "registry:npm/react-router-redux#4.0.0+20160602212457",
"main": "react-router-redux.d.ts",
"dependencies": {
"history": {
"src": "https://raw.githubusercontent.com/andrew-w-ross/typings-history/master/typings.json",
"raw": "github:andrew-w-ross/typings-history",
"main": "history.d.ts",
"name": "history",
"type": "typings"
},
"redux": {
"src": "https://raw.githubusercontent.com/andrew-w-ross/typings-redux/master/typings.json",
"raw": "github:andrew-w-ross/typings-redux",
"main": "redux.d.ts",
"name": "redux",
"type": "typings"
}
},
"name": "react-router-redux",
"type": "typings"
}
}

Some files were not shown because too many files have changed in this diff Show More