Added new sample react-functional-stateful-component (#886)
This commit is contained in:
parent
efc6137cd7
commit
b0fe474e82
|
@ -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": {
|
||||||
|
"version": "1.8.2",
|
||||||
|
"libraryName": "roman-numerals",
|
||||||
|
"libraryId": "f48eaad8-e027-4f27-bfca-0abd5239e65b",
|
||||||
|
"environment": "spo",
|
||||||
|
"packageManager": "npm",
|
||||||
|
"isCreatingSolution": true,
|
||||||
|
"isDomainIsolated": false,
|
||||||
|
"componentType": "webpart"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
# React Functional Stateful Component web part
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This web part demonstrates building a React functional component that includes state, using the recently introduced React Hooks feature. The example web part renders a number to Roman numerals conversion tool.
|
||||||
|
|
||||||
|
![Screenshot](Screenshot.png "Screenshot - Roman Numerals web part")
|
||||||
|
|
||||||
|
## Used SharePoint Framework Version
|
||||||
|
|
||||||
|
![drop](https://img.shields.io/badge/version-GA-green.svg)
|
||||||
|
|
||||||
|
## Applies to
|
||||||
|
|
||||||
|
* [SharePoint Framework](https:/dev.office.com/sharepoint)
|
||||||
|
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
This sample was built with version 1.82 of the SharePoint Framework. It has been modified to use version 16.8 of the React framework (by default the version used is React 16.7). React 16.8 supports React Hooks which is required to support state management in a React functional component.
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Solution|Author(s)
|
||||||
|
--------|---------
|
||||||
|
react-functional-stateful-component | Bill Ayers
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
Version|Date|Comments
|
||||||
|
-------|----|--------
|
||||||
|
1.0|June 5, 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
|
||||||
|
|
||||||
|
* Clone this repository
|
||||||
|
* Move to /samples/react-functional-stateful-component folder
|
||||||
|
* At the command line run:
|
||||||
|
* `npm install`
|
||||||
|
* `gulp serve`
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
The purpose of this web part is to demonstrate building a React functional component that includes state. This is achieved using the recent React Hooks feature. The resulting code is cleaner and easier to follow than using a JavaScript/TypeScript class derived from React.Component. The example web part renders a number to Roman numerals conversion tool, although the functionality is just for the purposes of the demonstration.
|
||||||
|
|
||||||
|
This is an extension of the approach used in the [React-Functional-Component](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-functional-component) sample.
|
||||||
|
|
||||||
|
* Simplification
|
||||||
|
* Functional Component
|
||||||
|
* Adding State
|
||||||
|
|
||||||
|
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-functional-stateful-component" />
|
||||||
|
|
||||||
|
## RomanNumeralsWebPart.ts Simplification
|
||||||
|
|
||||||
|
A number of simplifications have been made to the RomanNumeralsWebPart.ts file to make it easier to follow compared to the Yeoman generator starter project. The use of an external string collection has been removed - they are simply hard coded into the file to make it clear how the property pane configuration works.
|
||||||
|
|
||||||
|
The external interface to define the properties is moved from a separate file and inline into RomanNumeralsWebPart.ts. This interface is used by the web part and the component on the assumption that all the properties will be passed to the component as props. The property will then be available to the component through its **props** collection.
|
||||||
|
|
||||||
|
## Functional Component
|
||||||
|
|
||||||
|
The RomanNumerals.tsx React Component is a React functional component. This simplifies the code structure to a simple JavaScript function with the name of the component, a single argument containing the React props, and a simple return of the component rendering. Because it is just a function, there is no need to worry about **this** or **that**, constructors etc. In this example we are introducing state, so this is not a "pure" or "stateless" functional component. For a more complicated example it might be advantageous to break the component down into several nested components, some of which may well be stateless. Stateless components are very simple and trivially easy to test. But typically we need to manage the state for at least some of our components as described below.
|
||||||
|
|
||||||
|
## Adding State
|
||||||
|
|
||||||
|
State is managed by means of a fairly new feature called [React Hooks](https://reactjs.org/docs/hooks-intro.html). On line 11 of RomanNumerals.tsx the React.useState function is used to provide state:
|
||||||
|
|
||||||
|
```
|
||||||
|
const [value, setValue] = React.useState(parseInt(props.initialValue));
|
||||||
|
```
|
||||||
|
React.useState takes an initial value for the state variable and returns an array of two objects. The first is a variable containing the state value, and the second is a setter function for the value. We could refer to these as state[0] and state[1] or something, but the convention is to use the array destructuring operator to unpack them into local constants. The name of these is not important but a good practice is to use the form *[foo, setFoo]*, etc. Whenever we need to use the current value of the state variable we just refer to it (e.g. *{value}*), and wherever we need to change the value we call *useState(newValue)*. There is no need to use **this** because we are not inside a class, nor do we need to worry about the context of the **this** value, nor create a constructor to initialize state.
|
||||||
|
|
||||||
|
In the code we use the number input control to change the value of the state using a local function *onChange*, which simply sets a new value as the user types in characters. We also have a couple of additional buttons that can be used to increment and decrement the value which also demonstrates updating state with inline functions. Everything just works because the React framework will re-render the component whenever we update the state variable using the *setValue* function. If you need more complex state you could pass a more complex object to *useState* but a better approach is often to simply call *useState* once for each variable that makes up the state.
|
||||||
|
|
||||||
|
The output rendering uses the value of the state variable and does a conversion using the *romanToString* function:
|
||||||
|
|
||||||
|
```
|
||||||
|
<h3>{props.resultCaption} {romanToString(value)}</h3>
|
||||||
|
```
|
||||||
|
The *resultCaption* property is also rendered if defined.
|
||||||
|
|
||||||
|
## Building and testing
|
||||||
|
|
||||||
|
In the react-functional-component directory run **npm install** to resolve all the dependencies. Once this has completed you can run **gulp serve** to test the web part in the local workbench.
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||||
|
"version": "2.0",
|
||||||
|
"bundles": {
|
||||||
|
"roman-numerals-web-part": {
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"entrypoint": "./lib/webparts/romanNumerals/RomanNumeralsWebPart.js",
|
||||||
|
"manifest": "./src/webparts/romanNumerals/RomanNumeralsWebPart.manifest.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"externals": {},
|
||||||
|
"localizedResources": {
|
||||||
|
"RomanNumeralsWebPartStrings": "lib/webparts/romanNumerals/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": "roman-numerals",
|
||||||
|
"accessKey": "<!-- ACCESS KEY -->"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||||
|
"solution": {
|
||||||
|
"name": "roman-numerals-client-side-solution",
|
||||||
|
"id": "f48eaad8-e027-4f27-bfca-0abd5239e65b",
|
||||||
|
"version": "1.0.0.0",
|
||||||
|
"includeClientSideAssets": true,
|
||||||
|
"isDomainIsolated": false
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"zippedPackage": "solution/roman-numerals.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,13 @@
|
||||||
|
'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.configureWebpack.mergeConfig({
|
||||||
|
additionalConfiguration: (config) => {
|
||||||
|
config.externals = config.externals.filter(name => !(["react", "react-dom"].includes(name)))
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
build.initialize(gulp);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"name": "roman-numerals",
|
||||||
|
"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/webpack-env": "1.13.1",
|
||||||
|
"office-ui-fabric-react": "6.143.0",
|
||||||
|
"react": "^16.8.6",
|
||||||
|
"react-dom": "^16.8.6"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"@types/react": "16.7.22"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@microsoft/rush-stack-compiler-2.9": "0.7.7",
|
||||||
|
"@microsoft/rush-stack-compiler-3.3": "^0.2.15",
|
||||||
|
"@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",
|
||||||
|
"@types/react": "^16.8.19",
|
||||||
|
"@types/react-dom": "^16.8.4",
|
||||||
|
"ajv": "~5.2.2",
|
||||||
|
"gulp": "~3.9.1",
|
||||||
|
"typescript": "^3.5.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||||
|
"id": "0f227452-315b-4061-b627-930468a02acd",
|
||||||
|
"alias": "RomanNumeralsWebPart",
|
||||||
|
"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": "RomanNumerals" },
|
||||||
|
"description": { "default": "Roman Numeral Converter Web Part" },
|
||||||
|
"officeFabricIconFontName": "Page",
|
||||||
|
"properties": {
|
||||||
|
"title": "Convert Number to Roman Numerals",
|
||||||
|
"description": "Roman numeral converter - enter a number to see the equivalent in Roman numerals",
|
||||||
|
"initialValue": "2019",
|
||||||
|
"showUpdownButtons": false
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDom from 'react-dom';
|
||||||
|
import { Version } from '@microsoft/sp-core-library';
|
||||||
|
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||||
|
import { IPropertyPaneConfiguration, PropertyPaneTextField, PropertyPaneToggle } from '@microsoft/sp-property-pane';
|
||||||
|
import RomanNumerals from './components/RomanNumerals';
|
||||||
|
|
||||||
|
export interface IRomanNumeralsProps {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
initialValue: string;
|
||||||
|
inputCaption: string;
|
||||||
|
resultCaption: string;
|
||||||
|
showUpdownButtons: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class HelloWorldWebPart extends BaseClientSideWebPart<IRomanNumeralsProps> {
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
ReactDom.render(React.createElement(RomanNumerals, this.properties), this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onDispose(): void {
|
||||||
|
ReactDom.unmountComponentAtNode(this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get dataVersion(): Version {
|
||||||
|
return Version.parse('1.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||||
|
return {
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
header: {
|
||||||
|
description: "Properties"
|
||||||
|
},
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
groupName: "General",
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneTextField('title', { label: "Web part title" }),
|
||||||
|
PropertyPaneTextField('description', { label: "Description Text" }),
|
||||||
|
PropertyPaneToggle('showUpdownButtons', { label: "Show increment/decrement buttons" })
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
groupName: "Initialization",
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneTextField('initialValue', { label: "Initial Value (numeric)" })
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
groupName: "Captions",
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneTextField('inputCaption', { label: "Caption for input control" }),
|
||||||
|
PropertyPaneTextField('resultCaption', { label: "Caption for result" })
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
export function romanToString(val: number): string {
|
||||||
|
|
||||||
|
const roman = [
|
||||||
|
{ symbol: 'm', number: 1000000 },
|
||||||
|
{ symbol: 'd', number: 500000 },
|
||||||
|
{ symbol: 'c', number: 100000 },
|
||||||
|
{ symbol: 'l', number: 50000 },
|
||||||
|
{ symbol: 'x', number: 10000 },
|
||||||
|
{ symbol: 'v', number: 5000 },
|
||||||
|
{ symbol: 'M', number: 1000 },
|
||||||
|
{ symbol: 'D', number: 500 },
|
||||||
|
{ symbol: 'C', number: 100 },
|
||||||
|
{ symbol: 'L', number: 50 },
|
||||||
|
{ symbol: 'X', number: 10 },
|
||||||
|
{ symbol: 'V', number: 5 },
|
||||||
|
{ symbol: 'I', number: 1 }
|
||||||
|
];
|
||||||
|
|
||||||
|
var negative: boolean = (val < 0.0);
|
||||||
|
if (negative) val = -val;
|
||||||
|
var integer: number = Math.floor(val);
|
||||||
|
if (integer == 0) return "nihil";
|
||||||
|
var sText: string = "";
|
||||||
|
if (negative) sText += "minus ";
|
||||||
|
if (integer > 10000000 || integer > 80 * roman[0].number)
|
||||||
|
return "Sorry, number is too big for Roman Numerals";
|
||||||
|
|
||||||
|
for (var iDigit = 0; iDigit < roman.length; iDigit++) {
|
||||||
|
var n = Math.floor(integer / roman[iDigit].number);
|
||||||
|
integer = integer % roman[iDigit].number;
|
||||||
|
for (var i = 1; i <= n; i++) sText += roman[iDigit].symbol;
|
||||||
|
if (iDigit % 2 == 0 && iDigit + 2 < roman.length && Math.floor(integer / (roman[iDigit + 2].number * 9)) > 0) {
|
||||||
|
sText += roman[iDigit + 2].symbol;
|
||||||
|
sText += roman[iDigit].symbol;
|
||||||
|
integer -= roman[iDigit + 2].number * 9;
|
||||||
|
}
|
||||||
|
if (iDigit + 1 < roman.length && Math.floor(integer / (roman[iDigit + 1].number * 4)) > 0) {
|
||||||
|
sText += roman[iDigit + 1].symbol;
|
||||||
|
sText += roman[iDigit].symbol;
|
||||||
|
integer -= roman[iDigit + 1].number * 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sText;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 0px auto;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
@include ms-Grid-row;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
background-color: $ms-color-themeDark;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
@include ms-Grid-col;
|
||||||
|
@include ms-lg10;
|
||||||
|
@include ms-xl8;
|
||||||
|
@include ms-xlPush2;
|
||||||
|
@include ms-lgPush1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include ms-font-xl;
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import styles from './RomanNumerals.module.scss';
|
||||||
|
import { IRomanNumeralsProps } from '../RomanNumeralsWebPart';
|
||||||
|
import { escape } from '@microsoft/sp-lodash-subset';
|
||||||
|
import * as Fabric from 'office-ui-fabric-react';
|
||||||
|
import { romanToString } from '../RomanToString';
|
||||||
|
|
||||||
|
export default function RomanNumerals(props: IRomanNumeralsProps) {
|
||||||
|
|
||||||
|
// Use React Hooks to manage state - useState returns value and setter as array...
|
||||||
|
const [value, setValue] = React.useState(parseInt(props.initialValue));
|
||||||
|
|
||||||
|
function onChange(event) {
|
||||||
|
setValue(parseInt(event.target.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
var updownButtons = null;
|
||||||
|
if (props.showUpdownButtons) updownButtons = (
|
||||||
|
<div className={styles.column}>
|
||||||
|
<br />
|
||||||
|
<Fabric.PrimaryButton onClick={() => setValue(value + 1)}>+</Fabric.PrimaryButton>
|
||||||
|
|
||||||
|
<Fabric.PrimaryButton onClick={() => setValue(value - 1)}>-</Fabric.PrimaryButton>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div className={styles.row}>
|
||||||
|
<div className={styles.column}>
|
||||||
|
<span className={styles.title}>{props.title}</span>
|
||||||
|
<p>{escape(props.description)}</p>
|
||||||
|
</div>
|
||||||
|
<div className={styles.column}>
|
||||||
|
{props.inputCaption}<br />
|
||||||
|
<input type="number" min="0" max="9999999" value={value} onChange={onChange} />
|
||||||
|
</div>
|
||||||
|
{updownButtons}
|
||||||
|
<div className={styles.column}>
|
||||||
|
<br />
|
||||||
|
<h3>{props.resultCaption} {romanToString(value)}</h3>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
2
samples/react-functional-stateful-component/src/webparts/romanNumerals/loc/en-us.js
vendored
Normal file
2
samples/react-functional-stateful-component/src/webparts/romanNumerals/loc/en-us.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// dummy locale file to keep gulp happy
|
||||||
|
define([], function() { return {}});
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue