Merge pull request #98 from 30-seconds/const-components
Update component format Resolves #97
This commit is contained in:
@ -5,15 +5,15 @@ tags: components,children,state,advanced
|
||||
|
||||
Renders an accordion menu with multiple collapsible content components.
|
||||
|
||||
- Define an `AccordionItem` component, pass it to the `Accordion` and remove unnecessary nodes expect for `AccordionItem` by identifying the function's name in `props.children`.
|
||||
- Each `AccordionItem` component renders a `<button>` that is used to update the `Accordion` via the `props.handleClick` callback and the content of the component, passed down via `props.children`, while its appearance is determined by `props.isCollapsed` and based on `style`.
|
||||
- In the `Accordion` component, 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 individual collapsiple elements.
|
||||
- Define an `AccordionItem` component, pass it to the `Accordion` and remove unnecessary nodes expect for `AccordionItem` by identifying the function's name in `children`.
|
||||
- Each `AccordionItem` component renders a `<button>` that is used to update the `Accordion` via the `handleClick` callback and the content of the component, passed down via `children`, while its appearance is determined by `isCollapsed` and based on `style`.
|
||||
- In the `Accordion` component, use the `React.useState()` hook to initialize the value of the `bindIndex` state variable to `defaultIndex`.
|
||||
- Use `Array.prototype.map()` on the collected nodes to render the individual collapsiple elements.
|
||||
- Define `changeItem`, which will be executed when clicking an `AccordionItem`'s `<button>`.
|
||||
`changeItem` executes the passed callback, `onItemClick` and updates `bindIndex` based on the clicked element.
|
||||
|
||||
```jsx
|
||||
function AccordionItem(props) {
|
||||
const AccordionItem = ({ label, isCollapsed, handleClick, children }) => {
|
||||
const style = {
|
||||
collapsed: {
|
||||
display: 'none'
|
||||
@ -29,28 +29,28 @@ function AccordionItem(props) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button style={style.buttonStyle} onClick={() => props.handleClick()}>
|
||||
{props.label}
|
||||
<button style={style.buttonStyle} onClick={handleClick}>
|
||||
{label}
|
||||
</button>
|
||||
<div
|
||||
className="collapse-content"
|
||||
style={props.isCollapsed ? style.collapsed : style.expanded}
|
||||
aria-expanded={props.isCollapsed}
|
||||
style={isCollapsed ? style.collapsed : style.expanded}
|
||||
aria-expanded={isCollapsed}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function Accordion(props) {
|
||||
const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);
|
||||
const Accordion = ({ defaultIndex, onItemClick, children }) => {
|
||||
const [bindIndex, setBindIndex] = React.useState(defaultIndex);
|
||||
|
||||
const changeItem = itemIndex => {
|
||||
if (typeof props.onItemClick === 'function') props.onItemClick(itemIndex);
|
||||
if (typeof onItemClick === 'function') onItemClick(itemIndex);
|
||||
if (itemIndex !== bindIndex) setBindIndex(itemIndex);
|
||||
};
|
||||
const items = props.children.filter(item => item.type.name === 'AccordionItem');
|
||||
const items = children.filter(item => item.type.name === 'AccordionItem');
|
||||
|
||||
return (
|
||||
<div className="wrapper">
|
||||
@ -64,7 +64,7 @@ function Accordion(props) {
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
---
|
||||
title: Alert
|
||||
tags: components,beginner,state,effect
|
||||
tags: components,state,effect,beginner
|
||||
---
|
||||
|
||||
Creates an alert component with `type` prop.
|
||||
|
||||
- Define appropriate CSS styles and animations for the component's elements.
|
||||
- Use the `React.setState()` hook to create the `isShown` and `isLeaving` state variables and set their values to `false`.
|
||||
- Use the `React.useState()` hook to create the `isShown` and `isLeaving` state variables and set their values to `false`.
|
||||
- Define `timeoutId` to keep the timer instance for clearing on component unmount.
|
||||
- Use the `React.setEffect()` hook to update the value of `isShown` to `true` and clear interval by using `timeoutId` when component is unmounted.
|
||||
- Define `closeNotification` function to set the component is removed from DOM by displaying fading out animation and set `isShown` to `false` via `setTimeout()`.
|
||||
- Use the `React.useEffect()` hook to update the value of `isShown` to `true` and clear interval by using `timeoutId` when component is unmounted.
|
||||
- Define `closeAlert` function to set the component is removed from DOM by displaying fading out animation and set `isShown` to `false` via `setTimeout()`.
|
||||
- Define the component, which renders the alert component with user defined `message` and a close button to remove the component from DOM.
|
||||
|
||||
```css
|
||||
@ -64,36 +64,36 @@ Creates an alert component with `type` prop.
|
||||
```
|
||||
|
||||
```jsx
|
||||
function Notification(props) {
|
||||
const [isShown, setIsShown] = React.useState(false);
|
||||
const [isLeaving, setIsLeaving] = React.useState(false);
|
||||
const Alert = ({ isDefaultShown = false, timeout = 250, type, message }) => {
|
||||
const [isShown, setIsShown] = React.useState(isDefaultShown);
|
||||
const [isLeaving, setIsLeaving] = React.useState(false);
|
||||
|
||||
let timeoutId = null;
|
||||
let timeoutId = null;
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsShown(true);
|
||||
return () => {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
}, [props.isShown, props.timeout, timeoutId]);
|
||||
React.useEffect(() => {
|
||||
setIsShown(true);
|
||||
return () => {
|
||||
clearTimeout(timeoutId);
|
||||
};
|
||||
}, [isDefaultShown, timeout, timeoutId]);
|
||||
|
||||
const closeNotification = () => {
|
||||
setIsLeaving(true);
|
||||
timeoutId = setTimeout(() => {
|
||||
setIsLeaving(false);
|
||||
setIsShown(false);
|
||||
}, 250)
|
||||
}
|
||||
const closeAlert = () => {
|
||||
setIsLeaving(true);
|
||||
timeoutId = setTimeout(() => {
|
||||
setIsLeaving(false);
|
||||
setIsShown(false);
|
||||
}, timeout);
|
||||
};
|
||||
|
||||
return isShown && (
|
||||
<div className={`alert ${props.type}${isLeaving ? ' leaving' : ''}`} role="alert">
|
||||
<button className="close" onClick={closeNotification} />
|
||||
{props.message}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return isShown && (
|
||||
<div className={`alert ${type} ${isLeaving ? 'leaving' : ''}`} role="alert">
|
||||
<button className="close" onClick={closeAlert} />
|
||||
{message}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<Notification type="info" message="This is info" />, document.getElementById('root'));
|
||||
ReactDOM.render(<Alert type="info" message="This is info" />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
@ -9,7 +9,7 @@ Renders a string as plaintext, with URLs converted to appropriate `<a>` elements
|
||||
- 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 }) {
|
||||
const AutoLink = ({ text }) => {
|
||||
const delimiter = /((?:https?:\/\/)?(?:(?:[a-z0-9]?(?:[a-z0-9\-]{1,61}[a-z0-9])?\.[^\.|\s])+[a-z\.]*[a-z]+|(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})(?::\d{1,5})*[a-z0-9.,_\/~#&=;%+?\-\\(\\)]*)/gi;
|
||||
|
||||
return (
|
||||
@ -24,7 +24,7 @@ function AutoLink({ text }) {
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -12,7 +12,7 @@ Renders a carousel component.
|
||||
- Render the carousel items using `React.cloneElement()` and pass down rest `props` along with the computed styles.
|
||||
|
||||
```jsx
|
||||
function Carousel(props) {
|
||||
const Carousel = ({ carouselItems, ...rest }) => {
|
||||
const [active, setActive] = React.useState(0);
|
||||
let scrollInterval = null;
|
||||
const style = {
|
||||
@ -29,12 +29,11 @@ function Carousel(props) {
|
||||
};
|
||||
React.useEffect(() => {
|
||||
scrollInterval = setTimeout(() => {
|
||||
const { carouselItems } = props;
|
||||
setActive((active + 1) % carouselItems.length);
|
||||
}, 2000);
|
||||
return () => clearTimeout(scrollInterval);
|
||||
});
|
||||
const { carouselItems, ...rest } = props;
|
||||
|
||||
return (
|
||||
<div style={style.carousel}>
|
||||
{carouselItems.map((item, index) => {
|
||||
@ -49,7 +48,7 @@ function Carousel(props) {
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -5,15 +5,15 @@ tags: components,children,state,intermediate
|
||||
|
||||
Renders a component with collapsible content.
|
||||
|
||||
- Use the `React.setState()` hook to create the `isCollapsed` state variable with an initial value of `props.collapsed`.
|
||||
- Use the `React.useState()` hook to create the `isCollapsed` state variable with an initial value of `collapsed`.
|
||||
- Use an object, `style`, to hold the styles for individual components and their states.
|
||||
- Use a `<div>` to wrap both the `<button>` that alters the component's `isCollapsed` state and the content of the component, passed down via `props.children`.
|
||||
- Use a `<div>` to wrap both the `<button>` that alters the component's `isCollapsed` state and the content of the component, passed down via `children`.
|
||||
- Determine the appearance of the content, based on `isCollapsed` and apply the appropriate CSS rules from the `style` object.
|
||||
- Finally, update the value of the `aria-expanded` attribute based on `isCollapsed` to make the component accessible.
|
||||
|
||||
```jsx
|
||||
function Collapse(props) {
|
||||
const [isCollapsed, setIsCollapsed] = React.useState(props.collapsed);
|
||||
const Collapse = ({ collapsed, children }) => {
|
||||
const [isCollapsed, setIsCollapsed] = React.useState(collapsed);
|
||||
|
||||
const style = {
|
||||
collapsed: {
|
||||
@ -38,11 +38,11 @@ function Collapse(props) {
|
||||
style={isCollapsed ? style.collapsed : style.expanded}
|
||||
aria-expanded={isCollapsed}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -6,19 +6,19 @@ tags: components,state,effect,intermediate
|
||||
Renders an `<input>` element with internal state, that uses a callback function to pass its value to the parent component.
|
||||
|
||||
- Use object destructuring to set defaults for certain attributes of the `<input>` element.
|
||||
- Use the `React.setState()` hook to create the `value` state variable and give it a value of equal to the `defaultValue` prop.
|
||||
- Use the `React.useState()` hook to create the `value` state variable and give it a value of equal to the `defaultValue` prop.
|
||||
- Use the `React.useEffect()` hook with a second parameter set to the `value` state variable to call the `callback` function every time `value` is updated.
|
||||
- Render an `<input>` element with the appropriate attributes and use the the `onChange` event to upda the `value` state variable.
|
||||
|
||||
```jsx
|
||||
function ControlledInput({
|
||||
const ControlledInput = ({
|
||||
callback,
|
||||
type = 'text',
|
||||
disabled = false,
|
||||
readOnly = false,
|
||||
defaultValue,
|
||||
placeholder = ''
|
||||
}) {
|
||||
}) => {
|
||||
const [value, setValue] = React.useState(defaultValue);
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -35,7 +35,7 @@ function ControlledInput({
|
||||
onChange={({ target: { value } }) => setValue(value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -15,7 +15,7 @@ Renders a countdown timer that prints a message when it reaches zero.
|
||||
- If `over` is `true`, the timer will display a message instead of the value of `time`.
|
||||
|
||||
```jsx
|
||||
function CountDown({ hours = 0, minutes = 0, seconds = 0 }) {
|
||||
const CountDown = ({ hours = 0, minutes = 0, seconds = 0 }) => {
|
||||
const [paused, setPaused] = React.useState(false);
|
||||
const [over, setOver] = React.useState(false);
|
||||
const [time, setTime] = React.useState({
|
||||
@ -27,24 +27,25 @@ 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);
|
||||
else if (time.minutes == 0 && time.seconds == 0)
|
||||
else if (time.minutes == 0 && time.seconds == 0) {
|
||||
setTime({
|
||||
hours: time.hours - 1,
|
||||
minutes: 59,
|
||||
seconds: 59
|
||||
});
|
||||
else if (time.seconds == 0)
|
||||
} else if (time.seconds == 0) {
|
||||
setTime({
|
||||
hours: time.hours,
|
||||
minutes: time.minutes - 1,
|
||||
seconds: 59
|
||||
});
|
||||
else
|
||||
} else {
|
||||
setTime({
|
||||
hours: time.hours,
|
||||
minutes: time.minutes,
|
||||
seconds: time.seconds - 1
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
@ -72,7 +73,7 @@ function CountDown({ hours = 0, minutes = 0, seconds = 0 }) {
|
||||
<button onClick={() => reset()}>Restart</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -6,14 +6,14 @@ tags: components,array,beginner
|
||||
Renders a list of elements from an array of primitives.
|
||||
|
||||
- Use the value of the `isOrdered` prop to conditionally render a `<ol>` or `<ul>` list.
|
||||
- Use `Array.prototype.map` to render every item in `data` as a `<li>` element, give it a `key` produced from the concatenation of the its index and value.
|
||||
- Use `Array.prototype.map()` to render every item in `data` as a `<li>` element, give it a `key` produced from the concatenation of the its index and value.
|
||||
- Omit the `isOrdered` prop to render a `<ul>` list by default.
|
||||
|
||||
```jsx
|
||||
function DataList({ isOrdered, data }) {
|
||||
const DataList = ({ isOrdered, data }) => {
|
||||
const list = data.map((val, i) => <li key={`${i}_${val}`}>{val}</li>);
|
||||
return isOrdered ? <ol>{list}</ol> : <ul>{list}</ul>;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -6,10 +6,10 @@ tags: components,array,beginner
|
||||
Renders a table with rows dynamically created from an array of primitives.
|
||||
|
||||
- Render a `<table>` element with two columns (`ID` and `Value`).
|
||||
- Use `Array.prototype.map` to render every item in `data` as a `<tr>` element, consisting of its index and value, give it a `key` produced from the concatenation of the two.
|
||||
- Use `Array.prototype.map()` to render every item in `data` as a `<tr>` element, consisting of its index and value, give it a `key` produced from the concatenation of the two.
|
||||
|
||||
```jsx
|
||||
function DataTable({ data }) {
|
||||
const DataTable = ({ data }) => {
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
@ -28,7 +28,7 @@ function DataTable({ data }) {
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -10,7 +10,7 @@ Renders a file drag and drop component for a single file.
|
||||
The variables `dragCounter` and `drag` are used to determine if a file is being dragged, while `filename` is used to store the dropped file's name.
|
||||
- Create the `handleDrag`, `handleDragIn`, `handleDragOut` and `handleDrop` methods to handle drag and drop functionality, bind them to the component's context.
|
||||
- Each of the methods will handle a specific event, the listeners for which are created and removed in the `React.useEffect()` hook and its attached `cleanup()` method.
|
||||
- `handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `props.handleDrop`.
|
||||
- `handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `onDrop`.
|
||||
- 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`.
|
||||
|
||||
@ -34,7 +34,7 @@ Renders a file drag and drop component for a single file.
|
||||
```
|
||||
|
||||
```jsx
|
||||
function FileDrop(props) {
|
||||
const FileDrop = ({ onDrop }) => {
|
||||
const [drag, setDrag] = React.useState(false);
|
||||
const [filename, setFilename] = React.useState('');
|
||||
let dropRef = React.createRef();
|
||||
@ -64,7 +64,7 @@ function FileDrop(props) {
|
||||
e.stopPropagation();
|
||||
setDrag(false);
|
||||
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
|
||||
props.handleDrop(e.dataTransfer.files[0]);
|
||||
onDrop(e.dataTransfer.files[0]);
|
||||
setFilename(e.dataTransfer.files[0].name);
|
||||
e.dataTransfer.clearData();
|
||||
dragCounter = 0;
|
||||
@ -93,9 +93,9 @@ function FileDrop(props) {
|
||||
{filename && !drag ? <div>{filename}</div> : <div>Drop files here!</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<FileDrop handleDrop={console.log} />, document.getElementById('root'));
|
||||
ReactDOM.render(<FileDrop onDrop={console.log} />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
@ -11,7 +11,7 @@ Renders a textarea component with a character limit.
|
||||
- Use a`<div>` to wrap both the`<textarea>` and the `<p>` element that displays the character count and bind the `onChange` event of the `<textarea>` to call `setFormattedContent` with the value of `event.target.value`.
|
||||
|
||||
```jsx
|
||||
function LimitedTextarea({ rows, cols, value, limit }) {
|
||||
const LimitedTextarea = ({ rows, cols, value, limit }) => {
|
||||
const [content, setContent] = React.useState(value);
|
||||
|
||||
const setFormattedContent = text => {
|
||||
@ -35,7 +35,7 @@ function LimitedTextarea({ rows, cols, value, limit }) {
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -12,7 +12,7 @@ Renders a textarea component with a word limit.
|
||||
- Use a`<div>` to wrap both the`<textarea>` and the `<p>` element that displays the character count and bind the `onChange` event of the `<textarea>` to call `setFormattedContent` with the value of `event.target.value`.
|
||||
|
||||
```jsx
|
||||
function LimitedWordTextarea({ rows, cols, value, limit }) {
|
||||
const LimitedWordTextarea = ({ rows, cols, value, limit }) => {
|
||||
const [content, setContent] = React.useState(value);
|
||||
const [wordCount, setWordCount] = React.useState(0);
|
||||
|
||||
@ -49,7 +49,7 @@ function LimitedWordTextarea({ rows, cols, value, limit }) {
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -40,7 +40,7 @@ Creates a spinning loader component.
|
||||
```
|
||||
|
||||
```jsx
|
||||
function Loader({ size }) {
|
||||
const Loader = ({ size }) => {
|
||||
return (
|
||||
<svg
|
||||
className="loader"
|
||||
@ -57,7 +57,7 @@ function Loader({ size }) {
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -6,14 +6,14 @@ tags: components,beginner
|
||||
Renders a link formatted to send an email.
|
||||
|
||||
- Destructure the component's props, use `email`, `subject` and `body` to create a `<a>` element with an appropriate `href` attribute.
|
||||
- Render the link with `props.children` as its content.
|
||||
- Render the link with `children` as its content.
|
||||
|
||||
```jsx
|
||||
function Mailto({ email, subject, body, ...props }) {
|
||||
const Mailto = ({ email, subject, body, children }) => {
|
||||
return (
|
||||
<a href={`mailto:${email}?subject=${encodeURIComponent(subject) || ''}&body=${encodeURIComponent(body) || ''}`}>{props.children}</a>
|
||||
<a href={`mailto:${email}?subject=${encodeURIComponent(subject) || ''}&body=${encodeURIComponent(body) || ''}`}>{children}</a>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -7,13 +7,13 @@ Renders a table with rows dynamically created from an array of objects and a lis
|
||||
|
||||
- Use `Object.keys()`, `Array.prototype.filter()`, `Array.prototype.includes()` and `Array.prototype.reduce()` to produce a `filteredData` array, containing all objects with the keys specified in `propertyNames`.
|
||||
- Render a `<table>` element with a set of columns equal to the amount of values in `propertyNames`.
|
||||
- Use `Array.prototype.map` to render each value in the `propertyNames` array as a `<th>` element.
|
||||
- Use `Array.prototype.map` to render each object in the `filteredData` array as a `<tr>` element, containing a `<td>` for each key in the object.
|
||||
- Use `Array.prototype.map()` to render each value in the `propertyNames` array as a `<th>` element.
|
||||
- Use `Array.prototype.map()` to render each object in the `filteredData` array as a `<tr>` element, containing a `<td>` for each key in the object.
|
||||
|
||||
_This component does not work with nested objects and will break if there are nested objects inside any of the properties specified in `propertyNames`_
|
||||
|
||||
```jsx
|
||||
function MappedTable({ data, propertyNames }) {
|
||||
const MappedTable = ({ data, propertyNames }) => {
|
||||
let filteredData = data.map(v =>
|
||||
Object.keys(v)
|
||||
.filter(k => propertyNames.includes(k))
|
||||
@ -39,7 +39,7 @@ function MappedTable({ data, propertyNames }) {
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -92,20 +92,20 @@ To use the component, import `Modal` only once and then display it by passing a
|
||||
```
|
||||
|
||||
```jsx
|
||||
function Modal({ isVisible = false, title, content, footer, onClose }) {
|
||||
const Modal = ({ isVisible = false, title, content, footer, onClose }) => {
|
||||
React.useEffect(() => {
|
||||
document.addEventListener('keydown', keydownHandler);
|
||||
return () => document.removeEventListener('keydown', keydownHandler);
|
||||
});
|
||||
|
||||
function keydownHandler({ key }) {
|
||||
const keydownHandler = ({ key }) => {
|
||||
switch (key) {
|
||||
case 'Escape':
|
||||
onClose();
|
||||
break;
|
||||
default:
|
||||
case 'Escape':
|
||||
onClose();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return !isVisible ? null : (
|
||||
<div className="modal" onClick={onClose}>
|
||||
@ -123,12 +123,11 @@ function Modal({ isVisible = false, title, content, footer, onClose }) {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
//Add the component to the render function
|
||||
function App() {
|
||||
const App = () => {
|
||||
const [isModal, setModal] = React.useState(false);
|
||||
|
||||
return (
|
||||
@ -143,7 +142,7 @@ function App() {
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
@ -5,7 +5,7 @@ tags: components,input,state,array,intermediate
|
||||
|
||||
Renders a checkbox list that uses a callback function to pass its selected value/values to the parent component.
|
||||
|
||||
- Use `React.setState()` to create a `data` state variable and set its initial value equal to the `options` prop.
|
||||
- Use `React.useState()` to create a `data` state variable and set its initial value equal to the `options` prop.
|
||||
- Create a function `toggle` that is used to toggle the `checked` to update the `data` state variable and call the `onChange` callback passed via the component's props.
|
||||
- Render a `<ul>` element and use `Array.prototype.map()` to map the `data` state variable to individual `<li>` elements with `<input>` elements as their children.
|
||||
- Each `<input>` element has the `type='checkbox'` attribute and is marked as `readOnly`, as its click events are handled by the parent `<li>` element's `onClick` handler.
|
||||
@ -22,7 +22,7 @@ const style = {
|
||||
}
|
||||
};
|
||||
|
||||
function MultiselectCheckbox({ options, onChange }) {
|
||||
const MultiselectCheckbox = ({ options, onChange }) => {
|
||||
const [data, setData] = React.useState(options);
|
||||
|
||||
const toggle = item => {
|
||||
@ -45,7 +45,7 @@ function MultiselectCheckbox({ options, onChange }) {
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -9,7 +9,7 @@ Renders a password input field with a reveal button.
|
||||
- Use a`<div>` to wrap both the`<input>` and the `<button>` element that toggles the type of the input field between `"text"` and `"password"`.
|
||||
|
||||
```jsx
|
||||
function PasswordRevealer({ value }) {
|
||||
const PasswordRevealer = ({ value }) => {
|
||||
const [shown, setShown] = React.useState(false);
|
||||
|
||||
return (
|
||||
@ -18,7 +18,7 @@ function PasswordRevealer({ value }) {
|
||||
<button onClick={() => setShown(!shown)}>Show/Hide</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -61,7 +61,7 @@ Renders a button that animates a ripple effect when clicked.
|
||||
```
|
||||
|
||||
```jsx
|
||||
function RippleButton({ children, onClick }) {
|
||||
const RippleButton = ({ children, onClick }) => {
|
||||
const [coords, setCoords] = React.useState({ x: -1, y: -1 });
|
||||
const [isRippling, setIsRippling] = React.useState(false);
|
||||
|
||||
@ -102,12 +102,12 @@ function RippleButton({ children, onClick }) {
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
""
|
||||
''
|
||||
)}
|
||||
<span className="content">{children}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -11,7 +11,7 @@ Renders a `<select>` element that uses a callback function to pass its value to
|
||||
- Use destructuring on the `values` array to pass an array of `value` and `text` elements.
|
||||
|
||||
```jsx
|
||||
function Select({ values, callback, disabled = false, readonly = false, selected }) {
|
||||
const Select = ({ values, callback, disabled = false, readonly = false, selected }) => {
|
||||
return (
|
||||
<select
|
||||
disabled={disabled}
|
||||
@ -26,22 +26,11 @@ function Select({ values, callback, disabled = false, readonly = false, selected
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
let choices = [
|
||||
['grapefruit', 'Grapefruit'],
|
||||
['lime', 'Lime'],
|
||||
['coconut', 'Coconut'],
|
||||
['mango', 'Mango']
|
||||
];
|
||||
ReactDOM.render(
|
||||
<Select values={choices} selected="lime" callback={val => console.log(val)} />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
let choices = [
|
||||
const choices = [
|
||||
['grapefruit', 'Grapefruit'],
|
||||
['lime', 'Lime'],
|
||||
['coconut', 'Coconut'],
|
||||
|
||||
@ -9,7 +9,7 @@ Renders a slider element that uses a callback function to pass its value to the
|
||||
- Render an `<input>` element of type `"range"` and the appropriate attributes, use the `callback` function in the `onChange` event to pass the value of the input to the parent.
|
||||
|
||||
```jsx
|
||||
function Slider({ callback, disabled = false, readOnly = false }) {
|
||||
const Slider = ({ callback, disabled = false, readOnly = false }) => {
|
||||
return (
|
||||
<input
|
||||
type="range"
|
||||
@ -18,7 +18,7 @@ function Slider({ callback, disabled = false, readOnly = false }) {
|
||||
onChange={({ target: { value } }) => callback(value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -6,22 +6,22 @@ tags: components,children,input,state,intermediate
|
||||
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.useState()` hook to define the `rating` and `selection` state variables with the initial values of `props.rating` (or `0` if invalid or not supplied) and `0`.
|
||||
- In the `StarRating` component, use the `React.useState()` hook to define the `rating` and `selection` state variables with the initial values of `value` (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 }) {
|
||||
const Star = ({ marked, starId }) => {
|
||||
return (
|
||||
<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 StarRating = ({ value }) => {
|
||||
const [rating, setRating] = React.useState(typeof value == 'number' ? value : 0);
|
||||
const [selection, setSelection] = React.useState(0);
|
||||
const hoverOver = event => {
|
||||
let val = 0;
|
||||
@ -44,10 +44,10 @@ function StarRating(props) {
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<StarRating />, document.getElementById('root'));
|
||||
ReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));
|
||||
ReactDOM.render(<StarRating value={2} />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
@ -5,9 +5,9 @@ tags: components,state,children,intermediate
|
||||
|
||||
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`.
|
||||
- Define a `TabItem` component, pass it to the `Tab` and remove unnecessary nodes expect for `TabItem` by identifying the function's name in `children`.
|
||||
- Use the `React.useState()` hook to initialize the value of the `bindIndex` state variable to `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`.
|
||||
|
||||
@ -28,17 +28,17 @@ Renders a tabbed menu and view component.
|
||||
```
|
||||
|
||||
```jsx
|
||||
function TabItem(props) {
|
||||
const TabItem = props => {
|
||||
return <div {...props} />;
|
||||
}
|
||||
};
|
||||
|
||||
function Tabs(props) {
|
||||
const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);
|
||||
const Tabs = ({ defaultIndex = 0, onTabClick, children }) => {
|
||||
const [bindIndex, setBindIndex] = React.useState(defaultIndex);
|
||||
const changeTab = newIndex => {
|
||||
if (typeof props.onTabClick === 'function') props.onTabClick(newIndex);
|
||||
if (typeof onTabClick === 'function') onTabClick(newIndex);
|
||||
setBindIndex(newIndex);
|
||||
};
|
||||
const items = props.children.filter(item => item.type.name === 'TabItem');
|
||||
const items = children.filter(item => item.type.name === 'TabItem');
|
||||
|
||||
return (
|
||||
<div className="wrapper">
|
||||
@ -61,7 +61,7 @@ function Tabs(props) {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -5,12 +5,12 @@ tags: components,input,state,intermediate
|
||||
|
||||
Renders a tag input field.
|
||||
|
||||
- Define a `TagInput` component and use `React.useState()` hook to initialize an array with tags passed as `props`.
|
||||
- Define a `TagInput` component and use `React.useState()` hook to initialize an array from `tags`.
|
||||
- Use `Array.prototype.map()` on collected nodes to render the list of tags.
|
||||
- Define the `addTags` method, which will be executed on pressing the `Enter` key.
|
||||
- The `addTags` method uses the `setTags` method to add the new tag using the spread (`...`) operator to prepend the existing tags and adds the new tag at the end of the `tags` array.
|
||||
- Define the `removeTags` method, which will be executed on clicking the delete icon in the tag.
|
||||
- Use `Array.prototype.filter()` in `removeTags` method to remove the tag using the `index` of the tag to filter it out from `tags` array.
|
||||
- Define the `addTagData` method, which will be executed on pressing the `Enter` key.
|
||||
- The `addTagData` method uses the `setTagData` method to add the new tag using the spread (`...`) operator to prepend the existing tags and adds the new tag at the end of the `tagData` array.
|
||||
- Define the `removeTagData` method, which will be executed on clicking the delete icon in the tag.
|
||||
- Use `Array.prototype.filter()` in `removeTagData` method to remove the tag using the `index` of the tag to filter it out from `tagData` array.
|
||||
|
||||
```css
|
||||
.tag-input {
|
||||
@ -70,24 +70,24 @@ Renders a tag input field.
|
||||
```
|
||||
|
||||
```jsx
|
||||
function TagInput(props) {
|
||||
const [tags, setTags] = React.useState(props.tags);
|
||||
const removeTags = indexToRemove => {
|
||||
setTags([...tags.filter((_, index) => index !== indexToRemove)]);
|
||||
const TagInput = ({ tags }) => {
|
||||
const [tagData, setTagData] = React.useState(tags);
|
||||
const removeTagData = indexToRemove => {
|
||||
setTagData([...tagData.filter((_, index) => index !== indexToRemove)]);
|
||||
};
|
||||
const addTags = event => {
|
||||
if (event.target.value !== "") {
|
||||
setTags([...tags, event.target.value]);
|
||||
event.target.value = "";
|
||||
const addTagData = event => {
|
||||
if (event.target.value !== '') {
|
||||
setTagData([...tagData, event.target.value]);
|
||||
event.target.value = '';
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="tag-input">
|
||||
<ul id="tags">
|
||||
{tags.map((tag, index) => (
|
||||
{tagData.map((tag, index) => (
|
||||
<li key={index} className="tag">
|
||||
<span className="tag-title">{tag}</span>
|
||||
<span className="tag-close-icon" onClick={() => removeTags(index)}>
|
||||
<span className="tag-close-icon" onClick={() => removeTagData(index)}>
|
||||
x
|
||||
</span>
|
||||
</li>
|
||||
@ -95,12 +95,12 @@ function TagInput(props) {
|
||||
</ul>
|
||||
<input
|
||||
type="text"
|
||||
onKeyUp={event => (event.key === "Enter" ? addTags(event) : null)}
|
||||
onKeyUp={event => (event.key === 'Enter' ? addTagData(event) : null)}
|
||||
placeholder="Press enter to add tags"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -9,14 +9,14 @@ Renders a `<textarea>` element that uses a callback function to pass its value t
|
||||
- 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({
|
||||
const TextArea = ({
|
||||
callback,
|
||||
cols = 20,
|
||||
rows = 2,
|
||||
disabled = false,
|
||||
readOnly = false,
|
||||
placeholder = ''
|
||||
}) {
|
||||
}) => {
|
||||
return (
|
||||
<textarea
|
||||
cols={cols}
|
||||
@ -27,7 +27,7 @@ function TextArea({
|
||||
onChange={({ target: { value } }) => callback(value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -6,25 +6,25 @@ tags: components,state,beginner
|
||||
Renders a ticker component.
|
||||
|
||||
- Use the `React.useState()` hook to initialize the `ticker` state variable to `0`.
|
||||
- Define two methods, `tick` and `reset`, that will periodically increment `timer` based on `interval` and reset `interval` respectively.
|
||||
- Define two methods, `tick` and `reset`, that will periodically increment `timer` based on `intervalId` and reset `intervalId` respectively.
|
||||
- Return a `<div>` with two `<button>` elements, each of which calls `tick` and `reset` respectively.
|
||||
|
||||
```jsx
|
||||
function Ticker(props) {
|
||||
const Ticker = ({ times, interval }) => {
|
||||
const [ticker, setTicker] = React.useState(0);
|
||||
let interval = null;
|
||||
let intervalId = null;
|
||||
|
||||
const tick = () => {
|
||||
reset();
|
||||
interval = setInterval(() => {
|
||||
if (ticker < props.times) setTicker(ticker + 1);
|
||||
else clearInterval(interval);
|
||||
}, props.interval);
|
||||
intervalId = setInterval(() => {
|
||||
if (ticker < times) setTicker(ticker + 1);
|
||||
else clearInterval(intervalId);
|
||||
}, interval);
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
setTicker(0);
|
||||
clearInterval(interval);
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -34,7 +34,7 @@ function Ticker(props) {
|
||||
<button onClick={reset}>Reset</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -5,14 +5,14 @@ tags: components,state,beginner
|
||||
|
||||
Renders a toggle component.
|
||||
|
||||
- Use the `React.useState()` to initialize the `isToggleOn` state variable to `false`.
|
||||
- Use the `React.useState()` to initialize the `isToggleOn` state variable to `defaultToggled`.
|
||||
- Use an object, `style`, to hold the styles for individual components and their states.
|
||||
- Return a `<button>` that alters the component's `isToggledOn` when its `onClick` event is fired and determine the appearance of the content based on `isToggleOn`, applying the appropriate CSS rules from the `style` object.
|
||||
|
||||
```jsx
|
||||
function Toggle(props) {
|
||||
const [isToggleOn, setIsToggleOn] = React.useState(false);
|
||||
style = {
|
||||
const Toggle = ({ defaultToggled = false }) => {
|
||||
const [isToggleOn, setIsToggleOn] = React.useState(defaultToggled);
|
||||
const style = {
|
||||
on: {
|
||||
backgroundColor: 'green'
|
||||
},
|
||||
@ -26,7 +26,7 @@ function Toggle(props) {
|
||||
{isToggleOn ? 'ON' : 'OFF'}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -29,7 +29,7 @@ Renders a tooltip component.
|
||||
```
|
||||
|
||||
```jsx
|
||||
function Tooltip({ children, text, ...rest }) {
|
||||
const Tooltip = ({ children, text, ...rest }) => {
|
||||
const [show, setShow] = React.useState(false);
|
||||
|
||||
return (
|
||||
@ -38,12 +38,12 @@ function Tooltip({ children, text, ...rest }) {
|
||||
{text}
|
||||
<span className="tooltip-arrow" />
|
||||
</div>
|
||||
<div {...rest} onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)}>
|
||||
<div onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)} {...rest}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
@ -7,7 +7,7 @@ Renders a tree view of a JSON object or array with collapsible content.
|
||||
|
||||
- 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.
|
||||
- Use the `React.useState()` 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`.
|
||||
- For each child in `data`, determine if it is an object or array and recursively render a sub-tree.
|
||||
@ -50,14 +50,14 @@ div.tree-element:before {
|
||||
```
|
||||
|
||||
```jsx
|
||||
function TreeView({
|
||||
const TreeView = ({
|
||||
data,
|
||||
toggled = true,
|
||||
name = null,
|
||||
isLast = true,
|
||||
isChildElement = false,
|
||||
isParentToggled = true
|
||||
}) {
|
||||
}) => {
|
||||
const [isToggled, setIsToggled] = React.useState(toggled);
|
||||
|
||||
return (
|
||||
@ -96,11 +96,11 @@ function TreeView({
|
||||
{!isLast ? ',' : ''}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
let data = {
|
||||
const data = {
|
||||
lorem: {
|
||||
ipsum: 'dolor sit',
|
||||
amet: {
|
||||
|
||||
@ -9,13 +9,13 @@ Renders an `<input>` element that uses a callback function to pass its value to
|
||||
- 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 UncontrolledInput({
|
||||
const UncontrolledInput = ({
|
||||
callback,
|
||||
type = 'text',
|
||||
disabled = false,
|
||||
readOnly = false,
|
||||
placeholder = ''
|
||||
}) {
|
||||
}) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
@ -25,7 +25,7 @@ function UncontrolledInput({
|
||||
onChange={({ target: { value } }) => callback(value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsx
|
||||
|
||||
Reference in New Issue
Block a user