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.
|
||||
- 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.
|
||||
- **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.
|
||||
@ -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.
|
||||
- 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.
|
||||
- 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
|
||||
@ -78,4 +78,4 @@ Here's what you can do to help:
|
||||
- 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.
|
||||
- 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```"
|
||||
],
|
||||
"expertise": 2,
|
||||
"tags": [
|
||||
"string",
|
||||
"fragment",
|
||||
"regexp"
|
||||
],
|
||||
"tags": ["string", "fragment", "regexp"],
|
||||
"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 ```"
|
||||
],
|
||||
"expertise": 2,
|
||||
"tags": [
|
||||
"visual",
|
||||
"children",
|
||||
"state",
|
||||
"effect"
|
||||
],
|
||||
"tags": ["visual", "children", "state", "effect"],
|
||||
"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```"
|
||||
],
|
||||
"expertise": 2,
|
||||
"tags": [
|
||||
"visual",
|
||||
"children",
|
||||
"state"
|
||||
],
|
||||
"tags": ["visual", "children", "state"],
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
@ -57,10 +44,7 @@
|
||||
"```jsx\nReactDOM.render(\n <CountDown hours=\"1\" minutes=\"45\" />,\n document.getElementById('root')\n);\n```"
|
||||
],
|
||||
"expertise": 2,
|
||||
"tags": [
|
||||
"visual",
|
||||
"state"
|
||||
],
|
||||
"tags": ["visual", "state"],
|
||||
"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```"
|
||||
],
|
||||
"expertise": 0,
|
||||
"tags": [
|
||||
"array"
|
||||
],
|
||||
"tags": ["array"],
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
@ -86,9 +68,7 @@
|
||||
"```jsx\nconst people = ['John', 'Jesse'];\nReactDOM.render(\n <DataTable data={people} />,\n document.getElementById('root')\n);\n```"
|
||||
],
|
||||
"expertise": 0,
|
||||
"tags": [
|
||||
"array"
|
||||
],
|
||||
"tags": ["array"],
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
@ -101,12 +81,7 @@
|
||||
"```jsx\nReactDOM.render(<FileDrop handleDrop={console.log}/>, document.getElementById('root'));\n```"
|
||||
],
|
||||
"expertise": 2,
|
||||
"tags": [
|
||||
"visual",
|
||||
"input",
|
||||
"state",
|
||||
"effect"
|
||||
],
|
||||
"tags": ["visual", "input", "state", "effect"],
|
||||
"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```"
|
||||
],
|
||||
"expertise": 0,
|
||||
"tags": [
|
||||
"input"
|
||||
],
|
||||
"tags": ["input"],
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
@ -132,11 +105,7 @@
|
||||
"```jsx\nReactDOM.render(\n <LimitedTextarea limit={32} value='Hello!' />,\n document.getElementById('root')\n);\n```"
|
||||
],
|
||||
"expertise": 0,
|
||||
"tags": [
|
||||
"input",
|
||||
"state",
|
||||
"effect"
|
||||
],
|
||||
"tags": ["input", "state", "effect"],
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
@ -148,11 +117,7 @@
|
||||
"```jsx\nReactDOM.render(\n <LimitedWordTextArea limit={5} value='Hello there!' />,\n document.getElementById('root')\n);\n```"
|
||||
],
|
||||
"expertise": 0,
|
||||
"tags": [
|
||||
"input",
|
||||
"state",
|
||||
"effect"
|
||||
],
|
||||
"tags": ["input", "state", "effect"],
|
||||
"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```"
|
||||
],
|
||||
"expertise": 0,
|
||||
"tags": [
|
||||
"visual"
|
||||
],
|
||||
"tags": ["visual"],
|
||||
"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```"
|
||||
],
|
||||
"expertise": 1,
|
||||
"tags": [
|
||||
"array",
|
||||
"object"
|
||||
],
|
||||
"tags": ["array", "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`.",
|
||||
"<!-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```"
|
||||
],
|
||||
"expertise": 1,
|
||||
"tags": [
|
||||
"input",
|
||||
"state",
|
||||
"array"
|
||||
],
|
||||
"tags": ["input", "state", "array"],
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
@ -213,10 +169,7 @@
|
||||
"```jsx\nReactDOM.render(<PasswordRevealer />, document.getElementById('root'));\n```"
|
||||
],
|
||||
"expertise": 0,
|
||||
"tags": [
|
||||
"input",
|
||||
"state"
|
||||
],
|
||||
"tags": ["input", "state"],
|
||||
"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```"
|
||||
],
|
||||
"expertise": 0,
|
||||
"tags": [
|
||||
"input"
|
||||
],
|
||||
"tags": ["input"],
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
@ -242,12 +193,7 @@
|
||||
"```jsx\nReactDOM.render(<StarRating/>, document.getElementById('root'));\nReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));\n```"
|
||||
],
|
||||
"expertise": 2,
|
||||
"tags": [
|
||||
"visual",
|
||||
"children",
|
||||
"input",
|
||||
"state"
|
||||
],
|
||||
"tags": ["visual", "children", "input", "state"],
|
||||
"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```"
|
||||
],
|
||||
"expertise": 1,
|
||||
"tags": [
|
||||
"visual",
|
||||
"state",
|
||||
"children"
|
||||
],
|
||||
"tags": ["visual", "state", "children"],
|
||||
"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```"
|
||||
],
|
||||
"expertise": 0,
|
||||
"tags": [
|
||||
"input"
|
||||
],
|
||||
"tags": ["input"],
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
@ -290,10 +230,7 @@
|
||||
"```jsx\nReactDOM.render(<Ticker times={5} interval={1000} />, document.getElementById('root'));\n```"
|
||||
],
|
||||
"expertise": 1,
|
||||
"tags": [
|
||||
"visual",
|
||||
"state"
|
||||
],
|
||||
"tags": ["visual", "state"],
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
@ -305,10 +242,7 @@
|
||||
"```jsx\nReactDOM.render(<Toggle />, document.getElementById('root'));\n```"
|
||||
],
|
||||
"expertise": 0,
|
||||
"tags": [
|
||||
"visual",
|
||||
"state"
|
||||
],
|
||||
"tags": ["visual", "state"],
|
||||
"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```"
|
||||
],
|
||||
"expertise": 1,
|
||||
"tags": [
|
||||
"visual",
|
||||
"state",
|
||||
"children"
|
||||
],
|
||||
"tags": ["visual", "state", "children"],
|
||||
"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```"
|
||||
],
|
||||
"expertise": 2,
|
||||
"tags": [
|
||||
"object",
|
||||
"visual",
|
||||
"state",
|
||||
"recursion"
|
||||
],
|
||||
"tags": ["object", "visual", "state", "recursion"],
|
||||
"notes": []
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
const chalk = require("chalk");
|
||||
const util = require("./util.js");
|
||||
const markdown = require("markdown-builder");
|
||||
const snippets = require("../data/snippet_data.json")
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const util = require('./util.js');
|
||||
const markdown = require('markdown-builder');
|
||||
const snippets = require('../data/snippet_data.json');
|
||||
|
||||
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);
|
||||
|
||||
const STATIC_PARTS_PATH = "./static-parts";
|
||||
const STATIC_PARTS_PATH = './static-parts';
|
||||
|
||||
let startPart = "";
|
||||
let endPart = "";
|
||||
let output = "";
|
||||
let startPart = '';
|
||||
let endPart = '';
|
||||
let output = '';
|
||||
|
||||
const detailsTOC = (title, snippetsArray) =>
|
||||
`\n${misc
|
||||
@ -23,13 +25,13 @@ const detailsTOC = (title, snippetsArray) =>
|
||||
.ul(snippetsArray, snippet =>
|
||||
misc.link(
|
||||
snippet.title
|
||||
.replace("\n", "")
|
||||
.split("```")[0]
|
||||
.replace('\n', '')
|
||||
.split('```')[0]
|
||||
.trim(),
|
||||
misc.anchor(
|
||||
snippet.title
|
||||
.replace("\n", "")
|
||||
.split("```")[0]
|
||||
.replace('\n', '')
|
||||
.split('```')[0]
|
||||
.trim()
|
||||
)
|
||||
)
|
||||
@ -38,19 +40,13 @@ const detailsTOC = (title, snippetsArray) =>
|
||||
)
|
||||
.trim()}\n\n`;
|
||||
|
||||
console.time("Builder");
|
||||
console.time('Builder');
|
||||
|
||||
try {
|
||||
startPart = fs.readFileSync(
|
||||
path.join(STATIC_PARTS_PATH, "README-start.md"),
|
||||
"utf8"
|
||||
);
|
||||
endPart = fs.readFileSync(
|
||||
path.join(STATIC_PARTS_PATH, "README-end.md"),
|
||||
"utf8"
|
||||
);
|
||||
startPart = fs.readFileSync(path.join(STATIC_PARTS_PATH, 'README-start.md'), 'utf8');
|
||||
endPart = fs.readFileSync(path.join(STATIC_PARTS_PATH, 'README-end.md'), 'utf8');
|
||||
} 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);
|
||||
}
|
||||
|
||||
@ -60,13 +56,13 @@ try {
|
||||
|
||||
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
|
||||
TAG_NAMES.forEach(tag => {
|
||||
const taggedSnippets = snippetsInTag[tag];
|
||||
output += headers.h3(util.capitalize(tag));
|
||||
output += detailsTOC("View contents", taggedSnippets);
|
||||
output += detailsTOC('View contents', taggedSnippets);
|
||||
});
|
||||
|
||||
// delimeter after TOC
|
||||
@ -78,27 +74,24 @@ try {
|
||||
const taggedSnippets = snippetsInTag[tag];
|
||||
taggedSnippets.forEach(snippet => {
|
||||
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) {
|
||||
output += headers.h4('Notes');
|
||||
output += `\n${snippet.notes}`;
|
||||
}
|
||||
output += misc.collapsible('Examples', snippet.codeBlocks.slice(-1));
|
||||
output += `\n<br>${misc.link(
|
||||
"⬆ Back to top",
|
||||
misc.anchor("Table of Contents")
|
||||
)}\n\n`
|
||||
})
|
||||
output += `\n<br>${misc.link('⬆ Back to top', misc.anchor('Table of Contents'))}\n\n`;
|
||||
});
|
||||
});
|
||||
|
||||
// add static part for end
|
||||
output += `\n${endPart}\n`;
|
||||
|
||||
fs.writeFileSync("README.md", output);
|
||||
fs.writeFileSync('README.md', output);
|
||||
} catch (err) {
|
||||
console.log(`${chalk.red("ERROR!")} During README generation: ${err}`);
|
||||
console.log(`${chalk.red('ERROR!')} During README generation: ${err}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`${chalk.green("SUCCESS!")} README file generated!`);
|
||||
console.timeEnd("Builder");
|
||||
console.log(`${chalk.green('SUCCESS!')} README file generated!`);
|
||||
console.timeEnd('Builder');
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
const fs = require("fs-extra")
|
||||
const path = require("path")
|
||||
const chalk = require("chalk")
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const {
|
||||
attempt,
|
||||
readSnippets,
|
||||
@ -8,36 +8,35 @@ const {
|
||||
getSection,
|
||||
getTitle,
|
||||
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 title = getTitle(contents);
|
||||
const text = getTextualContent(contents);
|
||||
const codeBlocks = getCodeBlocks(contents);
|
||||
const notes = getSection("#### Notes", contents, false)
|
||||
.split("\n")
|
||||
.map(v => v.replace(/[*-] /g, ""))
|
||||
.filter(v => v.trim() !== "")
|
||||
|
||||
const notes = getSection('#### Notes', contents, false)
|
||||
.split('\n')
|
||||
.map(v => v.replace(/[*-] /g, ''))
|
||||
.filter(v => v.trim() !== '');
|
||||
|
||||
return {
|
||||
name,
|
||||
title,
|
||||
text,
|
||||
codeBlocks,
|
||||
expertise: parseInt(
|
||||
(contents.match(/<!--\s*expertise:\s*\(*(.+)\)*/) || [])[1],
|
||||
10
|
||||
),
|
||||
tags: (contents.match(/<!--\s*tags:\s*\(*(.+)\)*\s*-->/) || [])[1].split(",").map(v => v.trim()),
|
||||
expertise: parseInt((contents.match(/<!--\s*expertise:\s*\(*(.+)\)*/) || [])[1], 10),
|
||||
tags: (contents.match(/<!--\s*tags:\s*\(*(.+)\)*\s*-->/) || [])[1]
|
||||
.split(',')
|
||||
.map(v => v.trim()),
|
||||
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.timeEnd("Extractor");
|
||||
console.log(`${chalk.green('SUCCESS!')} snippet_data.json file generated!`);
|
||||
console.timeEnd('Extractor');
|
||||
|
||||
@ -1,31 +1,29 @@
|
||||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
const chalk = require("chalk");
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
|
||||
const SNIPPETS_PATH = "./snippets";
|
||||
const SNIPPETS_PATH = './snippets';
|
||||
|
||||
const attempt = (task, cb) => {
|
||||
try {
|
||||
return cb();
|
||||
} catch (e) {
|
||||
console.log(`${chalk.red("ERROR!")} During ${task}: ${e}`);
|
||||
console.log(`${chalk.red('ERROR!')} During ${task}: ${e}`);
|
||||
process.exit(1);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const capitalize = ([first, ...rest], lowerRest = false) =>
|
||||
first.toUpperCase() + (lowerRest ? rest.join("").toLowerCase() : rest.join(""));
|
||||
first.toUpperCase() + (lowerRest ? rest.join('').toLowerCase() : rest.join(''));
|
||||
|
||||
const readSnippets = () =>
|
||||
attempt("read snippets", () =>
|
||||
attempt('read snippets', () =>
|
||||
fs
|
||||
.readdirSync(SNIPPETS_PATH)
|
||||
.sort((a, b) => (a.toLowerCase() < b.toLowerCase() ? -1 : 1))
|
||||
.reduce((acc, name) => {
|
||||
acc[name] = fs
|
||||
.readFileSync(path.join(SNIPPETS_PATH, name), "utf8")
|
||||
.replace(/\r\n/g, "\n");
|
||||
acc[name] = fs.readFileSync(path.join(SNIPPETS_PATH, name), 'utf8').replace(/\r\n/g, '\n');
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
@ -41,16 +39,16 @@ const getCodeBlocks = str => {
|
||||
m.forEach(match => results.push(match));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
const getSection = (searchString, contents, includeSubsections = true) => {
|
||||
const indexOfSearch = contents.indexOf(searchString);
|
||||
if (indexOfSearch < 0) return "";
|
||||
if (indexOfSearch < 0) return '';
|
||||
|
||||
let endSearch = "\\n#"
|
||||
let endSearch = '\\n#';
|
||||
if (includeSubsections) {
|
||||
let i;
|
||||
for (i = 0; searchString[i] === "#" && i < searchString.length; i++);
|
||||
for (i = 0; searchString[i] === '#' && i < searchString.length; i++);
|
||||
|
||||
if (i > 0) {
|
||||
endSearch += `{${i - 1},${i}}[^#]`;
|
||||
@ -63,15 +61,14 @@ const getSection = (searchString, contents, includeSubsections = true) => {
|
||||
const sliceEnd = endIndex === -1 ? undefined : endIndex + sliceStart;
|
||||
|
||||
return contents.slice(sliceStart, sliceEnd).trim();
|
||||
}
|
||||
};
|
||||
|
||||
const getTextualContent = str => {
|
||||
const regex = /###.*\n*([\s\S]*?)```/g;
|
||||
const results = [];
|
||||
let m = null;
|
||||
while ((m = regex.exec(str)) !== null) {
|
||||
if (m.index === regex.lastIndex)
|
||||
regex.lastIndex += 1;
|
||||
if (m.index === regex.lastIndex) regex.lastIndex += 1;
|
||||
|
||||
m.forEach((match, groupIndex) => {
|
||||
results.push(match);
|
||||
@ -80,8 +77,7 @@ const getTextualContent = str => {
|
||||
return results[1];
|
||||
};
|
||||
|
||||
const getTitle = (contents) =>
|
||||
contents.split('\n')[0].replace(/^#+\s+/g,'');
|
||||
const getTitle = contents => contents.split('\n')[0].replace(/^#+\s+/g, '');
|
||||
|
||||
module.exports = {
|
||||
attempt,
|
||||
@ -92,4 +88,4 @@ module.exports = {
|
||||
getCodeBlocks,
|
||||
getSection,
|
||||
getTitle
|
||||
};
|
||||
};
|
||||
|
||||
@ -11,18 +11,20 @@ function ComponentName(props) {
|
||||
setState(0);
|
||||
});
|
||||
return <div>{props}</div>;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<ComponentName />, document.getElementById("root"));
|
||||
ReactDOM.render(<ComponentName />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
<!-- OPTIONAL -->
|
||||
|
||||
#### Notes:
|
||||
* Things to remember when using this
|
||||
* Other options that might be less appealing or have lower compatibility
|
||||
* Common mistakes and issues
|
||||
|
||||
- Things to remember when using this
|
||||
- Other options that might be less appealing or have lower compatibility
|
||||
- Common mistakes and issues
|
||||
|
||||
<!-- tags: (separate each by a comma) -->
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
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.
|
||||
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
|
||||
function AutoLink({ text }) {
|
||||
@ -15,9 +15,7 @@ function AutoLink({ text }) {
|
||||
let match = word.match(delimiter);
|
||||
if (match) {
|
||||
let url = match[0];
|
||||
return (
|
||||
<a href={url.startsWith("http") ? url : `http://${url}`}>{url}</a>
|
||||
);
|
||||
return <a href={url.startsWith('http') ? url : `http://${url}`}>{url}</a>;
|
||||
}
|
||||
return word;
|
||||
})}
|
||||
@ -28,7 +26,7 @@ function AutoLink({ text }) {
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(
|
||||
<AutoLink text='foo bar baz http://example.org bar' />,
|
||||
<AutoLink text="foo bar baz http://example.org bar" />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
@ -14,14 +14,14 @@ function Carousel(props) {
|
||||
let scrollInterval = null;
|
||||
const style = {
|
||||
carousel: {
|
||||
position: "relative"
|
||||
position: 'relative'
|
||||
},
|
||||
carouselItem: {
|
||||
position: "absolute",
|
||||
visibility: "hidden"
|
||||
position: 'absolute',
|
||||
visibility: 'hidden'
|
||||
},
|
||||
visible: {
|
||||
visibility: "visible"
|
||||
visibility: 'visible'
|
||||
}
|
||||
};
|
||||
React.useEffect(() => {
|
||||
@ -57,11 +57,10 @@ ReactDOM.render(
|
||||
<div>carousel item 3</div>
|
||||
]}
|
||||
/>,
|
||||
document.getElementById("root")
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
```
|
||||
|
||||
<!-- tags: visual,children,state,effect -->
|
||||
|
||||
<!-- expertise: 2 -->
|
||||
|
||||
|
||||
@ -14,24 +14,21 @@ function Collapse(props) {
|
||||
|
||||
const style = {
|
||||
collapsed: {
|
||||
display: "none"
|
||||
display: 'none'
|
||||
},
|
||||
expanded: {
|
||||
display: "block"
|
||||
display: 'block'
|
||||
},
|
||||
buttonStyle: {
|
||||
display: "block",
|
||||
width: "100%"
|
||||
display: 'block',
|
||||
width: '100%'
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
style={style.buttonStyle}
|
||||
onClick={() => setIsCollapsed(!isCollapsed)}
|
||||
>
|
||||
{isCollapsed ? "Show" : "Hide"} content
|
||||
<button style={style.buttonStyle} onClick={() => setIsCollapsed(!isCollapsed)}>
|
||||
{isCollapsed ? 'Show' : 'Hide'} content
|
||||
</button>
|
||||
<div
|
||||
className="collapse-content"
|
||||
|
||||
@ -23,8 +23,7 @@ function CountDown({ hours = 0, minutes = 0, seconds = 0 }) {
|
||||
|
||||
const tick = () => {
|
||||
if (paused || over) return;
|
||||
if (time.hours == 0 && time.minutes == 0 && time.seconds == 0)
|
||||
setOver(true);
|
||||
if (time.hours == 0 && time.minutes == 0 && time.seconds == 0) setOver(true);
|
||||
else if (time.minutes == 0 && time.seconds == 0)
|
||||
setTime({
|
||||
hours: time.hours - 1,
|
||||
@ -62,15 +61,11 @@ function CountDown({ hours = 0, minutes = 0, seconds = 0 }) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>{`${time.hours
|
||||
<p>{`${time.hours.toString().padStart(2, '0')}:${time.minutes
|
||||
.toString()
|
||||
.padStart(2, "0")}:${time.minutes
|
||||
.toString()
|
||||
.padStart(2, "0")}:${time.seconds.toString().padStart(2, "0")}`}</p>
|
||||
<div>{over ? "Time's up!" : ""}</div>
|
||||
<button onClick={() => setPaused(!paused)}>
|
||||
{paused ? "Resume" : "Pause"}
|
||||
</button>
|
||||
.padStart(2, '0')}:${time.seconds.toString().padStart(2, '0')}`}</p>
|
||||
<div>{over ? "Time's up!" : ''}</div>
|
||||
<button onClick={() => setPaused(!paused)}>{paused ? 'Resume' : 'Pause'}</button>
|
||||
<button onClick={() => reset()}>Restart</button>
|
||||
</div>
|
||||
);
|
||||
@ -78,10 +73,7 @@ function CountDown({ hours = 0, minutes = 0, seconds = 0 }) {
|
||||
```
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(
|
||||
<CountDown hours="1" minutes="45" />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
ReactDOM.render(<CountDown hours="1" minutes="45" />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
<!-- tags: visual,state -->
|
||||
|
||||
@ -8,17 +8,15 @@ Omit the `isOrdered` prop to render a `<ul>` list by default.
|
||||
|
||||
```jsx
|
||||
function DataList({ isOrdered, data }) {
|
||||
const list = data.map((val, i) => (
|
||||
<li key={`${i}_${val}`}>{val}</li>
|
||||
));
|
||||
const list = data.map((val, i) => <li key={`${i}_${val}`}>{val}</li>);
|
||||
return isOrdered ? <ol>{list}</ol> : <ul>{list}</ul>;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const names = ['John', 'Paul', 'Mary'];
|
||||
ReactDOM.render(<DataList data={names}/>, document.getElementById('root'));
|
||||
ReactDOM.render(<DataList data={names} isOrdered/>, document.getElementById('root'));
|
||||
ReactDOM.render(<DataList data={names} />, document.getElementById('root'));
|
||||
ReactDOM.render(<DataList data={names} isOrdered />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
<!-- tags: array -->
|
||||
|
||||
@ -16,12 +16,12 @@ function DataTable({ data }) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.map((val, i) =>
|
||||
{data.map((val, i) => (
|
||||
<tr key={`${i}_${val}`}>
|
||||
<td>{i}</td>
|
||||
<td>{val}</td>
|
||||
</tr>
|
||||
)}
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
@ -30,10 +30,7 @@ function DataTable({ data }) {
|
||||
|
||||
```jsx
|
||||
const people = ['John', 'Jesse'];
|
||||
ReactDOM.render(
|
||||
<DataTable data={people} />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
ReactDOM.render(<DataTable data={people} />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
<!-- 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.
|
||||
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.
|
||||
`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`.
|
||||
|
||||
|
||||
```css
|
||||
.filedrop {
|
||||
min-height: 120px;
|
||||
border: 3px solid #D3D3D3;
|
||||
border: 3px solid #d3d3d3;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
padding: 32px;
|
||||
@ -24,11 +23,11 @@ Finally, bind the `ref` of the created `<div>` to `dropRef`.
|
||||
}
|
||||
|
||||
.filedrop.drag {
|
||||
border: 3px dashed #1E90FF;
|
||||
border: 3px dashed #1e90ff;
|
||||
}
|
||||
|
||||
.filedrop.ready {
|
||||
border: 3px solid #32CD32;
|
||||
border: 3px solid #32cd32;
|
||||
}
|
||||
```
|
||||
|
||||
@ -72,24 +71,22 @@ function FileDrop(props) {
|
||||
|
||||
React.useEffect(() => {
|
||||
let div = dropRef.current;
|
||||
div.addEventListener("dragenter", handleDragIn);
|
||||
div.addEventListener("dragleave", handleDragOut);
|
||||
div.addEventListener("dragover", handleDrag);
|
||||
div.addEventListener("drop", handleDrop);
|
||||
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);
|
||||
div.removeEventListener('dragenter', handleDragIn);
|
||||
div.removeEventListener('dragleave', handleDragOut);
|
||||
div.removeEventListener('dragover', handleDrag);
|
||||
div.removeEventListener('drop', handleDrop);
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={dropRef}
|
||||
className={
|
||||
drag ? "filedrop drag" : filename ? "filedrop ready" : "filedrop"
|
||||
}
|
||||
className={drag ? 'filedrop drag' : filename ? 'filedrop ready' : 'filedrop'}
|
||||
>
|
||||
{filename && !drag ? <div>{filename}</div> : <div>Drop files here!</div>}
|
||||
</div>
|
||||
@ -98,7 +95,7 @@ function FileDrop(props) {
|
||||
```
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<FileDrop handleDrop={console.log}/>, document.getElementById('root'));
|
||||
ReactDOM.render(<FileDrop handleDrop={console.log} />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
<!-- 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.
|
||||
|
||||
```jsx
|
||||
function Input ({ callback, type = 'text', disabled = false, readOnly = false, placeholder = '' }) {
|
||||
function Input({ callback, type = 'text', disabled = false, readOnly = false, placeholder = '' }) {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
@ -21,7 +21,7 @@ function Input ({ callback, type = 'text', disabled = false, readOnly = false, p
|
||||
|
||||
```jsx
|
||||
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')
|
||||
);
|
||||
```
|
||||
|
||||
@ -36,12 +36,9 @@ function LimitedTextarea({ rows, cols, value, limit }) {
|
||||
```
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(
|
||||
<LimitedTextarea limit={32} value='Hello!' />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
ReactDOM.render(<LimitedTextarea limit={32} value="Hello!" />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
<!-- tags: input,state,effect -->
|
||||
|
||||
<!-- expertise: 0 -->
|
||||
|
||||
|
||||
@ -14,13 +14,13 @@ function LimitedWordTextarea({ rows, cols, value, limit }) {
|
||||
const [wordCount, setWordCount] = React.useState(0);
|
||||
|
||||
const setFormattedContent = text => {
|
||||
let words = text.split(" ");
|
||||
let words = text.split(' ');
|
||||
if (words.filter(Boolean).length > limit) {
|
||||
setContent(
|
||||
text
|
||||
.split(" ")
|
||||
.split(' ')
|
||||
.slice(0, limit)
|
||||
.join(" ")
|
||||
.join(' ')
|
||||
);
|
||||
setWordCount(limit);
|
||||
} else {
|
||||
@ -51,11 +51,11 @@ function LimitedWordTextarea({ rows, cols, value, limit }) {
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(
|
||||
<LimitedWordTextArea limit={5} value='Hello there!' />,
|
||||
<LimitedWordTextArea limit={5} value="Hello there!" />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
<!-- tags: input,state,effect -->
|
||||
|
||||
<!-- expertise: 0 -->
|
||||
|
||||
|
||||
@ -8,9 +8,7 @@ Render the link with `props.children` as its content.
|
||||
```jsx
|
||||
function Mailto({ email, subject, body, ...props }) {
|
||||
return (
|
||||
<a href={`mailto:${email}?subject=${subject || ""}&body=${body || ""}`}>
|
||||
{props.children}
|
||||
</a>
|
||||
<a href={`mailto:${email}?subject=${subject || ''}&body=${body || ''}`}>{props.children}</a>
|
||||
);
|
||||
}
|
||||
```
|
||||
@ -20,7 +18,7 @@ ReactDOM.render(
|
||||
<Mailto email="foo@bar.baz" subject="Hello" body="Hello world!">
|
||||
Mail me!
|
||||
</Mailto>,
|
||||
document.getElementById("root")
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
@ -17,12 +17,18 @@ function MappedTable({ data, propertyNames }) {
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>{propertyNames.map(val => <th key={`h_${val}`}>{val}</th>)}</tr>
|
||||
<tr>
|
||||
{propertyNames.map(val => (
|
||||
<th key={`h_${val}`}>{val}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filteredData.map((val, 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>
|
||||
))}
|
||||
</tbody>
|
||||
@ -44,7 +50,8 @@ 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`.
|
||||
|
||||
- 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 -->
|
||||
|
||||
|
||||
@ -19,28 +19,23 @@ const style = {
|
||||
}
|
||||
};
|
||||
|
||||
function MultiselectCheckbox ({ options, onChange }) {
|
||||
function MultiselectCheckbox({ options, onChange }) {
|
||||
const [data, setData] = React.useState(options);
|
||||
|
||||
const toggle = (item) => {
|
||||
const toggle = item => {
|
||||
data.map((_, key) => {
|
||||
if (data[key].label === item.label)
|
||||
data[key].checked = !item.checked;
|
||||
if (data[key].label === item.label) data[key].checked = !item.checked;
|
||||
});
|
||||
setData([...data]);
|
||||
onChange(data);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ul style={style.listContainer}>
|
||||
{data.map(item => {
|
||||
return (
|
||||
<li
|
||||
key={item.label}
|
||||
style={style.itemStyle}
|
||||
onClick={() => toggle(item)}
|
||||
>
|
||||
<input readOnly type='checkbox' checked={item.checked || false} />
|
||||
<li key={item.label} style={style.itemStyle} onClick={() => toggle(item)}>
|
||||
<input readOnly type="checkbox" checked={item.checked || false} />
|
||||
{item.label}
|
||||
</li>
|
||||
);
|
||||
@ -51,7 +46,7 @@ function MultiselectCheckbox ({ options, onChange }) {
|
||||
```
|
||||
|
||||
```jsx
|
||||
const options = [{ label: "Item One" }, { label: "Item Two" }];
|
||||
const options = [{ label: 'Item One' }, { label: 'Item Two' }];
|
||||
|
||||
ReactDOM.render(
|
||||
<MultiselectCheckbox
|
||||
@ -60,7 +55,7 @@ ReactDOM.render(
|
||||
console.log(data);
|
||||
}}
|
||||
/>,
|
||||
document.getElementById("root")
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
@ -11,11 +11,7 @@ function PasswordRevealer({ value }) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
type={shown ? "text" : "password"}
|
||||
value={value}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
<input type={shown ? 'text' : 'password'} value={value} onChange={() => {}} />
|
||||
<button onClick={() => setShown(!shown)}>Show/Hide</button>
|
||||
</div>
|
||||
);
|
||||
@ -29,4 +25,3 @@ ReactDOM.render(<PasswordRevealer />, document.getElementById('root'));
|
||||
<!--tags: input,state -->
|
||||
|
||||
<!--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.
|
||||
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
|
||||
function Select ({ values, callback, disabled = false, readonly = false, selected }) {
|
||||
function Select({ values, callback, disabled = false, readonly = false, selected }) {
|
||||
return (
|
||||
<select
|
||||
disabled={disabled}
|
||||
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>
|
||||
);
|
||||
}
|
||||
@ -28,7 +32,7 @@ let choices = [
|
||||
['mango', 'Mango']
|
||||
];
|
||||
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')
|
||||
);
|
||||
```
|
||||
|
||||
@ -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.
|
||||
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 `<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`).
|
||||
|
||||
```jsx
|
||||
function Star({ marked, starId }) {
|
||||
return (
|
||||
<span star-id={starId} style={{ color: "#ff9933" }} role="button">
|
||||
{marked ? "\u2605" : "\u2606"}
|
||||
<span star-id={starId} style={{ color: '#ff9933' }} role="button">
|
||||
{marked ? '\u2605' : '\u2606'}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function StarRating(props) {
|
||||
const [rating, setRating] = React.useState(
|
||||
typeof props.rating == "number" ? props.rating : 0
|
||||
);
|
||||
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");
|
||||
if (event && event.target && event.target.getAttribute('star-id'))
|
||||
val = event.target.getAttribute('star-id');
|
||||
setSelection(val);
|
||||
};
|
||||
return (
|
||||
<div
|
||||
onMouseOut={() => hoverOver(null)}
|
||||
onClick={() =>
|
||||
setRating(event.target.getAttribute("star-id") || this.state.rating)
|
||||
}
|
||||
onClick={() => setRating(event.target.getAttribute('star-id') || this.state.rating)}
|
||||
onMouseOver={hoverOver}
|
||||
>
|
||||
{Array.from({ length: 5 }, (v, i) => (
|
||||
@ -49,7 +45,7 @@ function StarRating(props) {
|
||||
```
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<StarRating/>, document.getElementById('root'));
|
||||
ReactDOM.render(<StarRating />, document.getElementById('root'));
|
||||
ReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
### Tab
|
||||
|
||||
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`.
|
||||
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 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 `<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`.
|
||||
|
||||
@ -16,10 +17,10 @@ Define `changeTab`, which will be executed when clicking a `<button>` from the `
|
||||
background: none;
|
||||
}
|
||||
.tab-menu > button.focus {
|
||||
border-bottom: 2px solid #007BEF;
|
||||
border-bottom: 2px solid #007bef;
|
||||
}
|
||||
.tab-menu > button:hover {
|
||||
border-bottom: 2px solid #007BEF;
|
||||
border-bottom: 2px solid #007bef;
|
||||
}
|
||||
```
|
||||
|
||||
@ -31,19 +32,16 @@ function TabItem(props) {
|
||||
function Tabs(props) {
|
||||
const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);
|
||||
const changeTab = newIndex => {
|
||||
if (typeof props.onTabClick === "function") props.onTabClick(newIndex);
|
||||
if (typeof props.onTabClick === 'function') props.onTabClick(newIndex);
|
||||
setBindIndex(newIndex);
|
||||
};
|
||||
const items = props.children.filter(item => item.type.name === "TabItem");
|
||||
const items = props.children.filter(item => item.type.name === 'TabItem');
|
||||
|
||||
return (
|
||||
<div className="wrapper">
|
||||
<div className="tab-menu">
|
||||
{items.map(({ props: { index, label } }) => (
|
||||
<button
|
||||
onClick={() => changeTab(index)}
|
||||
className={bindIndex === index ? "focus" : ""}
|
||||
>
|
||||
<button onClick={() => changeTab(index)} className={bindIndex === index ? 'focus' : ''}>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
@ -54,7 +52,7 @@ function Tabs(props) {
|
||||
{...props}
|
||||
className="tab-view_item"
|
||||
key={props.index}
|
||||
style={{ display: bindIndex === props.index ? "block" : "none" }}
|
||||
style={{ display: bindIndex === props.index ? 'block' : 'none' }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -62,6 +60,7 @@ function Tabs(props) {
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(
|
||||
<Tabs defaultIndex="1" onTabClick={console.log}>
|
||||
@ -72,9 +71,8 @@ ReactDOM.render(
|
||||
Dolor sit amet
|
||||
</TabItem>
|
||||
</Tabs>,
|
||||
document.getElementById("root")
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
<!-- 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.
|
||||
|
||||
```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 (
|
||||
<textarea
|
||||
cols={cols}
|
||||
@ -14,7 +21,7 @@ function TextArea ({ callback, cols = 20, rows = 2, disabled = false, readOnly =
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
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
|
||||
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')
|
||||
);
|
||||
```
|
||||
|
||||
@ -14,17 +14,15 @@ function Ticker(props) {
|
||||
const tick = () => {
|
||||
reset();
|
||||
interval = setInterval(() => {
|
||||
if (ticker < props.times)
|
||||
setTicker(ticker + 1);
|
||||
else
|
||||
clearInterval(interval);
|
||||
if (ticker < props.times) setTicker(ticker + 1);
|
||||
else clearInterval(interval);
|
||||
}, props.interval);
|
||||
}
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
setTicker(0);
|
||||
clearInterval(interval);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -42,4 +40,4 @@ ReactDOM.render(<Ticker times={5} interval={1000} />, document.getElementById('r
|
||||
|
||||
<!-- tags: visual,state -->
|
||||
|
||||
<!-- expertise: 1 -->
|
||||
<!-- expertise: 1 -->
|
||||
|
||||
@ -11,19 +11,16 @@ function Toggle(props) {
|
||||
const [isToggleOn, setIsToggleOn] = React.useState(false);
|
||||
style = {
|
||||
on: {
|
||||
backgroundColor: "green"
|
||||
backgroundColor: 'green'
|
||||
},
|
||||
off: {
|
||||
backgroundColor: "grey"
|
||||
backgroundColor: 'grey'
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => setIsToggleOn(!isToggleOn)}
|
||||
style={isToggleOn ? style.on : style.off}
|
||||
>
|
||||
{isToggleOn ? "ON" : "OFF"}
|
||||
<button onClick={() => setIsToggleOn(!isToggleOn)} style={isToggleOn ? style.on : style.off}>
|
||||
{isToggleOn ? 'ON' : 'OFF'}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ Renders a tooltip component.
|
||||
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.
|
||||
Handle the `onMouseEnter` and `onMouseLeave` methods, by altering the value of the `show` variable.
|
||||
|
||||
|
||||
```css
|
||||
.tooltip {
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
function Tooltip({ children, text, ...rest }) {
|
||||
const [show, setShow] = React.useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="tooltip" style={show ? { visibility: "visible" } : {}}>
|
||||
<div className="tooltip" style={show ? { visibility: 'visible' } : {}}>
|
||||
{text}
|
||||
<span className="tooltip-arrow" />
|
||||
</div>
|
||||
<div
|
||||
{...rest}
|
||||
onMouseEnter={() => setShow(true)}
|
||||
onMouseLeave={() => setShow(false)}
|
||||
>
|
||||
<div {...rest} onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
@ -47,14 +44,14 @@ function Tooltip({ children, text, ...rest }) {
|
||||
```
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(
|
||||
<Tooltip text='Simple tooltip'>
|
||||
<button>Hover me!</button>
|
||||
</Tooltip>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
ReactDOM.render(
|
||||
<Tooltip text="Simple tooltip">
|
||||
<button>Hover me!</button>
|
||||
</Tooltip>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
<!-- tags: visual,state,children -->
|
||||
|
||||
<!-- expertise: 1 -->
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
|
||||
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 `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.
|
||||
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.
|
||||
Otherwise, render a `<p>` element with the appropriate style.
|
||||
|
||||
@ -29,8 +29,8 @@ div.tree-element:before {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 4px solid transparent;
|
||||
border-bottom: 4px solid transparent;
|
||||
border-left: 5px solid gray;
|
||||
@ -59,39 +59,38 @@ function TreeView({
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ marginLeft: isChildElement ? 16 : 4 + "px" }}
|
||||
className={isParentToggled ? "tree-element" : "tree-element collapsed"}
|
||||
style={{ marginLeft: isChildElement ? 16 : 4 + 'px' }}
|
||||
className={isParentToggled ? 'tree-element' : 'tree-element collapsed'}
|
||||
>
|
||||
<span
|
||||
className={isToggled ? "toggler" : "toggler closed"}
|
||||
className={isToggled ? 'toggler' : 'toggler closed'}
|
||||
onClick={() => setIsToggled(!isToggled)}
|
||||
/>
|
||||
{name ? <strong> {name}: </strong> : <span> </span>}
|
||||
{Array.isArray(data) ? "[" : "{"}
|
||||
{!isToggled && "..."}
|
||||
{Object.keys(data).map(
|
||||
(v, i, a) =>
|
||||
typeof data[v] == "object" ? (
|
||||
<TreeView
|
||||
data={data[v]}
|
||||
isLast={i === a.length - 1}
|
||||
name={Array.isArray(data) ? null : v}
|
||||
isChildElement
|
||||
isParentToggled={isParentToggled && isToggled}
|
||||
/>
|
||||
) : (
|
||||
<p
|
||||
style={{ marginLeft: 16 + "px" }}
|
||||
className={isToggled ? "tree-element" : "tree-element collapsed"}
|
||||
>
|
||||
{Array.isArray(data) ? "" : <strong>{v}: </strong>}
|
||||
{data[v]}
|
||||
{i === a.length - 1 ? "" : ","}
|
||||
</p>
|
||||
)
|
||||
{Array.isArray(data) ? '[' : '{'}
|
||||
{!isToggled && '...'}
|
||||
{Object.keys(data).map((v, i, a) =>
|
||||
typeof data[v] == 'object' ? (
|
||||
<TreeView
|
||||
data={data[v]}
|
||||
isLast={i === a.length - 1}
|
||||
name={Array.isArray(data) ? null : v}
|
||||
isChildElement
|
||||
isParentToggled={isParentToggled && isToggled}
|
||||
/>
|
||||
) : (
|
||||
<p
|
||||
style={{ marginLeft: 16 + 'px' }}
|
||||
className={isToggled ? 'tree-element' : 'tree-element collapsed'}
|
||||
>
|
||||
{Array.isArray(data) ? '' : <strong>{v}: </strong>}
|
||||
{data[v]}
|
||||
{i === a.length - 1 ? '' : ','}
|
||||
</p>
|
||||
)
|
||||
)}
|
||||
{Array.isArray(data) ? "]" : "}"}
|
||||
{!isLast ? "," : ""}
|
||||
{Array.isArray(data) ? ']' : '}'}
|
||||
{!isLast ? ',' : ''}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -100,27 +99,27 @@ function TreeView({
|
||||
```jsx
|
||||
let data = {
|
||||
lorem: {
|
||||
ipsum: "dolor sit",
|
||||
ipsum: 'dolor sit',
|
||||
amet: {
|
||||
consectetur: "adipiscing",
|
||||
consectetur: 'adipiscing',
|
||||
elit: [
|
||||
"duis",
|
||||
"vitae",
|
||||
'duis',
|
||||
'vitae',
|
||||
{
|
||||
semper: "orci"
|
||||
semper: 'orci'
|
||||
},
|
||||
{
|
||||
est: "sed ornare"
|
||||
est: 'sed ornare'
|
||||
},
|
||||
"etiam",
|
||||
["laoreet", "tincidunt"],
|
||||
["vestibulum", "ante"]
|
||||
'etiam',
|
||||
['laoreet', 'tincidunt'],
|
||||
['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 -->
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
### 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.
|
||||
|
||||
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`.
|
||||
|
||||
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
|
||||
|
||||
- `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 `<div>` 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;
|
||||
}
|
||||
.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.modalHandler = e => {
|
||||
this.setState({
|
||||
data: e.detail.data,
|
||||
visible: true
|
||||
@ -79,16 +80,19 @@ class ModalDialog extends React.Component {
|
||||
this.modalClick = this.modalClick.bind(this);
|
||||
}
|
||||
render() {
|
||||
return !this.state.visible ? null : <div className="modal" onClick={this.modalClick}>
|
||||
<div className="dialog">
|
||||
<div className="dialog-title">{ this.state.data.title }<span className="dialog-close" onClick={this.close}>+</span></div>
|
||||
<div className="dialog-content">
|
||||
{
|
||||
this.state.data.content
|
||||
}
|
||||
return !this.state.visible ? null : (
|
||||
<div className="modal" onClick={this.modalClick}>
|
||||
<div className="dialog">
|
||||
<div className="dialog-title">
|
||||
{this.state.data.title}
|
||||
<span className="dialog-close" onClick={this.close}>
|
||||
+
|
||||
</span>
|
||||
</div>
|
||||
<div className="dialog-content">{this.state.data.content}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
componentDidMount() {
|
||||
document.addEventListener('modal', this.modalHandler);
|
||||
@ -107,11 +111,13 @@ class ModalDialog extends React.Component {
|
||||
});
|
||||
}
|
||||
static show(data) {
|
||||
document.dispatchEvent(new CustomEvent('modal', {
|
||||
detail: {
|
||||
data
|
||||
}
|
||||
}));
|
||||
document.dispatchEvent(
|
||||
new CustomEvent('modal', {
|
||||
detail: {
|
||||
data
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
modalClick() {
|
||||
if (this.state.data.closeOnClick) this.close();
|
||||
@ -121,20 +127,21 @@ class ModalDialog extends React.Component {
|
||||
|
||||
```jsx
|
||||
// add to render function
|
||||
<ModalDialog />
|
||||
<ModalDialog />;
|
||||
|
||||
// every time you wanna call the dialog
|
||||
// content is a jsx element
|
||||
ModalDialog.show({
|
||||
title: 'Hello, world!',
|
||||
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:
|
||||
* 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 -->
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
* 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).
|
||||
* Snippets are written in React 16.8+, using hooks.
|
||||
- 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).
|
||||
- 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';
|
||||
|
||||
@ -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:
|
||||
|
||||
```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 `<div>`) and that you have imported `ReactDOM`, like this:
|
||||
|
||||
```js
|
||||
import ReactDOM from 'react-dom';
|
||||
```
|
||||
|
||||
#### Related projects
|
||||
|
||||
* [30 Seconds of Code](https://30secondsofcode.org)
|
||||
* [30 Seconds of CSS](https://30-seconds.github.io/30-seconds-of-css/)
|
||||
* [30 Seconds of Interviews](https://30secondsofinterviews.org/)
|
||||
- [30 Seconds of Code](https://30secondsofcode.org)
|
||||
- [30 Seconds of CSS](https://30-seconds.github.io/30-seconds-of-css/)
|
||||
- [30 Seconds of Interviews](https://30secondsofinterviews.org/)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
Reference in New Issue
Block a user