Ran linter

This commit is contained in:
Angelos Chalaris
2019-03-02 10:10:14 +02:00
parent ddd852962d
commit 3868acab34
32 changed files with 583 additions and 708 deletions

View File

@ -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

384
README.md
View File

@ -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,89 +21,86 @@ 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
### Array ### Array
<details> <details>
<summary>View contents</summary> <summary>View contents</summary>
* [DataList](#datalist) - [DataList](#datalist)
* [DataTable](#datatable) - [DataTable](#datatable)
* [MappedTable](#mappedtable) - [MappedTable](#mappedtable)
</details> </details>
### Input ### Input
<details> <details>
<summary>View contents</summary> <summary>View contents</summary>
* [Input](#input) - [Input](#input)
* [LimitedTextarea](#limitedtextarea) - [LimitedTextarea](#limitedtextarea)
* [LimitedWordTextarea](#limitedwordtextarea) - [LimitedWordTextarea](#limitedwordtextarea)
* [MultiselectCheckbox](#multiselectcheckbox) - [MultiselectCheckbox](#multiselectcheckbox)
* [PasswordRevealer](#passwordrevealer) - [PasswordRevealer](#passwordrevealer)
* [Select](#select) - [Select](#select)
* [TextArea](#textarea) - [TextArea](#textarea)
</details> </details>
### Object ### Object
<details> <details>
<summary>View contents</summary> <summary>View contents</summary>
* [TreeView](#treeview) - [TreeView](#treeview)
</details> </details>
### String ### String
<details> <details>
<summary>View contents</summary> <summary>View contents</summary>
* [AutoLink](#autolink) - [AutoLink](#autolink)
</details> </details>
### Visual ### Visual
<details> <details>
<summary>View contents</summary> <summary>View contents</summary>
* [Carousel](#carousel) - [Carousel](#carousel)
* [Collapse](#collapse) - [Collapse](#collapse)
* [CountDown](#countdown) - [CountDown](#countdown)
* [FileDrop](#filedrop) - [FileDrop](#filedrop)
* [Mailto](#mailto) - [Mailto](#mailto)
* [StarRating](#starrating) - [StarRating](#starrating)
* [Tab](#tab) - [Tab](#tab)
* [Ticker](#ticker) - [Ticker](#ticker)
* [Toggle](#toggle) - [Toggle](#toggle)
* [Tooltip](#tooltip) - [Tooltip](#tooltip)
</details> </details>
--- ---
## Array ## Array
### DataList ### DataList
Renders a list of elements from an array of primitives. Renders a list of elements from an array of primitives.
@ -113,9 +111,7 @@ 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>;
} }
``` ```
@ -128,6 +124,7 @@ 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'));
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -150,12 +147,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>
); );
@ -167,11 +164,9 @@ 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')
);
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -195,12 +190,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>
@ -208,6 +209,7 @@ function MappedTable({ data, propertyNames }) {
); );
} }
``` ```
#### 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`.,<!-tags: array,object -->,<!-expertise: 1 --> 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 -->,<!-expertise: 1 -->
@ -226,12 +228,13 @@ ReactDOM.render(
document.getElementById('root') document.getElementById('root')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
## Input ## Input
### Input ### Input
Renders an `<input>` element that uses a callback function to pass its value to the parent component. Renders an `<input>` element that uses a callback function to pass its value to the parent component.
@ -258,10 +261,11 @@ 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')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -307,11 +311,9 @@ function LimitedTextarea({ rows, cols, value, limit }) {
<summary>Examples</summary> <summary>Examples</summary>
```jsx ```jsx
ReactDOM.render( ReactDOM.render(<LimitedTextarea limit={32} value="Hello!" />, document.getElementById('root'));
<LimitedTextarea limit={32} value='Hello!' />,
document.getElementById('root')
);
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -332,13 +334,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 {
@ -372,10 +374,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')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -404,25 +407,20 @@ 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>
); );
@ -436,7 +434,7 @@ function MultiselectCheckbox ({ options, onChange }) {
<summary>Examples</summary> <summary>Examples</summary>
```jsx ```jsx
const options = [{ label: "Item One" }, { label: "Item Two" }]; const options = [{ label: 'Item One' }, { label: 'Item Two' }];
ReactDOM.render( ReactDOM.render(
<MultiselectCheckbox <MultiselectCheckbox
@ -445,9 +443,10 @@ ReactDOM.render(
console.log(data); console.log(data);
}} }}
/>, />,
document.getElementById("root") document.getElementById('root')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -465,11 +464,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>
); );
@ -482,6 +477,7 @@ function PasswordRevealer({ value }) {
```jsx ```jsx
ReactDOM.render(<PasswordRevealer />, document.getElementById('root')); ReactDOM.render(<PasswordRevealer />, document.getElementById('root'));
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -502,7 +498,11 @@ function Select ({ values, callback, disabled = false, readonly = false, selecte
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>
); );
} }
@ -519,10 +519,11 @@ 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')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -535,7 +536,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}
@ -554,16 +562,17 @@ 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')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
## Object ## Object
### TreeView ### TreeView
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.
@ -625,19 +634,18 @@ 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>&nbsp;&nbsp;{name}: </strong> : <span>&nbsp;&nbsp;</span>} {name ? <strong>&nbsp;&nbsp;{name}: </strong> : <span>&nbsp;&nbsp;</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}
@ -647,17 +655,17 @@ function TreeView({
/> />
) : ( ) : (
<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>
); );
} }
@ -669,34 +677,35 @@ 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'));
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
## String ## String
### AutoLink ### AutoLink
Renders a string as plaintext, with URLs converted to appropriate `<a>` elements. Renders a string as plaintext, with URLs converted to appropriate `<a>` elements.
@ -714,9 +723,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;
})} })}
@ -730,16 +737,17 @@ 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')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
## Visual ## Visual
### Carousel ### Carousel
Renders a carousel component. Renders a carousel component.
@ -756,14 +764,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(() => {
@ -802,9 +810,10 @@ ReactDOM.render(
<div>carousel item 3</div> <div>carousel item 3</div>
]} ]}
/>, />,
document.getElementById("root") document.getElementById('root')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -825,24 +834,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"
@ -868,6 +874,7 @@ ReactDOM.render(
document.getElementById('root') document.getElementById('root')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -897,8 +904,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,
@ -936,15 +942,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>
); );
@ -955,11 +957,9 @@ function CountDown({ hours = 0, minutes = 0, seconds = 0 }) {
<summary>Examples</summary> <summary>Examples</summary>
```jsx ```jsx
ReactDOM.render( ReactDOM.render(<CountDown hours="1" minutes="45" />, document.getElementById('root'));
<CountDown hours="1" minutes="45" />,
document.getElementById('root')
);
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -978,11 +978,10 @@ Each of the methods will handle a specific event, the listeners for which are cr
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;
@ -990,11 +989,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;
} }
``` ```
@ -1038,24 +1037,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>
@ -1069,6 +1066,7 @@ function FileDrop(props) {
```jsx ```jsx
ReactDOM.render(<FileDrop handleDrop={console.log} />, document.getElementById('root')); ReactDOM.render(<FileDrop handleDrop={console.log} />, document.getElementById('root'));
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -1083,9 +1081,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>
); );
} }
``` ```
@ -1098,9 +1094,10 @@ 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')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -1118,29 +1115,25 @@ Finally, pass the appropriate values to each `<Star>` component (`starId` and `m
```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) => (
@ -1162,6 +1155,7 @@ function StarRating(props) {
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'));
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -1185,10 +1179,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;
} }
``` ```
@ -1200,19 +1194,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>
))} ))}
@ -1223,7 +1214,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>
@ -1245,10 +1236,10 @@ ReactDOM.render(
Dolor sit amet Dolor sit amet
</TabItem> </TabItem>
</Tabs>, </Tabs>,
document.getElementById("root") document.getElementById('root')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -1269,17 +1260,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>
@ -1297,6 +1286,7 @@ function Ticker(props) {
```jsx ```jsx
ReactDOM.render(<Ticker times={5} interval={1000} />, document.getElementById('root')); ReactDOM.render(<Ticker times={5} interval={1000} />, document.getElementById('root'));
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -1314,19 +1304,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>
); );
} }
@ -1338,6 +1325,7 @@ function Toggle(props) {
```jsx ```jsx
ReactDOM.render(<Toggle />, document.getElementById('root')); ReactDOM.render(<Toggle />, document.getElementById('root'));
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
@ -1375,15 +1363,11 @@ function Tooltip({ children, text, ...rest }) {
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>
@ -1396,19 +1380,19 @@ 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')
); );
``` ```
</details> </details>
<br>[⬆ Back to top](#table-of-contents) <br>[⬆ Back to top](#table-of-contents)
---
----- _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).*

View File

@ -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": []
} }
] ]

View File

@ -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
@ -84,21 +80,18 @@ try {
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');

View File

@ -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');

View File

@ -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,

View File

@ -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) -->

View File

@ -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')
); );
``` ```

View File

@ -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 -->

View File

@ -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"

View File

@ -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 -->

View File

@ -8,9 +8,7 @@ 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>;
} }
``` ```

View File

@ -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 -->

View File

@ -12,11 +12,10 @@ Each of the methods will handle a specific event, the listeners for which are cr
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>

View File

@ -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')
); );
``` ```

View File

@ -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 -->

View File

@ -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 -->

View File

@ -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')
); );
``` ```

View File

@ -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 -->

View File

@ -22,25 +22,20 @@ 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')
); );
``` ```

View File

@ -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 -->

View File

@ -14,7 +14,11 @@ function Select ({ values, callback, disabled = false, readonly = false, selecte
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')
); );
``` ```

View File

@ -11,29 +11,25 @@ Finally, pass the appropriate values to each `<Star>` component (`starId` and `m
```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) => (

View File

@ -1,4 +1,5 @@
### 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`.
@ -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 -->

