feat: delay flow

This commit is contained in:
cv
2021-03-28 13:48:42 +02:00
parent dd98867c7e
commit 714f6bc377
7 changed files with 229 additions and 73 deletions
+17 -8
View File
@@ -16,32 +16,41 @@ export const sampleData = {
title: 'Is the internet a fad?', title: 'Is the internet a fad?',
subtitle: 'It is', subtitle: 'It is',
presenter: 'Carlos Valente', presenter: 'Carlos Valente',
timeStart: new Date('March 24, 2021 03:25:00'), timeStart: new Date('October 13, 2014 11:30:00'),
timeEnd: new Date('March 24, 2021 04:24:00'), timeEnd: new Date('October 13, 2014 11:50:00'),
clockStarted: dummy, clockStarted: dummy,
timerDuration: 60, timerDuration: 60,
type: 'event',
},
{
id: 0.4849093424577693,
order: 2,
timerDuration: 25,
type: 'delay',
}, },
{ {
id: '2', id: '2',
order: 2, order: 3,
title: 'Is reddit a dictatorship?', title: 'Is reddit a dictatorship?',
subtitle: 'It is', subtitle: 'It is',
presenter: 'Carlos Valente', presenter: 'Carlos Valente',
timeStart: new Date('March 24, 2021 04:25:00'), timeStart: new Date('October 13, 2014 12:30:00'),
timeEnd: new Date('March 24, 2021 05:24:00'), timeEnd: new Date('October 13, 2014 12:50:00'),
clockStarted: dummy, clockStarted: dummy,
timerDuration: 60, timerDuration: 60,
type: 'event',
}, },
{ {
id: '3', id: '3',
order: 3, order: 4,
title: 'Out of words', title: 'Out of words',
subtitle: '', subtitle: '',
presenter: 'Carlos Valente', presenter: 'Carlos Valente',
timeStart: new Date('March 24, 2021 05:25:00'), timeStart: new Date('October 13, 2014 13:30:00'),
timeEnd: new Date('March 24, 2021 05:55:00'), timeEnd: new Date('October 13, 2014 13:50:00'),
clockStarted: dummy, clockStarted: dummy,
timerDuration: 60, timerDuration: 60,
type: 'event',
}, },
], ],
}; };
+7 -1
View File
@@ -1 +1,7 @@
export const timeFormat = 'HH:mm'; export const timeFormat = 'HH:mm';
// make date with string
export const timeToDate = (time) => {
const today = new Date();
return new Date(today.toDateString() + ' ' + time);
};
+22 -2
View File
@@ -1,4 +1,24 @@
import { IconButton } from '@chakra-ui/button';
import { AddIcon, MinusIcon } from '@chakra-ui/icons';
import style from './List.module.css'; import style from './List.module.css';
export default function BlockBlock() {
return <div className={style.blockContainer} />; export default function BlockBlock(props) {
return (
<div className={style.blockContainer}>
<div className={style.actionOverlay}>
<IconButton
size='xs'
icon={<MinusIcon />}
colorScheme='red'
onClick={() => props.deleteEvent(props.index)}
/>
<IconButton
size='xs'
icon={<AddIcon />}
colorScheme='blue'
onClick={() => props.createEvent(props.index)}
/>
</div>
</div>
);
} }
+33 -18
View File
@@ -8,35 +8,40 @@ import {
SliderThumb, SliderThumb,
SliderTrack, SliderTrack,
} from '@chakra-ui/slider'; } from '@chakra-ui/slider';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import style from './List.module.css'; import style from './List.module.css';
export default function DelayBlock() { export default function DelayBlock(props) {
const [delay, setDelay] = useState(0); const [delay, setDelay] = useState(0);
const { getIncrementButtonProps, getDecrementButtonProps } = useNumberInput({ const { data, ...rest } = props;
step: 1,
defaultValue: 0,
min: -60,
max: 60,
});
const inc = getIncrementButtonProps(); // update delay value in parent
const dec = getDecrementButtonProps(); const populateDelay = (value) => {
// check if value has changed
if (data.timerDuration !== value) {
// create object with new field
const newData = { ...data, timerDuration: delay };
const populateDelay = (val) => { // request update in parent
console.log('set delay in parent', val); props.updateData(props.index, newData);
}
}; };
// update delay value in state
useEffect(() => {
setDelay(data.timerDuration);
}, [data]);
return ( return (
<div className={style.delayContainer}> <div className={style.delayContainer}>
<div className={style.delayValue}>{`${delay} min`}</div> <div className={style.delayValue}>{`${delay} min`}</div>
<Slider <Slider
defaultValue={0} defaultValue={data.timerDuration}
min={-60} min={0}
max={60} max={60}
step={5} step={5}
onChange={(val) => setDelay(val)} onChange={(value) => setDelay(value)}
onChangeEnd={(val) => populateDelay(val)} onChangeEnd={(value) => populateDelay(value)}
> >
<SliderTrack bg='orange.100'> <SliderTrack bg='orange.100'>
<Box position='relative' right={10} /> <Box position='relative' right={10} />
@@ -45,8 +50,18 @@ export default function DelayBlock() {
<SliderThumb boxSize={4} /> <SliderThumb boxSize={4} />
</Slider> </Slider>
<div className={style.actionOverlay}> <div className={style.actionOverlay}>
<IconButton size='xs' icon={<MinusIcon />} colorScheme='red' /> <IconButton
<IconButton size='xs' icon={<AddIcon />} colorScheme='blue' /> size='xs'
icon={<MinusIcon />}
colorScheme='red'
onClick={() => props.deleteEvent(props.index)}
/>
<IconButton
size='xs'
icon={<AddIcon />}
colorScheme='blue'
onClick={() => props.createEvent(props.index)}
/>
</div> </div>
</div> </div>
); );
+101 -35
View File
@@ -8,52 +8,87 @@ import { sortByOrderVal } from './listUtils';
import style from './List.module.css'; import style from './List.module.css';
import DelayBlock from './DelayBlock'; import DelayBlock from './DelayBlock';
import BlockBlock from './BlockBlock'; import BlockBlock from './BlockBlock';
import { createEvent } from '@testing-library/dom';
export default function EventList(props) { export default function EventList(props) {
const [events, setEvents] = useContext(EventListContext); const [events, setEvents] = useContext(EventListContext);
const [event, setEvent] = useContext(EventContext); const [event, setEvent] = useContext(EventContext);
console.log('in el', events);
const selected = event?.id || -1; const selected = event?.id || -1;
const createEvent = (startPosition = 0) => { const insertItemAt = (item, index) => {
// handle insert at beggining of array
if (index === -1) {
// move all items one element down, starting from new position
events.forEach((e) => {
e.order = e.order + 1;
});
return [item, ...events];
} else {
let before = events.slice(0, index + 1);
let after = events.slice(index + 1);
// move all items one element down, starting from new position
after.forEach((e) => {
e.order = e.order + 1;
});
return [...before, item, ...after];
}
};
const createEvent = (itemIndex = -1) => {
// make an event // make an event
// TODO: Replace this with global def somewhere // TODO: Replace this with global def somewhere
// TODO: handle random ids better // TODO: handle random ids better
let newEvent = { let newEvent = {
id: Math.floor(Math.random() * 10000000), id: Math.random(),
order: startPosition + 1, order: itemIndex + 1,
title: '', title: '',
subtitle: '', subtitle: '',
presenter: '', presenter: '',
timerStart: '', timeStart: new Date(),
timerEnd: '', timeEnd: new Date(),
clockStarted: null, clockStarted: null,
timerDuration: 0, timerDuration: 0,
type: 'event',
}; };
// move all items one element down, starting from new position
let newEvents = events.map((e) => {
if (e.order > startPosition) e.order = e.order + 1;
return e;
});
// add an event at the beggining
newEvents = [newEvent, ...events];
// set to state // set to state
setEvents(newEvents); setEvents(insertItemAt(newEvent, itemIndex));
}; };
const deleteEvent = (position) => { const createDelay = (itemIndex = 0) => {
// ?? Should i reorder after? // make an event
// TODO: Replace this with global def somewhere
// remove event from array // TODO: handle random ids better
let filtered = events.filter((e) => e.id !== position); let newEvent = {
id: Math.random(),
order: itemIndex + 1,
timerDuration: 0,
type: 'delay',
};
// set to state // set to state
setEvents(filtered); setEvents(insertItemAt(newEvent, itemIndex));
};
const createBlock = (itemIndex = 0) => {
// make an event
// TODO: Replace this with global def somewhere
// TODO: handle random ids better
let newEvent = {
id: Math.random(),
order: itemIndex + 1,
type: 'block',
};
// set to state
setEvents(insertItemAt(newEvent, itemIndex));
};
const deleteEvent = (index) => {
// TODO: This feels weird?
let e = events.splice(index, 1);
setEvents([...events]);
}; };
const replaceAt = (array, index, value) => { const replaceAt = (array, index, value) => {
@@ -65,8 +100,10 @@ export default function EventList(props) {
const updateData = (itemIndex, data) => { const updateData = (itemIndex, data) => {
const newEvents = replaceAt(events, itemIndex, data); const newEvents = replaceAt(events, itemIndex, data);
setEvents(newEvents); setEvents(newEvents);
}; }
console.log('events in event list', events);
let cumulativeDelay = 0;
return ( return (
<> <>
<div className={style.headerButtons}> <div className={style.headerButtons}>
@@ -84,17 +121,46 @@ export default function EventList(props) {
/> />
</div> </div>
<div className={style.eventContainer}> <div className={style.eventContainer}>
{sortByOrderVal(events).map((e, index) => ( {events.map((e, index) => {
<EventListItem if (e.type === 'event') {
key={e.id} return (
index={index} <EventListItem
data={e} key={e.id}
selected={e.id === selected} index={index}
createEvent={createEvent} data={e}
deleteEvent={deleteEvent} selected={e.id === selected}
updateData={updateData} createEvent={createEvent}
/> deleteEvent={deleteEvent}
))} createDelay={createDelay}
createBlock={createBlock}
updateData={updateData}
delay={cumulativeDelay}
/>
);
} else if (e.type === 'block') {
cumulativeDelay = 0;
return (
<BlockBlock
key={e.id}
index={index}
createEvent={createEvent}
deleteEvent={deleteEvent}
/>
);
} else if (e.type === 'delay') {
cumulativeDelay = cumulativeDelay + e.timerDuration;
return (
<DelayBlock
key={e.id}
index={index}
data={e}
createEvent={createEvent}
deleteEvent={deleteEvent}
updateData={updateData}
/>
);
}
})}
</div> </div>
</> </>
); );
+38 -8
View File
@@ -12,14 +12,34 @@ import {
EditablePreview, EditablePreview,
EditableInput, EditableInput,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { addMinutes, format } from 'date-fns';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { timeFormat, timeToDate } from '../../../common/dateConfig';
import style from './List.module.css'; import style from './List.module.css';
// small shorthand for adding delay and formatting date
const addAndFormat = (time, delay) => {
return format(addMinutes(time, delay), timeFormat);
};
export default function EventListItem(props) { export default function EventListItem(props) {
const [more, setMore] = useState(false); const [more, setMore] = useState(false);
const [armed, setArmed] = useState(false); const [armed, setArmed] = useState(false);
// const [timeStart, setTimeStart] = useState(
// addAndFormat(props.data.timeStart, props.delay)
// );
const [timeStart, setTimeStart] = useState(0);
const [timeEnd, setTimeEnd] = useState(
addAndFormat(props.data.timeEnd, props.delay)
);
const { data, selected, ...rest } = props; const { data, selected, delay, ...rest } = props;
// prepare time fields
useEffect(() => {
setTimeStart(addAndFormat(props.data.timeStart, props.delay));
setTimeEnd(addAndFormat(props.data.timeEnd, props.delay));
}, [props.data, props.delay]);
const updateValues = (field, value) => { const updateValues = (field, value) => {
// validate field // validate field
@@ -43,10 +63,12 @@ export default function EventListItem(props) {
/> />
<div className={style.time}> <div className={style.time}>
<Editable <Editable
onSubmit={(v) => updateValues('timerStart', v)} onSubmit={(v) => updateValues('timeStart', timeToDate(v))}
defaultValue={data.timerStart} value={timeStart}
onChange={(val) => setTimeStart(val)}
placeholder='--:--' placeholder='--:--'
style={{ textAlign: 'center' }} style={{ textAlign: 'center' }}
className={delay > 0 && style.delayedEditable}
> >
<EditablePreview /> <EditablePreview />
<EditableInput type='time' min='00:00' max='23:59' /> <EditableInput type='time' min='00:00' max='23:59' />
@@ -54,10 +76,12 @@ export default function EventListItem(props) {
</div> </div>
<div className={style.time}> <div className={style.time}>
<Editable <Editable
onSubmit={(v) => updateValues('timerEnd', v)} onSubmit={(v) => updateValues('timeEnd', timeToDate(v))}
defaultValue={data.timerEnd} value={timeEnd}
onChange={(val) => setTimeEnd(val)}
placeholder='--:--' placeholder='--:--'
style={{ textAlign: 'center' }} style={{ textAlign: 'center' }}
className={delay > 0 && style.delayedEditable}
> >
<EditablePreview /> <EditablePreview />
<EditableInput type='time' min='00:00' max='23:59' /> <EditableInput type='time' min='00:00' max='23:59' />
@@ -129,19 +153,25 @@ export default function EventListItem(props) {
size='xs' size='xs'
icon={<MinusIcon />} icon={<MinusIcon />}
colorScheme='red' colorScheme='red'
onClick={() => props.deleteEvent(data.id)} onClick={() => props.deleteEvent(props.index)}
/> />
<IconButton <IconButton
size='xs' size='xs'
icon={<AddIcon />} icon={<AddIcon />}
colorScheme='blue' colorScheme='blue'
onClick={() => props.createEvent(data.order)} onClick={() => props.createEvent(props.index)}
/>
<IconButton
size='xs'
icon={<TimeIcon />}
colorScheme='yellow'
onClick={() => props.createDelay(props.index)}
/> />
<IconButton size='xs' icon={<TimeIcon />} colorScheme='yellow' />
<IconButton <IconButton
size='xs' size='xs'
icon={<NotAllowedIcon />} icon={<NotAllowedIcon />}
colorScheme='purple' colorScheme='purple'
onClick={() => props.createBlock(props.index)}
/> />
</div> </div>
</div> </div>
+11 -1
View File
@@ -160,6 +160,10 @@
border: 1px solid #0001; border: 1px solid #0001;
} }
.delayedEditable {
background-color: #ecc94b55;
}
/* ============= BLOCK BLOCK ============= */ /* ============= BLOCK BLOCK ============= */
.blockContainer { .blockContainer {
position: relative; position: relative;
@@ -170,8 +174,14 @@
background-color: #805ad5aa; background-color: #805ad5aa;
border-radius: 8px; border-radius: 8px;
color: white; color: white;
height: 2em; height: 3em;
border: 1px solid #0001; border: 1px solid #0001;
border-bottom: 8px solid #805ad5; border-bottom: 8px solid #805ad5;
box-sizing: border-box; box-sizing: border-box;
width: 100%;
display: flex;
flex-direction: row-reverse;
margin: auto;
gap: 1em;
} }