commit
1ba602962a
|
@ -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
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Build generated files
|
||||||
|
dist
|
||||||
|
lib
|
||||||
|
solution
|
||||||
|
temp
|
||||||
|
*.sppkg
|
||||||
|
|
||||||
|
# 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
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"@microsoft/generator-sharepoint": {
|
||||||
|
"isCreatingSolution": true,
|
||||||
|
"environment": "spo",
|
||||||
|
"version": "1.8.2",
|
||||||
|
"libraryName": "react-mobx-multiple-stores",
|
||||||
|
"libraryId": "94924d67-e7b2-415f-9bd3-3f69b18b37c8",
|
||||||
|
"packageManager": "npm",
|
||||||
|
"isDomainIsolated": false,
|
||||||
|
"componentType": "webpart"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Webpart with React and Mobx using multiple stores
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
A sample webpart that uses the [Mobx](https://mobx.js.org/) library with multiple stores to keep track of the applications state.
|
||||||
|
|
||||||
|
<img src="assets/demo.gif"/>
|
||||||
|
|
||||||
|
## Used SharePoint Framework Version
|
||||||
|
![drop](https://img.shields.io/badge/version-1.8.2-green.svg)
|
||||||
|
|
||||||
|
## Applies to
|
||||||
|
|
||||||
|
* [SharePoint Framework](https://dev.office.com/sharepoint)
|
||||||
|
* [SharePoint Framework Webpart Samples](https://github.com/SharePoint/sp-dev-fx-webparts)
|
||||||
|
* [Office 365 developer tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant)
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Solution|Author(s)
|
||||||
|
--------|---------
|
||||||
|
react-mobx-multiple-stores | Kemal Sinanagic / [@kemicza](http://twitter.com/kemicza) / kemicza@gmail.com
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
Version|Date|Comments
|
||||||
|
-------|----|--------
|
||||||
|
1.0|May 24, 2019|Initial release
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ git clone https://github.com/SharePoint/sp-dev-fx-webparts
|
||||||
|
$ cd sp-dev-fx-webparts/samples/react-mobx-multiple-stores
|
||||||
|
$ npm install
|
||||||
|
$ gulp serve
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Enforces that the state always needs be updated in **actions**, using the <em>always</em> flag for <em>enforceActions</em>.
|
||||||
|
* Demonstrates the **toJS** method to convert an observable array to a javascript structure. This is used to render the items in a DetailsList.
|
||||||
|
* Out-of-the-box MobX **decorators** to keep the code clean.
|
||||||
|
* **Asynchronous** actions
|
||||||
|
* MobX **computed** values
|
||||||
|
* **Typescript** version 3.3.4 using <em>@microsoft/rush-stack-compiler-3.3</em> for compatibility with the latest MobX version and typings
|
||||||
|
|
||||||
|
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-mobx-multiple-stores" />
|
Binary file not shown.
After Width: | Height: | Size: 449 KiB |
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||||
|
"version": "2.0",
|
||||||
|
"bundles": {
|
||||||
|
"mobx-tutorial-web-part": {
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"entrypoint": "./lib/webparts/mobxTutorial/MobxTutorialWebPart.js",
|
||||||
|
"manifest": "./src/webparts/mobxTutorial/MobxTutorialWebPart.manifest.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"externals": {},
|
||||||
|
"localizedResources": {
|
||||||
|
"MobxTutorialWebPartStrings": "lib/webparts/mobxTutorial/loc/{locale}.js"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||||
|
"deployCdnPath": "temp/deploy"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||||
|
"workingDir": "./temp/deploy/",
|
||||||
|
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||||
|
"container": "react-mobx-multiple-stores",
|
||||||
|
"accessKey": "<!-- ACCESS KEY -->"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||||
|
"solution": {
|
||||||
|
"name": "react-mobx-multiple-stores-client-side-solution",
|
||||||
|
"id": "94924d67-e7b2-415f-9bd3-3f69b18b37c8",
|
||||||
|
"version": "1.0.0.0",
|
||||||
|
"includeClientSideAssets": true,
|
||||||
|
"isDomainIsolated": false
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"zippedPackage": "solution/react-mobx-multiple-stores.sppkg"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
|
||||||
|
"port": 4321,
|
||||||
|
"https": true,
|
||||||
|
"initialPage": "https://localhost:5432/workbench",
|
||||||
|
"api": {
|
||||||
|
"port": 5432,
|
||||||
|
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||||
|
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const gulp = require('gulp');
|
||||||
|
const build = require('@microsoft/sp-build-web');
|
||||||
|
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||||
|
|
||||||
|
build.initialize(gulp);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"name": "react-mobx-multiple-stores",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "gulp bundle",
|
||||||
|
"clean": "gulp clean",
|
||||||
|
"test": "gulp test"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@microsoft/sp-core-library": "1.8.2",
|
||||||
|
"@microsoft/sp-lodash-subset": "1.8.2",
|
||||||
|
"@microsoft/sp-office-ui-fabric-core": "1.8.2",
|
||||||
|
"@microsoft/sp-property-pane": "1.8.2",
|
||||||
|
"@microsoft/sp-webpart-base": "1.8.2",
|
||||||
|
"@types/es6-promise": "0.0.33",
|
||||||
|
"@types/react": "16.7.22",
|
||||||
|
"@types/react-dom": "16.8.0",
|
||||||
|
"@types/webpack-env": "1.13.1",
|
||||||
|
"mobx": "5.9.4",
|
||||||
|
"mobx-react": "5.4.4",
|
||||||
|
"office-ui-fabric-react": "6.143.0",
|
||||||
|
"react": "16.7.0",
|
||||||
|
"react-dom": "16.7.0"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"@types/react": "16.7.22"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@microsoft/rush-stack-compiler-3.3": "0.2.13",
|
||||||
|
"@microsoft/sp-build-web": "1.8.2",
|
||||||
|
"@microsoft/sp-module-interfaces": "1.8.2",
|
||||||
|
"@microsoft/sp-tslint-rules": "1.8.2",
|
||||||
|
"@microsoft/sp-webpart-workbench": "1.8.2",
|
||||||
|
"@types/chai": "3.4.34",
|
||||||
|
"@types/mocha": "2.2.38",
|
||||||
|
"ajv": "~5.2.2",
|
||||||
|
"gulp": "~3.9.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { action, computed, observable, runInAction } from "mobx";
|
||||||
|
import { RootStore } from "./RootStore";
|
||||||
|
|
||||||
|
export enum ApplicationStatus {
|
||||||
|
CreateList = "Create List",
|
||||||
|
CreateItems = "Create Items",
|
||||||
|
Completed = "Completed"
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFakeItem {
|
||||||
|
title: string;
|
||||||
|
important: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AppStore {
|
||||||
|
|
||||||
|
@observable public isLoadingConfiguration: boolean;
|
||||||
|
@observable public isLoadingOtherStuff: boolean;
|
||||||
|
@observable public status: ApplicationStatus;
|
||||||
|
@observable public listTitle: string;
|
||||||
|
@observable public items: IFakeItem[];
|
||||||
|
|
||||||
|
constructor(private rootStore: RootStore) {
|
||||||
|
this.setInitialState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
private setInitialState(): void {
|
||||||
|
this.status = ApplicationStatus.CreateList;
|
||||||
|
this.listTitle = null;
|
||||||
|
this.items = [];
|
||||||
|
this.isLoadingConfiguration = true;
|
||||||
|
this.isLoadingOtherStuff = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed
|
||||||
|
public get appStatus(): string {
|
||||||
|
let result: string = `The current status is '${this.status}'. `;
|
||||||
|
result += this.status === ApplicationStatus.CreateItems || this.items.length > 0 ? `List '${this.listTitle}' successfully created. ` : "";
|
||||||
|
result += this.status === ApplicationStatus.Completed ? `In total there were ${this.items.length} items added. ` : "";
|
||||||
|
return result + `${this.rootStore.configStore.allowImportantItems ? "" : "Adding important items is currently not allowed."}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed
|
||||||
|
public get importantItems(): IFakeItem[] {
|
||||||
|
return this.items.filter(x => x.important);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed
|
||||||
|
public get isInitializing(): boolean {
|
||||||
|
return this.isLoadingConfiguration || this.isLoadingOtherStuff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
public async createList(listTitle: string): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
// Mock creating list
|
||||||
|
setTimeout(() => {
|
||||||
|
// Make sure we change our state in an action.
|
||||||
|
runInAction(() => {
|
||||||
|
this.status = ApplicationStatus.CreateItems;
|
||||||
|
this.listTitle = listTitle;
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
public async addListItem(item: IFakeItem): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
// Mock adding item a new item
|
||||||
|
setTimeout(() => {
|
||||||
|
// Make sure we change our state in an action.
|
||||||
|
runInAction(() => {
|
||||||
|
this.items.push(item);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
public confirmItems(): void {
|
||||||
|
this.status = ApplicationStatus.Completed;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { action, observable } from "mobx";
|
||||||
|
import { RootStore } from "./RootStore";
|
||||||
|
|
||||||
|
export class ConfigStore {
|
||||||
|
|
||||||
|
@observable public isLoading: boolean;
|
||||||
|
@observable public allowImportantItems: boolean;
|
||||||
|
@observable public applicationTitle: string;
|
||||||
|
|
||||||
|
constructor(private rootStore: RootStore) {
|
||||||
|
this.setInitialState();
|
||||||
|
|
||||||
|
// Mock REST call for fetching configuration data
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loadConfigration();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
public setInitialState(): void {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.allowImportantItems = true;
|
||||||
|
this.applicationTitle = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
private loadConfigration() {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.rootStore.appStore.isLoadingConfiguration = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
public setApplicationTitle(title: string): void {
|
||||||
|
this.applicationTitle = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
public setAllowImportantItems(allow: boolean): void {
|
||||||
|
this.allowImportantItems = allow;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { AppStore } from "./AppStore";
|
||||||
|
import { ConfigStore } from "./ConfigStore";
|
||||||
|
|
||||||
|
export enum Stores {
|
||||||
|
AppStore = "appStore",
|
||||||
|
ConfigurationStore = "configStore"
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RootStore {
|
||||||
|
public readonly appStore: AppStore;
|
||||||
|
public readonly configStore: ConfigStore;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.configStore = new ConfigStore(this);
|
||||||
|
this.appStore = new AppStore(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||||
|
"id": "c2ebe6a8-5c9f-4199-bff0-4e0222926d4f",
|
||||||
|
"alias": "MobxTutorialWebPart",
|
||||||
|
"componentType": "WebPart",
|
||||||
|
|
||||||
|
// The "*" signifies that the version should be taken from the package.json
|
||||||
|
"version": "*",
|
||||||
|
"manifestVersion": 2,
|
||||||
|
|
||||||
|
// If true, the component can only be installed on sites where Custom Script is allowed.
|
||||||
|
// Components that allow authors to embed arbitrary script code should set this to true.
|
||||||
|
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||||
|
"requiresCustomScript": false,
|
||||||
|
"supportedHosts": ["SharePointWebPart"],
|
||||||
|
|
||||||
|
"preconfiguredEntries": [{
|
||||||
|
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||||
|
"group": { "default": "Other" },
|
||||||
|
"title": { "default": "MobxTutorial" },
|
||||||
|
"description": { "default": "An example of a webpart using mobx for managing the application state" },
|
||||||
|
"iconImageUrl": "data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM\/rhtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAnwSURBVFhHzVjbq1xnFV\/7cmbmzJzL5NySKhQMpVh90odSEaWCCkWE1AZRqtLWW0Ef9Kn1glTESqVWBR98EEWUViESY+ODCqK0vliw4EsrCMZYJTbnNsnJmZyZffH3+62195mT+Ad05cxe61vfuvy+9V32t5PUIHsNUwtw8ydfsqt\/fMaSJLUGcd1KJMj+h0dtFYT+fGIVhUQqf4ScwLKCKoFcQYYaej7REo\/oeDivraxKW+h1bfXdD1jvvseodYBbALf5zOOWLw2lVBJGb4KQPFO0oYfQ7SaWQi9w8ole4YDecaAfj5mYesoHNtKZlXVpvU7X1oZLVuxcsu4Hvmz90191gBc+edLKvW1L8o5AKJgCU\/bxM5c6K1RNACrL88Tm5lAryCi92yNjHXICuYKMEOCVorgp9AjCKtcAWZU14uS2sTK0skBfeWBJ\/5gNn3rZUqZN0kzOMEeDSZRP5NCYmeQyi4nSWYnA0goQ9Oj2wUQc9kFmJRM8GF82kNNULYXO88zBlQDXVAhLjeRPkUfUtAS1wSAHFHm4CarhShk2bgmAs3ryQRINBp2NBWNKISLgdAYcjDkQssh4CBBSKnToaFgTR+EbFyiRlYlZOaxr1wqYasIW\/CMOHvKFzPiMAlMNnPGPry5rowkciscp90xydoAuglgJ9AlnEIMoWLRl7X+SMXARobkttAQLnVfRY7qDxxEE6Fg5rkm2E20qKJuNFrunraAc8WgnNJinwo8gEVU7kp0IRh+s6TYIK0pbPrWGYCvQ0MgX645xWLGNlSWBJDgK3EwCB87TgFlIEVshIzD+wpH\/iiubVu1uWrG9aeX2lpVXthwk7WgB0fe5A\/HpQ2ByhfMG+8vRCHFGtppcs3r3v1Zduezg2ItSavdrmyuYSMfMxU\/fjmNmy5Isk3FzLJQ7m3b7+bCcoZfvSSxfWYOEoLDlUZNnSMQqE1gcOypIHDvF9o7d9uzNsbbun7N0ecNBalrhN51Ysrhma99+yStIwE4cKaoCBRd02uvZ1s+\/Zju\/+q5tPe0ne3l1W9zJPcuYGvlSy0qgMl5F6CFTX6EIpPHZr9v133zP9n\/5DUu6\/aPgFAtirEFV8F9RwTqbo8mRKtbjPauxE4rNid3xHF5HAPj3D65aigoqDqz51+\/jFYnK3VhFAivL0gaTkR3\/IQY3OGZbp2BzbBH98OktwJhWCBO8LqaWLKzY+nf+FmvQM4nxqUqgh1VM5hcsGyxb2ldnS221wkvHDQDxTS49dzIrB6B9zMR8r6O2qIf+eWyS7o3gYE8fcalnAAbTlHgTBBmGnPIbiapq\/woqvwsQU59mavlHH1St2tuxTnVgC\/2u3jpNHEEBAg2SgCiLR2\/DQQ6QMp3BnUUl0NseOxG8oWp\/zzYeetJe9+gZm9t4gxUTLGzo+fawqrB0MLTjj5y19QefsmLvCqO5IynyedUggPs6RBujoG1j7QBJAcBZNEgQWZFGJRFeNeY0Hx63xbedsuW777diPHYDUD0dW\/\/OU\/rVi+uwLdwxiJXUZmBQbIZmk6im3BzoYkVJhwBJBMFOsdSriIZXcYZo05m30W9\/oObC209bfVDorcLASTGx5Xd8SH0Hf\/gRzqEeXVryDRugwHUUCSStWEEMgIYgzxzAxA8ZiOuxAXmUMtzd9v78O8mdEyctWxhYURTKu9TvWXbbXeqbvvhrq\/Nug0o6EjcPwfGAbqcXpGnXT80GoG8M2VAD7iw2DB6a5lmCT9pN7eqffqHm8nsesmJ\/bEud1PI775Nu8sJZqzt9uMcaC\/JCoY1p1iuOCiRod3L7nJ1inqqhVCz6RENVPLRs+7P5gQ5x0vCeh20OF81yfNU6d39cOh7GXApthZp5I6HdXg4IjrdgcF5M2uMIpLQOxDGqLxRkbRVnqenH1I1ffA6bdmrdW9+ET4Y1m0xKm3vzu3DMFDb56+9jelkPAnE\/5lAbgHW94u2aLwW\/qrOzBTlbFwTwiVWXowt2uE0ihwTapYOOjc48LtXCez9l6V0fljw+h9fYYNH9CdDziSSiUhUrqAWOdagKCrFXNqgFqED4+UxDIgc5iwaIkkwiSA8fWnvnviV58X2ftcX3f07y+Nkn8Z4dxLoiHSalyK0hcJQacOACza4wP1pBEqooAJJdIDtC0DEAP3TSLLfq4Lrtv3AeN5xbrHvyLTb5y3mchdeRh574wdgvvZ5VT4ELfUwzB+0XV3a57QzAeHtQandW0xNBSezCrwNgWZr6Gse7eufHj4SB2einj+pdq52K46XZqSiME3yYyTFwg4CpctzVkBuQIAfYZg9CQPYrAC3Am5FQlaORZQ5OGoAtNl+x\/3z+rfYKfuNLF1FeXA7irEvIaRrkbnzG0UJwHAAH0oCL0XheoaEQbw+q4tipcGxU+yOrrrHfY+UA56NnXEwLIqa9eZtevmjFqxeszOeVSBVUVcDDnsRY1bWRjiQlJzjyGJCu\/EwPCoDRCuJa4c6163s2PP0FW3ngCVt72C+sJOYiQL43CU67kPa4kVuOOyVGXxCgqgLjNqGjXP7EY7b80W\/a4r1fRAF4kUCV47XHJcFQSgDShfXfn7kD3xqbqNqcdyIdE5Y7l+zkOTdsiK2Lp\/ChP9wIcBw9Ozgoryarys8A\/ryCuOhuv2qvP3M0Fumf9yaWDVfbGKQK7\/J0Yc1u\/f4\/GoBvxE0Z3yRJjoSC7\/9gzw+bCp9uc1nswTS3ZGn1CDheELjKHJx780gd4FWIWyGC0Ba3cnx81fzfCP\/T\/KVLKxiD5yRpTZZTAFwFwAsxxSAdAmzJzjcJKVtat976Lbjin8BITwic5gvgBITg8BNgJuYiZcUKB+22ftal8M2G+FQ4hk8GVI2xmuOHVefPZbjxAWoBOnlixaUjmhnXFd10TkFEn84tCiCBox+aWm9y9jgFQLI9+5aQDbjHYjAfoAicS1d6xgQJYMwqGu4Y5jrnSM05xd3lpm5xeOlswKFmTK6rFTYKr\/nou\/kgpgM7ZmOw4s6puel\/FiIzGAQE4DnHlvszkVfKX3HgSgANGAfFr0GCaxJQyQoS3P876+jbxIAA8+CMTXtwkgNs7CRjregMhCgFOdxYGUyTArQHsMxBWGMtOLeVFv3N\/90IHJO2Z91RLj37IylbpADITACJFmdVlULbf4dnnQdgQDUjxMzUoEoaiNQMiHWoXSvYaAYY5hM42qANPntxVQXjv80EcIDvh3J0GYtmimNgqvsdP57rcgJdIV7hfodPN93zbEqd91fgNoWMT89WD1\/qaVseuE8N39kY\/O8NPHCpgF6+HqMCL3bHtvjOjwigzkEKu09\/xfaf\/xk0fI2xChy5LNqKOOejYXjiT7Vt9FJFgxKKNd\/8Zzvb6o8+CXjA5vCaVQrc+seekFUL8LVJZv8DKLJX7WNRDjkAAAAASUVORK5CYII=",
|
||||||
|
"properties": {
|
||||||
|
"ApplicationTitle": "Mobx Tutorial Title (change in webpart properties) 😎",
|
||||||
|
"AllowImportantItems": true
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
import { Version } from '@microsoft/sp-core-library';
|
||||||
|
import { IPropertyPaneConfiguration, PropertyPaneTextField } from '@microsoft/sp-property-pane';
|
||||||
|
import { BaseClientSideWebPart, PropertyPaneCheckbox } from '@microsoft/sp-webpart-base';
|
||||||
|
import { configure } from "mobx";
|
||||||
|
import * as strings from 'MobxTutorialWebPartStrings';
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDom from 'react-dom';
|
||||||
|
import { RootStore } from '../../stores/RootStore';
|
||||||
|
import MobxTutorialProvider from './components/MobxTutorialProvider';
|
||||||
|
|
||||||
|
// State modification should always happen through actions
|
||||||
|
configure({ enforceActions: "always" });
|
||||||
|
|
||||||
|
export interface IMobxTutorialWebPartProps {
|
||||||
|
ApplicationTitle: string;
|
||||||
|
AllowImportantItems: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MobxTutorialWebPart extends BaseClientSideWebPart<IMobxTutorialWebPartProps> {
|
||||||
|
private readonly dependencies = { rootStore: new RootStore() };
|
||||||
|
|
||||||
|
protected onInit() {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
const { configStore } = this.dependencies.rootStore;
|
||||||
|
configStore.setAllowImportantItems(this.properties.AllowImportantItems);
|
||||||
|
configStore.setApplicationTitle(this.properties.ApplicationTitle);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
public render(): void {
|
||||||
|
const element: React.ReactElement<{}> = React.createElement(
|
||||||
|
MobxTutorialProvider,
|
||||||
|
{
|
||||||
|
stores: { ...this.dependencies.rootStore }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactDom.render(element, this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
|
||||||
|
const { configStore } = this.dependencies.rootStore;
|
||||||
|
|
||||||
|
if (propertyPath === "ApplicationTitle") {
|
||||||
|
configStore.setApplicationTitle(newValue);
|
||||||
|
}
|
||||||
|
else if (propertyPath === "AllowImportantItems") {
|
||||||
|
configStore.setAllowImportantItems(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onDispose(): void {
|
||||||
|
ReactDom.unmountComponentAtNode(this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get dataVersion(): Version {
|
||||||
|
return Version.parse('1.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||||
|
return {
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
header: {
|
||||||
|
description: strings.PropertyPaneDescription
|
||||||
|
},
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
groupName: strings.BasicGroupName,
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneTextField('ApplicationTitle', {
|
||||||
|
label: strings.AppTitleFieldLabel,
|
||||||
|
}),
|
||||||
|
PropertyPaneCheckbox('AllowImportantItems', {
|
||||||
|
text: strings.AllowImportantItemsLabel
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { toJS } from 'mobx';
|
||||||
|
import { DetailsList, DetailsListLayoutMode, IColumn } from 'office-ui-fabric-react/lib/DetailsList';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { IFakeItem } from '../../../stores/AppStore';
|
||||||
|
|
||||||
|
type DetailedFakeItemViewerProps = {
|
||||||
|
items: IFakeItem[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export class DetailedFakeItemViewer extends React.Component<DetailedFakeItemViewerProps, {}> {
|
||||||
|
private _columns: IColumn[];
|
||||||
|
public state = {
|
||||||
|
items: []
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props: DetailedFakeItemViewerProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._columns = [
|
||||||
|
{ key: 'Title', name: 'Title', fieldName: 'title', minWidth: 100, maxWidth: 200, isResizable: true },
|
||||||
|
{ key: 'Important', name: 'Important', fieldName: 'important', minWidth: 100, maxWidth: 150, isResizable: true, onRender: (item: IFakeItem) => { return item.important ? "Yes" : "No"; } }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): React.ReactElement<DetailedFakeItemViewerProps> {
|
||||||
|
const { items } = this.props;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<DetailsList
|
||||||
|
compact={true}
|
||||||
|
items={toJS<IFakeItem[]>(items)}
|
||||||
|
columns={this._columns}
|
||||||
|
setKey="set"
|
||||||
|
layoutMode={DetailsListLayoutMode.fixedColumns}
|
||||||
|
selectionPreservedOnEmptyClick={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { inject, observer } from "mobx-react";
|
||||||
|
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { AppStore } from "../../../stores/AppStore";
|
||||||
|
import { Stores } from '../../../stores/RootStore';
|
||||||
|
import { DetailedFakeItemViewer } from "./DetailedFakeItemViewer";
|
||||||
|
import { FakeItemCreator } from "./FakeItemCreator";
|
||||||
|
import styles from "./MobxTutorial.module.scss";
|
||||||
|
|
||||||
|
type FakeItemContainerStoreProps = {
|
||||||
|
appStore: AppStore;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FakeItemContainerOwnProps = {};
|
||||||
|
type FakeItemContainerProps = Partial<FakeItemContainerStoreProps> & FakeItemContainerOwnProps;
|
||||||
|
|
||||||
|
@inject(Stores.AppStore)
|
||||||
|
@observer
|
||||||
|
export class FakeItemContainer extends React.Component<FakeItemContainerProps, {}> {
|
||||||
|
public render(): React.ReactElement<FakeItemContainerProps> {
|
||||||
|
const { appStore } = this.props;
|
||||||
|
return (
|
||||||
|
<div className={styles.grid}>
|
||||||
|
|
||||||
|
<div className={styles.row}>
|
||||||
|
<div className={`${styles.columnCreateItems}`}>
|
||||||
|
<FakeItemCreator></FakeItemCreator>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`${styles.columnItemDetails}`}>
|
||||||
|
<p>Count items: {appStore.items.length} | Count important items: {appStore.importantItems.length}</p>
|
||||||
|
<DetailedFakeItemViewer items={appStore.items}></DetailedFakeItemViewer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.row}>
|
||||||
|
<PrimaryButton
|
||||||
|
text="Confirm items"
|
||||||
|
onClick={() => { appStore.confirmItems(); }}
|
||||||
|
className={styles.inputElement}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
import { inject, observer } from "mobx-react";
|
||||||
|
import { DefaultButton } from 'office-ui-fabric-react/lib/Button';
|
||||||
|
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
|
||||||
|
import { TextField } from "office-ui-fabric-react/lib/TextField";
|
||||||
|
import * as React from 'react';
|
||||||
|
import { AppStore } from "../../../stores/AppStore";
|
||||||
|
import { Stores } from '../../../stores/RootStore';
|
||||||
|
import styles from "./MobxTutorial.module.scss";
|
||||||
|
import { ConfigStore } from "../../../stores/ConfigStore";
|
||||||
|
|
||||||
|
type FakeItemCreatorStoreProps = {
|
||||||
|
appStore: AppStore;
|
||||||
|
configStore: ConfigStore;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FakeItemCreatorOwnProps = {};
|
||||||
|
|
||||||
|
type FakeItemCreatorState = {
|
||||||
|
itemTitle: string;
|
||||||
|
isImportant: boolean;
|
||||||
|
requiredTitle: string;
|
||||||
|
isLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FakeItemCreatorProps = Partial<FakeItemCreatorStoreProps> & FakeItemCreatorOwnProps;
|
||||||
|
|
||||||
|
const initialState: FakeItemCreatorState = {
|
||||||
|
itemTitle: "",
|
||||||
|
isImportant: false,
|
||||||
|
requiredTitle: undefined,
|
||||||
|
isLoading: false
|
||||||
|
};
|
||||||
|
|
||||||
|
@inject(Stores.AppStore, Stores.ConfigurationStore)
|
||||||
|
@observer
|
||||||
|
export class FakeItemCreator extends React.Component<FakeItemCreatorProps, FakeItemCreatorState> {
|
||||||
|
public state = initialState;
|
||||||
|
|
||||||
|
public render(): React.ReactElement<FakeItemCreatorProps> {
|
||||||
|
const { configStore } = this.props;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TextField
|
||||||
|
label="Title"
|
||||||
|
errorMessage={this.state.requiredTitle}
|
||||||
|
onChange={this._onChangeItemTitle}
|
||||||
|
value={this.state.itemTitle}
|
||||||
|
className={styles.inputElement}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Checkbox
|
||||||
|
label="Important?"
|
||||||
|
onChange={this._onIsImportantCheckboxChange}
|
||||||
|
className={styles.inputElement}
|
||||||
|
checked={configStore.allowImportantItems && this.state.isImportant}
|
||||||
|
disabled={!configStore.allowImportantItems}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DefaultButton
|
||||||
|
onClick={() => this._onAddFakeItem()}
|
||||||
|
iconProps={{ iconName: 'Add' }}
|
||||||
|
allowDisabledFocus={true}
|
||||||
|
className={styles.inputElement}
|
||||||
|
disabled={this.state.isLoading}
|
||||||
|
>{this.state.isLoading ? "Adding..." : "Add"}</DefaultButton>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _onAddFakeItem(): Promise<void> {
|
||||||
|
if (this.state.itemTitle === "" && this.state.itemTitle.length === 0) {
|
||||||
|
this.setState({ ...this.state, requiredTitle: "Required" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { appStore, configStore } = this.props;
|
||||||
|
this.setState({ ...this.state, isLoading: true });
|
||||||
|
await appStore.addListItem({
|
||||||
|
title: this.state.itemTitle,
|
||||||
|
important: configStore.allowImportantItems && this.state.isImportant
|
||||||
|
});
|
||||||
|
this.setState(initialState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onChangeItemTitle = (ev: React.FormEvent<HTMLInputElement>, newValue?: string): void => {
|
||||||
|
if (newValue === "" && newValue.length === 0) {
|
||||||
|
this.setState({ ...this.state, itemTitle: newValue, requiredTitle: "Required" });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setState({ ...this.state, itemTitle: newValue, requiredTitle: undefined });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onIsImportantCheckboxChange = (ev: React.FormEvent<HTMLElement>, isChecked: boolean): void => {
|
||||||
|
this.setState({ ...this.state, isImportant: isChecked });
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { inject, observer } from "mobx-react";
|
||||||
|
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
|
||||||
|
import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner';
|
||||||
|
import { TextField } from 'office-ui-fabric-react/lib/TextField';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { AppStore } from '../../../stores/AppStore';
|
||||||
|
import { Stores } from "../../../stores/RootStore";
|
||||||
|
import styles from './MobxTutorial.module.scss';
|
||||||
|
|
||||||
|
type ListCreatorStoreProps = {
|
||||||
|
appStore: AppStore;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ListCreatorProps = Partial<ListCreatorStoreProps>;
|
||||||
|
type ListCreatorState = {
|
||||||
|
loading: boolean;
|
||||||
|
errorMessage: string;
|
||||||
|
listTitle: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
@inject(Stores.AppStore)
|
||||||
|
@observer
|
||||||
|
export class ListCreator extends React.Component<ListCreatorProps, ListCreatorState> {
|
||||||
|
public state = {
|
||||||
|
loading: false,
|
||||||
|
errorMessage: undefined,
|
||||||
|
listTitle: null
|
||||||
|
};
|
||||||
|
|
||||||
|
public render(): React.ReactElement<ListCreatorProps> {
|
||||||
|
const spinner = (<Spinner size={SpinnerSize.xSmall} label="Creating list ..." labelPosition="right" />);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.grid}>
|
||||||
|
<div className={styles.row}>
|
||||||
|
<TextField
|
||||||
|
label="List title"
|
||||||
|
errorMessage={this.state.errorMessage}
|
||||||
|
onChange={this._onChangeListTitle}
|
||||||
|
value={this.state.listTitle}
|
||||||
|
disabled={this.state.loading}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PrimaryButton
|
||||||
|
onClick={() => this.createList()}
|
||||||
|
disabled={this.state.loading}
|
||||||
|
className={styles.inputElement}
|
||||||
|
>
|
||||||
|
{this.state.loading ? spinner : "Create List"}
|
||||||
|
</PrimaryButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createList() {
|
||||||
|
if (this.state.listTitle && this.state.listTitle.length > 0) {
|
||||||
|
this.setState({ ...this.state, loading: true, errorMessage: undefined });
|
||||||
|
await this.props.appStore.createList(this.state.listTitle);
|
||||||
|
this.setState({ ...this.state, loading: false });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setState({ ...this.state, errorMessage: "Required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onChangeListTitle = (ev: React.FormEvent<HTMLInputElement>, newValue?: string) => {
|
||||||
|
if (newValue === "" && newValue.length === 0) {
|
||||||
|
this.setState({ ...this.state, listTitle: newValue, errorMessage: "Required" });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setState({ ...this.state, listTitle: newValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||||
|
|
||||||
|
.mobxTutorial {
|
||||||
|
margin: 20px;
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
@include ms-Grid;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
@include ms-Grid-row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.columnCreateItems {
|
||||||
|
@include ms-Grid-col;
|
||||||
|
@include ms-lg4;
|
||||||
|
@include ms-xl4;
|
||||||
|
@include ms-sm12;
|
||||||
|
padding-right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.columnItemDetails {
|
||||||
|
@include ms-Grid-col;
|
||||||
|
@include ms-lg8;
|
||||||
|
@include ms-xl8;
|
||||||
|
@include ms-sm12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include ms-font-xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subTitle {
|
||||||
|
@include ms-font-l;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputElement {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { inject, observer } from 'mobx-react';
|
||||||
|
import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { ApplicationStatus, AppStore } from '../../../stores/AppStore';
|
||||||
|
import { ConfigStore } from '../../../stores/ConfigStore';
|
||||||
|
import { Stores } from '../../../stores/RootStore';
|
||||||
|
import { DetailedFakeItemViewer } from './DetailedFakeItemViewer';
|
||||||
|
import { FakeItemContainer } from './FakeItemContainer';
|
||||||
|
import { ListCreator } from './ListCreator';
|
||||||
|
import styles from './MobxTutorial.module.scss';
|
||||||
|
import { ProgressIndicator } from './ProgressIndicator';
|
||||||
|
|
||||||
|
export type MobxTutorialStoreProps = {
|
||||||
|
appStore: AppStore;
|
||||||
|
configStore: ConfigStore;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MobxTutorialProps = Partial<MobxTutorialStoreProps>;
|
||||||
|
|
||||||
|
@inject(Stores.AppStore, Stores.ConfigurationStore)
|
||||||
|
@observer
|
||||||
|
export class MobxTutorial extends React.Component<MobxTutorialProps, {}> {
|
||||||
|
public render(): React.ReactElement<MobxTutorialProps> {
|
||||||
|
const { appStore, configStore } = this.props;
|
||||||
|
|
||||||
|
if (appStore.isInitializing)
|
||||||
|
return (<Spinner size={SpinnerSize.large} label="Initializing... please hodl" ariaLive="assertive" labelPosition="left" />);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.mobxTutorial}>
|
||||||
|
<div className={styles.row}>
|
||||||
|
<div className={styles.title}>{configStore.applicationTitle}</div>
|
||||||
|
<ProgressIndicator></ProgressIndicator>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{appStore.status === ApplicationStatus.CreateList ?
|
||||||
|
<div className={styles.row}>
|
||||||
|
<div className={styles.subTitle}>1) Create List</div>
|
||||||
|
<ListCreator></ListCreator>
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
{appStore.status === ApplicationStatus.CreateItems ?
|
||||||
|
<div className={styles.row}>
|
||||||
|
<div className={styles.subTitle}>2) Create Items</div>
|
||||||
|
<FakeItemContainer></FakeItemContainer>
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
{appStore.status === ApplicationStatus.Completed ?
|
||||||
|
<div className={styles.row}>
|
||||||
|
<DetailedFakeItemViewer items={appStore.items}></DetailedFakeItemViewer>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Provider } from "mobx-react";
|
||||||
|
import * as React from 'react';
|
||||||
|
import { MobxTutorial } from './MobxTutorial';
|
||||||
|
|
||||||
|
type MobxTutorialProviderOwnProps = {
|
||||||
|
stores: {};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class MobxTutorialProvider extends React.Component<MobxTutorialProviderOwnProps, {}> {
|
||||||
|
|
||||||
|
public render(): React.ReactElement<{}> {
|
||||||
|
return (
|
||||||
|
<Provider {...this.props.stores}>
|
||||||
|
<MobxTutorial></MobxTutorial>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { inject, observer } from "mobx-react";
|
||||||
|
import * as React from 'react';
|
||||||
|
import { AppStore } from "../../../stores/AppStore";
|
||||||
|
import { Stores } from '../../../stores/RootStore';
|
||||||
|
|
||||||
|
type ProgressIndicatorStoreProps = {
|
||||||
|
appStore: AppStore;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ProgressIndicatorOwnProps = {};
|
||||||
|
type ProgressIndicatorProps = Partial<ProgressIndicatorStoreProps> & ProgressIndicatorOwnProps;
|
||||||
|
|
||||||
|
@inject(Stores.AppStore)
|
||||||
|
@observer
|
||||||
|
export class ProgressIndicator extends React.Component<ProgressIndicatorProps, {}> {
|
||||||
|
public render(): React.ReactElement<ProgressIndicatorProps> {
|
||||||
|
const { appStore } = this.props;
|
||||||
|
return (<p>{appStore.appStatus}</p>);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
define([], function () {
|
||||||
|
return {
|
||||||
|
"PropertyPaneDescription": "Application configuration",
|
||||||
|
"BasicGroupName": "Global Settings",
|
||||||
|
"AppTitleFieldLabel": "Application Title",
|
||||||
|
"AllowImportantItemsLabel": "Allow important items"
|
||||||
|
}
|
||||||
|
});
|
11
samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/mystrings.d.ts
vendored
Normal file
11
samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
declare interface IMobxTutorialWebPartStrings {
|
||||||
|
PropertyPaneDescription: string;
|
||||||
|
BasicGroupName: string;
|
||||||
|
AppTitleFieldLabel: string;
|
||||||
|
AllowImportantItemsLabel: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'MobxTutorialWebPartStrings' {
|
||||||
|
const strings: IMobxTutorialWebPartStrings;
|
||||||
|
export = strings;
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"jsx": "react",
|
||||||
|
"declaration": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"outDir": "lib",
|
||||||
|
"inlineSources": false,
|
||||||
|
"strictNullChecks": false,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"typeRoots": [
|
||||||
|
"./node_modules/@types",
|
||||||
|
"./node_modules/@microsoft"
|
||||||
|
],
|
||||||
|
"types": [
|
||||||
|
"es6-promise",
|
||||||
|
"webpack-env"
|
||||||
|
],
|
||||||
|
"lib": [
|
||||||
|
"es5",
|
||||||
|
"dom",
|
||||||
|
"es2015.collection"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"lib"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
|
||||||
|
"rules": {
|
||||||
|
"class-name": false,
|
||||||
|
"export-name": false,
|
||||||
|
"forin": false,
|
||||||
|
"label-position": false,
|
||||||
|
"member-access": true,
|
||||||
|
"no-arg": false,
|
||||||
|
"no-console": false,
|
||||||
|
"no-construct": 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-use-before-declare": true,
|
||||||
|
"no-with-statement": true,
|
||||||
|
"semicolon": true,
|
||||||
|
"trailing-comma": false,
|
||||||
|
"typedef": false,
|
||||||
|
"typedef-whitespace": false,
|
||||||
|
"use-named-parameter": true,
|
||||||
|
"variable-name": false,
|
||||||
|
"whitespace": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -112,6 +112,7 @@ Web part displaying hierarchical information from SharePoint list<br/>[react-dis
|
||||||
Webhooks Realtime<br/>[react-webhooks-realtime](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-webhooks-realtime)|This web part demonstrates how to leverage the capabilities of SharePoint Webhooks.The libraries used by this web part are Socket.io, sp pnp js, moment.|![react-webhooks-realtime](https://raw.githubusercontent.com/SharePoint/sp-dev-fx-webparts/master/samples/react-webhooks-realtime/assets/spfx-react-webhooks-realtime.gif)|![drop](https://img.shields.io/badge/version-GA-green.svg)
|
Webhooks Realtime<br/>[react-webhooks-realtime](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-webhooks-realtime)|This web part demonstrates how to leverage the capabilities of SharePoint Webhooks.The libraries used by this web part are Socket.io, sp pnp js, moment.|![react-webhooks-realtime](https://raw.githubusercontent.com/SharePoint/sp-dev-fx-webparts/master/samples/react-webhooks-realtime/assets/spfx-react-webhooks-realtime.gif)|![drop](https://img.shields.io/badge/version-GA-green.svg)
|
||||||
Webpart showing Url validation for SharePoint using Office Graph<br/>[react-graph-evalurl](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-graph-evalurl)|This sample contains a class that evaluates the url input of a text field against the Microsoft Graph. It is possible to evalute the existance of the following three SharePoint Elements:|![react-graph-evalurl](https://raw.githubusercontent.com/SharePoint/sp-dev-fx-webparts/master/samples/react-graph-evalurl/assets/evaluation-client-searching-for-site-collection.png)|![drop](https://img.shields.io/badge/drop-1.4.1-green.svg)
|
Webpart showing Url validation for SharePoint using Office Graph<br/>[react-graph-evalurl](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-graph-evalurl)|This sample contains a class that evaluates the url input of a text field against the Microsoft Graph. It is possible to evalute the existance of the following three SharePoint Elements:|![react-graph-evalurl](https://raw.githubusercontent.com/SharePoint/sp-dev-fx-webparts/master/samples/react-graph-evalurl/assets/evaluation-client-searching-for-site-collection.png)|![drop](https://img.shields.io/badge/drop-1.4.1-green.svg)
|
||||||
Webpart with React and Mobx<br/>[react-mobx](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-mobx)|Sample webpart implementation that uses Mobx to keep track of its state.|![react-mobx](https://i.gyazo.com/e6f1903b9a9c8201985cd25cc1fe28bc.gif)|![drop](https://img.shields.io/badge/drop-drop5-red.svg)
|
Webpart with React and Mobx<br/>[react-mobx](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-mobx)|Sample webpart implementation that uses Mobx to keep track of its state.|![react-mobx](https://i.gyazo.com/e6f1903b9a9c8201985cd25cc1fe28bc.gif)|![drop](https://img.shields.io/badge/drop-drop5-red.svg)
|
||||||
|
Webpart with React and Mobx (multiple stores)<br/> [react-mobx-multiple-stores](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-mobx-multiple-stores)|A sample webpart that uses the [Mobx](https://mobx.js.org/) library with multiple stores to keep track of the applications state.|![react-mobx-multiple-stores](https://raw.githubusercontent.com/KEMiCZA/sp-dev-fx-webparts/dev/samples/react-mobx-multiple-stores/assets/demo.gif)|![drop](https://img.shields.io/badge/version-1.8.2-green.svg)
|
||||||
Webpart with React and Redux<br/>[react-redux](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-redux)|Sample webpart implementation that uses Redux to keep track of its state.|![react-redux](https://i.gyazo.com/729c4addf6c992513f8eb91a3fa0e302.gif)|![drop](https://img.shields.io/badge/drop-drop5-red.svg)
|
Webpart with React and Redux<br/>[react-redux](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-redux)|Sample webpart implementation that uses Redux to keep track of its state.|![react-redux](https://i.gyazo.com/729c4addf6c992513f8eb91a3fa0e302.gif)|![drop](https://img.shields.io/badge/drop-drop5-red.svg)
|
||||||
Yammer REST API SPFx webpart<br/>[react-yammer-api](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-yammer-api)|This sample shows how Yammer REST APIs can be consumed by using SharePoint Framework React webpart and the Yammer JavaScript SDK. The SPFx webpart contains wrapper around the Yammer JavaScript SDK that can be extended for fluent typescript api experience.|![react-yammer-api](https://raw.githubusercontent.com/SharePoint/sp-dev-fx-webparts/master/samples/react-yammer-api/assets/spfx-yammer-api-webpart.jpg)|![drop](https://img.shields.io/badge/drop-GA-green.svg)
|
Yammer REST API SPFx webpart<br/>[react-yammer-api](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-yammer-api)|This sample shows how Yammer REST APIs can be consumed by using SharePoint Framework React webpart and the Yammer JavaScript SDK. The SPFx webpart contains wrapper around the Yammer JavaScript SDK that can be extended for fluent typescript api experience.|![react-yammer-api](https://raw.githubusercontent.com/SharePoint/sp-dev-fx-webparts/master/samples/react-yammer-api/assets/spfx-yammer-api-webpart.jpg)|![drop](https://img.shields.io/badge/drop-GA-green.svg)
|
||||||
Youtube Web Part<br/>[react-youtube](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-youtube)|This web part allows to search and view the Youtube videos, across the Youtube API, directly on a SharePoint page, furthermore the property panel offers the possibility to specify the api key, the number of items to display and it is also possible specify a Youtube Channel Id.|![react-youtube](https://raw.githubusercontent.com/SharePoint/sp-dev-fx-webparts/master/samples/react-youtube/assets/Preview.gif)|![drop](https://img.shields.io/badge/version-GA-green.svg)
|
Youtube Web Part<br/>[react-youtube](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-youtube)|This web part allows to search and view the Youtube videos, across the Youtube API, directly on a SharePoint page, furthermore the property panel offers the possibility to specify the api key, the number of items to display and it is also possible specify a Youtube Channel Id.|![react-youtube](https://raw.githubusercontent.com/SharePoint/sp-dev-fx-webparts/master/samples/react-youtube/assets/Preview.gif)|![drop](https://img.shields.io/badge/version-GA-green.svg)
|
Loading…
Reference in New Issue