From fd813aa738f0961aeee003cc2b5bafe3fdbc0f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Feje=C5=A1?= Date: Thu, 13 Dec 2018 10:33:36 +0100 Subject: [PATCH 01/28] add limited word text area --- snippets/LimitedWordTextArea.md | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 snippets/LimitedWordTextArea.md diff --git a/snippets/LimitedWordTextArea.md b/snippets/LimitedWordTextArea.md new file mode 100644 index 000000000..662ec93dd --- /dev/null +++ b/snippets/LimitedWordTextArea.md @@ -0,0 +1,62 @@ +### LimitedWordTextArea + +Renders a textarea component with a word limit. + +Use the value of the `value` prop to determine the initial `state.content` and `state.wordCount` and the value of the `limit` props to determine the value of `state.limit`. +Create a method, `handleChange`, which trims the `event.target.value` data if necessary and uses `Component.prototype.setState` to update `state.content` and `state.wordCount`, and bind it to the component's context. +In the`render()` method, use a`
` to wrap both the` +

{this.state.wordCount} / {this.props.limit}

+
+ ); + } +} +``` + +```jsx +ReactDOM.render( + , + document.getElementById('root') +); +``` + +< !--tags: input,state,class -- > + +< !--expertise: 0 -- > + From 0a3d9ec27f9750d2783b139c4678a9c4c0aed4f1 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Wed, 6 Feb 2019 22:17:03 +0200 Subject: [PATCH 02/28] Create TreeView.md --- snippets/TreeView.md | 126 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 snippets/TreeView.md diff --git a/snippets/TreeView.md b/snippets/TreeView.md new file mode 100644 index 000000000..de2e1bf52 --- /dev/null +++ b/snippets/TreeView.md @@ -0,0 +1,126 @@ +### TreeView + +Renders a tree view of a JSON object or array with collapsible content. + +Use `defaultProps` to set the default values of certain props. +Use the value of the `toggled` prop to determine the initial state of the content (collapsed/expanded). +Set the `state` of the component to the value of the `toggled` prop and bind the `toggle` method to the component's context. +Create a method, `toggle`, which uses `Component.prototype.setState` to change the component's `state` from collapsed to expanded and vice versa. +In the `render()` method, use a `
` to wrap the contents of the component and the `` element, used to alter the component's `state`. +Determine the appearance of the component, based on `this.props.isParentToggled`, `this.state.toggled`, `this.props.name` and `Array.isArray()` on `this.props.data`. +For each child in `this.props.data`, determine if it is an object or array and recursively render a sub-tree. +Otherwise, render a `

` element with the appropriate style. + +```css +.tree-element { + margin: 0; + position: relative; +} + +div.tree-element:before { + content: ''; + position: absolute; + top: 24px; + left: 1px; + height: calc(100% - 48px); + border-left: 1px solid gray; +} + +.toggler { + position: absolute; + top: 10px; + left: 0px; + width: 0; + height: 0; + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + border-left: 5px solid gray; + cursor: pointer; +} + +.toggler.closed { + transform: rotate(90deg); +} + +.collapsed { + display: none; +} +``` + +```jsx +class TreeView extends React.Component { + constructor(props) { + super(props); + this.state = { + toggled: props.toggled + } + this.toggle = this.toggle.bind(this); + } + + toggle() { + this.setState(state => ({ toggled: !state.toggled })); + } + + render() { + return ( +

+ + {this.props.name ?   {this.props.name}: :   } + {Array.isArray(this.props.data) ? '[' : '{'} + {!this.state.toggled && '...'} + {Object.keys(this.props.data).map( + (v, i, a) => + typeof this.props.data[v] == "object" ? ( + + ) : ( +

+ {Array.isArray(this.props.data) ? '' : {v}: } + {this.props.data[v]}{i === a.length - 1 ? '' : ','} +

+ ) + )} + {Array.isArray(this.props.data) ? ']' : '}'} + {!this.props.isLast ? ',' : ''} +
+ ) + } +} + +TreeView.defaultProps = { + isLast: true, + toggled: true, + name: null, + isChildElement: false, + isParentToggled: true +} +``` + +```jsx +let data = { + lorem: { + ipsum: "dolor sit", + amet: { + consectetur: "adipiscing", + elit: [ + "duis", + "vitae", + { + semper: "orci" + }, + { + est: "sed ornare" + }, + "etiam", + ["laoreet", "tincidunt"], + ["vestibulum", "ante"] + ] + }, + ipsum: "primis" + } +}; +ReactDOM.render(, document.getElementById("root")); +``` + + + + From d4ce87ed63bab98cc9a8e5f0617054e2c037d9c5 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Wed, 6 Feb 2019 22:21:04 +0200 Subject: [PATCH 03/28] Build README --- README.md | 261 ++++++++++++++++++++++++++++++++++++ data/snippet_data.json | 37 ++++++ package-lock.json | 292 +++++++++++++++++++++++++---------------- snippets/TreeView.md | 2 +- 4 files changed, 481 insertions(+), 111 deletions(-) diff --git a/README.md b/README.md index e307f7443..9515a25d0 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,15 @@ +### Object + +
+View contents + +* [TreeView](#treeview) +
+ + ### String
@@ -53,6 +62,7 @@ * [Carousel](#carousel) * [Collapse](#collapse) +* [FileDrop](#filedrop) * [Mailto](#mailto) * [ModalDialog](#modaldialog) * [StarRating](#starrating) @@ -408,6 +418,137 @@ ReactDOM.render(
[⬆ Back to top](#table-of-contents) +## Object +### TreeView + +Renders a tree view of a JSON object or array with collapsible content. + +Use `defaultProps` to set the default values of certain props. +Use the value of the `toggled` prop to determine the initial state of the content (collapsed/expanded). +Set the `state` of the component to the value of the `toggled` prop and bind the `toggle` method to the component's context. +Create a method, `toggle`, which uses `Component.prototype.setState` to change the component's `state` from collapsed to expanded and vice versa. +In the `render()` method, use a `
` to wrap the contents of the component and the `` element, used to alter the component's `state`. +Determine the appearance of the component, based on `this.props.isParentToggled`, `this.state.toggled`, `this.props.name` and `Array.isArray()` on `this.props.data`. +For each child in `this.props.data`, determine if it is an object or array and recursively render a sub-tree. +Otherwise, render a `

` element with the appropriate style. + +```css +.tree-element { + margin: 0; + position: relative; +} + +div.tree-element:before { + content: ''; + position: absolute; + top: 24px; + left: 1px; + height: calc(100% - 48px); + border-left: 1px solid gray; +} + +.toggler { + position: absolute; + top: 10px; + left: 0px; + width: 0; + height: 0; + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + border-left: 5px solid gray; + cursor: pointer; +} + +.toggler.closed { + transform: rotate(90deg); +} + +.collapsed { + display: none; +} +``` + +```jsx +class TreeView extends React.Component { + constructor(props) { + super(props); + this.state = { + toggled: props.toggled + } + this.toggle = this.toggle.bind(this); + } + + toggle() { + this.setState(state => ({ toggled: !state.toggled })); + } + + render() { + return ( +

+ + {this.props.name ?   {this.props.name}: :   } + {Array.isArray(this.props.data) ? '[' : '{'} + {!this.state.toggled && '...'} + {Object.keys(this.props.data).map( + (v, i, a) => + typeof this.props.data[v] == "object" ? ( + + ) : ( +

+ {Array.isArray(this.props.data) ? '' : {v}: } + {this.props.data[v]}{i === a.length - 1 ? '' : ','} +

+ ) + )} + {Array.isArray(this.props.data) ? ']' : '}'} + {!this.props.isLast ? ',' : ''} +
+ ) + } +} + +TreeView.defaultProps = { + isLast: true, + toggled: true, + name: null, + isChildElement: false, + isParentToggled: true +} +``` + +
+Examples + +```jsx +let data = { + lorem: { + ipsum: "dolor sit", + amet: { + consectetur: "adipiscing", + elit: [ + "duis", + "vitae", + { + semper: "orci" + }, + { + est: "sed ornare" + }, + "etiam", + ["laoreet", "tincidunt"], + ["vestibulum", "ante"] + ] + }, + ipsum: "primis" + } +}; +ReactDOM.render(, document.getElementById("root")); +``` +
+ +
[⬆ Back to top](#table-of-contents) + + ## String ### AutoLink @@ -614,6 +755,126 @@ ReactDOM.render(
[⬆ Back to top](#table-of-contents) +### FileDrop + +Renders a file drag and drop component for a single file. + +Create a ref called `dropRef` for this component. +Initialize `state.drag` and `state.filename` to `false` and `''` respectively. +The variables `dragCounter` and `state.drag` are used to determine if a file is being dragged, while `state.filename` is used to store the dropped file's name. +Create the `handleDrag`, `handleDragIn`, `handleDragOut` and `handleDrop` methods to handle drag and drop functionality, bind them to the component's context. +Each of the methods will handle a specific event, the listeners for which are created and removed in `componentDidMount` and `componentWillUnmount` respectively. +`handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `this.props.handleDrop`. +In the `render()` method, create an appropriately styled `
` and use `state.drag` and `state.filename` to determine its contents and style. +Finally, bind the `ref` of the created `
` to `dropRef`. + + +```css +.filedrop { + min-height: 120px; + border: 3px solid #D3D3D3; + text-align: center; + font-size: 24px; + padding: 32px; + border-radius: 4px; +} + +.filedrop.drag { + border: 3px dashed #1E90FF; +} + +.filedrop.ready { + border: 3px solid #32CD32; +} +``` + +```jsx +class FileDrop extends React.Component { + constructor(props) { + super(props); + this.dropRef = React.createRef(); + this.state = { + drag: false, + filename: '' + } + this.handleDrag = this.handleDrag.bind(this); + this.handleDragIn = this.handleDragIn.bind(this); + this.handleDragOut = this.handleDragOut.bind(this); + this.handleDrop = this.handleDrop.bind(this); + } + + handleDrag(e) { + e.preventDefault(); + e.stopPropagation(); + } + + handleDragIn(e) { + e.preventDefault(); + e.stopPropagation(); + this.dragCounter++; + if (e.dataTransfer.items && e.dataTransfer.items.length > 0) + this.setState({ drag: true }); + } + + handleDragOut(e) { + e.preventDefault(); + e.stopPropagation(); + this.dragCounter--; + if (this.dragCounter === 0) + this.setState({ drag: false }); + } + + handleDrop(e) { + e.preventDefault(); + e.stopPropagation(); + this.setState({ drag: false }); + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + this.props.handleDrop(e.dataTransfer.files[0]); + this.setState({ filename : e.dataTransfer.files[0].name}); + e.dataTransfer.clearData(); + this.dragCounter = 0; + } + } + + componentDidMount() { + let div = this.dropRef.current; + div.addEventListener('dragenter', this.handleDragIn); + div.addEventListener('dragleave', this.handleDragOut); + div.addEventListener('dragover', this.handleDrag); + div.addEventListener('drop', this.handleDrop); + } + + componentWillUnmount() { + let div = this.dropRef.current; + div.removeEventListener('dragenter', this.handleDragIn); + div.removeEventListener('dragleave', this.handleDragOut); + div.removeEventListener('dragover', this.handleDrag); + div.removeEventListener('drop', this.handleDrop); + } + + render() { + return ( +
+ {this.state.filename && !this.state.drag ? +
{this.state.filename}
+ :
Drop files here!
+ } +
+ ) + } +} +``` + +
+Examples + +```jsx +ReactDOM.render(, document.getElementById('root')); +``` +
+ +
[⬆ Back to top](#table-of-contents) + ### Mailto Renders a link formatted to send an email. diff --git a/data/snippet_data.json b/data/snippet_data.json index 111bb6414..ff439afa7 100644 --- a/data/snippet_data.json +++ b/data/snippet_data.json @@ -79,6 +79,24 @@ ], "notes": [] }, + { + "name": "FileDrop.md", + "title": "FileDrop", + "text": "Renders a file drag and drop component for a single file.\n\nCreate a ref called `dropRef` for this component.\nInitialize `state.drag` and `state.filename` to `false` and `''` respectively.\nThe variables `dragCounter` and `state.drag` are used to determine if a file is being dragged, while `state.filename` is used to store the dropped file's name.\nCreate the `handleDrag`, `handleDragIn`, `handleDragOut` and `handleDrop` methods to handle drag and drop functionality, bind them to the component's context.\nEach of the methods will handle a specific event, the listeners for which are created and removed in `componentDidMount` and `componentWillUnmount` respectively.\n`handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `this.props.handleDrop`.\nIn the `render()` method, create an appropriately styled `
` and use `state.drag` and `state.filename` to determine its contents and style. \nFinally, bind the `ref` of the created `
` to `dropRef`.\n\n\n", + "codeBlocks": [ + "```css\n.filedrop {\n min-height: 120px;\n border: 3px solid #D3D3D3;\n text-align: center;\n font-size: 24px;\n padding: 32px;\n border-radius: 4px;\n}\n\n.filedrop.drag {\n border: 3px dashed #1E90FF;\n}\n\n.filedrop.ready {\n border: 3px solid #32CD32;\n}\n```", + "```jsx\nclass FileDrop extends React.Component {\n constructor(props) {\n super(props);\n this.dropRef = React.createRef();\n this.state = {\n drag: false,\n filename: ''\n }\n this.handleDrag = this.handleDrag.bind(this);\n this.handleDragIn = this.handleDragIn.bind(this);\n this.handleDragOut = this.handleDragOut.bind(this);\n this.handleDrop = this.handleDrop.bind(this);\n }\n\n handleDrag(e) {\n e.preventDefault();\n e.stopPropagation();\n }\n\n handleDragIn(e) {\n e.preventDefault();\n e.stopPropagation();\n this.dragCounter++;\n if (e.dataTransfer.items && e.dataTransfer.items.length > 0) \n this.setState({ drag: true });\n }\n\n handleDragOut(e) {\n e.preventDefault();\n e.stopPropagation();\n this.dragCounter--;\n if (this.dragCounter === 0) \n this.setState({ drag: false });\n }\n\n handleDrop(e) {\n e.preventDefault();\n e.stopPropagation();\n this.setState({ drag: false });\n if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n this.props.handleDrop(e.dataTransfer.files[0]);\n this.setState({ filename : e.dataTransfer.files[0].name});\n e.dataTransfer.clearData();\n this.dragCounter = 0;\n }\n }\n\n componentDidMount() {\n let div = this.dropRef.current;\n div.addEventListener('dragenter', this.handleDragIn);\n div.addEventListener('dragleave', this.handleDragOut);\n div.addEventListener('dragover', this.handleDrag);\n div.addEventListener('drop', this.handleDrop);\n }\n\n componentWillUnmount() {\n let div = this.dropRef.current;\n div.removeEventListener('dragenter', this.handleDragIn);\n div.removeEventListener('dragleave', this.handleDragOut);\n div.removeEventListener('dragover', this.handleDrag);\n div.removeEventListener('drop', this.handleDrop);\n }\n\n render() {\n return (\n
\n {this.state.filename && !this.state.drag ? \n
{this.state.filename}
\n :
Drop files here!
\n }\n
\n )\n }\n}\n```", + "```jsx\nReactDOM.render(, document.getElementById('root'));\n```" + ], + "expertise": 2, + "tags": [ + "visual", + "input", + "state", + "class" + ], + "notes": [] + }, { "name": "Input.md", "title": "Input", @@ -298,5 +316,24 @@ "class" ], "notes": [] + }, + { + "name": "TreeView.md", + "title": "TreeView", + "text": "Renders a tree view of a JSON object or array with collapsible content.\n\nUse `defaultProps` to set the default values of certain props.\nUse the value of the `toggled` prop to determine the initial state of the content (collapsed/expanded).\nSet the `state` of the component to the value of the `toggled` prop and bind the `toggle` method to the component's context.\nCreate a method, `toggle`, which uses `Component.prototype.setState` to change the component's `state` from collapsed to expanded and vice versa.\nIn the `render()` method, use a `
` to wrap the contents of the component and the `` element, used to alter the component's `state`.\nDetermine the appearance of the component, based on `this.props.isParentToggled`, `this.state.toggled`, `this.props.name` and `Array.isArray()` on `this.props.data`. \nFor each child in `this.props.data`, determine if it is an object or array and recursively render a sub-tree.\nOtherwise, render a `

