-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
rizovs
committed
Nov 3, 2018
1 parent
fe5ef6b
commit 0c1c40f
Showing
19 changed files
with
882 additions
and
307 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,3 +21,6 @@ | |
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
/.idea | ||
/ignored |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,55 @@ | ||
import React, { Component } from 'react'; | ||
import logo from './logo.svg'; | ||
import './App.css'; | ||
import React from 'react'; | ||
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'; | ||
|
||
class App extends Component { | ||
render() { | ||
return ( | ||
<div className="App"> | ||
<header className="App-header"> | ||
<img src={logo} className="App-logo" alt="logo" /> | ||
<p> | ||
Edit <code>src/App.js</code> and save to reload. | ||
</p> | ||
<a | ||
className="App-link" | ||
href="https://reactjs.org" | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
Learn React | ||
</a> | ||
</header> | ||
import AccordionScreen from './components/accordion'; | ||
import Screen from './components/form-library'; | ||
import WindowWidthScreen from './components/window-width'; | ||
import TodoList from "./components/todolist"; | ||
|
||
function App() { | ||
return ( | ||
<Router> | ||
<div className="main"> | ||
<nav className="sidebar"> | ||
<div className="item"> | ||
<Link className="link" to="/accordion"> | ||
Accordion | ||
</Link> | ||
<span> | ||
Panels scroll into view if not fully visible when toggled. | ||
Using <pre>useImperativeMethods</pre> and <pre>useRef</pre>. | ||
</span> | ||
</div> | ||
<div className="item"> | ||
<Link className="link" to="/form-library"> | ||
Extremely basic form validation library | ||
</Link> | ||
<span>Pass state deeper using context, then read it using <pre>useContext</pre>. Heavily inspired by <pre>formik</pre>.</span> | ||
</div> | ||
<div className="item"> | ||
<Link className="link" to="/window-width"> | ||
Window width | ||
</Link> | ||
<span>Multiple <pre>useEffects</pre> are allowed.</span> | ||
</div> | ||
<div className="item"> | ||
<Link className="link" to="/todo-list"> | ||
Todo-list | ||
</Link> | ||
<span>Look mum, <pre>useReducer</pre> is almost Redux!</span> | ||
</div> | ||
</nav> | ||
|
||
<div className="content"> | ||
<Route path="/" exact component={() => <div>use navigation on the left</div>}/> | ||
<Route path="/accordion" component={AccordionScreen}/> | ||
<Route path="/form-library" component={Screen}/> | ||
<Route path="/window-width" component={WindowWidthScreen}/> | ||
<Route path="/todo-list" component={TodoList}/> | ||
</div> | ||
</div> | ||
); | ||
} | ||
</Router> | ||
) | ||
} | ||
|
||
export default App; | ||
export default App; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import React, { useRef, createRef, useImperativeMethods, useState, useEffect } from 'react'; | ||
import scrollIntoView from 'scroll-into-view-if-needed'; | ||
import FoobarIpsum from 'foobar-ipsum'; | ||
|
||
function useAccordion(panelsCount) { | ||
const [currentIndex, setCurrentIndex] = useState(); | ||
const [refs, setRefs] = useState(); | ||
|
||
// This part is smelly | ||
// https://github.com/facebook/react/issues/14072 | ||
// TODO rewrite | ||
useEffect(() => { | ||
let refs = {}; | ||
for (let i = 0; i <= panelsCount; i++) { | ||
refs[i] = createRef(); | ||
} | ||
setRefs(refs); | ||
}, []); | ||
|
||
useEffect(() => { | ||
// Scroll current accordion panel into view | ||
if (currentIndex !== undefined) { | ||
refs[currentIndex].current.scrollIntoView(); | ||
} | ||
}, [currentIndex]); | ||
|
||
function setCurrent(newIndex) { | ||
setCurrentIndex(currentIndex === newIndex ? undefined : newIndex); | ||
} | ||
|
||
return [currentIndex, setCurrent, refs]; | ||
} | ||
|
||
const AccordionPanel = React.forwardRef((props, ref) => { | ||
const containerRef = useRef(); | ||
const textRef = useRef(); | ||
|
||
useImperativeMethods(ref, () => ({ | ||
scrollIntoView: () => { | ||
scrollIntoView(containerRef.current, { block: 'nearest', scrollMode: 'if-needed' }); | ||
} | ||
})); | ||
|
||
return <div onClick={props.onClick} ref={containerRef}> | ||
<div className="accordion-label">{props.label}</div> | ||
{props.isOpen && | ||
<div>{generateRandomText()}</div>} | ||
</div>; | ||
}); | ||
|
||
function Accordion(props) { | ||
return <div>{props.children}</div>; | ||
} | ||
|
||
function generateRandomNumber(max) { | ||
return Math.floor(Math.random() * Math.floor(max)); | ||
} | ||
|
||
function generateRandomText() { | ||
return new FoobarIpsum({ | ||
size: { | ||
sentence: generateRandomNumber(100), | ||
paragraph: generateRandomNumber(10) | ||
} | ||
}).paragraph(); | ||
} | ||
|
||
export { | ||
useAccordion, | ||
Accordion, | ||
AccordionPanel | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import React from 'react'; | ||
|
||
import { Accordion, AccordionPanel, useAccordion } from './Accordion'; | ||
|
||
function AccordionScreen() { | ||
const panels = [...Array(100).keys()].map(e => `Panel number ${e}`); | ||
|
||
const [currentIndex, setCurrent, refs] = useAccordion(panels.length); | ||
|
||
return <Accordion> | ||
{panels.map((panel, index) => ( | ||
<AccordionPanel | ||
ref={refs && refs[index]} | ||
key={index} | ||
label={panel} | ||
isOpen={currentIndex === index} | ||
onClick={() => setCurrent(index)} | ||
/> | ||
))} | ||
</Accordion>; | ||
} | ||
|
||
export default AccordionScreen; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import React, { useState, useEffect, createContext } from "react"; | ||
|
||
const FormContext = createContext(null); | ||
|
||
function MyFormLibrary({ children, initialValues, onValuesChanged, onSubmit, validate }) { | ||
const [values, updateValues] = useState(initialValues); | ||
const [errors, updateErrors] = useState({}); | ||
|
||
useEffect(() => { | ||
if (typeof onValuesChanged === 'function') { | ||
onValuesChanged(values); | ||
} | ||
}, [values]); | ||
|
||
function handleChange(e) { | ||
updateValues({ | ||
...values, | ||
[e.target.name]: e.target.value | ||
}); | ||
updateErrors({ | ||
...errors, | ||
[e.target.name]: undefined | ||
}); | ||
} | ||
|
||
async function submitForm() { | ||
try { | ||
validate && validate(values); | ||
await onSubmit(values); | ||
} catch (e) { | ||
updateErrors(convertErrors(e)); | ||
} | ||
} | ||
|
||
function handleSubmit(e) { | ||
e.preventDefault(); | ||
submitForm(); | ||
} | ||
|
||
const ctx = { | ||
values, | ||
errors, | ||
handleChange, | ||
handleSubmit | ||
}; | ||
|
||
return <FormContext.Provider value={ctx}> | ||
{children} | ||
</FormContext.Provider>; | ||
} | ||
|
||
function convertErrors(yupError) { | ||
return yupError.inner | ||
.reduce((acc, cur) => { | ||
acc[cur.path] = cur.message; | ||
return acc; | ||
}, {}); | ||
} | ||
|
||
export default MyFormLibrary; | ||
export { FormContext }; |
Oops, something went wrong.