Ran linter
This commit is contained in:
@ -19,7 +19,7 @@ Here's what you can do to help:
|
|||||||
- All snippet titles must be prefixed with `###` and be at the very first line of your 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).
|
- 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.
|
- 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.
|
- **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.
|
- Follow snippet descriptions with an empty line.
|
||||||
- **Snippet code** must be enclosed inside ` ```jsx ` and ` ``` `.
|
- **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.
|
- If your snippet is accompanied by CSS code, enclose it inside ` ```css ` and ` ``` ` and present it before the JS/JSX code.
|
||||||
@ -31,8 +31,8 @@ Here's what you can do to help:
|
|||||||
- 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(<MyComponent />, document.getElementById("root"));`. Use multiline examples only if necessary.
|
- 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(<MyComponent />, 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.
|
- 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 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_ solve real-world problems, no matter how simple.
|
||||||
- Snippets *should* be abstract enough to be applied to different scenarios.
|
- 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.
|
- 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
|
### Additional guidelines and conventions regarding snippets
|
||||||
@ -78,4 +78,4 @@ Here's what you can do to help:
|
|||||||
- Use semicolons whenever necessary.
|
- Use semicolons whenever necessary.
|
||||||
- Leave a single space after a comma (`,`) character.
|
- Leave a single space after a comma (`,`) character.
|
||||||
- Try to strike a balance between readability, brevity, and performance.
|
- Try to strike a balance between readability, brevity, and performance.
|
||||||
- Never use `eval()`. Your snippet will be disqualified immediately.
|
- Never use `eval()`. Your snippet will be disqualified immediately.
|
||||||
|
|||||||
@ -8,11 +8,7 @@
|
|||||||
"```jsx\nReactDOM.render(\n <AutoLink text='foo bar baz http://example.org bar' />,\n document.getElementById('root')\n);\n```"
|
"```jsx\nReactDOM.render(\n <AutoLink text='foo bar baz http://example.org bar' />,\n document.getElementById('root')\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 2,
|
"expertise": 2,
|
||||||
"tags": [
|
"tags": ["string", "fragment", "regexp"],
|
||||||
"string",
|
|
||||||
"fragment",
|
|
||||||
"regexp"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -24,12 +20,7 @@
|
|||||||
"```jsx\nReactDOM.render(\n <Carousel\n carouselItems={[\n <div>carousel item 1</div>,\n <div>carousel item 2</div>,\n <div>carousel item 3</div>\n ]}\n />,\n document.getElementById(\"root\")\n);\n ```"
|
"```jsx\nReactDOM.render(\n <Carousel\n carouselItems={[\n <div>carousel item 1</div>,\n <div>carousel item 2</div>,\n <div>carousel item 3</div>\n ]}\n />,\n document.getElementById(\"root\")\n);\n ```"
|
||||||
],
|
],
|
||||||
"expertise": 2,
|
"expertise": 2,
|
||||||
"tags": [
|
"tags": ["visual", "children", "state", "effect"],
|
||||||
"visual",
|
|
||||||
"children",
|
|
||||||
"state",
|
|
||||||
"effect"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -41,11 +32,7 @@
|
|||||||
"```jsx\nReactDOM.render(\n <Collapse>\n <h1>This is a collapse</h1>\n <p>Hello world!</p>\n </Collapse>,\n document.getElementById('root')\n);\n```"
|
"```jsx\nReactDOM.render(\n <Collapse>\n <h1>This is a collapse</h1>\n <p>Hello world!</p>\n </Collapse>,\n document.getElementById('root')\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 2,
|
"expertise": 2,
|
||||||
"tags": [
|
"tags": ["visual", "children", "state"],
|
||||||
"visual",
|
|
||||||
"children",
|
|
||||||
"state"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -57,10 +44,7 @@
|
|||||||
"```jsx\nReactDOM.render(\n <CountDown hours=\"1\" minutes=\"45\" />,\n document.getElementById('root')\n);\n```"
|
"```jsx\nReactDOM.render(\n <CountDown hours=\"1\" minutes=\"45\" />,\n document.getElementById('root')\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 2,
|
"expertise": 2,
|
||||||
"tags": [
|
"tags": ["visual", "state"],
|
||||||
"visual",
|
|
||||||
"state"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -72,9 +56,7 @@
|
|||||||
"```jsx\nconst names = ['John', 'Paul', 'Mary'];\nReactDOM.render(<DataList data={names}/>, document.getElementById('root'));\nReactDOM.render(<DataList data={names} isOrdered/>, document.getElementById('root'));\n```"
|
"```jsx\nconst names = ['John', 'Paul', 'Mary'];\nReactDOM.render(<DataList data={names}/>, document.getElementById('root'));\nReactDOM.render(<DataList data={names} isOrdered/>, document.getElementById('root'));\n```"
|
||||||
],
|
],
|
||||||
"expertise": 0,
|
"expertise": 0,
|
||||||
"tags": [
|
"tags": ["array"],
|
||||||
"array"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -86,9 +68,7 @@
|
|||||||
"```jsx\nconst people = ['John', 'Jesse'];\nReactDOM.render(\n <DataTable data={people} />,\n document.getElementById('root')\n);\n```"
|
"```jsx\nconst people = ['John', 'Jesse'];\nReactDOM.render(\n <DataTable data={people} />,\n document.getElementById('root')\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 0,
|
"expertise": 0,
|
||||||
"tags": [
|
"tags": ["array"],
|
||||||
"array"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -101,12 +81,7 @@
|
|||||||
"```jsx\nReactDOM.render(<FileDrop handleDrop={console.log}/>, document.getElementById('root'));\n```"
|
"```jsx\nReactDOM.render(<FileDrop handleDrop={console.log}/>, document.getElementById('root'));\n```"
|
||||||
],
|
],
|
||||||
"expertise": 2,
|
"expertise": 2,
|
||||||
"tags": [
|
"tags": ["visual", "input", "state", "effect"],
|
||||||
"visual",
|
|
||||||
"input",
|
|
||||||
"state",
|
|
||||||
"effect"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -118,9 +93,7 @@
|
|||||||
"```jsx\nReactDOM.render(\n <Input type='text' placeholder='Insert some text here...' callback={(val) => console.log(val)}/>,\n document.getElementById('root')\n);\n```"
|
"```jsx\nReactDOM.render(\n <Input type='text' placeholder='Insert some text here...' callback={(val) => console.log(val)}/>,\n document.getElementById('root')\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 0,
|
"expertise": 0,
|
||||||
"tags": [
|
"tags": ["input"],
|
||||||
"input"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -132,11 +105,7 @@
|
|||||||
"```jsx\nReactDOM.render(\n <LimitedTextarea limit={32} value='Hello!' />,\n document.getElementById('root')\n);\n```"
|
"```jsx\nReactDOM.render(\n <LimitedTextarea limit={32} value='Hello!' />,\n document.getElementById('root')\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 0,
|
"expertise": 0,
|
||||||
"tags": [
|
"tags": ["input", "state", "effect"],
|
||||||
"input",
|
|
||||||
"state",
|
|
||||||
"effect"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -148,11 +117,7 @@
|
|||||||
"```jsx\nReactDOM.render(\n <LimitedWordTextArea limit={5} value='Hello there!' />,\n document.getElementById('root')\n);\n```"
|
"```jsx\nReactDOM.render(\n <LimitedWordTextArea limit={5} value='Hello there!' />,\n document.getElementById('root')\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 0,
|
"expertise": 0,
|
||||||
"tags": [
|
"tags": ["input", "state", "effect"],
|
||||||
"input",
|
|
||||||
"state",
|
|
||||||
"effect"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -164,9 +129,7 @@
|
|||||||
"```jsx\nReactDOM.render(\n <Mailto email=\"foo@bar.baz\" subject=\"Hello\" body=\"Hello world!\">\n Mail me!\n </Mailto>,\n document.getElementById(\"root\")\n);\n```"
|
"```jsx\nReactDOM.render(\n <Mailto email=\"foo@bar.baz\" subject=\"Hello\" body=\"Hello world!\">\n Mail me!\n </Mailto>,\n document.getElementById(\"root\")\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 0,
|
"expertise": 0,
|
||||||
"tags": [
|
"tags": ["visual"],
|
||||||
"visual"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -178,10 +141,7 @@
|
|||||||
"```jsx\nconst people = [\n { name: 'John', surname: 'Smith', age: 42 },\n { name: 'Adam', surname: 'Smith', gender: 'male' }\n];\nconst propertyNames = ['name', 'surname', 'age'];\nReactDOM.render(\n <MappedTable data={people} propertyNames={propertyNames} />,\n document.getElementById('root')\n);\n```"
|
"```jsx\nconst people = [\n { name: 'John', surname: 'Smith', age: 42 },\n { name: 'Adam', surname: 'Smith', gender: 'male' }\n];\nconst propertyNames = ['name', 'surname', 'age'];\nReactDOM.render(\n <MappedTable data={people} propertyNames={propertyNames} />,\n document.getElementById('root')\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 1,
|
"expertise": 1,
|
||||||
"tags": [
|
"tags": ["array", "object"],
|
||||||
"array",
|
|
||||||
"object"
|
|
||||||
],
|
|
||||||
"notes": [
|
"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`.",
|
||||||
"<!-tags: array,object -->",
|
"<!-tags: array,object -->",
|
||||||
@ -197,11 +157,7 @@
|
|||||||
"```jsx\nconst options = [{ label: \"Item One\" }, { label: \"Item Two\" }];\n\nReactDOM.render(\n <MultiselectCheckbox\n options={options}\n onChange={data => {\n console.log(data);\n }}\n />,\n document.getElementById(\"root\")\n);\n```"
|
"```jsx\nconst options = [{ label: \"Item One\" }, { label: \"Item Two\" }];\n\nReactDOM.render(\n <MultiselectCheckbox\n options={options}\n onChange={data => {\n console.log(data);\n }}\n />,\n document.getElementById(\"root\")\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 1,
|
"expertise": 1,
|
||||||
"tags": [
|
"tags": ["input", "state", "array"],
|
||||||
"input",
|
|
||||||
"state",
|
|
||||||
"array"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -213,10 +169,7 @@
|
|||||||
"```jsx\nReactDOM.render(<PasswordRevealer />, document.getElementById('root'));\n```"
|
"```jsx\nReactDOM.render(<PasswordRevealer />, document.getElementById('root'));\n```"
|
||||||
],
|
],
|
||||||
"expertise": 0,
|
"expertise": 0,
|
||||||
"tags": [
|
"tags": ["input", "state"],
|
||||||
"input",
|
|
||||||
"state"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -228,9 +181,7 @@
|
|||||||
"```jsx\nlet choices = [\n ['grapefruit', 'Grapefruit'],\n ['lime', 'Lime'],\n ['coconut', 'Coconut'],\n ['mango', 'Mango']\n];\nReactDOM.render(\n <Select values={choices} selected='lime' callback={(val) => console.log(val)}/>,\n document.getElementById('root')\n);\n```"
|
"```jsx\nlet choices = [\n ['grapefruit', 'Grapefruit'],\n ['lime', 'Lime'],\n ['coconut', 'Coconut'],\n ['mango', 'Mango']\n];\nReactDOM.render(\n <Select values={choices} selected='lime' callback={(val) => console.log(val)}/>,\n document.getElementById('root')\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 0,
|
"expertise": 0,
|
||||||
"tags": [
|
"tags": ["input"],
|
||||||
"input"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -242,12 +193,7 @@
|
|||||||
"```jsx\nReactDOM.render(<StarRating/>, document.getElementById('root'));\nReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));\n```"
|
"```jsx\nReactDOM.render(<StarRating/>, document.getElementById('root'));\nReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));\n```"
|
||||||
],
|
],
|
||||||
"expertise": 2,
|
"expertise": 2,
|
||||||
"tags": [
|
"tags": ["visual", "children", "input", "state"],
|
||||||
"visual",
|
|
||||||
"children",
|
|
||||||
"input",
|
|
||||||
"state"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -260,11 +206,7 @@
|
|||||||
"```jsx\nReactDOM.render(\n <Tabs defaultIndex=\"1\" onTabClick={console.log}>\n <TabItem label=\"A\" index=\"1\">\n Lorem ipsum\n </TabItem>\n <TabItem label=\"B\" index=\"2\">\n Dolor sit amet\n </TabItem>\n </Tabs>,\n document.getElementById(\"root\")\n);\n\n```"
|
"```jsx\nReactDOM.render(\n <Tabs defaultIndex=\"1\" onTabClick={console.log}>\n <TabItem label=\"A\" index=\"1\">\n Lorem ipsum\n </TabItem>\n <TabItem label=\"B\" index=\"2\">\n Dolor sit amet\n </TabItem>\n </Tabs>,\n document.getElementById(\"root\")\n);\n\n```"
|
||||||
],
|
],
|
||||||
"expertise": 1,
|
"expertise": 1,
|
||||||
"tags": [
|
"tags": ["visual", "state", "children"],
|
||||||
"visual",
|
|
||||||
"state",
|
|
||||||
"children"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -276,9 +218,7 @@
|
|||||||
"```jsx\nReactDOM.render(\n <TextArea placeholder='Insert some text here...' callback={(val) => console.log(val)}/>,\n document.getElementById('root')\n);\n```"
|
"```jsx\nReactDOM.render(\n <TextArea placeholder='Insert some text here...' callback={(val) => console.log(val)}/>,\n document.getElementById('root')\n);\n```"
|
||||||
],
|
],
|
||||||
"expertise": 0,
|
"expertise": 0,
|
||||||
"tags": [
|
"tags": ["input"],
|
||||||
"input"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -290,10 +230,7 @@
|
|||||||
"```jsx\nReactDOM.render(<Ticker times={5} interval={1000} />, document.getElementById('root'));\n```"
|
"```jsx\nReactDOM.render(<Ticker times={5} interval={1000} />, document.getElementById('root'));\n```"
|
||||||
],
|
],
|
||||||
"expertise": 1,
|
"expertise": 1,
|
||||||
"tags": [
|
"tags": ["visual", "state"],
|
||||||
"visual",
|
|
||||||
"state"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -305,10 +242,7 @@
|
|||||||
"```jsx\nReactDOM.render(<Toggle />, document.getElementById('root'));\n```"
|
"```jsx\nReactDOM.render(<Toggle />, document.getElementById('root'));\n```"
|
||||||
],
|
],
|
||||||
"expertise": 0,
|
"expertise": 0,
|
||||||
"tags": [
|
"tags": ["visual", "state"],
|
||||||
"visual",
|
|
||||||
"state"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -321,11 +255,7 @@
|
|||||||
"```jsx\n ReactDOM.render(\n <Tooltip text='Simple tooltip'>\n <button>Hover me!</button>\n </Tooltip>,\n document.getElementById('root')\n );\n```"
|
"```jsx\n ReactDOM.render(\n <Tooltip text='Simple tooltip'>\n <button>Hover me!</button>\n </Tooltip>,\n document.getElementById('root')\n );\n```"
|
||||||
],
|
],
|
||||||
"expertise": 1,
|
"expertise": 1,
|
||||||
"tags": [
|
"tags": ["visual", "state", "children"],
|
||||||
"visual",
|
|
||||||
"state",
|
|
||||||
"children"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -338,12 +268,7 @@
|
|||||||
"```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(<TreeView data={data} name='data'/>, document.getElementById(\"root\"));\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(<TreeView data={data} name='data'/>, document.getElementById(\"root\"));\n```"
|
||||||
],
|
],
|
||||||
"expertise": 2,
|
"expertise": 2,
|
||||||
"tags": [
|
"tags": ["object", "visual", "state", "recursion"],
|
||||||
"object",
|
|
||||||
"visual",
|
|
||||||
"state",
|
|
||||||
"recursion"
|
|
||||||
],
|
|
||||||
"notes": []
|
"notes": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,19 +1,21 @@
|
|||||||
const fs = require("fs-extra");
|
const fs = require('fs-extra');
|
||||||
const path = require("path");
|
const path = require('path');
|
||||||
const chalk = require("chalk");
|
const chalk = require('chalk');
|
||||||
const util = require("./util.js");
|
const util = require('./util.js');
|
||||||
const markdown = require("markdown-builder");
|
const markdown = require('markdown-builder');
|
||||||
const snippets = require("../data/snippet_data.json")
|
const snippets = require('../data/snippet_data.json');
|
||||||
|
|
||||||
const { headers, misc, lists } = markdown;
|
const { headers, misc, lists } = markdown;
|
||||||
const TAG_NAMES = [...new Set(snippets.reduce((acc,v) => [...acc,v.tags[0]],[]))].sort((a,b) => a.localeCompare(b));
|
const TAG_NAMES = [...new Set(snippets.reduce((acc, v) => [...acc, v.tags[0]], []))].sort((a, b) =>
|
||||||
|
a.localeCompare(b)
|
||||||
|
);
|
||||||
console.log(TAG_NAMES);
|
console.log(TAG_NAMES);
|
||||||
|
|
||||||
const STATIC_PARTS_PATH = "./static-parts";
|
const STATIC_PARTS_PATH = './static-parts';
|
||||||
|
|
||||||
let startPart = "";
|
let startPart = '';
|
||||||
let endPart = "";
|
let endPart = '';
|
||||||
let output = "";
|
let output = '';
|
||||||
|
|
||||||
const detailsTOC = (title, snippetsArray) =>
|
const detailsTOC = (title, snippetsArray) =>
|
||||||
`\n${misc
|
`\n${misc
|
||||||
@ -23,13 +25,13 @@ const detailsTOC = (title, snippetsArray) =>
|
|||||||
.ul(snippetsArray, snippet =>
|
.ul(snippetsArray, snippet =>
|
||||||
misc.link(
|
misc.link(
|
||||||
snippet.title
|
snippet.title
|
||||||
.replace("\n", "")
|
.replace('\n', '')
|
||||||
.split("```")[0]
|
.split('```')[0]
|
||||||
.trim(),
|
.trim(),
|
||||||
misc.anchor(
|
misc.anchor(
|
||||||
snippet.title
|
snippet.title
|
||||||
.replace("\n", "")
|
.replace('\n', '')
|
||||||
.split("```")[0]
|
.split('```')[0]
|
||||||
.trim()
|
.trim()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -38,19 +40,13 @@ const detailsTOC = (title, snippetsArray) =>
|
|||||||
)
|
)
|
||||||
.trim()}\n\n`;
|
.trim()}\n\n`;
|
||||||
|
|
||||||
console.time("Builder");
|
console.time('Builder');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
startPart = fs.readFileSync(
|
startPart = fs.readFileSync(path.join(STATIC_PARTS_PATH, 'README-start.md'), 'utf8');
|
||||||
path.join(STATIC_PARTS_PATH, "README-start.md"),
|
endPart = fs.readFileSync(path.join(STATIC_PARTS_PATH, 'README-end.md'), 'utf8');
|
||||||
"utf8"
|
|
||||||
);
|
|
||||||
endPart = fs.readFileSync(
|
|
||||||
path.join(STATIC_PARTS_PATH, "README-end.md"),
|
|
||||||
"utf8"
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`${chalk.red("ERROR!")} During static part loading: ${err}`);
|
console.log(`${chalk.red('ERROR!')} During static part loading: ${err}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,13 +56,13 @@ try {
|
|||||||
|
|
||||||
const snippetsInTag = {};
|
const snippetsInTag = {};
|
||||||
|
|
||||||
TAG_NAMES.forEach(tag => snippetsInTag[tag] = snippets.filter(v => v.tags[0] == tag));
|
TAG_NAMES.forEach(tag => (snippetsInTag[tag] = snippets.filter(v => v.tags[0] == tag)));
|
||||||
|
|
||||||
// write Table of Contents
|
// write Table of Contents
|
||||||
TAG_NAMES.forEach(tag => {
|
TAG_NAMES.forEach(tag => {
|
||||||
const taggedSnippets = snippetsInTag[tag];
|
const taggedSnippets = snippetsInTag[tag];
|
||||||
output += headers.h3(util.capitalize(tag));
|
output += headers.h3(util.capitalize(tag));
|
||||||
output += detailsTOC("View contents", taggedSnippets);
|
output += detailsTOC('View contents', taggedSnippets);
|
||||||
});
|
});
|
||||||
|
|
||||||
// delimeter after TOC
|
// delimeter after TOC
|
||||||
@ -78,27 +74,24 @@ try {
|
|||||||
const taggedSnippets = snippetsInTag[tag];
|
const taggedSnippets = snippetsInTag[tag];
|
||||||
taggedSnippets.forEach(snippet => {
|
taggedSnippets.forEach(snippet => {
|
||||||
output += headers.h3(snippet.title).trim();
|
output += headers.h3(snippet.title).trim();
|
||||||
output += `\n\n${snippet.text}${snippet.codeBlocks.slice(0,-1).join('\n\n')}`;
|
output += `\n\n${snippet.text}${snippet.codeBlocks.slice(0, -1).join('\n\n')}`;
|
||||||
if (snippet.notes && snippet.notes.length) {
|
if (snippet.notes && snippet.notes.length) {
|
||||||
output += headers.h4('Notes');
|
output += headers.h4('Notes');
|
||||||
output += `\n${snippet.notes}`;
|
output += `\n${snippet.notes}`;
|
||||||
}
|
}
|
||||||
output += misc.collapsible('Examples', snippet.codeBlocks.slice(-1));
|
output += misc.collapsible('Examples', snippet.codeBlocks.slice(-1));
|
||||||
output += `\n<br>${misc.link(
|
output += `\n<br>${misc.link('⬆ Back to top', misc.anchor('Table of Contents'))}\n\n`;
|
||||||
"⬆ Back to top",
|
});
|
||||||
misc.anchor("Table of Contents")
|
|
||||||
)}\n\n`
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// add static part for end
|
// add static part for end
|
||||||
output += `\n${endPart}\n`;
|
output += `\n${endPart}\n`;
|
||||||
|
|
||||||
fs.writeFileSync("README.md", output);
|
fs.writeFileSync('README.md', output);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`${chalk.red("ERROR!")} During README generation: ${err}`);
|
console.log(`${chalk.red('ERROR!')} During README generation: ${err}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`${chalk.green("SUCCESS!")} README file generated!`);
|
console.log(`${chalk.green('SUCCESS!')} README file generated!`);
|
||||||
console.timeEnd("Builder");
|
console.timeEnd('Builder');
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
const fs = require("fs-extra")
|
const fs = require('fs-extra');
|
||||||
const path = require("path")
|
const path = require('path');
|
||||||
const chalk = require("chalk")
|
const chalk = require('chalk');
|
||||||
const {
|
const {
|
||||||
attempt,
|
attempt,
|
||||||
readSnippets,
|
readSnippets,
|
||||||
@ -8,36 +8,35 @@ const {
|
|||||||
getSection,
|
getSection,
|
||||||
getTitle,
|
getTitle,
|
||||||
getTextualContent
|
getTextualContent
|
||||||
} = require("./util");
|
} = require('./util');
|
||||||
|
|
||||||
console.time("Extractor");
|
console.time('Extractor');
|
||||||
|
|
||||||
attempt("snippet_data.json generation", () => {
|
attempt('snippet_data.json generation', () => {
|
||||||
const output = Object.entries(readSnippets()).map(([name, contents]) => {
|
const output = Object.entries(readSnippets()).map(([name, contents]) => {
|
||||||
const title = getTitle(contents);
|
const title = getTitle(contents);
|
||||||
const text = getTextualContent(contents);
|
const text = getTextualContent(contents);
|
||||||
const codeBlocks = getCodeBlocks(contents);
|
const codeBlocks = getCodeBlocks(contents);
|
||||||
const notes = getSection("#### Notes", contents, false)
|
const notes = getSection('#### Notes', contents, false)
|
||||||
.split("\n")
|
.split('\n')
|
||||||
.map(v => v.replace(/[*-] /g, ""))
|
.map(v => v.replace(/[*-] /g, ''))
|
||||||
.filter(v => v.trim() !== "")
|
.filter(v => v.trim() !== '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
title,
|
title,
|
||||||
text,
|
text,
|
||||||
codeBlocks,
|
codeBlocks,
|
||||||
expertise: parseInt(
|
expertise: parseInt((contents.match(/<!--\s*expertise:\s*\(*(.+)\)*/) || [])[1], 10),
|
||||||
(contents.match(/<!--\s*expertise:\s*\(*(.+)\)*/) || [])[1],
|
tags: (contents.match(/<!--\s*tags:\s*\(*(.+)\)*\s*-->/) || [])[1]
|
||||||
10
|
.split(',')
|
||||||
),
|
.map(v => v.trim()),
|
||||||
tags: (contents.match(/<!--\s*tags:\s*\(*(.+)\)*\s*-->/) || [])[1].split(",").map(v => v.trim()),
|
|
||||||
notes
|
notes
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
fs.writeFileSync("./data/snippet_data.json", JSON.stringify(output, null, 2))
|
fs.writeFileSync('./data/snippet_data.json', JSON.stringify(output, null, 2));
|
||||||
})
|
});
|
||||||
|
|
||||||
console.log(`${chalk.green("SUCCESS!")} snippet_data.json file generated!`);
|
console.log(`${chalk.green('SUCCESS!')} snippet_data.json file generated!`);
|
||||||
console.timeEnd("Extractor");
|
console.timeEnd('Extractor');
|
||||||
|
|||||||
@ -1,31 +1,29 @@
|
|||||||
const fs = require("fs-extra");
|
const fs = require('fs-extra');
|
||||||
const path = require("path");
|
const path = require('path');
|
||||||
const chalk = require("chalk");
|
const chalk = require('chalk');
|
||||||
|
|
||||||
const SNIPPETS_PATH = "./snippets";
|
const SNIPPETS_PATH = './snippets';
|
||||||
|
|
||||||
const attempt = (task, cb) => {
|
const attempt = (task, cb) => {
|
||||||
try {
|
try {
|
||||||
return cb();
|
return cb();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`${chalk.red("ERROR!")} During ${task}: ${e}`);
|
console.log(`${chalk.red('ERROR!')} During ${task}: ${e}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const capitalize = ([first, ...rest], lowerRest = false) =>
|
const capitalize = ([first, ...rest], lowerRest = false) =>
|
||||||
first.toUpperCase() + (lowerRest ? rest.join("").toLowerCase() : rest.join(""));
|
first.toUpperCase() + (lowerRest ? rest.join('').toLowerCase() : rest.join(''));
|
||||||
|
|
||||||
const readSnippets = () =>
|
const readSnippets = () =>
|
||||||
attempt("read snippets", () =>
|
attempt('read snippets', () =>
|
||||||
fs
|
fs
|
||||||
.readdirSync(SNIPPETS_PATH)
|
.readdirSync(SNIPPETS_PATH)
|
||||||
.sort((a, b) => (a.toLowerCase() < b.toLowerCase() ? -1 : 1))
|
.sort((a, b) => (a.toLowerCase() < b.toLowerCase() ? -1 : 1))
|
||||||
.reduce((acc, name) => {
|
.reduce((acc, name) => {
|
||||||
acc[name] = fs
|
acc[name] = fs.readFileSync(path.join(SNIPPETS_PATH, name), 'utf8').replace(/\r\n/g, '\n');
|
||||||
.readFileSync(path.join(SNIPPETS_PATH, name), "utf8")
|
|
||||||
.replace(/\r\n/g, "\n");
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
);
|
);
|
||||||
@ -41,16 +39,16 @@ const getCodeBlocks = str => {
|
|||||||
m.forEach(match => results.push(match));
|
m.forEach(match => results.push(match));
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
};
|
||||||
|
|
||||||
const getSection = (searchString, contents, includeSubsections = true) => {
|
const getSection = (searchString, contents, includeSubsections = true) => {
|
||||||
const indexOfSearch = contents.indexOf(searchString);
|
const indexOfSearch = contents.indexOf(searchString);
|
||||||
if (indexOfSearch < 0) return "";
|
if (indexOfSearch < 0) return '';
|
||||||
|
|
||||||
let endSearch = "\\n#"
|
let endSearch = '\\n#';
|
||||||
if (includeSubsections) {
|
if (includeSubsections) {
|
||||||
let i;
|
let i;
|
||||||
for (i = 0; searchString[i] === "#" && i < searchString.length; i++);
|
for (i = 0; searchString[i] === '#' && i < searchString.length; i++);
|
||||||
|
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
endSearch += `{${i - 1},${i}}[^#]`;
|
endSearch += `{${i - 1},${i}}[^#]`;
|
||||||
@ -63,15 +61,14 @@ const getSection = (searchString, contents, includeSubsections = true) => {
|
|||||||
const sliceEnd = endIndex === -1 ? undefined : endIndex + sliceStart;
|
const sliceEnd = endIndex === -1 ? undefined : endIndex + sliceStart;
|
||||||
|
|
||||||
return contents.slice(sliceStart, sliceEnd).trim();
|
return contents.slice(sliceStart, sliceEnd).trim();
|
||||||
}
|
};
|
||||||
|
|
||||||
const getTextualContent = str => {
|
const getTextualContent = str => {
|
||||||
const regex = /###.*\n*([\s\S]*?)```/g;
|
const regex = /###.*\n*([\s\S]*?)```/g;
|
||||||
const results = [];
|
const results = [];
|
||||||
let m = null;
|
let m = null;
|
||||||
while ((m = regex.exec(str)) !== null) {
|
while ((m = regex.exec(str)) !== null) {
|
||||||
if (m.index === regex.lastIndex)
|
if (m.index === regex.lastIndex) regex.lastIndex += 1;
|
||||||
regex.lastIndex += 1;
|
|
||||||
|
|
||||||
m.forEach((match, groupIndex) => {
|
m.forEach((match, groupIndex) => {
|
||||||
results.push(match);
|
results.push(match);
|
||||||
@ -80,8 +77,7 @@ const getTextualContent = str => {
|
|||||||
return results[1];
|
return results[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTitle = (contents) =>
|
const getTitle = contents => contents.split('\n')[0].replace(/^#+\s+/g, '');
|
||||||
contents.split('\n')[0].replace(/^#+\s+/g,'');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
attempt,
|
attempt,
|
||||||
@ -92,4 +88,4 @@ module.exports = {
|
|||||||
getCodeBlocks,
|
getCodeBlocks,
|
||||||
getSection,
|
getSection,
|
||||||
getTitle
|
getTitle
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,18 +11,20 @@ function ComponentName(props) {
|
|||||||
setState(0);
|
setState(0);
|
||||||
});
|
});
|
||||||
return <div>{props}</div>;
|
return <div>{props}</div>;
|
||||||
};
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
ReactDOM.render(<ComponentName />, document.getElementById("root"));
|
ReactDOM.render(<ComponentName />, document.getElementById('root'));
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- OPTIONAL -->
|
<!-- OPTIONAL -->
|
||||||
|
|
||||||
#### Notes:
|
#### Notes:
|
||||||
* Things to remember when using this
|
|
||||||
* Other options that might be less appealing or have lower compatibility
|
- Things to remember when using this
|
||||||
* Common mistakes and issues
|
- Other options that might be less appealing or have lower compatibility
|
||||||
|
- Common mistakes and issues
|
||||||
|
|
||||||
<!-- tags: (separate each by a comma) -->
|
<!-- tags: (separate each by a comma) -->
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
Renders a string as plaintext, with URLs converted to appropriate `<a>` elements.
|
Renders a string as plaintext, with URLs converted to appropriate `<a>` elements.
|
||||||
|
|
||||||
Use `String.prototype.split()` and `String.prototype.match()` with a regular expression to find URLs in a string.
|
Use `String.prototype.split()` and `String.prototype.match()` with a regular expression to find URLs in a string.
|
||||||
Return a `<React.Fragment>` with matched URLs rendered as `<a>` elements, dealing with missing protocol prefixes if necessary, and the rest of the string rendered as plaintext.
|
Return a `<React.Fragment>` with matched URLs rendered as `<a>` elements, dealing with missing protocol prefixes if necessary, and the rest of the string rendered as plaintext.
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
function AutoLink({ text }) {
|
function AutoLink({ text }) {
|
||||||
@ -15,9 +15,7 @@ function AutoLink({ text }) {
|
|||||||
let match = word.match(delimiter);
|
let match = word.match(delimiter);
|
||||||
if (match) {
|
if (match) {
|
||||||
let url = match[0];
|
let url = match[0];
|
||||||
return (
|
return <a href={url.startsWith('http') ? url : `http://${url}`}>{url}</a>;
|
||||||
<a href={url.startsWith("http") ? url : `http://${url}`}>{url}</a>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return word;
|
return word;
|
||||||
})}
|
})}
|
||||||
@ -28,7 +26,7 @@ function AutoLink({ text }) {
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<AutoLink text='foo bar baz http://example.org bar' />,
|
<AutoLink text="foo bar baz http://example.org bar" />,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|||||||
@ -14,14 +14,14 @@ function Carousel(props) {
|
|||||||
let scrollInterval = null;
|
let scrollInterval = null;
|
||||||
const style = {
|
const style = {
|
||||||
carousel: {
|
carousel: {
|
||||||
position: "relative"
|
position: 'relative'
|
||||||
},
|
},
|
||||||
carouselItem: {
|
carouselItem: {
|
||||||
position: "absolute",
|
position: 'absolute',
|
||||||
visibility: "hidden"
|
visibility: 'hidden'
|
||||||
},
|
},
|
||||||
visible: {
|
visible: {
|
||||||
visibility: "visible"
|
visibility: 'visible'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -57,11 +57,10 @@ ReactDOM.render(
|
|||||||
<div>carousel item 3</div>
|
<div>carousel item 3</div>
|
||||||
]}
|
]}
|
||||||
/>,
|
/>,
|
||||||
document.getElementById("root")
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- tags: visual,children,state,effect -->
|
<!-- tags: visual,children,state,effect -->
|
||||||
|
|
||||||
<!-- expertise: 2 -->
|
<!-- expertise: 2 -->
|
||||||
|
|
||||||
|
|||||||
@ -14,24 +14,21 @@ function Collapse(props) {
|
|||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
collapsed: {
|
collapsed: {
|
||||||
display: "none"
|
display: 'none'
|
||||||
},
|
},
|
||||||
expanded: {
|
expanded: {
|
||||||
display: "block"
|
display: 'block'
|
||||||
},
|
},
|
||||||
buttonStyle: {
|
buttonStyle: {
|
||||||
display: "block",
|
display: 'block',
|
||||||
width: "100%"
|
width: '100%'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button style={style.buttonStyle} onClick={() => setIsCollapsed(!isCollapsed)}>
|
||||||
style={style.buttonStyle}
|
{isCollapsed ? 'Show' : 'Hide'} content
|
||||||
onClick={() => setIsCollapsed(!isCollapsed)}
|
|
||||||
>
|
|
||||||
{isCollapsed ? "Show" : "Hide"} content
|
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
className="collapse-content"
|
className="collapse-content"
|
||||||
|
|||||||
@ -23,8 +23,7 @@ function CountDown({ hours = 0, minutes = 0, seconds = 0 }) {
|
|||||||
|
|
||||||
const tick = () => {
|
const tick = () => {
|
||||||
if (paused || over) return;
|
if (paused || over) return;
|
||||||
if (time.hours == 0 && time.minutes == 0 && time.seconds == 0)
|
if (time.hours == 0 && time.minutes == 0 && time.seconds == 0) setOver(true);
|
||||||
setOver(true);
|
|
||||||
else if (time.minutes == 0 && time.seconds == 0)
|
else if (time.minutes == 0 && time.seconds == 0)
|
||||||
setTime({
|
setTime({
|
||||||
hours: time.hours - 1,
|
hours: time.hours - 1,
|
||||||
@ -62,15 +61,11 @@ function CountDown({ hours = 0, minutes = 0, seconds = 0 }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>{`${time.hours
|
<p>{`${time.hours.toString().padStart(2, '0')}:${time.minutes
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(2, "0")}:${time.minutes
|
.padStart(2, '0')}:${time.seconds.toString().padStart(2, '0')}`}</p>
|
||||||
.toString()
|
<div>{over ? "Time's up!" : ''}</div>
|
||||||
.padStart(2, "0")}:${time.seconds.toString().padStart(2, "0")}`}</p>
|
<button onClick={() => setPaused(!paused)}>{paused ? 'Resume' : 'Pause'}</button>
|
||||||
<div>{over ? "Time's up!" : ""}</div>
|
|
||||||
<button onClick={() => setPaused(!paused)}>
|
|
||||||
{paused ? "Resume" : "Pause"}
|
|
||||||
</button>
|
|
||||||
<button onClick={() => reset()}>Restart</button>
|
<button onClick={() => reset()}>Restart</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -78,10 +73,7 @@ function CountDown({ hours = 0, minutes = 0, seconds = 0 }) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
ReactDOM.render(
|
ReactDOM.render(<CountDown hours="1" minutes="45" />, document.getElementById('root'));
|
||||||
<CountDown hours="1" minutes="45" />,
|
|
||||||
document.getElementById('root')
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- tags: visual,state -->
|
<!-- tags: visual,state -->
|
||||||
|
|||||||
@ -8,17 +8,15 @@ Omit the `isOrdered` prop to render a `<ul>` list by default.
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
function DataList({ isOrdered, data }) {
|
function DataList({ isOrdered, data }) {
|
||||||
const list = data.map((val, i) => (
|
const list = data.map((val, i) => <li key={`${i}_${val}`}>{val}</li>);
|
||||||
<li key={`${i}_${val}`}>{val}</li>
|
|
||||||
));
|
|
||||||
return isOrdered ? <ol>{list}</ol> : <ul>{list}</ul>;
|
return isOrdered ? <ol>{list}</ol> : <ul>{list}</ul>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
const names = ['John', 'Paul', 'Mary'];
|
const names = ['John', 'Paul', 'Mary'];
|
||||||
ReactDOM.render(<DataList data={names}/>, document.getElementById('root'));
|
ReactDOM.render(<DataList data={names} />, document.getElementById('root'));
|
||||||
ReactDOM.render(<DataList data={names} isOrdered/>, document.getElementById('root'));
|
ReactDOM.render(<DataList data={names} isOrdered />, document.getElementById('root'));
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- tags: array -->
|
<!-- tags: array -->
|
||||||
|
|||||||
@ -16,12 +16,12 @@ function DataTable({ data }) {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{data.map((val, i) =>
|
{data.map((val, i) => (
|
||||||
<tr key={`${i}_${val}`}>
|
<tr key={`${i}_${val}`}>
|
||||||
<td>{i}</td>
|
<td>{i}</td>
|
||||||
<td>{val}</td>
|
<td>{val}</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
);
|
);
|
||||||
@ -30,10 +30,7 @@ function DataTable({ data }) {
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
const people = ['John', 'Jesse'];
|
const people = ['John', 'Jesse'];
|
||||||
ReactDOM.render(
|
ReactDOM.render(<DataTable data={people} />, document.getElementById('root'));
|
||||||
<DataTable data={people} />,
|
|
||||||
document.getElementById('root')
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- tags: array -->
|
<!-- tags: array -->
|
||||||
|
|||||||
@ -6,17 +6,16 @@ Create a ref called `dropRef` for this component.
|
|||||||
Use the `React.useState()` hook to create the `drag` and `filename` variables, initialized to `false` and `''` respectively.
|
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.
|
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.
|
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 the `React.useEffect()` hook and its attached `cleanup()` method.
|
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`.
|
`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 `<div>` and use `drag` and `filename` to determine its contents and style.
|
Return an appropriately styled `<div>` and use `drag` and `filename` to determine its contents and style.
|
||||||
Finally, bind the `ref` of the created `<div>` to `dropRef`.
|
Finally, bind the `ref` of the created `<div>` to `dropRef`.
|
||||||
|
|
||||||
|
|
||||||
```css
|
```css
|
||||||
.filedrop {
|
.filedrop {
|
||||||
min-height: 120px;
|
min-height: 120px;
|
||||||
border: 3px solid #D3D3D3;
|
border: 3px solid #d3d3d3;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
@ -24,11 +23,11 @@ Finally, bind the `ref` of the created `<div>` to `dropRef`.
|
|||||||
}
|
}
|
||||||
|
|
||||||
.filedrop.drag {
|
.filedrop.drag {
|
||||||
border: 3px dashed #1E90FF;
|
border: 3px dashed #1e90ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filedrop.ready {
|
.filedrop.ready {
|
||||||
border: 3px solid #32CD32;
|
border: 3px solid #32cd32;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -72,24 +71,22 @@ function FileDrop(props) {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
let div = dropRef.current;
|
let div = dropRef.current;
|
||||||
div.addEventListener("dragenter", handleDragIn);
|
div.addEventListener('dragenter', handleDragIn);
|
||||||
div.addEventListener("dragleave", handleDragOut);
|
div.addEventListener('dragleave', handleDragOut);
|
||||||
div.addEventListener("dragover", handleDrag);
|
div.addEventListener('dragover', handleDrag);
|
||||||
div.addEventListener("drop", handleDrop);
|
div.addEventListener('drop', handleDrop);
|
||||||
return function cleanup() {
|
return function cleanup() {
|
||||||
div.removeEventListener("dragenter", handleDragIn);
|
div.removeEventListener('dragenter', handleDragIn);
|
||||||
div.removeEventListener("dragleave", handleDragOut);
|
div.removeEventListener('dragleave', handleDragOut);
|
||||||
div.removeEventListener("dragover", handleDrag);
|
div.removeEventListener('dragover', handleDrag);
|
||||||
div.removeEventListener("drop", handleDrop);
|
div.removeEventListener('drop', handleDrop);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={dropRef}
|
ref={dropRef}
|
||||||
className={
|
className={drag ? 'filedrop drag' : filename ? 'filedrop ready' : 'filedrop'}
|
||||||
drag ? "filedrop drag" : filename ? "filedrop ready" : "filedrop"
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{filename && !drag ? <div>{filename}</div> : <div>Drop files here!</div>}
|
{filename && !drag ? <div>{filename}</div> : <div>Drop files here!</div>}
|
||||||
</div>
|
</div>
|
||||||
@ -98,7 +95,7 @@ function FileDrop(props) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
ReactDOM.render(<FileDrop handleDrop={console.log}/>, document.getElementById('root'));
|
ReactDOM.render(<FileDrop handleDrop={console.log} />, document.getElementById('root'));
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- tags: visual,input,state,effect -->
|
<!-- tags: visual,input,state,effect -->
|
||||||
|
|||||||
@ -6,7 +6,7 @@ Use object destructuring to set defaults for certain attributes of the `<input>`
|
|||||||
Render an `<input>` element with the appropriate attributes and use the `callback` function in the `onChange` event to pass the value of the input to the parent.
|
Render an `<input>` element with the appropriate attributes and use the `callback` function in the `onChange` event to pass the value of the input to the parent.
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
function Input ({ callback, type = 'text', disabled = false, readOnly = false, placeholder = '' }) {
|
function Input({ callback, type = 'text', disabled = false, readOnly = false, placeholder = '' }) {
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
@ -21,7 +21,7 @@ function Input ({ callback, type = 'text', disabled = false, readOnly = false, p
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Input type='text' placeholder='Insert some text here...' callback={(val) => console.log(val)}/>,
|
<Input type="text" placeholder="Insert some text here..." callback={val => console.log(val)} />,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|||||||
@ -36,12 +36,9 @@ function LimitedTextarea({ rows, cols, value, limit }) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
ReactDOM.render(
|
ReactDOM.render(<LimitedTextarea limit={32} value="Hello!" />, document.getElementById('root'));
|
||||||
<LimitedTextarea limit={32} value='Hello!' />,
|
|
||||||
document.getElementById('root')
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- tags: input,state,effect -->
|
<!-- tags: input,state,effect -->
|
||||||
|
|
||||||
<!-- expertise: 0 -->
|
<!-- expertise: 0 -->
|
||||||
|
|
||||||
|
|||||||
@ -14,13 +14,13 @@ function LimitedWordTextarea({ rows, cols, value, limit }) {
|
|||||||
const [wordCount, setWordCount] = React.useState(0);
|
const [wordCount, setWordCount] = React.useState(0);
|
||||||
|
|
||||||
const setFormattedContent = text => {
|
const setFormattedContent = text => {
|
||||||
let words = text.split(" ");
|
let words = text.split(' ');
|
||||||
if (words.filter(Boolean).length > limit) {
|
if (words.filter(Boolean).length > limit) {
|
||||||
setContent(
|
setContent(
|
||||||
text
|
text
|
||||||
.split(" ")
|
.split(' ')
|
||||||
.slice(0, limit)
|
.slice(0, limit)
|
||||||
.join(" ")
|
.join(' ')
|
||||||
);
|
);
|
||||||
setWordCount(limit);
|
setWordCount(limit);
|
||||||
} else {
|
} else {
|
||||||
@ -51,11 +51,11 @@ function LimitedWordTextarea({ rows, cols, value, limit }) {
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<LimitedWordTextArea limit={5} value='Hello there!' />,
|
<LimitedWordTextArea limit={5} value="Hello there!" />,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- tags: input,state,effect -->
|
<!-- tags: input,state,effect -->
|
||||||
|
|
||||||
<!-- expertise: 0 -->
|
<!-- expertise: 0 -->
|
||||||
|
|
||||||
|
|||||||
@ -8,9 +8,7 @@ Render the link with `props.children` as its content.
|
|||||||
```jsx
|
```jsx
|
||||||
function Mailto({ email, subject, body, ...props }) {
|
function Mailto({ email, subject, body, ...props }) {
|
||||||
return (
|
return (
|
||||||
<a href={`mailto:${email}?subject=${subject || ""}&body=${body || ""}`}>
|
<a href={`mailto:${email}?subject=${subject || ''}&body=${body || ''}`}>{props.children}</a>
|
||||||
{props.children}
|
|
||||||
</a>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -20,7 +18,7 @@ ReactDOM.render(
|
|||||||
<Mailto email="foo@bar.baz" subject="Hello" body="Hello world!">
|
<Mailto email="foo@bar.baz" subject="Hello" body="Hello world!">
|
||||||
Mail me!
|
Mail me!
|
||||||
</Mailto>,
|
</Mailto>,
|
||||||
document.getElementById("root")
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -17,12 +17,18 @@ function MappedTable({ data, propertyNames }) {
|
|||||||
return (
|
return (
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>{propertyNames.map(val => <th key={`h_${val}`}>{val}</th>)}</tr>
|
<tr>
|
||||||
|
{propertyNames.map(val => (
|
||||||
|
<th key={`h_${val}`}>{val}</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{filteredData.map((val, i) => (
|
{filteredData.map((val, i) => (
|
||||||
<tr key={`i_${i}`}>
|
<tr key={`i_${i}`}>
|
||||||
{propertyNames.map(p => <td key={`i_${i}_${p}`}>{val[p]}</td>)}
|
{propertyNames.map(p => (
|
||||||
|
<td key={`i_${i}_${p}`}>{val[p]}</td>
|
||||||
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -44,7 +50,8 @@ ReactDOM.render(
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Notes:
|
#### 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`.
|
||||||
|
|
||||||
<!-- tags: array,object -->
|
<!-- tags: array,object -->
|
||||||
|
|
||||||
|
|||||||
@ -19,28 +19,23 @@ const style = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function MultiselectCheckbox ({ options, onChange }) {
|
function MultiselectCheckbox({ options, onChange }) {
|
||||||
const [data, setData] = React.useState(options);
|
const [data, setData] = React.useState(options);
|
||||||
|
|
||||||
const toggle = (item) => {
|
const toggle = item => {
|
||||||
data.map((_, key) => {
|
data.map((_, key) => {
|
||||||
if (data[key].label === item.label)
|
if (data[key].label === item.label) data[key].checked = !item.checked;
|
||||||
data[key].checked = !item.checked;
|
|
||||||
});
|
});
|
||||||
setData([...data]);
|
setData([...data]);
|
||||||
onChange(data);
|
onChange(data);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul style={style.listContainer}>
|
<ul style={style.listContainer}>
|
||||||
{data.map(item => {
|
{data.map(item => {
|
||||||
return (
|
return (
|
||||||
<li
|
<li key={item.label} style={style.itemStyle} onClick={() => toggle(item)}>
|
||||||
key={item.label}
|
<input readOnly type="checkbox" checked={item.checked || false} />
|
||||||
style={style.itemStyle}
|
|
||||||
onClick={() => toggle(item)}
|
|
||||||
>
|
|
||||||
<input readOnly type='checkbox' checked={item.checked || false} />
|
|
||||||
{item.label}
|
{item.label}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
@ -51,7 +46,7 @@ function MultiselectCheckbox ({ options, onChange }) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
const options = [{ label: "Item One" }, { label: "Item Two" }];
|
const options = [{ label: 'Item One' }, { label: 'Item Two' }];
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<MultiselectCheckbox
|
<MultiselectCheckbox
|
||||||
@ -60,7 +55,7 @@ ReactDOM.render(
|
|||||||
console.log(data);
|
console.log(data);
|
||||||
}}
|
}}
|
||||||
/>,
|
/>,
|
||||||
document.getElementById("root")
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -11,11 +11,7 @@ function PasswordRevealer({ value }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input type={shown ? 'text' : 'password'} value={value} onChange={() => {}} />
|
||||||
type={shown ? "text" : "password"}
|
|
||||||
value={value}
|
|
||||||
onChange={() => {}}
|
|
||||||
/>
|
|
||||||
<button onClick={() => setShown(!shown)}>Show/Hide</button>
|
<button onClick={() => setShown(!shown)}>Show/Hide</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -29,4 +25,3 @@ ReactDOM.render(<PasswordRevealer />, document.getElementById('root'));
|
|||||||
<!--tags: input,state -->
|
<!--tags: input,state -->
|
||||||
|
|
||||||
<!--expertise: 0 -->
|
<!--expertise: 0 -->
|
||||||
|
|
||||||
|
|||||||
@ -4,17 +4,21 @@ Renders a `<select>` element that uses a callback function to pass its value to
|
|||||||
|
|
||||||
Use object destructuring to set defaults for certain attributes of the `<select>` element.
|
Use object destructuring to set defaults for certain attributes of the `<select>` element.
|
||||||
Render a `<select>` element with the appropriate attributes and use the `callback` function in the `onChange` event to pass the value of the textarea to the parent.
|
Render a `<select>` element with the appropriate attributes and use the `callback` function in the `onChange` event to pass the value of the textarea to the parent.
|
||||||
Use destructuring on the `values` array to pass an array of `value` and `text` elements and the `selected` attribute to define the initial `value` of the `<select>` element.
|
Use destructuring on the `values` array to pass an array of `value` and `text` elements and the `selected` attribute to define the initial `value` of the `<select>` element.
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
function Select ({ values, callback, disabled = false, readonly = false, selected }) {
|
function Select({ values, callback, disabled = false, readonly = false, selected }) {
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
readOnly={readonly}
|
readOnly={readonly}
|
||||||
onChange={({ target : { value } }) => callback(value)}
|
onChange={({ target: { value } }) => callback(value)}
|
||||||
>
|
>
|
||||||
{values.map(([value, text]) => <option selected={selected === value}value={value}>{text}</option>)}
|
{values.map(([value, text]) => (
|
||||||
|
<option selected={selected === value} value={value}>
|
||||||
|
{text}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
</select>
|
</select>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -28,7 +32,7 @@ let choices = [
|
|||||||
['mango', 'Mango']
|
['mango', 'Mango']
|
||||||
];
|
];
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Select values={choices} selected='lime' callback={(val) => console.log(val)}/>,
|
<Select values={choices} selected="lime" callback={val => console.log(val)} />,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|||||||
@ -5,35 +5,31 @@ Renders a star rating component.
|
|||||||
Define a component, called `Star` that will render each individual star with the appropriate appearance, based on the parent component's state.
|
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`.
|
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 method, `hoverOver`, that updates `selected` and `rating` according to the provided `event`.
|
||||||
Create a `<div>` to wrap the `<Star>` 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.
|
Create a `<div>` to wrap the `<Star>` 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 `<Star>` component (`starId` and `marked`).
|
Finally, pass the appropriate values to each `<Star>` component (`starId` and `marked`).
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
function Star({ marked, starId }) {
|
function Star({ marked, starId }) {
|
||||||
return (
|
return (
|
||||||
<span star-id={starId} style={{ color: "#ff9933" }} role="button">
|
<span star-id={starId} style={{ color: '#ff9933' }} role="button">
|
||||||
{marked ? "\u2605" : "\u2606"}
|
{marked ? '\u2605' : '\u2606'}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function StarRating(props) {
|
function StarRating(props) {
|
||||||
const [rating, setRating] = React.useState(
|
const [rating, setRating] = React.useState(typeof props.rating == 'number' ? props.rating : 0);
|
||||||
typeof props.rating == "number" ? props.rating : 0
|
|
||||||
);
|
|
||||||
const [selection, setSelection] = React.useState(0);
|
const [selection, setSelection] = React.useState(0);
|
||||||
const hoverOver = event => {
|
const hoverOver = event => {
|
||||||
let val = 0;
|
let val = 0;
|
||||||
if (event && event.target && event.target.getAttribute("star-id"))
|
if (event && event.target && event.target.getAttribute('star-id'))
|
||||||
val = event.target.getAttribute("star-id");
|
val = event.target.getAttribute('star-id');
|
||||||
setSelection(val);
|
setSelection(val);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onMouseOut={() => hoverOver(null)}
|
onMouseOut={() => hoverOver(null)}
|
||||||
onClick={() =>
|
onClick={() => setRating(event.target.getAttribute('star-id') || this.state.rating)}
|
||||||
setRating(event.target.getAttribute("star-id") || this.state.rating)
|
|
||||||
}
|
|
||||||
onMouseOver={hoverOver}
|
onMouseOver={hoverOver}
|
||||||
>
|
>
|
||||||
{Array.from({ length: 5 }, (v, i) => (
|
{Array.from({ length: 5 }, (v, i) => (
|
||||||
@ -49,7 +45,7 @@ function StarRating(props) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
ReactDOM.render(<StarRating/>, document.getElementById('root'));
|
ReactDOM.render(<StarRating />, document.getElementById('root'));
|
||||||
ReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));
|
ReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
### Tab
|
### Tab
|
||||||
|
|
||||||
Renders a tabbed menu and view component.
|
Renders a tabbed menu and view component.
|
||||||
|
|
||||||
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`.
|
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 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`.
|
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 `<button>` from the `tab-menu`.
|
Define `changeTab`, which will be executed when clicking a `<button>` from the `tab-menu`.
|
||||||
`changeTab` executes the passed callback, `onTabClick` and updates `bindIndex`, which in turn causes a re-render, evaluating the `style` and `className` of the `tab-view` items and `tab-menu` buttons according to their `index`.
|
`changeTab` executes the passed callback, `onTabClick` and updates `bindIndex`, which in turn causes a re-render, evaluating the `style` and `className` of the `tab-view` items and `tab-menu` buttons according to their `index`.
|
||||||
|
|
||||||
@ -16,10 +17,10 @@ Define `changeTab`, which will be executed when clicking a `<button>` from the `
|
|||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
.tab-menu > button.focus {
|
.tab-menu > button.focus {
|
||||||
border-bottom: 2px solid #007BEF;
|
border-bottom: 2px solid #007bef;
|
||||||
}
|
}
|
||||||
.tab-menu > button:hover {
|
.tab-menu > button:hover {
|
||||||
border-bottom: 2px solid #007BEF;
|
border-bottom: 2px solid #007bef;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -31,19 +32,16 @@ function TabItem(props) {
|
|||||||
function Tabs(props) {
|
function Tabs(props) {
|
||||||
const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);
|
const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);
|
||||||
const changeTab = newIndex => {
|
const changeTab = newIndex => {
|
||||||
if (typeof props.onTabClick === "function") props.onTabClick(newIndex);
|
if (typeof props.onTabClick === 'function') props.onTabClick(newIndex);
|
||||||
setBindIndex(newIndex);
|
setBindIndex(newIndex);
|
||||||
};
|
};
|
||||||
const items = props.children.filter(item => item.type.name === "TabItem");
|
const items = props.children.filter(item => item.type.name === 'TabItem');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="wrapper">
|
<div className="wrapper">
|
||||||
<div className="tab-menu">
|
<div className="tab-menu">
|
||||||
{items.map(({ props: { index, label } }) => (
|
{items.map(({ props: { index, label } }) => (
|
||||||
<button
|
<button onClick={() => changeTab(index)} className={bindIndex === index ? 'focus' : ''}>
|
||||||
onClick={() => changeTab(index)}
|
|
||||||
className={bindIndex === index ? "focus" : ""}
|
|
||||||
>
|
|
||||||
{label}
|
{label}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
@ -54,7 +52,7 @@ function Tabs(props) {
|
|||||||
{...props}
|
{...props}
|
||||||
className="tab-view_item"
|
className="tab-view_item"
|
||||||
key={props.index}
|
key={props.index}
|
||||||
style={{ display: bindIndex === props.index ? "block" : "none" }}
|
style={{ display: bindIndex === props.index ? 'block' : 'none' }}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -62,6 +60,7 @@ function Tabs(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Tabs defaultIndex="1" onTabClick={console.log}>
|
<Tabs defaultIndex="1" onTabClick={console.log}>
|
||||||
@ -72,9 +71,8 @@ ReactDOM.render(
|
|||||||
Dolor sit amet
|
Dolor sit amet
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>,
|
</Tabs>,
|
||||||
document.getElementById("root")
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- tags: visual,state,children -->
|
<!-- tags: visual,state,children -->
|
||||||
|
|||||||
@ -6,7 +6,14 @@ Use object destructuring to set defaults for certain attributes of the `<textare
|
|||||||
Render a `<textarea>` element with the appropriate attributes and use the `callback` function in the `onChange` event to pass the value of the textarea to the parent.
|
Render a `<textarea>` element with the appropriate attributes and use the `callback` function in the `onChange` event to pass the value of the textarea to the parent.
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
function TextArea ({ callback, cols = 20, rows = 2, disabled = false, readOnly = false, placeholder='' }) {
|
function TextArea({
|
||||||
|
callback,
|
||||||
|
cols = 20,
|
||||||
|
rows = 2,
|
||||||
|
disabled = false,
|
||||||
|
readOnly = false,
|
||||||
|
placeholder = ''
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<textarea
|
<textarea
|
||||||
cols={cols}
|
cols={cols}
|
||||||
@ -14,7 +21,7 @@ function TextArea ({ callback, cols = 20, rows = 2, disabled = false, readOnly =
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
onChange={({ target : { value } }) => callback(value)}
|
onChange={({ target: { value } }) => callback(value)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -22,7 +29,7 @@ function TextArea ({ callback, cols = 20, rows = 2, disabled = false, readOnly =
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<TextArea placeholder='Insert some text here...' callback={(val) => console.log(val)}/>,
|
<TextArea placeholder="Insert some text here..." callback={val => console.log(val)} />,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|||||||
@ -14,17 +14,15 @@ function Ticker(props) {
|
|||||||
const tick = () => {
|
const tick = () => {
|
||||||
reset();
|
reset();
|
||||||
interval = setInterval(() => {
|
interval = setInterval(() => {
|
||||||
if (ticker < props.times)
|
if (ticker < props.times) setTicker(ticker + 1);
|
||||||
setTicker(ticker + 1);
|
else clearInterval(interval);
|
||||||
else
|
|
||||||
clearInterval(interval);
|
|
||||||
}, props.interval);
|
}, props.interval);
|
||||||
}
|
};
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
setTicker(0);
|
setTicker(0);
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -42,4 +40,4 @@ ReactDOM.render(<Ticker times={5} interval={1000} />, document.getElementById('r
|
|||||||
|
|
||||||
<!-- tags: visual,state -->
|
<!-- tags: visual,state -->
|
||||||
|
|
||||||
<!-- expertise: 1 -->
|
<!-- expertise: 1 -->
|
||||||
|
|||||||
@ -11,19 +11,16 @@ function Toggle(props) {
|
|||||||
const [isToggleOn, setIsToggleOn] = React.useState(false);
|
const [isToggleOn, setIsToggleOn] = React.useState(false);
|
||||||
style = {
|
style = {
|
||||||
on: {
|
on: {
|
||||||
backgroundColor: "green"
|
backgroundColor: 'green'
|
||||||
},
|
},
|
||||||
off: {
|
off: {
|
||||||
backgroundColor: "grey"
|
backgroundColor: 'grey'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button onClick={() => setIsToggleOn(!isToggleOn)} style={isToggleOn ? style.on : style.off}>
|
||||||
onClick={() => setIsToggleOn(!isToggleOn)}
|
{isToggleOn ? 'ON' : 'OFF'}
|
||||||
style={isToggleOn ? style.on : style.off}
|
|
||||||
>
|
|
||||||
{isToggleOn ? "ON" : "OFF"}
|
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ Renders a tooltip component.
|
|||||||
Use the `React.useState()` hook to create the `show` variable and initialize it to `false`.
|
Use the `React.useState()` hook to create the `show` variable and initialize it to `false`.
|
||||||
Return a `<div>` element that contains the `<div>` that will be the tooltip and the `children` passed to the component.
|
Return a `<div>` element that contains the `<div>` 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.
|
Handle the `onMouseEnter` and `onMouseLeave` methods, by altering the value of the `show` variable.
|
||||||
|
|
||||||
```css
|
```css
|
||||||
.tooltip {
|
.tooltip {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -24,21 +24,18 @@ Handle the `onMouseEnter` and `onMouseLeave` methods, by altering the value of t
|
|||||||
border-color: rgba(0, 0, 0, 0.7) transparent transparent;
|
border-color: rgba(0, 0, 0, 0.7) transparent transparent;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
function Tooltip({ children, text, ...rest }) {
|
function Tooltip({ children, text, ...rest }) {
|
||||||
const [show, setShow] = React.useState(false);
|
const [show, setShow] = React.useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="tooltip" style={show ? { visibility: "visible" } : {}}>
|
<div className="tooltip" style={show ? { visibility: 'visible' } : {}}>
|
||||||
{text}
|
{text}
|
||||||
<span className="tooltip-arrow" />
|
<span className="tooltip-arrow" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div {...rest} onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)}>
|
||||||
{...rest}
|
|
||||||
onMouseEnter={() => setShow(true)}
|
|
||||||
onMouseLeave={() => setShow(false)}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -47,14 +44,14 @@ function Tooltip({ children, text, ...rest }) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Tooltip text='Simple tooltip'>
|
<Tooltip text="Simple tooltip">
|
||||||
<button>Hover me!</button>
|
<button>Hover me!</button>
|
||||||
</Tooltip>,
|
</Tooltip>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- tags: visual,state,children -->
|
<!-- tags: visual,state,children -->
|
||||||
|
|
||||||
<!-- expertise: 1 -->
|
<!-- expertise: 1 -->
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
Renders a tree view of a JSON object or array with collapsible content.
|
Renders a tree view of a JSON object or array with collapsible content.
|
||||||
|
|
||||||
Use object destructuring to set defaults for certain props.
|
Use object destructuring to set defaults for certain props.
|
||||||
Use the value of the `toggled` prop to determine the initial state of the content (collapsed/expanded).
|
Use the value of the `toggled` prop to determine the initial state of the content (collapsed/expanded).
|
||||||
Use the `React.setState()` hook to create the `isToggled` state variable and give it the value of the `toggled` prop initially.
|
Use the `React.setState()` hook to create the `isToggled` state variable and give it the value of the `toggled` prop initially.
|
||||||
Return a `<div>` to wrap the contents of the component and the `<span>` element, used to alter the component's `isToggled` state.
|
Return a `<div>` to wrap the contents of the component and the `<span>` element, used to alter the component's `isToggled` state.
|
||||||
Determine the appearance of the component, based on `isParentToggled`, `isToggled`, `name` and `Array.isArray()` on `data`.
|
Determine the appearance of the component, based on `isParentToggled`, `isToggled`, `name` and `Array.isArray()` on `data`.
|
||||||
For each child in `data`, determine if it is an object or array and recursively render a sub-tree.
|
For each child in `data`, determine if it is an object or array and recursively render a sub-tree.
|
||||||
Otherwise, render a `<p>` element with the appropriate style.
|
Otherwise, render a `<p>` element with the appropriate style.
|
||||||
|
|
||||||
@ -29,8 +29,8 @@ div.tree-element:before {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-top: 4px solid transparent;
|
border-top: 4px solid transparent;
|
||||||
border-bottom: 4px solid transparent;
|
border-bottom: 4px solid transparent;
|
||||||
border-left: 5px solid gray;
|
border-left: 5px solid gray;
|
||||||
@ -59,39 +59,38 @@ function TreeView({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{ marginLeft: isChildElement ? 16 : 4 + "px" }}
|
style={{ marginLeft: isChildElement ? 16 : 4 + 'px' }}
|
||||||
className={isParentToggled ? "tree-element" : "tree-element collapsed"}
|
className={isParentToggled ? 'tree-element' : 'tree-element collapsed'}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={isToggled ? "toggler" : "toggler closed"}
|
className={isToggled ? 'toggler' : 'toggler closed'}
|
||||||
onClick={() => setIsToggled(!isToggled)}
|
onClick={() => setIsToggled(!isToggled)}
|
||||||
/>
|
/>
|
||||||
{name ? <strong> {name}: </strong> : <span> </span>}
|
{name ? <strong> {name}: </strong> : <span> </span>}
|
||||||
{Array.isArray(data) ? "[" : "{"}
|
{Array.isArray(data) ? '[' : '{'}
|
||||||
{!isToggled && "..."}
|
{!isToggled && '...'}
|
||||||
{Object.keys(data).map(
|
{Object.keys(data).map((v, i, a) =>
|
||||||
(v, i, a) =>
|
typeof data[v] == 'object' ? (
|
||||||
typeof data[v] == "object" ? (
|
<TreeView
|
||||||
<TreeView
|
data={data[v]}
|
||||||
data={data[v]}
|
isLast={i === a.length - 1}
|
||||||
isLast={i === a.length - 1}
|
name={Array.isArray(data) ? null : v}
|
||||||
name={Array.isArray(data) ? null : v}
|
isChildElement
|
||||||
isChildElement
|
isParentToggled={isParentToggled && isToggled}
|
||||||
isParentToggled={isParentToggled && isToggled}
|
/>
|
||||||
/>
|
) : (
|
||||||
) : (
|
<p
|
||||||
<p
|
style={{ marginLeft: 16 + 'px' }}
|
||||||
style={{ marginLeft: 16 + "px" }}
|
className={isToggled ? 'tree-element' : 'tree-element collapsed'}
|
||||||
className={isToggled ? "tree-element" : "tree-element collapsed"}
|
>
|
||||||
>
|
{Array.isArray(data) ? '' : <strong>{v}: </strong>}
|
||||||
{Array.isArray(data) ? "" : <strong>{v}: </strong>}
|
{data[v]}
|
||||||
{data[v]}
|
{i === a.length - 1 ? '' : ','}
|
||||||
{i === a.length - 1 ? "" : ","}
|
</p>
|
||||||
</p>
|
)
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
{Array.isArray(data) ? "]" : "}"}
|
{Array.isArray(data) ? ']' : '}'}
|
||||||
{!isLast ? "," : ""}
|
{!isLast ? ',' : ''}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -100,27 +99,27 @@ function TreeView({
|
|||||||
```jsx
|
```jsx
|
||||||
let data = {
|
let data = {
|
||||||
lorem: {
|
lorem: {
|
||||||
ipsum: "dolor sit",
|
ipsum: 'dolor sit',
|
||||||
amet: {
|
amet: {
|
||||||
consectetur: "adipiscing",
|
consectetur: 'adipiscing',
|
||||||
elit: [
|
elit: [
|
||||||
"duis",
|
'duis',
|
||||||
"vitae",
|
'vitae',
|
||||||
{
|
{
|
||||||
semper: "orci"
|
semper: 'orci'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
est: "sed ornare"
|
est: 'sed ornare'
|
||||||
},
|
},
|
||||||
"etiam",
|
'etiam',
|
||||||
["laoreet", "tincidunt"],
|
['laoreet', 'tincidunt'],
|
||||||
["vestibulum", "ante"]
|
['vestibulum', 'ante']
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
ipsum: "primis"
|
ipsum: 'primis'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ReactDOM.render(<TreeView data={data} name='data'/>, document.getElementById("root"));
|
ReactDOM.render(<TreeView data={data} name="data" />, document.getElementById('root'));
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- tags: object,visual,state,recursion -->
|
<!-- tags: object,visual,state,recursion -->
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
### ModalDialog
|
### ModalDialog
|
||||||
|
|
||||||
Renders a dialog component in a modal, controllable through events.
|
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.
|
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 `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.
|
||||||
@ -8,60 +8,61 @@ Define `close` and `modalClick` to toggle the visibility of the modal dialog, ba
|
|||||||
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`.
|
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:
|
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
|
- `title`, a string for the dialog's title
|
||||||
* `content`, which is the JSX content to be rendered inside the dialog
|
- `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 `<div>` to wrap everything and render the modal dialog with the content passed to `show()`.
|
Finally, in the `render()` method, use a `<div>` to wrap everything and render the modal dialog with the content passed to `show()`.
|
||||||
|
|
||||||
```css
|
```css
|
||||||
.modal {
|
.modal {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba(0, 0, 0, 0.6);
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
z-index: 9998;
|
z-index: 9998;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.dialog {
|
.dialog {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.dialog-title {
|
.dialog-title {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
border-bottom: 0.5px solid #c3c3c3;
|
border-bottom: 0.5px solid #c3c3c3;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.dialog-close {
|
.dialog-close {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
color: #c3c3c3;
|
color: #c3c3c3;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
.dialog-close:hover {
|
.dialog-close:hover {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
.dialog-content {
|
.dialog-content {
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
class ModalDialog extends React.Component {
|
class ModalDialog extends React.Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.modalHandler = (e) => {
|
this.modalHandler = e => {
|
||||||
this.setState({
|
this.setState({
|
||||||
data: e.detail.data,
|
data: e.detail.data,
|
||||||
visible: true
|
visible: true
|
||||||
@ -79,16 +80,19 @@ class ModalDialog extends React.Component {
|
|||||||
this.modalClick = this.modalClick.bind(this);
|
this.modalClick = this.modalClick.bind(this);
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
return !this.state.visible ? null : <div className="modal" onClick={this.modalClick}>
|
return !this.state.visible ? null : (
|
||||||
<div className="dialog">
|
<div className="modal" onClick={this.modalClick}>
|
||||||
<div className="dialog-title">{ this.state.data.title }<span className="dialog-close" onClick={this.close}>+</span></div>
|
<div className="dialog">
|
||||||
<div className="dialog-content">
|
<div className="dialog-title">
|
||||||
{
|
{this.state.data.title}
|
||||||
this.state.data.content
|
<span className="dialog-close" onClick={this.close}>
|
||||||
}
|
+
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="dialog-content">{this.state.data.content}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
document.addEventListener('modal', this.modalHandler);
|
document.addEventListener('modal', this.modalHandler);
|
||||||
@ -107,11 +111,13 @@ class ModalDialog extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
static show(data) {
|
static show(data) {
|
||||||
document.dispatchEvent(new CustomEvent('modal', {
|
document.dispatchEvent(
|
||||||
detail: {
|
new CustomEvent('modal', {
|
||||||
data
|
detail: {
|
||||||
}
|
data
|
||||||
}));
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
modalClick() {
|
modalClick() {
|
||||||
if (this.state.data.closeOnClick) this.close();
|
if (this.state.data.closeOnClick) this.close();
|
||||||
@ -121,20 +127,21 @@ class ModalDialog extends React.Component {
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
// add to render function
|
// add to render function
|
||||||
<ModalDialog />
|
<ModalDialog />;
|
||||||
|
|
||||||
// every time you wanna call the dialog
|
// every time you wanna call the dialog
|
||||||
// content is a jsx element
|
// content is a jsx element
|
||||||
ModalDialog.show({
|
ModalDialog.show({
|
||||||
title: 'Hello, world!',
|
title: 'Hello, world!',
|
||||||
closeOnClick: true,
|
closeOnClick: true,
|
||||||
content: <img src="https://github.com/30-seconds/30-seconds-of-react/blob/master/logo.png"/>
|
content: <img src="https://github.com/30-seconds/30-seconds-of-react/blob/master/logo.png" />
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Notes:
|
#### 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+.
|
- 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+.
|
||||||
|
|
||||||
<!-- tags: visual,static,children,state,class -->
|
<!-- tags: visual,static,children,state,class -->
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
-----
|
---
|
||||||
|
|
||||||
*This repository is a work in progress. If you want to contribute, please check the open issues to see where and how you can help out!*
|
_This repository is a work in progress. If you want to contribute, please check the open issues to see where and how you can help out!_
|
||||||
|
|
||||||
*This README is built using [markdown-builder](https://github.com/30-seconds/markdown-builder).*
|
_This README is built using [markdown-builder](https://github.com/30-seconds/markdown-builder)._
|
||||||
|
|||||||
@ -4,13 +4,14 @@
|
|||||||
|
|
||||||
> Curated collection of useful React snippets that you can understand in 30 seconds or less.
|
> Curated collection of useful React snippets that you can understand in 30 seconds or less.
|
||||||
|
|
||||||
* Use <kbd>Ctrl</kbd> + <kbd>F</kbd> or <kbd>command</kbd> + <kbd>F</kbd> to search for a snippet.
|
- Use <kbd>Ctrl</kbd> + <kbd>F</kbd> or <kbd>command</kbd> + <kbd>F</kbd> to search for a snippet.
|
||||||
* Contributions welcome, please read the [contribution guide](CONTRIBUTING.md).
|
- Contributions welcome, please read the [contribution guide](CONTRIBUTING.md).
|
||||||
* Snippets are written in React 16.8+, using hooks.
|
- Snippets are written in React 16.8+, using hooks.
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
To import a snippet into your project, you must import `React` and copy-paste the component's JavaScript code like this:
|
To import a snippet into your project, you must import `React` and copy-paste the component's JavaScript code like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
@ -20,19 +21,21 @@ 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:
|
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
|
```js
|
||||||
import './MyComponent.css';
|
import './MyComponent.css';
|
||||||
```
|
```
|
||||||
|
|
||||||
To render your component, make sure there is a node with and id of `"root"` present in your element (preferrably a `<div>`) and that you have imported `ReactDOM`, like this:
|
To render your component, make sure there is a node with and id of `"root"` present in your element (preferrably a `<div>`) and that you have imported `ReactDOM`, like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Related projects
|
#### Related projects
|
||||||
|
|
||||||
* [30 Seconds of Code](https://30secondsofcode.org)
|
- [30 Seconds of Code](https://30secondsofcode.org)
|
||||||
* [30 Seconds of CSS](https://30-seconds.github.io/30-seconds-of-css/)
|
- [30 Seconds of CSS](https://30-seconds.github.io/30-seconds-of-css/)
|
||||||
* [30 Seconds of Interviews](https://30secondsofinterviews.org/)
|
- [30 Seconds of Interviews](https://30secondsofinterviews.org/)
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|||||||
Reference in New Issue
Block a user