` element with the appropriate style.\n\n", + "codeBlocks": [ + "```css\n.tree-element {\n margin: 0;\n position: relative;\n}\n\ndiv.tree-element:before {\n content: '';\n position: absolute;\n top: 24px;\n left: 1px;\n height: calc(100% - 48px);\n border-left: 1px solid gray;\n}\n\n.toggler {\n position: absolute;\n top: 10px;\n left: 0px;\n width: 0; \n height: 0; \n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n border-left: 5px solid gray;\n cursor: pointer;\n}\n\n.toggler.closed {\n transform: rotate(90deg);\n}\n\n.collapsed {\n display: none;\n}\n```", + "```jsx\nclass TreeView extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n toggled: props.toggled\n }\n this.toggle = this.toggle.bind(this);\n }\n\n toggle() {\n this.setState(state => ({ toggled: !state.toggled }));\n }\n\n render() {\n return (\n

\n \n {this.props.name ?   {this.props.name}: :   }\n {Array.isArray(this.props.data) ? '[' : '{'}\n {!this.state.toggled && '...'}\n {Object.keys(this.props.data).map(\n (v, i, a) =>\n typeof this.props.data[v] == \"object\" ? (\n \n ) : (\n

\n {Array.isArray(this.props.data) ? '' : {v}: }\n {this.props.data[v]}{i === a.length - 1 ? '' : ','}\n

\n )\n )}\n {Array.isArray(this.props.data) ? ']' : '}'}\n {!this.props.isLast ? ',' : ''}\n
\n )\n }\n}\n\nTreeView.defaultProps = {\n isLast: true,\n toggled: true,\n name: null,\n isChildElement: false,\n isParentToggled: true\n}\n```", + "```jsx\nlet data = {\n lorem: {\n ipsum: \"dolor sit\",\n amet: {\n consectetur: \"adipiscing\",\n elit: [\n \"duis\",\n \"vitae\",\n {\n semper: \"orci\"\n },\n {\n est: \"sed ornare\"\n },\n \"etiam\",\n [\"laoreet\", \"tincidunt\"],\n [\"vestibulum\", \"ante\"]\n ]\n },\n ipsum: \"primis\"\n }\n};\nReactDOM.render(, document.getElementById(\"root\"));\n```" + ], + "expertise": 2, + "tags": [ + "object", + "visual", + "children", + "state", + "class" + ], + "notes": [] } ] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7fd82ec42..87c16ff88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,63 +8,72 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.3" } }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "sprintf-js": "1.0.3" } }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true }, "caller-callsite": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, "requires": { - "callsites": "^2.0.0" + "callsites": "2.0.0" } }, "caller-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, "requires": { - "caller-callsite": "^2.0.0" + "caller-callsite": "2.0.0" } }, "callsites": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -72,450 +81,513 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "cosmiconfig": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.7.tgz", "integrity": "sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA==", + "dev": true, "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0" + "import-fresh": "2.0.0", + "is-directory": "0.3.1", + "js-yaml": "3.12.1", + "parse-json": "4.0.0" } }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "nice-try": "1.0.5", + "path-key": "2.0.1", + "semver": "5.6.0", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, "requires": { - "once": "^1.4.0" + "once": "1.4.0" } }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true }, "execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "6.0.5", + "get-stream": "4.1.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "3.0.0" } }, "fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "graceful-fs": "4.1.15", + "jsonfile": "4.0.0", + "universalify": "0.1.2" } }, "get-stdin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==" + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, "requires": { - "pump": "^3.0.0" + "pump": "3.0.0" } }, "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true }, "husky": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/husky/-/husky-1.3.1.tgz", "integrity": "sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg==", + "dev": true, "requires": { - "cosmiconfig": "^5.0.7", - "execa": "^1.0.0", - "find-up": "^3.0.0", - "get-stdin": "^6.0.0", - "is-ci": "^2.0.0", - "pkg-dir": "^3.0.0", - "please-upgrade-node": "^3.1.1", - "read-pkg": "^4.0.1", - "run-node": "^1.0.0", - "slash": "^2.0.0" + "cosmiconfig": "5.0.7", + "execa": "1.0.0", + "find-up": "3.0.0", + "get-stdin": "6.0.0", + "is-ci": "2.0.0", + "pkg-dir": "3.0.0", + "please-upgrade-node": "3.1.1", + "read-pkg": "4.0.1", + "run-node": "1.0.0", + "slash": "2.0.0" } }, "import-fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" + "caller-path": "2.0.0", + "resolve-from": "3.0.0" } }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true }, "is-builtin-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-ci": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, "requires": { - "ci-info": "^2.0.0" + "ci-info": "2.0.0" } }, "is-directory": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "js-yaml": { "version": "3.12.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "1.0.10", + "esprima": "4.0.1" } }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "4.1.15" } }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "3.0.0", + "path-exists": "3.0.0" } }, "markdown-builder": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/markdown-builder/-/markdown-builder-0.9.0.tgz", "integrity": "sha512-UovCyEEzMeKE7l88fbOk9SIJkOG7KXkg+TdudN8rvOtCtBO5uu1X27HSnM7LS/xH+vaShJLGpkBcYYcojWNx/g==", + "dev": true, "requires": { - "husky": "^1.0.0-rc.14" + "husky": "1.3.1" } }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "normalize-package-data": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.2.tgz", "integrity": "sha512-YcMnjqeoUckXTPKZSAsPjUPLxH85XotbpqK3w4RyCwdFQSU5FxxBys8buehkSfg0j9fKvV1hn7O0+8reEgkAiw==", + "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.7.1", + "is-builtin-module": "1.0.0", + "semver": "5.6.0", + "validate-npm-package-license": "3.0.4" } }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true }, "p-limit": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, "requires": { - "p-try": "^2.0.0" + "p-try": "2.0.0" } }, "p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "2.1.0" } }, "p-try": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "error-ex": "1.3.2", + "json-parse-better-errors": "1.0.2" } }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true }, "pkg-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, "requires": { - "find-up": "^3.0.0" + "find-up": "3.0.0" } }, "please-upgrade-node": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz", "integrity": "sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==", + "dev": true, "requires": { - "semver-compare": "^1.0.0" + "semver-compare": "1.0.0" } }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, "read-pkg": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=", + "dev": true, "requires": { - "normalize-package-data": "^2.3.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0" + "normalize-package-data": "2.4.2", + "parse-json": "4.0.0", + "pify": "3.0.0" } }, "resolve-from": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true }, "run-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz", - "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==" + "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", + "dev": true }, "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true }, "semver-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true }, "slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.3" } }, "spdx-exceptions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true }, "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.2.0", + "spdx-license-ids": "3.0.3" } }, "spdx-license-ids": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", - "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==" + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", + "dev": true }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "spdx-correct": "3.1.0", + "spdx-expression-parse": "3.0.0" } }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true } } } diff --git a/snippets/TreeView.md b/snippets/TreeView.md index de2e1bf52..9b47810ad 100644 --- a/snippets/TreeView.md +++ b/snippets/TreeView.md @@ -121,6 +121,6 @@ let data = { ReactDOM.render(, document.getElementById("root")); ``` - + From 509f7e87faf629c808e7c7346e7da614ceac61d9 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Wed, 6 Feb 2019 22:22:29 +0200 Subject: [PATCH 04/28] Build README --- README.md | 56 +++++++++++++++++++++++++++++++----------- data/snippet_data.json | 2 +- snippets/TreeView.md | 56 +++++++++++++++++++++++++++++++----------- 3 files changed, 83 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 9515a25d0..2b71ac8be 100644 --- a/README.md +++ b/README.md @@ -474,7 +474,7 @@ class TreeView extends React.Component { super(props); this.state = { toggled: props.toggled - } + }; this.toggle = this.toggle.bind(this); } @@ -484,26 +484,52 @@ class TreeView extends React.Component { render() { return ( -
- - {this.props.name ?   {this.props.name}: :   } - {Array.isArray(this.props.data) ? '[' : '{'} - {!this.state.toggled && '...'} +
+ + {this.props.name ? ( +   {this.props.name}: + ) : ( +    + )} + {Array.isArray(this.props.data) ? "[" : "{"} + {!this.state.toggled && "..."} {Object.keys(this.props.data).map( (v, i, a) => typeof this.props.data[v] == "object" ? ( - + ) : ( -

- {Array.isArray(this.props.data) ? '' : {v}: } - {this.props.data[v]}{i === a.length - 1 ? '' : ','} -

- ) +

+ {Array.isArray(this.props.data) ? "" : {v}: } + {this.props.data[v]} + {i === a.length - 1 ? "" : ","} +

+ ) )} - {Array.isArray(this.props.data) ? ']' : '}'} - {!this.props.isLast ? ',' : ''} + {Array.isArray(this.props.data) ? "]" : "}"} + {!this.props.isLast ? "," : ""}
- ) + ); } } diff --git a/data/snippet_data.json b/data/snippet_data.json index ff439afa7..1a2e70ca6 100644 --- a/data/snippet_data.json +++ b/data/snippet_data.json @@ -323,7 +323,7 @@ "text": "Renders a tree view of a JSON object or array with collapsible content.\n\nUse `defaultProps` to set the default values of certain props.\nUse the value of the `toggled` prop to determine the initial state of the content (collapsed/expanded).\nSet the `state` of the component to the value of the `toggled` prop and bind the `toggle` method to the component's context.\nCreate a method, `toggle`, which uses `Component.prototype.setState` to change the component's `state` from collapsed to expanded and vice versa.\nIn the `render()` method, use a `
` to wrap the contents of the component and the `` element, used to alter the component's `state`.\nDetermine the appearance of the component, based on `this.props.isParentToggled`, `this.state.toggled`, `this.props.name` and `Array.isArray()` on `this.props.data`. \nFor each child in `this.props.data`, determine if it is an object or array and recursively render a sub-tree.\nOtherwise, render a `

` element with the appropriate style.\n\n", "codeBlocks": [ "```css\n.tree-element {\n margin: 0;\n position: relative;\n}\n\ndiv.tree-element:before {\n content: '';\n position: absolute;\n top: 24px;\n left: 1px;\n height: calc(100% - 48px);\n border-left: 1px solid gray;\n}\n\n.toggler {\n position: absolute;\n top: 10px;\n left: 0px;\n width: 0; \n height: 0; \n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n border-left: 5px solid gray;\n cursor: pointer;\n}\n\n.toggler.closed {\n transform: rotate(90deg);\n}\n\n.collapsed {\n display: none;\n}\n```", - "```jsx\nclass TreeView extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n toggled: props.toggled\n }\n this.toggle = this.toggle.bind(this);\n }\n\n toggle() {\n this.setState(state => ({ toggled: !state.toggled }));\n }\n\n render() {\n return (\n

\n \n {this.props.name ?   {this.props.name}: :   }\n {Array.isArray(this.props.data) ? '[' : '{'}\n {!this.state.toggled && '...'}\n {Object.keys(this.props.data).map(\n (v, i, a) =>\n typeof this.props.data[v] == \"object\" ? (\n \n ) : (\n

\n {Array.isArray(this.props.data) ? '' : {v}: }\n {this.props.data[v]}{i === a.length - 1 ? '' : ','}\n

\n )\n )}\n {Array.isArray(this.props.data) ? ']' : '}'}\n {!this.props.isLast ? ',' : ''}\n
\n )\n }\n}\n\nTreeView.defaultProps = {\n isLast: true,\n toggled: true,\n name: null,\n isChildElement: false,\n isParentToggled: true\n}\n```", + "```jsx\nclass TreeView extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n toggled: props.toggled\n };\n this.toggle = this.toggle.bind(this);\n }\n\n toggle() {\n this.setState(state => ({ toggled: !state.toggled }));\n }\n\n render() {\n return (\n \n \n {this.props.name ? (\n   {this.props.name}: \n ) : (\n   \n )}\n {Array.isArray(this.props.data) ? \"[\" : \"{\"}\n {!this.state.toggled && \"...\"}\n {Object.keys(this.props.data).map(\n (v, i, a) =>\n typeof this.props.data[v] == \"object\" ? (\n \n ) : (\n \n {Array.isArray(this.props.data) ? \"\" : {v}: }\n {this.props.data[v]}\n {i === a.length - 1 ? \"\" : \",\"}\n

\n )\n )}\n {Array.isArray(this.props.data) ? \"]\" : \"}\"}\n {!this.props.isLast ? \",\" : \"\"}\n
\n );\n }\n}\n\nTreeView.defaultProps = {\n isLast: true,\n toggled: true,\n name: null,\n isChildElement: false,\n isParentToggled: true\n}\n```", "```jsx\nlet data = {\n lorem: {\n ipsum: \"dolor sit\",\n amet: {\n consectetur: \"adipiscing\",\n elit: [\n \"duis\",\n \"vitae\",\n {\n semper: \"orci\"\n },\n {\n est: \"sed ornare\"\n },\n \"etiam\",\n [\"laoreet\", \"tincidunt\"],\n [\"vestibulum\", \"ante\"]\n ]\n },\n ipsum: \"primis\"\n }\n};\nReactDOM.render(, document.getElementById(\"root\"));\n```" ], "expertise": 2, diff --git a/snippets/TreeView.md b/snippets/TreeView.md index 9b47810ad..955817b98 100644 --- a/snippets/TreeView.md +++ b/snippets/TreeView.md @@ -53,7 +53,7 @@ class TreeView extends React.Component { super(props); this.state = { toggled: props.toggled - } + }; this.toggle = this.toggle.bind(this); } @@ -63,26 +63,52 @@ class TreeView extends React.Component { render() { return ( -
- - {this.props.name ?   {this.props.name}: :   } - {Array.isArray(this.props.data) ? '[' : '{'} - {!this.state.toggled && '...'} +
+ + {this.props.name ? ( +   {this.props.name}: + ) : ( +    + )} + {Array.isArray(this.props.data) ? "[" : "{"} + {!this.state.toggled && "..."} {Object.keys(this.props.data).map( (v, i, a) => typeof this.props.data[v] == "object" ? ( - + ) : ( -

- {Array.isArray(this.props.data) ? '' : {v}: } - {this.props.data[v]}{i === a.length - 1 ? '' : ','} -

- ) +

+ {Array.isArray(this.props.data) ? "" : {v}: } + {this.props.data[v]} + {i === a.length - 1 ? "" : ","} +

+ ) )} - {Array.isArray(this.props.data) ? ']' : '}'} - {!this.props.isLast ? ',' : ''} + {Array.isArray(this.props.data) ? "]" : "}"} + {!this.props.isLast ? "," : ""}
- ) + ); } } From 088b247ad9a6f42d04fae8d62834b48c42f89dee Mon Sep 17 00:00:00 2001 From: komv8i Date: Thu, 7 Feb 2019 11:22:58 -0500 Subject: [PATCH 05/28] change password revealer component to hooks implementation --- snippets/PasswordRevealer.md | 38 +++++++++++++++--------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/snippets/PasswordRevealer.md b/snippets/PasswordRevealer.md index 731f1030b..24263d8ff 100644 --- a/snippets/PasswordRevealer.md +++ b/snippets/PasswordRevealer.md @@ -8,30 +8,24 @@ In the`render()` method, use a`
` to wrap both the`` and the ` -
- ); - } + return ( +
+ {}} + /> + +
+ ); } ``` From 125e61fead738cd7a27f66367feb62c276a97124 Mon Sep 17 00:00:00 2001 From: komv8i Date: Thu, 7 Feb 2019 11:22:58 -0500 Subject: [PATCH 06/28] change limited text area component to hooks implementation --- snippets/LimitedTextarea.md | 65 ++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/snippets/LimitedTextarea.md b/snippets/LimitedTextarea.md index 995e73630..3fb7111a2 100644 --- a/snippets/LimitedTextarea.md +++ b/snippets/LimitedTextarea.md @@ -2,41 +2,41 @@ Renders a textarea component with a character limit. -Use the value of the `value` prop to determine the initial `state.content` and `state.characterCount` and the value of the `limit` props to determine the value of `state.limit`. -Create a method, `handleChange`, which trims the `event.target.value` data if necessary and uses `Component.prototype.setState` to update `state.content` and `state.characterCount`, and bind it to the component's context. +Use the `value` and `limit` props to pass in the initial `content` and the `limit` values for the LimitedTextArea component. +Create a method, `handleChange`, which trims the `event.target.value` data if necessary and updates `content` with the new entered content. In the`render()` method, use a`
` to wrap both the` -

