mirror of
https://github.com/pnp/sp-dev-fx-webparts.git
synced 2025-02-18 19:07:12 +00:00
commit
1ba602962a
25
samples/react-mobx-multiple-stores/.editorconfig
Normal file
25
samples/react-mobx-multiple-stores/.editorconfig
Normal 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
|
32
samples/react-mobx-multiple-stores/.gitignore
vendored
Normal file
32
samples/react-mobx-multiple-stores/.gitignore
vendored
Normal file
@ -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
|
12
samples/react-mobx-multiple-stores/.yo-rc.json
Normal file
12
samples/react-mobx-multiple-stores/.yo-rc.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
52
samples/react-mobx-multiple-stores/README.md
Normal file
52
samples/react-mobx-multiple-stores/README.md
Normal file
@ -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
|
||||||
|

|
||||||
|
|
||||||
|
## 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" />
|
BIN
samples/react-mobx-multiple-stores/assets/demo.gif
Normal file
BIN
samples/react-mobx-multiple-stores/assets/demo.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 449 KiB |
18
samples/react-mobx-multiple-stores/config/config.json
Normal file
18
samples/react-mobx-multiple-stores/config/config.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
10
samples/react-mobx-multiple-stores/config/serve.json
Normal file
10
samples/react-mobx-multiple-stores/config/serve.json
Normal file
@ -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 -->"
|
||||||
|
}
|
7
samples/react-mobx-multiple-stores/gulpfile.js
vendored
Normal file
7
samples/react-mobx-multiple-stores/gulpfile.js
vendored
Normal file
@ -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);
|
17867
samples/react-mobx-multiple-stores/package-lock.json
generated
Normal file
17867
samples/react-mobx-multiple-stores/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
samples/react-mobx-multiple-stores/package.json
Normal file
43
samples/react-mobx-multiple-stores/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
1
samples/react-mobx-multiple-stores/src/index.ts
Normal file
1
samples/react-mobx-multiple-stores/src/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
89
samples/react-mobx-multiple-stores/src/stores/AppStore.ts
Normal file
89
samples/react-mobx-multiple-stores/src/stores/AppStore.ts
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
41
samples/react-mobx-multiple-stores/src/stores/ConfigStore.ts
Normal file
41
samples/react-mobx-multiple-stores/src/stores/ConfigStore.ts
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
17
samples/react-mobx-multiple-stores/src/stores/RootStore.ts
Normal file
17
samples/react-mobx-multiple-stores/src/stores/RootStore.ts
Normal file
@ -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>);
|
||||||
|
}
|
||||||
|
}
|
8
samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/en-us.js
vendored
Normal file
8
samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/en-us.js
vendored
Normal file
@ -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 |
38
samples/react-mobx-multiple-stores/tsconfig.json
Normal file
38
samples/react-mobx-multiple-stores/tsconfig.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
30
samples/react-mobx-multiple-stores/tslint.json
Normal file
30
samples/react-mobx-multiple-stores/tslint.json
Normal file
@ -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.||
|
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.||
|
||||||
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:||
|
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:||
|
||||||
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.||
|
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.||
|
||||||
|
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.||
|
||||||
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.||
|
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.||
|
||||||
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.||
|
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.||
|
||||||
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.||
|
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.||
|
Loading…
x
Reference in New Issue
Block a user