mirror of
https://github.com/seigler/triplebyte-react-spa
synced 2025-07-26 15:26:08 +00:00
feat: sync redux to localstorage
This commit is contained in:
parent
9242ce66ec
commit
ff4a941dd8
10 changed files with 230 additions and 149 deletions
|
@ -1,19 +1,38 @@
|
|||
import React from 'react';
|
||||
import { useDispatch } from 'react-redux'
|
||||
|
||||
import { withRedux } from '../lib/redux';
|
||||
import { withRedux } from 'lib/redux';
|
||||
|
||||
const addCard = ({ columnIndex }) => {
|
||||
const addCard = ({ column }) => {
|
||||
const dispatch = useDispatch();
|
||||
return <a onClick={
|
||||
() => {
|
||||
const userText = window.prompt('New card text:', '(no text)');
|
||||
dispatch({ type: 'ADD_CARD', payload: {
|
||||
column: columnIndex,
|
||||
text: userText
|
||||
} });
|
||||
}
|
||||
}>+ Add a card</a>;
|
||||
return <>
|
||||
<a className='addCard' onClick={
|
||||
() => {
|
||||
const userText = window.prompt('New card text:', '');
|
||||
if (userText !== null) {
|
||||
dispatch({ type: 'ADD_CARD', payload: {
|
||||
column,
|
||||
text: userText
|
||||
}});
|
||||
}
|
||||
}
|
||||
}><span>+</span> Add a card</a>
|
||||
<style jsx>{`
|
||||
.addCard {
|
||||
margin-top: 12px;
|
||||
cursor: pointer;
|
||||
opacity: 0.4;
|
||||
display: block;
|
||||
padding: 0 8px;
|
||||
}
|
||||
.addCard:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
span {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
`}</style>
|
||||
</>;
|
||||
}
|
||||
|
||||
export default withRedux(addCard);
|
||||
|
|
|
@ -1,7 +1,26 @@
|
|||
import React from 'react'
|
||||
import React from 'react';
|
||||
import CardMover from 'components/cardMover';
|
||||
|
||||
export default ({ children }) => {
|
||||
return <div className='card'>
|
||||
I am a card
|
||||
</div>
|
||||
export default ({ text = 'No text', moveLeft, moveRight }) => {
|
||||
return <>
|
||||
<div className='card'>
|
||||
{ moveLeft && <CardMover direction='left' action={moveLeft} /> }
|
||||
<div className='text'>
|
||||
{ text }
|
||||
</div>
|
||||
{ moveRight && <CardMover direction='right' action={moveRight} /> }
|
||||
</div>
|
||||
<style jsx>{`
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 6px;
|
||||
background-color: white;
|
||||
}
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
padding: 14px 12px;
|
||||
}
|
||||
`}</style>
|
||||
</>;
|
||||
}
|
||||
|
|
20
components/cardMover.js
Normal file
20
components/cardMover.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import React from 'react';
|
||||
|
||||
export default ({ direction, action }) => {
|
||||
return <>
|
||||
<a className='cardMover' onClick={action}>
|
||||
{ direction === 'left' ? '〈' : '〉' }
|
||||
</a>
|
||||
<style jsx>{`
|
||||
.cardMover {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
padding-${direction}: 8px;
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
`}</style>
|
||||
</>;
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
import React from 'react'
|
||||
import { useSelector, shallowEqual } from 'react-redux'
|
||||
|
||||
const useClock = () => {
|
||||
return useSelector(
|
||||
state => ({
|
||||
lastUpdate: state.lastUpdate,
|
||||
light: state.light,
|
||||
}),
|
||||
shallowEqual
|
||||
)
|
||||
}
|
||||
|
||||
const formatTime = time => {
|
||||
// cut off except hh:mm:ss
|
||||
return new Date(time).toJSON().slice(11, 19)
|
||||
}
|
||||
|
||||
const Clock = () => {
|
||||
const { lastUpdate, light } = useClock()
|
||||
return (
|
||||
<div className={light ? 'light' : ''}>
|
||||
{formatTime(lastUpdate)}
|
||||
<style jsx>{`
|
||||
div {
|
||||
padding: 15px;
|
||||
display: inline-block;
|
||||
color: #82fa58;
|
||||
font: 50px menlo, monaco, monospace;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.light {
|
||||
background-color: #999;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Clock
|
|
@ -1,23 +1,64 @@
|
|||
import React from 'react';
|
||||
import { useSelector, shallowEqual } from 'react-redux';
|
||||
import { withRedux } from '../lib/redux';
|
||||
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
||||
import { withRedux } from 'lib/redux';
|
||||
|
||||
import Card from './card';
|
||||
import AddCard from './addCard';
|
||||
import Card from 'components/card';
|
||||
import AddCard from 'components/addCard';
|
||||
|
||||
const column = ({ name, index, headerColor }) => {
|
||||
const cards = useSelector(state => state.columns[index].cards);
|
||||
const column = ({ name, id, headerColor }) => {
|
||||
const dispatch = useDispatch();
|
||||
const cards = useSelector(state => state.cards.filter(
|
||||
c => (c.column === id)
|
||||
), shallowEqual);
|
||||
const colIds = useSelector(state => state.columns.map(c => c.id));
|
||||
const myIndex = colIds.findIndex(x => x === id);
|
||||
const neighbors = [null, ...colIds, null];
|
||||
const prevColId = neighbors[myIndex+1-1];
|
||||
const nextColId = neighbors[myIndex+1+1];
|
||||
|
||||
return <div className='column'>
|
||||
<h1 className='card-title' style={{ color: 'white', backgroundColor: headerColor }}>{name}</h1>
|
||||
{ cards.map((c, cardIndex) => <Card key={`card-${index}-${cardIndex}`} />) }
|
||||
<AddCard index={index} />
|
||||
const moverFactory = (id, column) => {
|
||||
if (column) {
|
||||
return () => {
|
||||
dispatch({
|
||||
type: 'MOVE_CARD',
|
||||
payload: {
|
||||
id,
|
||||
column
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return <section className='column'>
|
||||
<h2
|
||||
className='card-title'
|
||||
style={{ color: '#FFFD', backgroundColor: headerColor }}
|
||||
>{name}</h2>
|
||||
{ cards.map(({ text, id }) => <Card
|
||||
key={id}
|
||||
text={text}
|
||||
moveLeft={moverFactory(id, prevColId)}
|
||||
moveRight={moverFactory(id, nextColId)}
|
||||
/>) }
|
||||
<AddCard column={id} />
|
||||
<style jsx>{`
|
||||
.column {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
margin: 12.5px;
|
||||
}
|
||||
h2 {
|
||||
padding: 8px;
|
||||
line-height: 14px;
|
||||
margin-bottom: 6px;
|
||||
font-size: inherit;
|
||||
text-align: center;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
export default withRedux(column);
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import React from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
|
||||
const useCounter = () => {
|
||||
const count = useSelector(state => state.count)
|
||||
const dispatch = useDispatch()
|
||||
const increment = () =>
|
||||
dispatch({
|
||||
type: 'INCREMENT',
|
||||
})
|
||||
const decrement = () =>
|
||||
dispatch({
|
||||
type: 'DECREMENT',
|
||||
})
|
||||
const reset = () =>
|
||||
dispatch({
|
||||
type: 'RESET',
|
||||
})
|
||||
return { count, increment, decrement, reset }
|
||||
}
|
||||
|
||||
const Counter = () => {
|
||||
const { count, increment, decrement, reset } = useCounter()
|
||||
return (
|
||||
<div>
|
||||
<h1>
|
||||
Count: <span>{count}</span>
|
||||
</h1>
|
||||
<button onClick={increment}>+1</button>
|
||||
<button onClick={decrement}>-1</button>
|
||||
<button onClick={reset}>Reset</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Counter
|
Loading…
Add table
Add a link
Reference in a new issue