{this.state.characterCount}/{this.props.limit}

-
- ); - } +function LimitedTextArea(props) { + const { rows, cols, value, limit } = props; + + const setFormattedContent = text => { + text.length > limit ? setContent(text.slice(0, limit)) : setContent(text); + }; + + const [content, setContent] = useState(value); + // Run once to test if the initial value is greater than the limit + useEffect(() => { + setFormattedContent(content); + }, []); + + const handleChange = event => { + setFormattedContent(event.target.value); + }; + + return ( +
+ -

{this.state.characterCount}/{this.props.limit}

-
- ); - } +function LimitedTextarea({ rows, cols, value, limit }) { + const [content, setContent] = React.useState(value); + + const setFormattedContent = text => { + text.length > limit ? setContent(text.slice(0, limit)) : setContent(text); + }; + + React.useEffect(() => { + setFormattedContent(content); + }, []); + + return ( +
+ \n

{this.state.characterCount}/{this.props.limit}

\n
\n );\n }\n}\n```", + "```jsx\nfunction LimitedTextarea({ rows, cols, value, limit }) {\n const [content, setContent] = React.useState(value);\n\n const setFormattedContent = text => {\n text.length > limit ? setContent(text.slice(0, limit)) : setContent(text);\n };\n\n React.useEffect(() => {\n setFormattedContent(content);\n }, []);\n\n return (\n
\n setFormattedContent(event.target.value)}\n value={content}\n />\n

\n {content.length}/{limit}\n

\n
\n );\n}\n```", "```jsx\nReactDOM.render(\n ,\n document.getElementById('root')\n);\n```" ], "expertise": 0, @@ -190,9 +190,9 @@ { "name": "PasswordRevealer.md", "title": "PasswordRevealer", - "text": "Renders a password input field with a reveal button.\n\nInitially set `state.shown` to `false` to ensure that the password is not shown by default.\nCreate a method, `toggleShown`, which uses `Component.prototype.setState` to change the input's state from shown to hidden and vice versa, bind it to the component's context.\nIn the`render()` method, use a`
` to wrap both the`` and the `\n
\n );\n }\n}\n```", + "```jsx\nfunction PasswordRevealer({ value }) {\n const [shown, setShown] = React.useState(false);\n\n return (\n
\n {}}\n />\n \n
\n );\n}\n```", "```jsx\nReactDOM.render(, document.getElementById('root'));\n```" ], "expertise": 0, @@ -320,10 +320,10 @@ { "name": "TreeView.md", "title": "TreeView", - "text": "Renders a tree view of a JSON object or array with collapsible content.\n\nUse `defaultProps` to set the default values of certain props.\nUse the value of the `toggled` prop to determine the initial state of the content (collapsed/expanded).\nSet the `state` of the component to the value of the `toggled` prop and bind the `toggle` method to the component's context.\nCreate a method, `toggle`, which uses `Component.prototype.setState` to change the component's `state` from collapsed to expanded and vice versa.\nIn the `render()` method, use a `
` to wrap the contents of the component and the `` element, used to alter the component's `state`.\nDetermine the appearance of the component, based on `this.props.isParentToggled`, `this.state.toggled`, `this.props.name` and `Array.isArray()` on `this.props.data`. \nFor each child in `this.props.data`, determine if it is an object or array and recursively render a sub-tree.\nOtherwise, render a `

` element with the appropriate style.\n\n", + "text": "Renders a tree view of a JSON object or array with collapsible content.\n\nUse object destructuring to set defaults for certain props. \nUse the value of the `toggled` prop to determine the initial state of the content (collapsed/expanded).\nUse the `React.setState()` hook to create the `isToggled` state variable and give it the value of the `toggled` prop initially.\nReturn a `

` to wrap the contents of the component and the `` element, used to alter the component's `isToggled` state.\nDetermine the appearance of the component, based on `isParentToggled`, `isToggled`, `name` and `Array.isArray()` on `data`. \nFor each child in `data`, determine if it is an object or array and recursively render a sub-tree.\nOtherwise, render a `