View File

@ -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}
@ -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')
); );
``` ```

View File

@ -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>

View File

@ -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>
); );
} }

View File

@ -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>
@ -48,7 +45,7 @@ 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')

View File

@ -59,19 +59,18 @@ 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>&nbsp;&nbsp;{name}: </strong> : <span>&nbsp;&nbsp;</span>} {name ? <strong>&nbsp;&nbsp;{name}: </strong> : <span>&nbsp;&nbsp;</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}
@ -81,17 +80,17 @@ function TreeView({
/> />
) : ( ) : (
<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 -->

View File

@ -8,9 +8,10 @@ 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()`.
@ -61,7 +62,7 @@ Finally, in the `render()` method, use a `<div>` to wrap everything and render t
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="modal" onClick={this.modalClick}>
<div className="dialog"> <div className="dialog">
<div className="dialog-title">{ this.state.data.title }<span className="dialog-close" onClick={this.close}>+</span></div> <div className="dialog-title">
<div className="dialog-content"> {this.state.data.title}
{ <span className="dialog-close" onClick={this.close}>
this.state.data.content +
} </span>
</div> </div>
<div className="dialog-content">{this.state.data.content}</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(
new CustomEvent('modal', {
detail: { detail: {
data data
} }
})); })
);
} }
modalClick() { modalClick() {
if (this.state.data.closeOnClick) this.close(); if (this.state.data.closeOnClick) this.close();
@ -121,7 +127,7 @@ 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
@ -133,8 +139,9 @@ ModalDialog.show({
``` ```
#### 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 -->

View File

@ -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)._

View File

@ -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