From 255df75ad339fa6ef65a4976e4e4239b35d9ac57 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Wed, 21 Aug 2019 12:50:30 +0300 Subject: [PATCH] Initial testing of extractor and builder Configured appropriately --- README.md | 409 +- config.js | 16 + data/snippet_data.json | 395 - package-lock.json | 19307 +++++++++++++++- package.json | 46 +- scripts/build.js | 178 +- scripts/extract.js | 108 +- scripts/util.js | 91 - scripts/util/environmentCheck.js | 12 + scripts/util/helpers.js | 60 + scripts/util/index.js | 37 + scripts/util/snippetParser.js | 129 + snippet_data/snippetList.json | 437 + snippet_data/snippets.json | 593 + .../static-parts}/README-end.md | 0 .../static-parts}/README-start.md | 0 16 files changed, 20915 insertions(+), 903 deletions(-) create mode 100644 config.js delete mode 100644 data/snippet_data.json delete mode 100644 scripts/util.js create mode 100644 scripts/util/environmentCheck.js create mode 100644 scripts/util/helpers.js create mode 100644 scripts/util/index.js create mode 100644 scripts/util/snippetParser.js create mode 100644 snippet_data/snippetList.json create mode 100644 snippet_data/snippets.json rename {static-parts => src/static-parts}/README-end.md (100%) rename {static-parts => src/static-parts}/README-start.md (100%) diff --git a/README.md b/README.md index 241d432db..3098a1096 100644 --- a/README.md +++ b/README.md @@ -40,75 +40,94 @@ import ReactDOM from 'react-dom'; ## Table of Contents - -### Array +### Array
View contents -* [DataList](#datalist) -* [DataTable](#datatable) -* [MappedTable](#mappedtable) +* [`DataList`](#datalist) +* [`DataTable`](#datatable) +* [`MappedTable`](#mappedtable) +
- -### Input +### Hooks
View contents -* [Input](#input) -* [LimitedTextarea](#limitedtextarea) -* [LimitedWordTextarea](#limitedwordtextarea) -* [MultiselectCheckbox](#multiselectcheckbox) -* [PasswordRevealer](#passwordrevealer) -* [Select](#select) -* [Slider](#slider) -* [TextArea](#textarea) +* [`ClickInside and ClickOutside`](#clickinside-and-clickoutside) +
- -### Object +### Input
View contents -* [TreeView](#treeview) +* [`LimitedTextarea`](#limitedtextarea) +* [`LimitedWordTextarea`](#limitedwordtextarea) +* [`MultiselectCheckbox`](#multiselectcheckbox) +* [`PasswordRevealer`](#passwordrevealer) +* [`Select`](#select) +* [`Slider`](#slider) +* [`TextArea`](#textarea) +* [`UncontrolledInput`](#uncontrolledinput) +
- -### String +### Object
View contents -* [AutoLink](#autolink) +* [`TreeView`](#treeview-) +
- -### Visual +### String
View contents -* [Accordion](#accordion) -* [Carousel](#carousel) -* [Collapse](#collapse) -* [CountDown](#countdown) -* [FileDrop](#filedrop) -* [Mailto](#mailto) -* [Modal](#modal) -* [StarRating](#starrating) -* [Tabs](#tabs) -* [Ticker](#ticker) -* [Toggle](#toggle) -* [Tooltip](#tooltip) +* [`AutoLink`](#autolink-) + +
+ +### Visual + +
+View contents + +* [`Accordion`](#accordion-) +* [`Carousel`](#carousel) +* [`Collapse`](#collapse) +* [`CountDown`](#countdown-) +* [`FileDrop`](#filedrop) +* [`Modal`](#modal) +* [`StarRating`](#starrating) +* [`Tabs`](#tabs) +* [`Ticker`](#ticker) +* [`Toggle`](#toggle) +* [`Tooltip`](#tooltip) + +
+ +### Viual + +
+View contents + +* [`Mailto`](#mailto) +
--- -## Array +## Array + + ### DataList Renders a list of elements from an array of primitives. @@ -134,7 +153,7 @@ ReactDOM.render(, document.getElementById('ro ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### DataTable @@ -175,7 +194,7 @@ ReactDOM.render(, document.getElementById('root')); ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### MappedTable @@ -186,6 +205,8 @@ Renders a table with rows dynamically created from an array of objects and a lis * Use `Array.prototype.map` to render each value in the `propertyNames` array as a `` element. * Use `Array.prototype.map` to render each object in the `filteredData` array as a `` element, containing a `` 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 }) { let filteredData = data.map(v => @@ -215,9 +236,6 @@ function MappedTable({ data, propertyNames }) { ); } ``` -#### Notes - -This component does not work with nested objects and will break if there are nested objects inside any of the properties specified in `propertyNames`.,,
Examples @@ -235,27 +253,79 @@ ReactDOM.render( ```
-
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) + +--- + +## Hooks -## Input -### Input +### ClickInside and ClickOutside -Renders an `` element that uses a callback function to pass its value to the parent component. +Two handy hooks to handle the click outside and inside event on the wrapped component. -* Use object destructuring to set defaults for certain attributes of the `` element. -* Render an `` element with the appropriate attributes and use the `callback` function in the `onChange` event to pass the value of the input to the parent. +* Create customized hooks that take in a `ref` component(node) and a `callback` function to hanlde the customized `click` event +* Use the `React.useEffect()` hook to append and clean up the `click` event. +* Use the `React.useRef()` hook to create a `ref` for your click component and pass it to `useClickInside` and `useClickOutside` hooks. + +```css +.click-box { + border: 2px dashed orangered; + height: 200px; + width: 400px; + display: flex; + justify-content: center; + align-items: center; +} + +p { + border: 2px solid blue; + padding: 16px; +} +``` ```jsx -function Input({ callback, type = 'text', disabled = false, readOnly = false, placeholder = '' }) { +const useClickInside = (ref, callback) => { + const handleClick = e => { + //use the node contains to verify if we click inside + if (ref.current && ref.current.contains(e.target)) { + callback(); + } + }; + + //clean up using useEffect + useEffect(() => { + document.addEventListener("click", handleClick); + return () => { + document.removeEventListener("click", handleClick); + }; + }); +}; + +const useClickOutside = (ref, callback) => { + const handleClick = e => { + //use the node contains to verify if we click outside + if (ref.current && !ref.current.contains(e.target)) { + callback(); + } + }; + // clean up using useEffect + useEffect(() => { + document.addEventListener("click", handleClick); + return () => { + document.removeEventListener("click", handleClick); + }; + }); +}; + +function ClickBox({onClickOutside,onClickInside}) { + const clickRef = useRef(); + useClickOutside(clickRef, onClickOutside); + useClickInside(clickRef, onClickInside); return ( - callback(value)} - /> +
+

Hello Click Me Inside!

+
); } ``` @@ -264,14 +334,16 @@ function Input({ callback, type = 'text', disabled = false, readOnly = false, pl Examples ```jsx -ReactDOM.render( - console.log(val)} />, - document.getElementById('root') -); +ReactDOM.render( alert("click outside")} onClickInside={()=> alert("click inside")}/>,document.getElementById('root')) ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) + +--- + +## Input + ### LimitedTextarea @@ -318,7 +390,7 @@ ReactDOM.render(, document.getEleme ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### LimitedWordTextarea @@ -382,7 +454,7 @@ ReactDOM.render( ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### MultiselectCheckbox @@ -409,7 +481,7 @@ function MultiselectCheckbox({ options, onChange }) { const [data, setData] = React.useState(options); const toggle = item => { - data.map((_, key) => { + data.forEach((_, key) => { if (data[key].label === item.label) data[key].checked = !item.checked; }); setData([...data]); @@ -449,7 +521,7 @@ ReactDOM.render( ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### PasswordRevealer @@ -479,7 +551,7 @@ ReactDOM.render(, document.getElementById('root')); ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### Select @@ -524,7 +596,7 @@ ReactDOM.render( ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### Slider @@ -554,7 +626,7 @@ ReactDOM.render( console.log(val)} />, document.getEleme ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### TextArea @@ -596,11 +668,48 @@ ReactDOM.render( ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) + +### UncontrolledInput + +Renders an `` element that uses a callback function to pass its value to the parent component. + +* Use object destructuring to set defaults for certain attributes of the `` element. +* Render an `` 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({ callback, type = 'text', disabled = false, readOnly = false, placeholder = '' }) { + return ( + callback(value)} + /> + ); +} +``` + +
+Examples + +```jsx +ReactDOM.render( + console.log(val)} />, + document.getElementById('root') +); +``` +
+ +
[⬆ Back to top](#contents) + +--- + +## Object -## Object -### TreeView +### TreeView ![advanced](/advanced.svg) Renders a tree view of a JSON object or array with collapsible content. @@ -728,11 +837,14 @@ ReactDOM.render(, document.getElementById('r ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) + +--- + +## String -## String -### AutoLink +### AutoLink ![advanced](/advanced.svg) Renders a string as plaintext, with URLs converted to appropriate `` elements. @@ -769,11 +881,14 @@ ReactDOM.render( ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) + +--- + +## Visual -## Visual -### Accordion +### Accordion ![advanced](/advanced.svg) Renders an accordion menu with multiple collapsible content components. @@ -857,7 +972,7 @@ ReactDOM.render( ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### Carousel @@ -926,7 +1041,7 @@ ReactDOM.render( ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### Collapse @@ -986,9 +1101,9 @@ ReactDOM.render( ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) -### CountDown +### CountDown ![advanced](/advanced.svg) Renders a countdown timer that prints a message when it reaches zero. @@ -1047,7 +1162,7 @@ function CountDown({ hours = 0, minutes = 0, seconds = 0 }) { React.useEffect(() => { let timerID = setInterval(() => tick(), 1000); return () => clearInterval(timerID); - }, [tick]); + }); return (
@@ -1070,7 +1185,7 @@ ReactDOM.render(, document.getElementById('r ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### FileDrop @@ -1175,37 +1290,7 @@ ReactDOM.render(, document.getElementById(' ``` -
[⬆ Back to top](#table-of-contents) - -### Mailto - -Renders a link formatted to send an email. - -* Destructure the component's props, use `email`, `subject` and `body` to create a `
` element with an appropriate `href` attribute. -* Render the link with `props.children` as its content. - -```jsx -function Mailto({ email, subject, body, ...props }) { - return ( - {props.children} - ); -} -``` - -
-Examples - -```jsx -ReactDOM.render( - - Mail me! - , - document.getElementById('root') -); -``` -
- -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### Modal @@ -1345,7 +1430,7 @@ ReactDOM.render( , document.getElementById('root')); ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### StarRating @@ -1402,7 +1487,7 @@ ReactDOM.render(, document.getElementById('root')); ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### Tabs @@ -1485,7 +1570,7 @@ ReactDOM.render( ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### Ticker @@ -1496,50 +1581,28 @@ Renders a ticker component. * Return a `
` with two ` - + +
); } @@ -1553,7 +1616,7 @@ ReactDOM.render(, document.getElementById('r ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### Toggle @@ -1591,7 +1654,7 @@ ReactDOM.render(, document.getElementById('root')); ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) ### Tooltip @@ -1651,8 +1714,42 @@ ReactDOM.render( ``` -
[⬆ Back to top](#table-of-contents) +
[⬆ Back to top](#contents) +--- + +## Viual + + +### Mailto + +Renders a link formatted to send an email. + +* Destructure the component's props, use `email`, `subject` and `body` to create a `` element with an appropriate `href` attribute. +* Render the link with `props.children` as its content. + +```jsx +function Mailto({ email, subject, body, ...props }) { + return ( + {props.children} + ); +} +``` + +
+Examples + +```jsx +ReactDOM.render( + + Mail me! + , + document.getElementById('root') +); +``` +
+ +
[⬆ Back to top](#contents) --- diff --git a/config.js b/config.js new file mode 100644 index 000000000..b61d6e579 --- /dev/null +++ b/config.js @@ -0,0 +1,16 @@ +module.exports = { + // Project metadata + name: `30 seconds starter`, + description: `Curated collection of useful React snippets that you can understand in 30 seconds or less.`, + shortName: `30s`, + repositoryUrl: `https://github.com/30-seconds/30-seconds-of-react`, + // Path information + snippetPath: `snippets`, + snippetDataPath: `snippet_data`, + assetPath: `assets`, + pagePath: `src/docs/pages`, + staticPartsPath: `src/static-parts`, + // General information + language: `jsx`, + optionalLanguage: `css`, +}; diff --git a/data/snippet_data.json b/data/snippet_data.json deleted file mode 100644 index cd37c9bf9..000000000 --- a/data/snippet_data.json +++ /dev/null @@ -1,395 +0,0 @@ -[ - { - "name": "Accordion.md", - "title": "Accordion", - "text": "Renders an accordion menu with multiple collapsible content components.\n\n* 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`.\n* Each `AccordionItem` component renders a `\n \n {props.children}\n
\n \n );\n}\n\nfunction Accordion(props) {\n const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);\n\n const changeItem = itemIndex => {\n if (typeof props.onItemClick === 'function') props.onItemClick(itemIndex);\n if (itemIndex !== bindIndex) setBindIndex(itemIndex);\n };\n const items = props.children.filter(item => item.type.name === 'AccordionItem');\n\n return (\n
\n {items.map(({ props }) => (\n changeItem(props.index)}\n children={props.children}\n />\n ))}\n
\n );\n}\n```", - "```jsx\nReactDOM.render(\n \n \n Lorem ipsum\n \n \n Dolor sit amet\n \n ,\n document.getElementById('root')\n);\n```" - ], - "expertise": 2, - "tags": [ - "visual", - "children", - "state" - ], - "notes": [] - }, - { - "name": "AutoLink.md", - "title": "AutoLink", - "text": "Renders a string as plaintext, with URLs converted to appropriate `` elements.\n\n* Use `String.prototype.split()` and `String.prototype.match()` with a regular expression to find URLs in a string.\n* Return a `` with matched URLs rendered as `` elements, dealing with missing protocol prefixes if necessary, and the rest of the string rendered as plaintext.\n\n", - "codeBlocks": [ - "```jsx\nfunction AutoLink({ text }) {\n 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;\n\n return (\n \n {text.split(delimiter).map(word => {\n let match = word.match(delimiter);\n if (match) {\n let url = match[0];\n return {url};\n }\n return word;\n })}\n \n );\n}\n```", - "```jsx\nReactDOM.render(\n ,\n document.getElementById('root')\n);\n```" - ], - "expertise": 2, - "tags": [ - "string", - "fragment", - "regexp" - ], - "notes": [] - }, - { - "name": "Carousel.md", - "title": "Carousel", - "text": "Renders a carousel component.\n\n* Use the `React.setState()` hook to create the `active` state variable and give it a value of `0` (index of the first item).\n* Use an object, `style`, to hold the styles for the individual components.\n* Use the `React.setEffect()` hook to update the value of `active` to the index of the next item, using `setTimeout`.\n* Destructure `props`, compute if visibility style should be set to `visible` or not for each carousel item while mapping over and applying the combined style to the carousel item component accordingly.\n* Render the carousel items using `React.cloneElement()` and pass down rest `props` along with the computed styles.\n\n", - "codeBlocks": [ - "```jsx\nfunction Carousel(props) {\n const [active, setActive] = React.useState(0);\n let scrollInterval = null;\n const style = {\n carousel: {\n position: 'relative'\n },\n carouselItem: {\n position: 'absolute',\n visibility: 'hidden'\n },\n visible: {\n visibility: 'visible'\n }\n };\n React.useEffect(() => {\n scrollInterval = setTimeout(() => {\n const { carouselItems } = props;\n setActive((active + 1) % carouselItems.length);\n }, 2000);\n });\n const { carouselItems, ...rest } = props;\n return (\n
\n {carouselItems.map((item, index) => {\n const activeStyle = active === index ? style.visible : {};\n return React.cloneElement(item, {\n ...rest,\n style: {\n ...style.carouselItem,\n ...activeStyle\n }\n });\n })}\n
\n );\n}\n```", - "```jsx\nReactDOM.render(\n carousel item 1,\n
carousel item 2
,\n
carousel item 3
\n ]}\n />,\n document.getElementById('root')\n);\n```" - ], - "expertise": 2, - "tags": [ - "visual", - "children", - "state", - "effect" - ], - "notes": [] - }, - { - "name": "Collapse.md", - "title": "Collapse", - "text": "Renders a component with collapsible content.\n\n* Use the `React.setState()` hook to create the `isCollapsed` state variable with an initial value of `props.collapsed`.\n* Use an object, `style`, to hold the styles for individual components and their states.\n* Use a `
` to wrap both the `\n \n {props.children}\n
\n \n );\n}\n```", - "```jsx\nReactDOM.render(\n \n

This is a collapse

\n

Hello world!

\n
,\n document.getElementById('root')\n);\n```" - ], - "expertise": 2, - "tags": [ - "visual", - "children", - "state" - ], - "notes": [] - }, - { - "name": "CountDown.md", - "title": "CountDown", - "text": "Renders a countdown timer that prints a message when it reaches zero.\n\n* Use object destructuring to set defaults for the `hours`, `minutes` and `seconds` props.\n* Use the `React.useState()` hook to create the `time`, `paused` and `over` state variables and set their values to the values of the passed props, `false` and `false` respectively.\n* Create a method `tick`, that updates the value of `time` based on the current value (i.e. decreasing the time by one second).\n* If `paused` or `over` is `true`, `tick` will return immediately.\n* Create a method `reset`, that resets all state variables to their initial states.\n* Use the the `React.useEffect()` hook to call the `tick` method every second via the use of `setInterval()` and use `clearInterval()` to cleanup when the component is unmounted.\n* Use a `
` to wrap a `

` element with the textual representation of the components `time` state variable, as well as two `\n \n

\n );\n}\n```", - "```jsx\nReactDOM.render(, document.getElementById('root'));\n```" - ], - "expertise": 2, - "tags": [ - "visual", - "state" - ], - "notes": [] - }, - { - "name": "DataList.md", - "title": "DataList", - "text": "Renders a list of elements from an array of primitives.\n\n* Use the value of the `isOrdered` prop to conditionally render a `
    ` or `
      ` list.\n* Use `Array.prototype.map` to render every item in `data` as a `
    • ` element, give it a `key` produced from the concatenation of the its index and value.\n* Omit the `isOrdered` prop to render a `
        ` list by default.\n\n", - "codeBlocks": [ - "```jsx\nfunction DataList({ isOrdered, data }) {\n const list = data.map((val, i) =>
      • {val}
      • );\n return isOrdered ?
          {list}
        :
          {list}
        ;\n}\n```", - "```jsx\nconst names = ['John', 'Paul', 'Mary'];\nReactDOM.render(, document.getElementById('root'));\nReactDOM.render(, document.getElementById('root'));\n```" - ], - "expertise": 0, - "tags": [ - "array" - ], - "notes": [] - }, - { - "name": "DataTable.md", - "title": "DataTable", - "text": "Renders a table with rows dynamically created from an array of primitives.\n\n* Render a `` element with two columns (`ID` and `Value`).\n* Use `Array.prototype.map` to render every item in `data` as a `` element, consisting of its index and value, give it a `key` produced from the concatenation of the two.\n\n", - "codeBlocks": [ - "```jsx\nfunction DataTable({ data }) {\n return (\n
        \n \n \n \n \n \n \n \n {data.map((val, i) => (\n \n \n \n \n ))}\n \n
        IDValue
        {i}{val}
        \n );\n}\n```", - "```jsx\nconst people = ['John', 'Jesse'];\nReactDOM.render(, document.getElementById('root'));\n```" - ], - "expertise": 0, - "tags": [ - "array" - ], - "notes": [] - }, - { - "name": "FileDrop.md", - "title": "FileDrop", - "text": "Renders a file drag and drop component for a single file.\n\n* Create a ref called `dropRef` for this component.\n* Use the `React.useState()` hook to create the `drag` and `filename` variables, initialized to `false` and `''` respectively.\nThe variables `dragCounter` and `drag` are used to determine if a file is being dragged, while `filename` is used to store the dropped file's name.\n* Create the `handleDrag`, `handleDragIn`, `handleDragOut` and `handleDrop` methods to handle drag and drop functionality, bind them to the component's context.\n* 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.\n* `handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `props.handleDrop`.\n* Return an appropriately styled `
        ` and use `drag` and `filename` to determine its contents and style.\n* Finally, bind the `ref` of the created `
        ` to `dropRef`.\n\n", - "codeBlocks": [ - "```css\n.filedrop {\n min-height: 120px;\n border: 3px solid #d3d3d3;\n text-align: center;\n font-size: 24px;\n padding: 32px;\n border-radius: 4px;\n}\n\n.filedrop.drag {\n border: 3px dashed #1e90ff;\n}\n\n.filedrop.ready {\n border: 3px solid #32cd32;\n}\n```", - "```jsx\nfunction FileDrop(props) {\n const [drag, setDrag] = React.useState(false);\n const [filename, setFilename] = React.useState('');\n let dropRef = React.createRef();\n let dragCounter = 0;\n\n const handleDrag = e => {\n e.preventDefault();\n e.stopPropagation();\n };\n\n const handleDragIn = e => {\n e.preventDefault();\n e.stopPropagation();\n dragCounter++;\n if (e.dataTransfer.items && e.dataTransfer.items.length > 0) setDrag(true);\n };\n\n const handleDragOut = e => {\n e.preventDefault();\n e.stopPropagation();\n dragCounter--;\n if (dragCounter === 0) setDrag(false);\n };\n\n const handleDrop = e => {\n e.preventDefault();\n e.stopPropagation();\n setDrag(false);\n if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n props.handleDrop(e.dataTransfer.files[0]);\n setFilename(e.dataTransfer.files[0].name);\n e.dataTransfer.clearData();\n dragCounter = 0;\n }\n };\n\n React.useEffect(() => {\n let div = dropRef.current;\n div.addEventListener('dragenter', handleDragIn);\n div.addEventListener('dragleave', handleDragOut);\n div.addEventListener('dragover', handleDrag);\n div.addEventListener('drop', handleDrop);\n return function cleanup() {\n div.removeEventListener('dragenter', handleDragIn);\n div.removeEventListener('dragleave', handleDragOut);\n div.removeEventListener('dragover', handleDrag);\n div.removeEventListener('drop', handleDrop);\n };\n });\n\n return (\n \n {filename && !drag ?
        {filename}
        :
        Drop files here!
        }\n
        \n );\n}\n```", - "```jsx\nReactDOM.render(, document.getElementById('root'));\n```" - ], - "expertise": 2, - "tags": [ - "visual", - "input", - "state", - "effect" - ], - "notes": [] - }, - { - "name": "Input.md", - "title": "Input", - "text": "Renders an `` element that uses a callback function to pass its value to the parent component.\n\n* Use object destructuring to set defaults for certain attributes of the `` element.\n* Render an `` element with the appropriate attributes and use the `callback` function in the `onChange` event to pass the value of the input to the parent.\n\n", - "codeBlocks": [ - "```jsx\nfunction Input({ callback, type = 'text', disabled = false, readOnly = false, placeholder = '' }) {\n return (\n callback(value)}\n />\n );\n}\n```", - "```jsx\nReactDOM.render(\n console.log(val)} />,\n document.getElementById('root')\n);\n```" - ], - "expertise": 0, - "tags": [ - "input" - ], - "notes": [] - }, - { - "name": "LimitedTextarea.md", - "title": "LimitedTextarea", - "text": "Renders a textarea component with a character limit.\n\n* Use the `React.useState()` hook to create the `content` state variable and set its value to `value`.\nCreate a method `setFormattedContent`, which trims the content of the input if it's longer than `limit`.\n* Use the `React.useEffect()` hook to call the `setFormattedContent` method on the value of the `content` state variable.\n* Use a`
        ` to wrap both the`