` element with the appropriate style.\n\n", "codeBlocks": [ "```css\n.tree-element {\n margin: 0;\n position: relative;\n}\n\ndiv.tree-element:before {\n content: '';\n position: absolute;\n top: 24px;\n left: 1px;\n height: calc(100% - 48px);\n border-left: 1px solid gray;\n}\n\n.toggler {\n position: absolute;\n top: 10px;\n left: 0px;\n width: 0; \n height: 0; \n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n border-left: 5px solid gray;\n cursor: pointer;\n}\n\n.toggler.closed {\n transform: rotate(90deg);\n}\n\n.collapsed {\n display: none;\n}\n```", - "```jsx\nclass TreeView extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n toggled: props.toggled\n };\n this.toggle = this.toggle.bind(this);\n }\n\n toggle() {\n this.setState(state => ({ toggled: !state.toggled }));\n }\n\n render() {\n return (\n \n \n {this.props.name ? (\n   {this.props.name}: \n ) : (\n   \n )}\n {Array.isArray(this.props.data) ? \"[\" : \"{\"}\n {!this.state.toggled && \"...\"}\n {Object.keys(this.props.data).map(\n (v, i, a) =>\n typeof this.props.data[v] == \"object\" ? (\n \n ) : (\n \n {Array.isArray(this.props.data) ? \"\" : {v}: }\n {this.props.data[v]}\n {i === a.length - 1 ? \"\" : \",\"}\n

\n )\n )}\n {Array.isArray(this.props.data) ? \"]\" : \"}\"}\n {!this.props.isLast ? \",\" : \"\"}\n
\n );\n }\n}\n\nTreeView.defaultProps = {\n isLast: true,\n toggled: true,\n name: null,\n isChildElement: false,\n isParentToggled: true\n}\n```", + "```jsx\nfunction TreeView({\n data,\n toggled = true,\n name = null,\n isLast = true,\n isChildElement = false,\n isParentToggled = true\n}) {\n const [isToggled, setIsToggled] = React.useState(toggled);\n\n return (\n \n setIsToggled(!isToggled)}\n />\n {name ?   {name}: :   }\n {Array.isArray(data) ? \"[\" : \"{\"}\n {!isToggled && \"...\"}\n {Object.keys(data).map(\n (v, i, a) =>\n typeof data[v] == \"object\" ? (\n \n ) : (\n \n {Array.isArray(data) ? \"\" : {v}: }\n {data[v]}\n {i === a.length - 1 ? \"\" : \",\"}\n

\n )\n )}\n {Array.isArray(data) ? \"]\" : \"}\"}\n {!isLast ? \",\" : \"\"}\n
\n );\n}\n```", "```jsx\nlet data = {\n lorem: {\n ipsum: \"dolor sit\",\n amet: {\n consectetur: \"adipiscing\",\n elit: [\n \"duis\",\n \"vitae\",\n {\n semper: \"orci\"\n },\n {\n est: \"sed ornare\"\n },\n \"etiam\",\n [\"laoreet\", \"tincidunt\"],\n [\"vestibulum\", \"ante\"]\n ]\n },\n ipsum: \"primis\"\n }\n};\nReactDOM.render(, document.getElementById(\"root\"));\n```" ], "expertise": 2, From be19eaadd163ce6e718857542b8a96c010b7f21a Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Tue, 12 Feb 2019 20:46:21 +0200 Subject: [PATCH 16/28] Update FileDrop to Hooks --- README.md | 111 +++++++++++++++++++---------------------- data/snippet_data.json | 4 +- snippets/FileDrop.md | 111 +++++++++++++++++++---------------------- 3 files changed, 102 insertions(+), 124 deletions(-) diff --git a/README.md b/README.md index 39e3adc24..c1b257ad1 100644 --- a/README.md +++ b/README.md @@ -714,12 +714,13 @@ ReactDOM.render( Renders a file drag and drop component for a single file. Create a ref called `dropRef` for this component. -Initialize `state.drag` and `state.filename` to `false` and `''` respectively. -The variables `dragCounter` and `state.drag` are used to determine if a file is being dragged, while `state.filename` is used to store the dropped file's name. +Use the `React.useState()` hook to create the `drag` and `filename` variables, initialized to `false` and `''` respectively. +The variables `dragCounter` and `drag` are used to determine if a file is being dragged, while `filename` is used to store the dropped file's name. + Create the `handleDrag`, `handleDragIn`, `handleDragOut` and `handleDrop` methods to handle drag and drop functionality, bind them to the component's context. -Each of the methods will handle a specific event, the listeners for which are created and removed in `componentDidMount` and `componentWillUnmount` respectively. -`handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `this.props.handleDrop`. -In the `render()` method, create an appropriately styled `
` and use `state.drag` and `state.filename` to determine its contents and style. +Each of the methods will handle a specific event, the listeners for which are created and removed in the `React.useEffect()` hook and its attached `cleanup()` method. +`handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `props.handleDrop`. +Return an appropriately styled `
` and use `drag` and `filename` to determine its contents and style. Finally, bind the `ref` of the created `
` to `dropRef`. @@ -743,79 +744,67 @@ Finally, bind the `ref` of the created `
` to `dropRef`. ``` ```jsx -class FileDrop extends React.Component { - constructor(props) { - super(props); - this.dropRef = React.createRef(); - this.state = { - drag: false, - filename: '' - } - this.handleDrag = this.handleDrag.bind(this); - this.handleDragIn = this.handleDragIn.bind(this); - this.handleDragOut = this.handleDragOut.bind(this); - this.handleDrop = this.handleDrop.bind(this); - } +function FileDrop(props) { + const [drag, setDrag] = React.useState(false); + const [filename, setFilename] = React.useState(''); + let dropRef = React.createRef(); + let dragCounter = 0; - handleDrag(e) { + const handleDrag = e => { e.preventDefault(); e.stopPropagation(); - } + }; - handleDragIn(e) { + const handleDragIn = e => { e.preventDefault(); e.stopPropagation(); - this.dragCounter++; - if (e.dataTransfer.items && e.dataTransfer.items.length > 0) - this.setState({ drag: true }); - } + dragCounter++; + if (e.dataTransfer.items && e.dataTransfer.items.length > 0) setDrag(true); + }; - handleDragOut(e) { + const handleDragOut = e => { e.preventDefault(); e.stopPropagation(); - this.dragCounter--; - if (this.dragCounter === 0) - this.setState({ drag: false }); - } + dragCounter--; + if (dragCounter === 0) setDrag(false); + }; - handleDrop(e) { + const handleDrop = e => { e.preventDefault(); e.stopPropagation(); - this.setState({ drag: false }); + setDrag(false); if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { - this.props.handleDrop(e.dataTransfer.files[0]); - this.setState({ filename : e.dataTransfer.files[0].name}); + props.handleDrop(e.dataTransfer.files[0]); + setFilename(e.dataTransfer.files[0].name); e.dataTransfer.clearData(); - this.dragCounter = 0; + dragCounter = 0; } - } + }; - componentDidMount() { - let div = this.dropRef.current; - div.addEventListener('dragenter', this.handleDragIn); - div.addEventListener('dragleave', this.handleDragOut); - div.addEventListener('dragover', this.handleDrag); - div.addEventListener('drop', this.handleDrop); - } + React.useEffect(() => { + let div = dropRef.current; + div.addEventListener("dragenter", handleDragIn); + div.addEventListener("dragleave", handleDragOut); + div.addEventListener("dragover", handleDrag); + div.addEventListener("drop", handleDrop); + return function cleanup() { + div.removeEventListener("dragenter", handleDragIn); + div.removeEventListener("dragleave", handleDragOut); + div.removeEventListener("dragover", handleDrag); + div.removeEventListener("drop", handleDrop); + }; + }); - componentWillUnmount() { - let div = this.dropRef.current; - div.removeEventListener('dragenter', this.handleDragIn); - div.removeEventListener('dragleave', this.handleDragOut); - div.removeEventListener('dragover', this.handleDrag); - div.removeEventListener('drop', this.handleDrop); - } - - render() { - return ( -
- {this.state.filename && !this.state.drag ? -
{this.state.filename}
- :
Drop files here!
- } -
- ) - } + return ( +
+ {filename && !drag ?
{filename}
:
Drop files here!
} +
+ ); } ``` diff --git a/data/snippet_data.json b/data/snippet_data.json index 42be599d0..fdc493f20 100644 --- a/data/snippet_data.json +++ b/data/snippet_data.json @@ -82,10 +82,10 @@ { "name": "FileDrop.md", "title": "FileDrop", - "text": "Renders a file drag and drop component for a single file.\n\nCreate a ref called `dropRef` for this component.\nInitialize `state.drag` and `state.filename` to `false` and `''` respectively.\nThe variables `dragCounter` and `state.drag` are used to determine if a file is being dragged, while `state.filename` is used to store the dropped file's name.\nCreate the `handleDrag`, `handleDragIn`, `handleDragOut` and `handleDrop` methods to handle drag and drop functionality, bind them to the component's context.\nEach of the methods will handle a specific event, the listeners for which are created and removed in `componentDidMount` and `componentWillUnmount` respectively.\n`handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `this.props.handleDrop`.\nIn the `render()` method, create an appropriately styled `
` and use `state.drag` and `state.filename` to determine its contents and style. \nFinally, bind the `ref` of the created `
` to `dropRef`.\n\n\n", + "text": "Renders a file drag and drop component for a single file.\n\nCreate a ref called `dropRef` for this component.\nUse the `React.useState()` hook to create the `drag` and `filename` variables, initialized to `false` and `''` respectively.\nThe variables `dragCounter` and `drag` are used to determine if a file is being dragged, while `filename` is used to store the dropped file's name.\n\nCreate the `handleDrag`, `handleDragIn`, `handleDragOut` and `handleDrop` methods to handle drag and drop functionality, bind them to the component's context.\nEach of the methods will handle a specific event, the listeners for which are created and removed in the `React.useEffect()` hook and its attached `cleanup()` method.\n`handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `props.handleDrop`.\nReturn an appropriately styled `
` and use `drag` and `filename` to determine its contents and style. \nFinally, bind the `ref` of the created `
` to `dropRef`.\n\n\n", "codeBlocks": [ "```css\n.filedrop {\n min-height: 120px;\n border: 3px solid #D3D3D3;\n text-align: center;\n font-size: 24px;\n padding: 32px;\n border-radius: 4px;\n}\n\n.filedrop.drag {\n border: 3px dashed #1E90FF;\n}\n\n.filedrop.ready {\n border: 3px solid #32CD32;\n}\n```", - "```jsx\nclass FileDrop extends React.Component {\n constructor(props) {\n super(props);\n this.dropRef = React.createRef();\n this.state = {\n drag: false,\n filename: ''\n }\n this.handleDrag = this.handleDrag.bind(this);\n this.handleDragIn = this.handleDragIn.bind(this);\n this.handleDragOut = this.handleDragOut.bind(this);\n this.handleDrop = this.handleDrop.bind(this);\n }\n\n handleDrag(e) {\n e.preventDefault();\n e.stopPropagation();\n }\n\n handleDragIn(e) {\n e.preventDefault();\n e.stopPropagation();\n this.dragCounter++;\n if (e.dataTransfer.items && e.dataTransfer.items.length > 0) \n this.setState({ drag: true });\n }\n\n handleDragOut(e) {\n e.preventDefault();\n e.stopPropagation();\n this.dragCounter--;\n if (this.dragCounter === 0) \n this.setState({ drag: false });\n }\n\n handleDrop(e) {\n e.preventDefault();\n e.stopPropagation();\n this.setState({ drag: false });\n if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n this.props.handleDrop(e.dataTransfer.files[0]);\n this.setState({ filename : e.dataTransfer.files[0].name});\n e.dataTransfer.clearData();\n this.dragCounter = 0;\n }\n }\n\n componentDidMount() {\n let div = this.dropRef.current;\n div.addEventListener('dragenter', this.handleDragIn);\n div.addEventListener('dragleave', this.handleDragOut);\n div.addEventListener('dragover', this.handleDrag);\n div.addEventListener('drop', this.handleDrop);\n }\n\n componentWillUnmount() {\n let div = this.dropRef.current;\n div.removeEventListener('dragenter', this.handleDragIn);\n div.removeEventListener('dragleave', this.handleDragOut);\n div.removeEventListener('dragover', this.handleDrag);\n div.removeEventListener('drop', this.handleDrop);\n }\n\n render() {\n return (\n
\n {this.state.filename && !this.state.drag ? \n
{this.state.filename}
\n :
Drop files here!
\n }\n
\n )\n }\n}\n```", + "```jsx\nfunction FileDrop(props) {\n const [drag, setDrag] = React.useState(false);\n const [filename, setFilename] = React.useState('');\n let dropRef = React.createRef();\n let dragCounter = 0;\n\n const handleDrag = e => {\n e.preventDefault();\n e.stopPropagation();\n };\n\n const handleDragIn = e => {\n e.preventDefault();\n e.stopPropagation();\n dragCounter++;\n if (e.dataTransfer.items && e.dataTransfer.items.length > 0) setDrag(true);\n };\n\n const handleDragOut = e => {\n e.preventDefault();\n e.stopPropagation();\n dragCounter--;\n if (dragCounter === 0) setDrag(false);\n };\n\n const handleDrop = e => {\n e.preventDefault();\n e.stopPropagation();\n setDrag(false);\n if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n props.handleDrop(e.dataTransfer.files[0]);\n setFilename(e.dataTransfer.files[0].name);\n e.dataTransfer.clearData();\n dragCounter = 0;\n }\n };\n\n React.useEffect(() => {\n let div = dropRef.current;\n div.addEventListener(\"dragenter\", handleDragIn);\n div.addEventListener(\"dragleave\", handleDragOut);\n div.addEventListener(\"dragover\", handleDrag);\n div.addEventListener(\"drop\", handleDrop);\n return function cleanup() {\n div.removeEventListener(\"dragenter\", handleDragIn);\n div.removeEventListener(\"dragleave\", handleDragOut);\n div.removeEventListener(\"dragover\", handleDrag);\n div.removeEventListener(\"drop\", handleDrop);\n };\n });\n\n return (\n \n {filename && !drag ?
{filename}
:
Drop files here!
}\n
\n );\n}\n```", "```jsx\nReactDOM.render(, document.getElementById('root'));\n```" ], "expertise": 2, diff --git a/snippets/FileDrop.md b/snippets/FileDrop.md index c64075fee..7fefbb103 100644 --- a/snippets/FileDrop.md +++ b/snippets/FileDrop.md @@ -3,12 +3,13 @@ Renders a file drag and drop component for a single file. Create a ref called `dropRef` for this component. -Initialize `state.drag` and `state.filename` to `false` and `''` respectively. -The variables `dragCounter` and `state.drag` are used to determine if a file is being dragged, while `state.filename` is used to store the dropped file's name. +Use the `React.useState()` hook to create the `drag` and `filename` variables, initialized to `false` and `''` respectively. +The variables `dragCounter` and `drag` are used to determine if a file is being dragged, while `filename` is used to store the dropped file's name. + Create the `handleDrag`, `handleDragIn`, `handleDragOut` and `handleDrop` methods to handle drag and drop functionality, bind them to the component's context. -Each of the methods will handle a specific event, the listeners for which are created and removed in `componentDidMount` and `componentWillUnmount` respectively. -`handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `this.props.handleDrop`. -In the `render()` method, create an appropriately styled `
` and use `state.drag` and `state.filename` to determine its contents and style. +Each of the methods will handle a specific event, the listeners for which are created and removed in the `React.useEffect()` hook and its attached `cleanup()` method. +`handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `props.handleDrop`. +Return an appropriately styled `
` and use `drag` and `filename` to determine its contents and style. Finally, bind the `ref` of the created `
` to `dropRef`. @@ -32,79 +33,67 @@ Finally, bind the `ref` of the created `
` to `dropRef`. ``` ```jsx -class FileDrop extends React.Component { - constructor(props) { - super(props); - this.dropRef = React.createRef(); - this.state = { - drag: false, - filename: '' - } - this.handleDrag = this.handleDrag.bind(this); - this.handleDragIn = this.handleDragIn.bind(this); - this.handleDragOut = this.handleDragOut.bind(this); - this.handleDrop = this.handleDrop.bind(this); - } +function FileDrop(props) { + const [drag, setDrag] = React.useState(false); + const [filename, setFilename] = React.useState(''); + let dropRef = React.createRef(); + let dragCounter = 0; - handleDrag(e) { + const handleDrag = e => { e.preventDefault(); e.stopPropagation(); - } + }; - handleDragIn(e) { + const handleDragIn = e => { e.preventDefault(); e.stopPropagation(); - this.dragCounter++; - if (e.dataTransfer.items && e.dataTransfer.items.length > 0) - this.setState({ drag: true }); - } + dragCounter++; + if (e.dataTransfer.items && e.dataTransfer.items.length > 0) setDrag(true); + }; - handleDragOut(e) { + const handleDragOut = e => { e.preventDefault(); e.stopPropagation(); - this.dragCounter--; - if (this.dragCounter === 0) - this.setState({ drag: false }); - } + dragCounter--; + if (dragCounter === 0) setDrag(false); + }; - handleDrop(e) { + const handleDrop = e => { e.preventDefault(); e.stopPropagation(); - this.setState({ drag: false }); + setDrag(false); if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { - this.props.handleDrop(e.dataTransfer.files[0]); - this.setState({ filename : e.dataTransfer.files[0].name}); + props.handleDrop(e.dataTransfer.files[0]); + setFilename(e.dataTransfer.files[0].name); e.dataTransfer.clearData(); - this.dragCounter = 0; + dragCounter = 0; } - } + }; - componentDidMount() { - let div = this.dropRef.current; - div.addEventListener('dragenter', this.handleDragIn); - div.addEventListener('dragleave', this.handleDragOut); - div.addEventListener('dragover', this.handleDrag); - div.addEventListener('drop', this.handleDrop); - } + React.useEffect(() => { + let div = dropRef.current; + div.addEventListener("dragenter", handleDragIn); + div.addEventListener("dragleave", handleDragOut); + div.addEventListener("dragover", handleDrag); + div.addEventListener("drop", handleDrop); + return function cleanup() { + div.removeEventListener("dragenter", handleDragIn); + div.removeEventListener("dragleave", handleDragOut); + div.removeEventListener("dragover", handleDrag); + div.removeEventListener("drop", handleDrop); + }; + }); - componentWillUnmount() { - let div = this.dropRef.current; - div.removeEventListener('dragenter', this.handleDragIn); - div.removeEventListener('dragleave', this.handleDragOut); - div.removeEventListener('dragover', this.handleDrag); - div.removeEventListener('drop', this.handleDrop); - } - - render() { - return ( -
- {this.state.filename && !this.state.drag ? -
{this.state.filename}
- :
Drop files here!
- } -
- ) - } + return ( +
+ {filename && !drag ?
{filename}
:
Drop files here!
} +
+ ); } ``` From a6cb089ed4b28b81ec52215132d4fcd946af9295 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Tue, 12 Feb 2019 21:01:31 +0200 Subject: [PATCH 17/28] Update StarRating --- README.md | 240 +++--------------- data/snippet_data.json | 28 +- snippets/StarRating.md | 96 +++---- {snippets => snippets_archive}/ModalDialog.md | 0 4 files changed, 80 insertions(+), 284 deletions(-) rename {snippets => snippets_archive}/ModalDialog.md (100%) diff --git a/README.md b/README.md index c1b257ad1..918e8a4f3 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,6 @@ * [Collapse](#collapse) * [FileDrop](#filedrop) * [Mailto](#mailto) -* [ModalDialog](#modaldialog) * [StarRating](#starrating) * [Tab](#tab) * [Ticker](#ticker) @@ -850,215 +849,54 @@ ReactDOM.render(
[⬆ Back to top](#table-of-contents) -### ModalDialog - -Renders a dialog component in a modal, controllable through events. -To use the component, import `ModalDialog` only once and then display it using `ModalDialog.show()`, passing the JSX templates and data as parameters. - -Define `modalHandler`, a method that will handle showing the modal dialog, set `state` to the default values initially and bind the `close` and `modalClick` methods to the component's context. -Define `close` and `modalClick` to toggle the visibility of the modal dialog, based on `state.closeOnClick`. -Use the CustomEvent API to listen for `modal` events, that can be dispatched from the `static` `show()` method, handle listeners appropriately from `componentDidMount` and `componentWillUnmount`. - -The `show()` method accepts an argument, that should contain three parameters: -* `title`, a string for the dialog's title -* `closeOnClick`, `true` if the modal should close on click or `false` if it should only close when clicking the *X* button -* `content`, which is the JSX content to be rendered inside the dialog - -Finally, in the `render()` method, use a `
` to wrap everything and render the modal dialog with the content passed to `show()`. - -```css - .modal { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.6); - z-index: 9998; - display: flex; - justify-content: center; - align-items: center; - } - .dialog { - background-color: white; - border-radius: 5px; - overflow: hidden; - } - .dialog-title { - box-sizing: border-box; - width: 100%; - height: 48px; - padding: 0 16px; - border-bottom: 0.5px solid #c3c3c3; - display: flex; - justify-content: space-between; - align-items: center; - } - .dialog-close { - font-size: 32px; - color: #c3c3c3; - cursor: pointer; - transform: rotate(45deg); - user-select: none; - } - .dialog-close:hover { - color: red; - } - .dialog-content { - min-width: 300px; - } -``` - -```jsx -class ModalDialog extends React.Component { - constructor() { - super(); - this.modalHandler = (e) => { - this.setState({ - data: e.detail.data, - visible: true - }); - }; - this.state = { - data: { - title: '', - closeOnClick: false, - content: '' - }, - visible: false - }; - this.close = this.close.bind(this); - this.modalClick = this.modalClick.bind(this); - } - render() { - return !this.state.visible ? null :
-
-
{ this.state.data.title }+
-
- { - this.state.data.content - } -
-
-
- } - componentDidMount() { - document.addEventListener('modal', this.modalHandler); - } - componentWillUnmount() { - document.removeEventListener('modal', this.modalHandler); - } - close() { - this.setState({ - visible: false, - data: { - title: '', - closeOnClick: false, - content: '' - } - }); - } - static show(data) { - document.dispatchEvent(new CustomEvent('modal', { - detail: { - data - } - })); - } - modalClick() { - if (this.state.data.closeOnClick) this.close(); - } -} -``` -#### Notes - -This component includes a lot of CSS, which might conflict with other CSS in your project. It is recomended for the modal to be a direct child of the body tag.,A more up-to-date method with lower compatibility is to use [Portals](https://reactjs.org/docs/portals.html) in React 16+.,, - -
-Examples - -```jsx -// add to render function - - -// every time you wanna call the dialog -// content is a jsx element -ModalDialog.show({ - title: 'Hello, world!', - closeOnClick: true, - content: -}); -``` -
- -
[⬆ Back to top](#table-of-contents) - ### StarRating Renders a star rating component. -Use and IIFE to define a functional component, called `Star` that will render each individual star with the appropriate appearance, based on the parent component's `state` and return the class component `StarRating`. -Use the value of the `rating` prop to determine if a valid rating is supplied and store it in `state.rating` (or `0` if invalid or not supplied). -Initialize `state.selection` to `0`. -Create two methods, `hoverOver` and `setRating`, that take an event as argument and update `state.selected` and `state.rating` according to it, bind them both to the component's context. -In the `render()` method, create a `
` to wrap the `` components, which are created using `Array.prototype.map` on an array of 5 elements, created using `Array.from`, and handle the `onMouseLeave` event to set `state.selection` to `0`, the `onClick` event to set -the `state.rating` and the `onMouseOver` event to set `state.selection` to the `star-id` attribute of the `event.target` respectively. +Define a component, called `Star` that will render each individual star with the appropriate appearance, based on the parent component's state. +In the `StarRating` component, use the `React.setState()` hook to define the `rating` and `selection` state variables with the initial values of `props.rating` (or `0` if invalid or not supplied) and `0`. +Create a method, `hoverOver`, that updates `selected` and `rating` according to the provided `event`. +Create a `
` to wrap the `` components, which are created using `Array.prototype.map` on an array of 5 elements, created using `Array.from`, and handle the `onMouseLeave` event to set `selection` to `0`, the `onClick` event to set the `rating` and the `onMouseOver` event to set `selection` to the `star-id` attribute of the `event.target` respectively. Finally, pass the appropriate values to each `` component (`starId` and `marked`). ```jsx -const StarRating = (function() { - function Star({ marked, starId }) { - return ( - - {marked ? '\u2605' : '\u2606'} - - ); - } +function Star({ marked, starId }) { + return ( + + {marked ? "\u2605" : "\u2606"} + + ); +} - return class StarRating extends React.Component { - constructor(props) { - super(props); - this.state = { - rating: typeof props.rating == 'number' ? props.rating : 0, - selection: 0 - }; - this.hoverOver = this.hoverOver.bind(this); - this.hoverOut = this.hoverOver.bind(this, null); - this.handleClick = this.handleClick.bind(this); - } - hoverOver(event) { - let val = 0; - if (event && event.target && event.target.getAttribute('star-id')) - val = event.target.getAttribute('star-id'); - this.setState(state => ({ selection: val })); - } - handleClick(event) { - const val = event.target.getAttribute('star-id') || this.state.rating; - this.setState(state => ({ rating: val })); - } - render() { - return ( -
- {Array.from({ length: 5 }, (v, i) => ( - = i+1 - : this.state.rating >= i+1 - } - /> - ))} -
- ); - } +function StarRating(props) { + const [rating, setRating] = React.useState( + typeof props.rating == "number" ? props.rating : 0 + ); + const [selection, setSelection] = React.useState(0); + const hoverOver = event => { + let val = 0; + if (event && event.target && event.target.getAttribute("star-id")) + val = event.target.getAttribute("star-id"); + setSelection(val); }; -})(); + return ( +
hoverOver(null)} + onClick={() => + setRating(event.target.getAttribute("star-id") || this.state.rating) + } + onMouseOver={hoverOver} + > + {Array.from({ length: 5 }, (v, i) => ( + = i + 1 : rating >= i + 1} + /> + ))} +
+ ); +} ```
diff --git a/data/snippet_data.json b/data/snippet_data.json index fdc493f20..6c8de8b51 100644 --- a/data/snippet_data.json +++ b/data/snippet_data.json @@ -163,30 +163,6 @@ "" ] }, - { - "name": "ModalDialog.md", - "title": "ModalDialog", - "text": "Renders a dialog component in a modal, controllable through events. \nTo use the component, import `ModalDialog` only once and then display it using `ModalDialog.show()`, passing the JSX templates and data as parameters.\n\nDefine `modalHandler`, a method that will handle showing the modal dialog, set `state` to the default values initially and bind the `close` and `modalClick` methods to the component's context.\nDefine `close` and `modalClick` to toggle the visibility of the modal dialog, based on `state.closeOnClick`.\nUse the CustomEvent API to listen for `modal` events, that can be dispatched from the `static` `show()` method, handle listeners appropriately from `componentDidMount` and `componentWillUnmount`.\n\nThe `show()` method accepts an argument, that should contain three parameters:\n* `title`, a string for the dialog's title\n* `closeOnClick`, `true` if the modal should close on click or `false` if it should only close when clicking the *X* button\n* `content`, which is the JSX content to be rendered inside the dialog\n\nFinally, in the `render()` method, use a `
` to wrap everything and render the modal dialog with the content passed to `show()`.\n\n", - "codeBlocks": [ - "```css\n .modal {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.6);\n z-index: 9998;\n display: flex;\n justify-content: center;\n align-items: center;\n }\n .dialog {\n background-color: white;\n border-radius: 5px;\n overflow: hidden;\n }\n .dialog-title {\n box-sizing: border-box;\n width: 100%;\n height: 48px;\n padding: 0 16px;\n border-bottom: 0.5px solid #c3c3c3;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .dialog-close {\n font-size: 32px;\n color: #c3c3c3;\n cursor: pointer;\n transform: rotate(45deg);\n user-select: none;\n }\n .dialog-close:hover {\n color: red;\n }\n .dialog-content {\n min-width: 300px;\n }\n```", - "```jsx\nclass ModalDialog extends React.Component {\n constructor() {\n super();\n this.modalHandler = (e) => {\n this.setState({\n data: e.detail.data,\n visible: true\n });\n };\n this.state = {\n data: {\n title: '',\n closeOnClick: false,\n content: ''\n },\n visible: false\n };\n this.close = this.close.bind(this);\n this.modalClick = this.modalClick.bind(this);\n }\n render() {\n return !this.state.visible ? null :
\n
\n
{ this.state.data.title }+
\n
\n {\n this.state.data.content\n }\n
\n
\n
\n }\n componentDidMount() {\n document.addEventListener('modal', this.modalHandler);\n }\n componentWillUnmount() {\n document.removeEventListener('modal', this.modalHandler);\n }\n close() {\n this.setState({\n visible: false,\n data: {\n title: '',\n closeOnClick: false,\n content: ''\n }\n });\n }\n static show(data) {\n document.dispatchEvent(new CustomEvent('modal', {\n detail: {\n data\n }\n }));\n }\n modalClick() {\n if (this.state.data.closeOnClick) this.close();\n }\n}\n```", - "```jsx\n// add to render function\n\n\n// every time you wanna call the dialog\n// content is a jsx element\nModalDialog.show({\n title: 'Hello, world!',\n closeOnClick: true,\n content: \n}); \n```" - ], - "expertise": 1, - "tags": [ - "visual", - "static", - "children", - "state", - "class" - ], - "notes": [ - "This component includes a lot of CSS, which might conflict with other CSS in your project. It is recomended for the modal to be a direct child of the body tag.", - "A more up-to-date method with lower compatibility is to use [Portals](https://reactjs.org/docs/portals.html) in React 16+.", - "", - "" - ] - }, { "name": "PasswordRevealer.md", "title": "PasswordRevealer", @@ -221,9 +197,9 @@ { "name": "StarRating.md", "title": "StarRating", - "text": "Renders a star rating component.\n\nUse and IIFE to define a functional component, called `Star` that will render each individual star with the appropriate appearance, based on the parent component's `state` and return the class component `StarRating`.\nUse the value of the `rating` prop to determine if a valid rating is supplied and store it in `state.rating` (or `0` if invalid or not supplied).\nInitialize `state.selection` to `0`.\nCreate two methods, `hoverOver` and `setRating`, that take an event as argument and update `state.selected` and `state.rating` according to it, bind them both to the component's context.\nIn the `render()` method, create a `
` to wrap the `` components, which are created using `Array.prototype.map` on an array of 5 elements, created using `Array.from`, and handle the `onMouseLeave` event to set `state.selection` to `0`, the `onClick` event to set\nthe `state.rating` and the `onMouseOver` event to set `state.selection` to the `star-id` attribute of the `event.target` respectively. \nFinally, pass the appropriate values to each `` component (`starId` and `marked`).\n\n", + "text": "Renders a star rating component.\n\nDefine a component, called `Star` that will render each individual star with the appropriate appearance, based on the parent component's state.\nIn the `StarRating` component, use the `React.setState()` hook to define the `rating` and `selection` state variables with the initial values of `props.rating` (or `0` if invalid or not supplied) and `0`.\nCreate a method, `hoverOver`, that updates `selected` and `rating` according to the provided `event`.\nCreate a `
` to wrap the `` components, which are created using `Array.prototype.map` on an array of 5 elements, created using `Array.from`, and handle the `onMouseLeave` event to set `selection` to `0`, the `onClick` event to set the `rating` and the `onMouseOver` event to set `selection` to the `star-id` attribute of the `event.target` respectively. \nFinally, pass the appropriate values to each `` component (`starId` and `marked`).\n\n", "codeBlocks": [ - "```jsx\nconst StarRating = (function() {\n function Star({ marked, starId }) {\n return (\n \n {marked ? '\\u2605' : '\\u2606'}\n \n );\n }\n\n return class StarRating extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n rating: typeof props.rating == 'number' ? props.rating : 0,\n selection: 0\n };\n this.hoverOver = this.hoverOver.bind(this);\n this.hoverOut = this.hoverOver.bind(this, null);\n this.handleClick = this.handleClick.bind(this);\n }\n hoverOver(event) {\n let val = 0;\n if (event && event.target && event.target.getAttribute('star-id'))\n val = event.target.getAttribute('star-id');\n this.setState(state => ({ selection: val }));\n }\n handleClick(event) {\n const val = event.target.getAttribute('star-id') || this.state.rating;\n this.setState(state => ({ rating: val }));\n }\n render() {\n return (\n \n {Array.from({ length: 5 }, (v, i) => (\n = i+1\n : this.state.rating >= i+1\n }\n />\n ))}\n
\n );\n }\n };\n})();\n```", + "```jsx\nfunction Star({ marked, starId }) {\n return (\n \n {marked ? \"\\u2605\" : \"\\u2606\"}\n \n );\n}\n\nfunction StarRating(props) {\n const [rating, setRating] = React.useState(\n typeof props.rating == \"number\" ? props.rating : 0\n );\n const [selection, setSelection] = React.useState(0);\n const hoverOver = event => {\n let val = 0;\n if (event && event.target && event.target.getAttribute(\"star-id\"))\n val = event.target.getAttribute(\"star-id\");\n setSelection(val);\n };\n return (\n hoverOver(null)}\n onClick={() =>\n setRating(event.target.getAttribute(\"star-id\") || this.state.rating)\n }\n onMouseOver={hoverOver}\n >\n {Array.from({ length: 5 }, (v, i) => (\n = i + 1 : rating >= i + 1}\n />\n ))}\n
\n );\n}\n```", "```jsx\nReactDOM.render(, document.getElementById('root'));\nReactDOM.render(, document.getElementById('root'));\n```" ], "expertise": 2, diff --git a/snippets/StarRating.md b/snippets/StarRating.md index 39bf22d34..60fe1309b 100644 --- a/snippets/StarRating.md +++ b/snippets/StarRating.md @@ -2,68 +2,50 @@ Renders a star rating component. -Use and IIFE to define a functional component, called `Star` that will render each individual star with the appropriate appearance, based on the parent component's `state` and return the class component `StarRating`. -Use the value of the `rating` prop to determine if a valid rating is supplied and store it in `state.rating` (or `0` if invalid or not supplied). -Initialize `state.selection` to `0`. -Create two methods, `hoverOver` and `setRating`, that take an event as argument and update `state.selected` and `state.rating` according to it, bind them both to the component's context. -In the `render()` method, create a `
` to wrap the `` components, which are created using `Array.prototype.map` on an array of 5 elements, created using `Array.from`, and handle the `onMouseLeave` event to set `state.selection` to `0`, the `onClick` event to set -the `state.rating` and the `onMouseOver` event to set `state.selection` to the `star-id` attribute of the `event.target` respectively. +Define a component, called `Star` that will render each individual star with the appropriate appearance, based on the parent component's state. +In the `StarRating` component, use the `React.setState()` hook to define the `rating` and `selection` state variables with the initial values of `props.rating` (or `0` if invalid or not supplied) and `0`. +Create a method, `hoverOver`, that updates `selected` and `rating` according to the provided `event`. +Create a `
` to wrap the `` components, which are created using `Array.prototype.map` on an array of 5 elements, created using `Array.from`, and handle the `onMouseLeave` event to set `selection` to `0`, the `onClick` event to set the `rating` and the `onMouseOver` event to set `selection` to the `star-id` attribute of the `event.target` respectively. Finally, pass the appropriate values to each `` component (`starId` and `marked`). ```jsx -const StarRating = (function() { - function Star({ marked, starId }) { - return ( - - {marked ? '\u2605' : '\u2606'} - - ); - } +function Star({ marked, starId }) { + return ( + + {marked ? "\u2605" : "\u2606"} + + ); +} - return class StarRating extends React.Component { - constructor(props) { - super(props); - this.state = { - rating: typeof props.rating == 'number' ? props.rating : 0, - selection: 0 - }; - this.hoverOver = this.hoverOver.bind(this); - this.hoverOut = this.hoverOver.bind(this, null); - this.handleClick = this.handleClick.bind(this); - } - hoverOver(event) { - let val = 0; - if (event && event.target && event.target.getAttribute('star-id')) - val = event.target.getAttribute('star-id'); - this.setState(state => ({ selection: val })); - } - handleClick(event) { - const val = event.target.getAttribute('star-id') || this.state.rating; - this.setState(state => ({ rating: val })); - } - render() { - return ( -
- {Array.from({ length: 5 }, (v, i) => ( - = i+1 - : this.state.rating >= i+1 - } - /> - ))} -
- ); - } +function StarRating(props) { + const [rating, setRating] = React.useState( + typeof props.rating == "number" ? props.rating : 0 + ); + const [selection, setSelection] = React.useState(0); + const hoverOver = event => { + let val = 0; + if (event && event.target && event.target.getAttribute("star-id")) + val = event.target.getAttribute("star-id"); + setSelection(val); }; -})(); + return ( +
hoverOver(null)} + onClick={() => + setRating(event.target.getAttribute("star-id") || this.state.rating) + } + onMouseOver={hoverOver} + > + {Array.from({ length: 5 }, (v, i) => ( + = i + 1 : rating >= i + 1} + /> + ))} +
+ ); +} ``` ```jsx diff --git a/snippets/ModalDialog.md b/snippets_archive/ModalDialog.md similarity index 100% rename from snippets/ModalDialog.md rename to snippets_archive/ModalDialog.md From f485e78339a7adad338aa1509aa3ef12a24e5d7e Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Tue, 12 Feb 2019 21:10:05 +0200 Subject: [PATCH 18/28] Update tabs to Hooks --- README.md | 95 +++++++++++++++++------------------------- data/snippet_data.json | 6 +-- snippets/Tabs.md | 95 +++++++++++++++++------------------------- 3 files changed, 81 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index 918e8a4f3..78e729de2 100644 --- a/README.md +++ b/README.md @@ -914,10 +914,11 @@ ReactDOM.render(, document.getElementById('root')); Renders a tabbed menu and view component. -Define `TabItem` as a middleware, pass it to the `Tab` and remove unnecessary nodes expect for `TabItem` by identifying the function's name in `props.children`. +Define a `TabItem` component, pass it to the `Tab` and remove unnecessary nodes expect for `TabItem` by identifying the function's name in `props.children`. +Use the `React.useState()` hook to initialize the value of the `bindIndex` state variable to `props.defaultIndex`. Use `Array.prototype.map` on the collected nodes to render the `tab-menu` and `tab-view`. Define `changeTab`, which will be executed when clicking a ` - ))} -
-
- {items.map(({ props }) => ( -
- ))} -
-
- ); - } -} function TabItem(props) { return
; } + +function Tabs(props) { + const [bindIndex, setBindIndex] = React.useState(props.defaultIndex); + const changeTab = newIndex => { + if (typeof props.onTabClick === "function") props.onTabClick(newIndex); + setBindIndex(newIndex); + }; + const items = props.children.filter(item => item.type.name === "TabItem"); + + return ( +
+
+ {items.map(({ props: { index, label } }) => ( + + ))} +
+
+ {items.map(({ props }) => ( +
+ ))} +
+
+ ); +} ```
@@ -998,14 +981,14 @@ function TabItem(props) { ```jsx ReactDOM.render( - + Lorem ipsum Dolor sit amet - , + , document.getElementById("root") ); diff --git a/data/snippet_data.json b/data/snippet_data.json index 6c8de8b51..648993aa6 100644 --- a/data/snippet_data.json +++ b/data/snippet_data.json @@ -215,11 +215,11 @@ { "name": "Tabs.md", "title": "Tab", - "text": "Renders a tabbed menu and view component.\n\nDefine `TabItem` as a middleware, pass it to the `Tab` and remove unnecessary nodes expect for `TabItem` by identifying the function's name in `props.children`.\nUse `Array.prototype.map` on the collected nodes to render the `tab-menu` and `tab-view`. \nDefine `changeTab`, which will be executed when clicking a `\n ))}\n
\n
\n {items.map(({ props }) => (\n \n ))}\n
\n
\n );\n }\n}\nfunction TabItem(props) {\n return
;\n}\n```", - "```jsx\nReactDOM.render(\n \n \n Lorem ipsum\n \n \n Dolor sit amet\n \n ,\n document.getElementById(\"root\")\n);\n\n```" + "```jsx\nfunction TabItem(props) {\n return
;\n}\n\nfunction Tabs(props) {\n const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);\n const changeTab = newIndex => {\n if (typeof props.onTabClick === \"function\") props.onTabClick(newIndex);\n setBindIndex(newIndex);\n };\n const items = props.children.filter(item => item.type.name === \"TabItem\");\n\n return (\n
\n
\n {items.map(({ props: { index, label } }) => (\n changeTab(index)}\n className={bindIndex === index ? \"focus\" : \"\"}\n >\n {label}\n \n ))}\n
\n
\n {items.map(({ props }) => (\n \n ))}\n
\n
\n );\n}\n```", + "```jsx\nReactDOM.render(\n \n \n Lorem ipsum\n \n \n Dolor sit amet\n \n ,\n document.getElementById(\"root\")\n);\n\n```" ], "expertise": 1, "tags": [ diff --git a/snippets/Tabs.md b/snippets/Tabs.md index 8582e9974..e1847e625 100644 --- a/snippets/Tabs.md +++ b/snippets/Tabs.md @@ -1,10 +1,11 @@ ### Tab Renders a tabbed menu and view component. -Define `TabItem` as a middleware, pass it to the `Tab` and remove unnecessary nodes expect for `TabItem` by identifying the function's name in `props.children`. +Define a `TabItem` component, pass it to the `Tab` and remove unnecessary nodes expect for `TabItem` by identifying the function's name in `props.children`. +Use the `React.useState()` hook to initialize the value of the `bindIndex` state variable to `props.defaultIndex`. Use `Array.prototype.map` on the collected nodes to render the `tab-menu` and `tab-view`. Define `changeTab`, which will be executed when clicking a ` - ))} -
-
- {items.map(({ props }) => ( -
- ))} -
-
- ); - } -} function TabItem(props) { return
; } + +function Tabs(props) { + const [bindIndex, setBindIndex] = React.useState(props.defaultIndex); + const changeTab = newIndex => { + if (typeof props.onTabClick === "function") props.onTabClick(newIndex); + setBindIndex(newIndex); + }; + const items = props.children.filter(item => item.type.name === "TabItem"); + + return ( +
+
+ {items.map(({ props: { index, label } }) => ( + + ))} +
+
+ {items.map(({ props }) => ( +
+ ))} +
+
+ ); +} ``` ```jsx ReactDOM.render( - + Lorem ipsum Dolor sit amet - , + , document.getElementById("root") ); From d170e23ec26f5abc960810379aaaa84282e128f0 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Tue, 12 Feb 2019 21:18:22 +0200 Subject: [PATCH 19/28] Update Ticker --- snippets/Ticker.md | 60 ++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/snippets/Ticker.md b/snippets/Ticker.md index e319fb69b..06373cd78 100644 --- a/snippets/Ticker.md +++ b/snippets/Ticker.md @@ -2,45 +2,37 @@ Renders a ticker component. -- The ticker state is initially set to zero -- When the `Tick!` button is clicked, `timer` is incremented periodically at the given `interval` -- When the `Reset` button is clicked, the value of the timer is set to zero and the `setInterval` is cleared -- The `setInterval` is cleared once the desired `time` is reached -- `time` and `interval` are the required props +Use the `React.useState()` hook to initialize the `ticker` state variable to `0`. +Define two methods, `tick` and `reset`, that will periodically increment `timer` based on `interval` and reset `interval` respectively. +Return a `
` with two ` - -
- ); - } + return ( +
+ {this.state.ticker} + + +
+ ); } ``` From 8095487476f824acac8941e5d6edab7d3805f8d3 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Tue, 12 Feb 2019 21:22:50 +0200 Subject: [PATCH 20/28] Update Toggle --- snippets/Toggle.md | 59 ++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/snippets/Toggle.md b/snippets/Toggle.md index 92e86ab16..fc28ef00d 100644 --- a/snippets/Toggle.md +++ b/snippets/Toggle.md @@ -2,49 +2,30 @@ Renders a toggle component. -Initialize `state.isToggleOn` to `false`, bind the `handleClick` method to the component's context. +Use the `React.useState()` to initialize the `isToggleOn` state variable to `false`. Use an object, `style`, to hold the styles for individual components and their states. -Create a method, `handleClick`, which uses `Component.prototype.setState` to change the component's `state.toggleOn`. -In the `render()` method, destructure `state` and `style`, create a ` - ); - } + return ( + + ); } ``` From c842d0eecb5c3abf7aa8172d5bb21ce7e323f8c3 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Tue, 12 Feb 2019 22:02:49 +0200 Subject: [PATCH 21/28] Update Tooltip --- README.md | 214 ++++++++++++++++------------------------- data/snippet_data.json | 13 +-- snippets/Tooltip.md | 94 +++++++----------- 3 files changed, 128 insertions(+), 193 deletions(-) diff --git a/README.md b/README.md index 78e729de2..1fa1288bf 100644 --- a/README.md +++ b/README.md @@ -1001,45 +1001,37 @@ ReactDOM.render( Renders a ticker component. -- The ticker state is initially set to zero -- When the `Tick!` button is clicked, `timer` is incremented periodically at the given `interval` -- When the `Reset` button is clicked, the value of the timer is set to zero and the `setInterval` is cleared -- The `setInterval` is cleared once the desired `time` is reached -- `time` and `interval` are the required props +Use the `React.useState()` hook to initialize the `ticker` state variable to `0`. +Define two methods, `tick` and `reset`, that will periodically increment `timer` based on `interval` and reset `interval` respectively. +Return a `
` with two ` - -
- ); - } + return ( +
+ {this.state.ticker} + + +
+ ); } ``` @@ -1057,49 +1049,30 @@ ReactDOM.render(, document.getElementById('r Renders a toggle component. -Initialize `state.isToggleOn` to `false`, bind the `handleClick` method to the component's context. +Use the `React.useState()` to initialize the `isToggleOn` state variable to `false`. Use an object, `style`, to hold the styles for individual components and their states. -Create a method, `handleClick`, which uses `Component.prototype.setState` to change the component's `state.toggleOn`. -In the `render()` method, destructure `state` and `style`, create a ` - ); - } + return ( + + ); } ``` @@ -1117,67 +1090,48 @@ ReactDOM.render(, document.getElementById('root')); Renders a tooltip component. -Set the `state` of the component to `show: false` initially, define an object, `style`, to hold the styles for individual components and their states. -Create a method, `toggleTooltip`, which uses `this.setState` to change the state's `show` property from `true` to `false` and vice versa. -Bind `showTooltip` and `hideTooltip` to the component's context with the respective values of `true` and `false`. -In the `render()` method, compute if the tooltip should be shown or hidden, render the content of the tooltip and bind the `onMouseEnter` and `onMouseLeave` events to `showTooltip` and `hideTooltip` respectively. +Use the `React.useState()` hook to create the `show` variable and initialize it to `false`. +Return a `
` element that contains the `
` that will be the tooltip and the `children` passed to the component. +Handle the `onMouseEnter` and `onMouseLeave` methods, by altering the value of the `show` variable. +```css +.tooltip { + position: relative; + background: rgba(0, 0, 0, 0.7); + color: white; + visibility: hidden; + padding: 5px; + border-radius: 5px; +} +.tooltip-arrow { + position: absolute; + top: 100%; + left: 50%; + border-width: 5px; + border-style: solid; + border-color: rgba(0, 0, 0, 0.7) transparent transparent; +} +``` + ```jsx -class Tooltip extends React.Component { - constructor(props) { - super(props); - this.state = { - show: false - }; - this.style = { - tooltip: { - position: 'relative', - backgroundColor: "rgba(0,0,0,0.7)", - color: "white", - visibility: "hidden", - width: "fit-content", - padding: 5, - borderRadius: 5 - }, - tooltipArrow: { - position: 'absolute', - top: '100%', - left: '50%', - borderWidth: 5, - borderStyle: 'solid', - borderColor: "rgba(0,0,0,0.7) transparent transparent", - }, - visible: { - visibility: "visible" - }, - }; - this.showTooltip = this.toggleTooltip.bind(this, true); - this.hideTooltip = this.toggleTooltip.bind(this, false); - } +function Tooltip({ children, text, ...rest }) { + const [show, setShow] = React.useState(false); - toggleTooltip = tooltipState => { - this.setState({ - show: tooltipState - }); - }; - - render() { - const { children, text, ...rest } = this.props; - const { show } = this.state; - const { visible, tooltip, tooltipArrow } = this.style; - const showTooltip = show ? visible : {}; - return ( -
-
- {text} - -
-
- {children} -
+ return ( +
+
+ {text} +
- ); - } +
setShow(true)} + onMouseLeave={() => setShow(false)} + > + {children} +
+
+ ); } ``` diff --git a/data/snippet_data.json b/data/snippet_data.json index 648993aa6..3b19501ad 100644 --- a/data/snippet_data.json +++ b/data/snippet_data.json @@ -247,9 +247,9 @@ { "name": "Ticker.md", "title": "Ticker", - "text": "Renders a ticker component.\n\n- The ticker state is initially set to zero \n- When the `Tick!` button is clicked, `timer` is incremented periodically at the given `interval`\n- When the `Reset` button is clicked, the value of the timer is set to zero and the `setInterval` is cleared\n- The `setInterval` is cleared once the desired `time` is reached\n- `time` and `interval` are the required props\n\n", + "text": "Renders a ticker component.\n\nUse the `React.useState()` hook to initialize the `ticker` state variable to `0`.\nDefine two methods, `tick` and `reset`, that will periodically increment `timer` based on `interval` and reset `interval` respectively.\nReturn a `
` with two `\n\t\t\t\t\n\t\t\t
\n\t\t);\n\t}\n}\n```", + "```jsx\nfunction Ticker(props) {\n const [ticker, setTicker] = React.useState(0);\n let interval = null;\n\n const tick = () => {\n reset();\n interval = setInterval(() => {\n if (ticker < props.times) \n setTicker(ticker + 1);\n else \n clearInterval(interval);\n }, props.interval);\n }\n\n const reset = () => {\n setTicker(0);\n clearInterval(interval);\n }\n\n return (\n
\n {this.state.ticker}\n \n \n
\n );\n}\n```", "```jsx\nReactDOM.render(, document.getElementById('root'));\n```" ], "expertise": 1, @@ -263,9 +263,9 @@ { "name": "Toggle.md", "title": "Toggle", - "text": "Renders a toggle component.\n\nInitialize `state.isToggleOn` to `false`, bind the `handleClick` method to the component's context.\nUse an object, `style`, to hold the styles for individual components and their states.\nCreate a method, `handleClick`, which uses `Component.prototype.setState` to change the component's `state.toggleOn`.\nIn the `render()` method, destructure `state` and `style`, create a `\n );\n }\n}\n```", + "```jsx\nfunction Toggle(props) {\n const [isToggleOn, setIsToggleOn] = React.useState(false);\n style = {\n on: {\n backgroundColor: \"green\"\n },\n off: {\n backgroundColor: \"grey\"\n }\n };\n\n return (\n setIsToggleOn(!isToggleOn)}\n style={isToggleOn ? style.on : style.off}\n >\n {isToggleOn ? \"ON\" : \"OFF\"}\n \n );\n}\n```", "```jsx\nReactDOM.render(, document.getElementById('root'));\n```" ], "expertise": 0, @@ -279,9 +279,10 @@ { "name": "Tooltip.md", "title": "Tooltip", - "text": "Renders a tooltip component.\n\nSet the `state` of the component to `show: false` initially, define an object, `style`, to hold the styles for individual components and their states.\nCreate a method, `toggleTooltip`, which uses `this.setState` to change the state's `show` property from `true` to `false` and vice versa.\nBind `showTooltip` and `hideTooltip` to the component's context with the respective values of `true` and `false`.\nIn the `render()` method, compute if the tooltip should be shown or hidden, render the content of the tooltip and bind the `onMouseEnter` and `onMouseLeave` events to `showTooltip` and `hideTooltip` respectively.\n \n", + "text": "Renders a tooltip component.\n\nUse the `React.useState()` hook to create the `show` variable and initialize it to `false`.\nReturn a `
` element that contains the `
` that will be the tooltip and the `children` passed to the component.\nHandle the `onMouseEnter` and `onMouseLeave` methods, by altering the value of the `show` variable.\n \n", "codeBlocks": [ - "```jsx\nclass Tooltip extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n show: false\n };\n this.style = {\n tooltip: {\n position: 'relative',\n backgroundColor: \"rgba(0,0,0,0.7)\",\n color: \"white\",\n visibility: \"hidden\",\n width: \"fit-content\",\n padding: 5,\n borderRadius: 5\n },\n tooltipArrow: {\n position: 'absolute',\n top: '100%',\n left: '50%',\n borderWidth: 5,\n borderStyle: 'solid',\n borderColor: \"rgba(0,0,0,0.7) transparent transparent\",\n },\n visible: {\n visibility: \"visible\"\n },\n };\n this.showTooltip = this.toggleTooltip.bind(this, true);\n this.hideTooltip = this.toggleTooltip.bind(this, false);\n }\n\n toggleTooltip = tooltipState => {\n this.setState({\n show: tooltipState\n });\n };\n\n render() {\n const { children, text, ...rest } = this.props;\n const { show } = this.state;\n const { visible, tooltip, tooltipArrow } = this.style;\n const showTooltip = show ? visible : {};\n return (\n
\n
\n {text}\n \n
\n
\n {children}\n
\n
\n );\n }\n}\n```", + "```css\n.tooltip {\n position: relative;\n background: rgba(0, 0, 0, 0.7);\n color: white;\n visibility: hidden;\n padding: 5px;\n border-radius: 5px;\n}\n.tooltip-arrow {\n position: absolute;\n top: 100%;\n left: 50%;\n border-width: 5px;\n border-style: solid;\n border-color: rgba(0, 0, 0, 0.7) transparent transparent;\n}\n```", + "```jsx\nfunction Tooltip({ children, text, ...rest }) {\n const [show, setShow] = React.useState(false);\n\n return (\n
\n
\n {text}\n \n
\n setShow(true)}\n onMouseLeave={() => setShow(false)}\n >\n {children}\n
\n
\n );\n}\n```", "```jsx\n ReactDOM.render(\n \n \n ,\n document.getElementById('root')\n );\n```" ], "expertise": 1, diff --git a/snippets/Tooltip.md b/snippets/Tooltip.md index d99d1bca0..cf9e8285d 100644 --- a/snippets/Tooltip.md +++ b/snippets/Tooltip.md @@ -2,67 +2,47 @@ Renders a tooltip component. -Set the `state` of the component to `show: false` initially, define an object, `style`, to hold the styles for individual components and their states. -Create a method, `toggleTooltip`, which uses `this.setState` to change the state's `show` property from `true` to `false` and vice versa. -Bind `showTooltip` and `hideTooltip` to the component's context with the respective values of `true` and `false`. -In the `render()` method, compute if the tooltip should be shown or hidden, render the content of the tooltip and bind the `onMouseEnter` and `onMouseLeave` events to `showTooltip` and `hideTooltip` respectively. +Use the `React.useState()` hook to create the `show` variable and initialize it to `false`. +Return a `
` element that contains the `
` that will be the tooltip and the `children` passed to the component. +Handle the `onMouseEnter` and `onMouseLeave` methods, by altering the value of the `show` variable. +```css +.tooltip { + position: relative; + background: rgba(0, 0, 0, 0.7); + color: white; + visibility: hidden; + padding: 5px; + border-radius: 5px; +} +.tooltip-arrow { + position: absolute; + top: 100%; + left: 50%; + border-width: 5px; + border-style: solid; + border-color: rgba(0, 0, 0, 0.7) transparent transparent; +} +``` ```jsx -class Tooltip extends React.Component { - constructor(props) { - super(props); - this.state = { - show: false - }; - this.style = { - tooltip: { - position: 'relative', - backgroundColor: "rgba(0,0,0,0.7)", - color: "white", - visibility: "hidden", - width: "fit-content", - padding: 5, - borderRadius: 5 - }, - tooltipArrow: { - position: 'absolute', - top: '100%', - left: '50%', - borderWidth: 5, - borderStyle: 'solid', - borderColor: "rgba(0,0,0,0.7) transparent transparent", - }, - visible: { - visibility: "visible" - }, - }; - this.showTooltip = this.toggleTooltip.bind(this, true); - this.hideTooltip = this.toggleTooltip.bind(this, false); - } +function Tooltip({ children, text, ...rest }) { + const [show, setShow] = React.useState(false); - toggleTooltip = tooltipState => { - this.setState({ - show: tooltipState - }); - }; - - render() { - const { children, text, ...rest } = this.props; - const { show } = this.state; - const { visible, tooltip, tooltipArrow } = this.style; - const showTooltip = show ? visible : {}; - return ( -
-
- {text} - -
-
- {children} -
+ return ( +
+
+ {text} +
- ); - } +
setShow(true)} + onMouseLeave={() => setShow(false)} + > + {children} +
+
+ ); } ``` From bc9bbacad3f625d2eb8e3afd0b4f298a6c7f5096 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Tue, 12 Feb 2019 22:07:13 +0200 Subject: [PATCH 22/28] Update tags --- README.md | 2 +- data/snippet_data.json | 56 ++++++++++++++---------------------- snippets/AutoLink.md | 2 +- snippets/Carousel.md | 2 +- snippets/Collapse.md | 2 +- snippets/DataList.md | 2 +- snippets/DataTable.md | 2 +- snippets/FileDrop.md | 2 +- snippets/Input.md | 2 +- snippets/LimitedTextarea.md | 2 +- snippets/Mailto.md | 2 +- snippets/MappedTable.md | 2 +- snippets/PasswordRevealer.md | 2 +- snippets/Select.md | 2 +- snippets/StarRating.md | 2 +- snippets/Tabs.md | 2 +- snippets/TextArea.md | 2 +- snippets/Ticker.md | 2 +- snippets/Toggle.md | 2 +- snippets/Tooltip.md | 2 +- snippets/TreeView.md | 2 +- 21 files changed, 41 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 1fa1288bf..d1b03e367 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ function MappedTable({ data, propertyNames }) { ``` #### Notes -This component does not work with nested objects and will break if there are nested objects inside any of the properties specified in `propertyNames`.,, +This component does not work with nested objects and will break if there are nested objects inside any of the properties specified in `propertyNames`.,,
Examples diff --git a/data/snippet_data.json b/data/snippet_data.json index 3b19501ad..c984e6cdc 100644 --- a/data/snippet_data.json +++ b/data/snippet_data.json @@ -11,7 +11,7 @@ "tags": [ "string", "fragment", - "functional" + "regexp" ], "notes": [] }, @@ -28,7 +28,7 @@ "visual", "children", "state", - "class" + "effect" ], "notes": [] }, @@ -44,8 +44,7 @@ "tags": [ "visual", "children", - "state", - "class" + "state" ], "notes": [] }, @@ -59,8 +58,7 @@ ], "expertise": 0, "tags": [ - "array", - "functional" + "array" ], "notes": [] }, @@ -74,8 +72,7 @@ ], "expertise": 0, "tags": [ - "array", - "functional" + "array" ], "notes": [] }, @@ -93,7 +90,7 @@ "visual", "input", "state", - "class" + "effect" ], "notes": [] }, @@ -107,8 +104,7 @@ ], "expertise": 0, "tags": [ - "input", - "functional" + "input" ], "notes": [] }, @@ -124,7 +120,7 @@ "tags": [ "input", "state", - "class" + "effect" ], "notes": [] }, @@ -138,8 +134,7 @@ ], "expertise": 0, "tags": [ - "visual", - "functional" + "visual" ], "notes": [] }, @@ -154,12 +149,11 @@ "expertise": 1, "tags": [ "array", - "object", - "functional" + "object" ], "notes": [ "This component does not work with nested objects and will break if there are nested objects inside any of the properties specified in `propertyNames`.", - "", + "", "" ] }, @@ -174,8 +168,7 @@ "expertise": 0, "tags": [ "input", - "state", - "class" + "state" ], "notes": [] }, @@ -189,8 +182,7 @@ ], "expertise": 0, "tags": [ - "input", - "functional" + "input" ], "notes": [] }, @@ -207,8 +199,7 @@ "visual", "children", "input", - "state", - "class" + "state" ], "notes": [] }, @@ -224,8 +215,8 @@ "expertise": 1, "tags": [ "visual", - "children", - "class" + "state", + "children" ], "notes": [] }, @@ -239,8 +230,7 @@ ], "expertise": 0, "tags": [ - "input", - "functional" + "input" ], "notes": [] }, @@ -255,8 +245,7 @@ "expertise": 1, "tags": [ "visual", - "state", - "class" + "state" ], "notes": [] }, @@ -271,8 +260,7 @@ "expertise": 0, "tags": [ "visual", - "state", - "class" + "state" ], "notes": [] }, @@ -289,8 +277,7 @@ "tags": [ "visual", "state", - "children", - "class" + "children" ], "notes": [] }, @@ -307,9 +294,8 @@ "tags": [ "object", "visual", - "children", "state", - "class" + "recursion" ], "notes": [] } diff --git a/snippets/AutoLink.md b/snippets/AutoLink.md index 275cfe352..acfc046f2 100644 --- a/snippets/AutoLink.md +++ b/snippets/AutoLink.md @@ -33,6 +33,6 @@ ReactDOM.render( ); ``` - + diff --git a/snippets/Carousel.md b/snippets/Carousel.md index cb25268e3..ccf51ded0 100644 --- a/snippets/Carousel.md +++ b/snippets/Carousel.md @@ -61,7 +61,7 @@ ReactDOM.render( ); ``` - + diff --git a/snippets/Collapse.md b/snippets/Collapse.md index 04dd27182..a1f664798 100644 --- a/snippets/Collapse.md +++ b/snippets/Collapse.md @@ -55,6 +55,6 @@ ReactDOM.render( ); ``` - + diff --git a/snippets/DataList.md b/snippets/DataList.md index 99ee8980b..17cb6ccd3 100644 --- a/snippets/DataList.md +++ b/snippets/DataList.md @@ -21,6 +21,6 @@ ReactDOM.render(, document.getElementById('root')); ReactDOM.render(, document.getElementById('root')); ``` - + diff --git a/snippets/DataTable.md b/snippets/DataTable.md index 93dfce73e..53324bba3 100644 --- a/snippets/DataTable.md +++ b/snippets/DataTable.md @@ -36,6 +36,6 @@ ReactDOM.render( ); ``` - + diff --git a/snippets/FileDrop.md b/snippets/FileDrop.md index 7fefbb103..6f6a0daf9 100644 --- a/snippets/FileDrop.md +++ b/snippets/FileDrop.md @@ -101,6 +101,6 @@ function FileDrop(props) { ReactDOM.render(, document.getElementById('root')); ``` - + diff --git a/snippets/Input.md b/snippets/Input.md index d7734e36a..3278850eb 100644 --- a/snippets/Input.md +++ b/snippets/Input.md @@ -26,6 +26,6 @@ ReactDOM.render( ); ``` - + diff --git a/snippets/LimitedTextarea.md b/snippets/LimitedTextarea.md index 937746e81..e4be1428e 100644 --- a/snippets/LimitedTextarea.md +++ b/snippets/LimitedTextarea.md @@ -41,7 +41,7 @@ ReactDOM.render( document.getElementById('root') ); ``` - + diff --git a/snippets/Mailto.md b/snippets/Mailto.md index df3412efc..5b9b78fe3 100644 --- a/snippets/Mailto.md +++ b/snippets/Mailto.md @@ -24,6 +24,6 @@ ReactDOM.render( ); ``` - + diff --git a/snippets/MappedTable.md b/snippets/MappedTable.md index aada05e50..c97ec1b56 100644 --- a/snippets/MappedTable.md +++ b/snippets/MappedTable.md @@ -46,6 +46,6 @@ ReactDOM.render( #### Notes: * This component does not work with nested objects and will break if there are nested objects inside any of the properties specified in `propertyNames`. - + diff --git a/snippets/PasswordRevealer.md b/snippets/PasswordRevealer.md index 7e27c3e09..b00b0da53 100644 --- a/snippets/PasswordRevealer.md +++ b/snippets/PasswordRevealer.md @@ -26,7 +26,7 @@ function PasswordRevealer({ value }) { ReactDOM.render(, document.getElementById('root')); ``` - + diff --git a/snippets/Select.md b/snippets/Select.md index db4562517..114928c3c 100644 --- a/snippets/Select.md +++ b/snippets/Select.md @@ -33,6 +33,6 @@ ReactDOM.render( ); ``` - + diff --git a/snippets/StarRating.md b/snippets/StarRating.md index 60fe1309b..d1be76cad 100644 --- a/snippets/StarRating.md +++ b/snippets/StarRating.md @@ -53,6 +53,6 @@ ReactDOM.render(, document.getElementById('root')); ReactDOM.render(, document.getElementById('root')); ``` - + diff --git a/snippets/Tabs.md b/snippets/Tabs.md index e1847e625..d5d0268ae 100644 --- a/snippets/Tabs.md +++ b/snippets/Tabs.md @@ -77,6 +77,6 @@ ReactDOM.render( ``` - + diff --git a/snippets/TextArea.md b/snippets/TextArea.md index e67fac536..ac65e5943 100644 --- a/snippets/TextArea.md +++ b/snippets/TextArea.md @@ -27,6 +27,6 @@ ReactDOM.render( ); ``` - + diff --git a/snippets/Ticker.md b/snippets/Ticker.md index 06373cd78..3a6be6902 100644 --- a/snippets/Ticker.md +++ b/snippets/Ticker.md @@ -40,6 +40,6 @@ function Ticker(props) { ReactDOM.render(, document.getElementById('root')); ``` - + \ No newline at end of file diff --git a/snippets/Toggle.md b/snippets/Toggle.md index fc28ef00d..589958903 100644 --- a/snippets/Toggle.md +++ b/snippets/Toggle.md @@ -33,6 +33,6 @@ function Toggle(props) { ReactDOM.render(, document.getElementById('root')); ``` - + diff --git a/snippets/Tooltip.md b/snippets/Tooltip.md index cf9e8285d..39a2448c0 100644 --- a/snippets/Tooltip.md +++ b/snippets/Tooltip.md @@ -55,6 +55,6 @@ function Tooltip({ children, text, ...rest }) { ); ``` - + diff --git a/snippets/TreeView.md b/snippets/TreeView.md index 0a48dd5e2..6e8b7b8aa 100644 --- a/snippets/TreeView.md +++ b/snippets/TreeView.md @@ -123,6 +123,6 @@ let data = { ReactDOM.render(, document.getElementById("root")); ``` - + From 5fecba33a4e962eaaa4aff45c49ae1212f675742 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Tue, 12 Feb 2019 22:09:42 +0200 Subject: [PATCH 23/28] Update snippet template --- snippet-template.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/snippet-template.md b/snippet-template.md index d57150e6f..20398538c 100644 --- a/snippet-template.md +++ b/snippet-template.md @@ -6,18 +6,12 @@ Explain briefly how the snippet works. ```jsx function ComponentName(props) { + const [state, setState] = React.useState(null); + React.useEffect(() => { + setState(0); + }); return
{props}
; }; -``` - -```jsx -class ComponentName extends React.Component { - constructor(props){} - render(){ - return
{props}
; - } -} -``` ```jsx ReactDOM.render(, mountNode); From 77f218a3c22af963e4ca27238d42a8405b5411ca Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Tue, 12 Feb 2019 22:36:09 +0200 Subject: [PATCH 24/28] Update README, snippet-template, add CONTRIBUTING Resolve #36 Resolve #34 --- CONTRIBUTING.md | 81 ++++++++++++++++++++++++++++++++++++ README.md | 25 +++++++++++ snippet-template.md | 3 +- static-parts/README-start.md | 25 +++++++++++ 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..e7e82126a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,81 @@ +# Contribution Guidelines + +**30 seconds of React** is a community effort, so feel free to contribute in any way you can. Every contribution helps! + +Here's what you can do to help: + +- Submit [pull requests](https://github.com/30-seconds/30-seconds-of-react/pulls) with snippets that you have created (see below for guidelines). +- [Open issues](https://github.com/30-seconds/30-seconds-of-react/issues/new) for things you want to see added or modified. +- Be part of the discussion by helping out with [existing issues](https://github.com/30-seconds/30-seconds-of-react/issues). +- Fix typos in existing snippets, improve snippet descriptions and explanations or provide better examples. + +### Snippet submission and Pull request guidelines + +- **DO NOT MODIFY THE README.md or index.html FILES!** Make changes to individual snippet files. **Travis CI** will automatically build the `README.md` and `index.html` files when your pull request is merged. +- **Snippet filenames** must correspond to the title of the snippet. For example, if your snippet is titled `### AwesomeComponent` the filename should be `AwesomeComponent.md`. + - Use `TitleCase`, not `camelCase`, `kebab-case` or `snake_case` when naming components. + - Avoid capitalization of words, except if the whole word is capitalized (e.g. `URL` should be capitalized in the filename and the snippet title). +- **Snippet titles** should be the same as the name of the component that is present in the snippet. + - All snippet titles must be prefixed with `###` and be at the very first line of your snippet. + - Snippet titles must be unique (although if you cannot find a better title, just add some placeholder at the end of the filename and title and we will figure it out). + - Follow snippet titles with an empty line. +- **Snippet descriptions** must be short and to the point. Try to explain *what* the snippet does and *how* the snippet works and what Javascript/React features are used. Remember to include what functions you are using and why. + - Follow snippet descriptions with an empty line. +- **Snippet code** must be enclosed inside ` ```jsx ` and ` ``` `. + - If your snippet is accompanied by CSS code, enclose it inside ` ```css ` and ` ``` ` and present it before the JS/JSX code. + - Remember to start your snippet's code on a new line below the opening backticks. + - Use standard function notation to define your component. For example `function MyComponent(props) { ... }`. + - Do not write components using classes, use [React Hooks](https://reactjs.org/docs/hooks-intro.html) instead. + - Please use Javascript [Semi-Standard Style](https://github.com/Flet/semistandard). + - Try to keep your snippets' code short and to the point. Use modern techniques and features. Make sure to test your code before submitting. + - All snippets must be followed by one (more if necessary) test case after the code, in a new block enclosed inside ` ```jsx ` and ` ``` `. The syntax for this is `ReactDOM.render(, document.getElementById("root"));`. Use multiline examples only if necessary. + - Try to make your component name unique, so that it does not conflict with existing snippets. +- Snippets should be as brief as possible, without sacrificing functionality. If your snippet seems longer than most, you can still submit it, and we can help you shorten it or figure out ways to improve it. +- Snippets *should* solve real-world problems, no matter how simple. +- Snippets *should* be abstract enough to be applied to different scenarios. +- You can start creating a new snippet, by using the [snippet template](snippet-template.md) to format your snippets. + +### Additional guidelines and conventions regarding snippets + +- When describing snippets, refer to methods, using their full name. For example, use `Array.prototype.reduce()`, instead of `reduce()`. +- When using React Hooks, refer to the specific hooks with their full names, such as `React.useState()` and `React.useEffect()`. +- When using `React.useState()`, try matching the name of the state variable to the function that sets it. For example, use `[isShown, setIsShown]` instead of `[isShown, setShown]`. +- When using `React.useEffect()`, only return a function if you have to clean up. In that case, name that function `cleanup()`. +- Destructure your component's `props` whenever possible. If any of your props take default parameters, specify their default values in the destructured object. +- If your snippet uses recursion, explain the base cases. +- Always use `function MyComponent(props)` or `function MyComponent({ ... })` for function definitions. +- Use variables only when necessary. Prefer `const` when the values are not altered after assignment, otherwise, use `let`. Avoid using `var`. +- Use `camelCase` for function and variable names, if they consist of more than one word. +- Use `TitleCase` for component names. +- Try to give meaningful names to variables. For example use `letter`, instead of `lt`. Some exceptions to convention are: + - `arr` for arrays (usually as the snippet function's argument). + - `str` for strings. + - `num` or `n` for a numeric value (usually as the snippet function's argument). + - `el` for DOM elements (usually as the snippet function's argument). + - `val` or `v` for value (usually when iterating a list, mapping, sorting etc.). + - `acc` for accumulators in `Array.prototype.reduce()`. + - `(a,b)` for the two values compared when using `Array.prototype.sort()`. + - `i` for indexes. + - `fn` for function arguments. + - `nums` for arrays of numbers. +- Use `()` if your function takes no arguments. +- Use `_` if an argument inside some function (e.g. `Array.prototype.reduce()`) is not used anywhere in your code. +- Specify default parameters for arguments, if necessary. It is preferred to put default parameters last unless you have a pretty good reason not to. +- If your snippet's function takes variadic arguments, use `...args` or `...rest` (although in certain cases, it might be needed to use a different name). +- Always use soft tabs (2 spaces), never hard tabs. +- Omit curly braces (`{` and `}`) whenever possible. +- Always use single quotes for string literals. Use template literals, instead, if necessary. +- When rendering JSX, use double quotes, instead of single quotes. +- Prefer using `Array` methods whenever possible. +- Prefer `Array.prototype.concat()` instead of `Array.prototype.push()` when working with `Array.prototype.reduce()`. +- Use strict equality checking (`===` and `!==` instead of `==` and `!=`), unless you specifically have reason not to. +- Prefer using the ternary operator (`condition ? trueResult : falseResult`) instead of `if else` statements whenever possible. +- Avoid nesting ternary operators (but you can do it if you feel like you should). +- You should define multiple variables (e.g. `const x = 0, y = 0`) on the same line whenever possible. +- Do not use trailing or leading underscores in variable names. +- Use dot notation (`object.property`) for object properties, when possible. Use bracket notation (`object[variable]`) when accessing object properties using a variable. +- Use arrow functions as much as possible, except when you can't. +- Use semicolons whenever necessary. +- Leave a single space after a comma (`,`) character. +- Try to strike a balance between readability, brevity, and performance. +- Never use `eval()`. Your snippet will be disqualified immediately. \ No newline at end of file diff --git a/README.md b/README.md index d1b03e367..7ba36168c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,31 @@ > Curated collection of useful React snippets that you can understand in 30 seconds or less. +* Use Ctrl + F or command + F to search for a snippet. +* Contributions welcome, please read the [contribution guide](CONTRIBUTING.md). +* Snippets are written in React 16.8+, using hooks. + +### Prerequisites + +To import a snippet into your project, you must import `React` and copy-paste the component's JavaScript code like this: +```js +import React from 'react'; + +function MyComponent(props) { + /* ... */ +} +``` + +If there is any CSS related to your component, copy-paste it to a new file with the same name and the appropriate extension, then import it like this: +```js +import './MyComponent.css'; +``` + +To render your component, make sure there is a node with and id of `"root"` present in your element (preferrably a `
`) and that you have imported `ReactDOM`, like this: +```js +import ReactDOM from 'react-dom'; +``` + #### Related projects * [30 Seconds of Code](https://30secondsofcode.org) diff --git a/snippet-template.md b/snippet-template.md index 20398538c..4acb54fcc 100644 --- a/snippet-template.md +++ b/snippet-template.md @@ -12,9 +12,10 @@ function ComponentName(props) { }); return
{props}
; }; +``` ```jsx -ReactDOM.render(, mountNode); +ReactDOM.render(, document.getElementById("root")); ``` diff --git a/static-parts/README-start.md b/static-parts/README-start.md index c6bcd886d..4f524a7bb 100644 --- a/static-parts/README-start.md +++ b/static-parts/README-start.md @@ -4,6 +4,31 @@ > Curated collection of useful React snippets that you can understand in 30 seconds or less. +* Use Ctrl + F or command + F to search for a snippet. +* Contributions welcome, please read the [contribution guide](CONTRIBUTING.md). +* Snippets are written in React 16.8+, using hooks. + +### Prerequisites + +To import a snippet into your project, you must import `React` and copy-paste the component's JavaScript code like this: +```js +import React from 'react'; + +function MyComponent(props) { + /* ... */ +} +``` + +If there is any CSS related to your component, copy-paste it to a new file with the same name and the appropriate extension, then import it like this: +```js +import './MyComponent.css'; +``` + +To render your component, make sure there is a node with and id of `"root"` present in your element (preferrably a `
`) and that you have imported `ReactDOM`, like this: +```js +import ReactDOM from 'react-dom'; +``` + #### Related projects * [30 Seconds of Code](https://30secondsofcode.org) From 81f25f378ab9e6fe82c3f156315f6dad0fc665b3 Mon Sep 17 00:00:00 2001 From: raurir Date: Mon, 18 Feb 2019 15:35:57 +1100 Subject: [PATCH 25/28] fix vairable --- data/snippet_data.json | 2 +- snippets/PasswordRevealer.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/snippet_data.json b/data/snippet_data.json index c984e6cdc..c86fb17ac 100644 --- a/data/snippet_data.json +++ b/data/snippet_data.json @@ -160,7 +160,7 @@ { "name": "PasswordRevealer.md", "title": "PasswordRevealer", - "text": "Renders a password input field with a reveal button.\n\nUse the `React.useState()` hook to create the `shown` state vairable and set its value to `false`.\nUse a`
` to wrap both the`` and the `\n
\n );\n}\n```", "```jsx\nReactDOM.render(, document.getElementById('root'));\n```" diff --git a/snippets/PasswordRevealer.md b/snippets/PasswordRevealer.md index b00b0da53..6f6be083d 100644 --- a/snippets/PasswordRevealer.md +++ b/snippets/PasswordRevealer.md @@ -2,7 +2,7 @@ Renders a password input field with a reveal button. -Use the `React.useState()` hook to create the `shown` state vairable and set its value to `false`. +Use the `React.useState()` hook to create the `shown` state variable and set its value to `false`. Use a`
` to wrap both the`` and the `