mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-09 01:13:55 +00:00
major: event block redesign
This commit is contained in:
@@ -2,13 +2,13 @@ import { IconButton } from '@chakra-ui/button';
|
||||
import { FiMinus } from 'react-icons/fi';
|
||||
|
||||
export default function DeleteIconBtn(props) {
|
||||
const { clickhandler, ...rest } = props;
|
||||
const { actionHandler, ...rest } = props;
|
||||
return (
|
||||
<IconButton
|
||||
size={props.size || 'xs'}
|
||||
icon={<FiMinus />}
|
||||
colorScheme='red'
|
||||
onClick={clickhandler}
|
||||
onClick={() => actionHandler('delete')}
|
||||
_focus={{ boxShadow: 'none' }}
|
||||
{...rest}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { IconButton } from '@chakra-ui/button';
|
||||
import { FiTrash2 } from 'react-icons/fi';
|
||||
|
||||
export default function TrashIconBtn(props) {
|
||||
const { clickhandler, ...rest } = props;
|
||||
return (
|
||||
<IconButton
|
||||
size={props.size || 'xs'}
|
||||
icon={<FiTrash2 />}
|
||||
colorScheme='red'
|
||||
onClick={clickhandler}
|
||||
_focus={{ boxShadow: 'none' }}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -2,14 +2,16 @@ import { IconButton } from '@chakra-ui/button';
|
||||
import { FiSun } from 'react-icons/fi';
|
||||
|
||||
export default function VisibleIconBtn(props) {
|
||||
const { clickhandler, active, ...rest } = props;
|
||||
const { actionHandler, active, ...rest } = props;
|
||||
return (
|
||||
<IconButton
|
||||
size={props.size || 'xs'}
|
||||
icon={<FiSun />}
|
||||
colorScheme='blue'
|
||||
variant={active ? 'solid' : 'outline'}
|
||||
onClick={clickhandler}
|
||||
onClick={() =>
|
||||
actionHandler('update', { field: 'isPublic', value: !active })
|
||||
}
|
||||
_focus={{ boxShadow: 'none' }}
|
||||
{...rest}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@ import EditableTimer from '../../input/EditableTimer';
|
||||
import { showWarningToast } from '../../helpers/toastManager';
|
||||
|
||||
export default function EventTimes(props) {
|
||||
const { updateValues, delay, timeStart, timeEnd } = props;
|
||||
const { actionHandler, delay, timeStart, timeEnd } = props;
|
||||
|
||||
const handleValidate = (entry, v) => {
|
||||
// we dont inforce validation here
|
||||
@@ -26,14 +26,14 @@ export default function EventTimes(props) {
|
||||
<EditableTimer
|
||||
name='timeStart'
|
||||
validate={handleValidate}
|
||||
updateValues={updateValues}
|
||||
actionHandler={actionHandler}
|
||||
time={timeStart}
|
||||
delay={delay}
|
||||
/>
|
||||
<EditableTimer
|
||||
name='timeEnd'
|
||||
validate={handleValidate}
|
||||
updateValues={updateValues}
|
||||
actionHandler={actionHandler}
|
||||
time={timeEnd}
|
||||
delay={delay}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import EditableTimer from '../../input/EditableTimer';
|
||||
import { showWarningToast } from '../../helpers/toastManager';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { millisToMinutes, stringFromMillis } from '../../dateConfig';
|
||||
|
||||
const label = {
|
||||
fontSize: '0.75em',
|
||||
color: '#aaa',
|
||||
};
|
||||
|
||||
const TimesDelayed = (props) => {
|
||||
const { handleValidate, actionHandler, delay, timeStart, timeEnd } = props;
|
||||
|
||||
const scheduledStart = stringFromMillis(timeStart, false);
|
||||
|
||||
const scheduledEnd = stringFromMillis(timeEnd, false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<span style={label}>
|
||||
Start <span>{scheduledStart}</span>
|
||||
</span>
|
||||
<EditableTimer
|
||||
name='timeStart'
|
||||
validate={handleValidate}
|
||||
actionHandler={actionHandler}
|
||||
time={timeStart}
|
||||
delay={delay}
|
||||
/>
|
||||
<span style={label}>
|
||||
End <span>{scheduledEnd}</span>
|
||||
</span>
|
||||
<EditableTimer
|
||||
name='timeEnd'
|
||||
validate={handleValidate}
|
||||
actionHandler={actionHandler}
|
||||
time={timeEnd}
|
||||
delay={delay}
|
||||
/>
|
||||
<span style={label}>Duration</span>
|
||||
<EditableTimer
|
||||
name='duration'
|
||||
validate={handleValidate}
|
||||
actionHandler={actionHandler}
|
||||
time={timeEnd - timeStart}
|
||||
delay={0}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Times = (props) => {
|
||||
const { handleValidate, actionHandler, timeStart, timeEnd, duration } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<span style={label}>Start</span>
|
||||
<EditableTimer
|
||||
name='timeStart'
|
||||
validate={handleValidate}
|
||||
actionHandler={actionHandler}
|
||||
time={timeStart}
|
||||
delay={0}
|
||||
/>
|
||||
<span style={label}>End</span>
|
||||
<EditableTimer
|
||||
name='timeEnd'
|
||||
validate={handleValidate}
|
||||
actionHandler={actionHandler}
|
||||
time={timeEnd}
|
||||
delay={0}
|
||||
/>
|
||||
<span style={label}>Duration (min)</span>
|
||||
<EditableTimer
|
||||
name='durationOverride'
|
||||
validate={handleValidate}
|
||||
actionHandler={actionHandler}
|
||||
time={timeEnd - timeStart}
|
||||
delay={0}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default function EventTimesVertical(props) {
|
||||
const { delay, timeStart, timeEnd } = props;
|
||||
const [duration, setDuration] = useState(
|
||||
millisToMinutes(timeEnd - timeStart) || 0
|
||||
);
|
||||
useEffect(() => {
|
||||
setDuration(millisToMinutes(timeEnd - timeStart));
|
||||
}, [timeStart, timeEnd]);
|
||||
|
||||
const handleValidate = (entry, v) => {
|
||||
// we dont enforce validation here
|
||||
|
||||
if (v == null || timeStart == null || timeEnd == null) return true;
|
||||
if (timeStart === 0) return true;
|
||||
|
||||
let validate = { value: true, catch: '' };
|
||||
if (entry === 'timeStart' && v > timeEnd)
|
||||
validate.catch = 'Start time later than end time';
|
||||
else if (entry === 'timeEnd' && v < timeStart)
|
||||
validate.catch = 'End time earlier than start time';
|
||||
|
||||
if (validate.catch !== '')
|
||||
showWarningToast('Time Input Warning', validate.catch);
|
||||
return validate.value;
|
||||
};
|
||||
|
||||
return (delay != null) & (delay > 0) ? (
|
||||
<TimesDelayed
|
||||
handleValidate={handleValidate}
|
||||
actionHandler={props.actionHandler}
|
||||
delay={delay}
|
||||
timeStart={timeStart}
|
||||
timeEnd={timeEnd}
|
||||
duration={duration}
|
||||
/>
|
||||
) : (
|
||||
<Times
|
||||
handleValidate={handleValidate}
|
||||
actionHandler={props.actionHandler}
|
||||
timeStart={timeStart}
|
||||
timeEnd={timeEnd}
|
||||
duration={duration}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -8,11 +8,12 @@ export const stringFromMillis = (
|
||||
delim = ':',
|
||||
ifNull = '...'
|
||||
) => {
|
||||
if (ms === null) return ifNull;
|
||||
if (ms === null || isNaN(ms)) return ifNull;
|
||||
const showWith0 = (value) => (value < 10 ? `0${value}` : value);
|
||||
const hours = showWith0(Math.floor(((ms / (1000 * 60 * 60)) % 60) % 24));
|
||||
const minutes = showWith0(Math.floor((ms / (1000 * 60)) % 60));
|
||||
const seconds = showWith0(Math.floor((ms / 1000) % 60));
|
||||
|
||||
return showSeconds
|
||||
? `${
|
||||
parseInt(hours) ? `${hours}${delim}` : `00${delim}`
|
||||
|
||||
@@ -13,7 +13,7 @@ const inputProps = {
|
||||
textAlign: 'center',
|
||||
};
|
||||
|
||||
export default function TimeInput(props) {
|
||||
export default function DelayInput(props) {
|
||||
const [value, setValue] = useState(props.value);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -29,9 +29,10 @@ export default function TimeInput(props) {
|
||||
if (val === props.value) return;
|
||||
if (val === '') setValue(0);
|
||||
if (val >= 0 && val <= 60) {
|
||||
props.submitHandler(val);
|
||||
// convert to ms and updates
|
||||
props.actionHandler('update', { field: 'duration', value: val * 60000 });
|
||||
} else {
|
||||
setValue(60);
|
||||
props.actionHandler('update', { field: 'duration', value: 3600000 });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { millisToMinutes } from '../dateConfig';
|
||||
import style from '../../features/editors/list/Block.module.css';
|
||||
|
||||
export default function DelayValue(props) {
|
||||
const { delay } = props;
|
||||
const delayed = delay > 0;
|
||||
return (
|
||||
<div className={style.delayValue}>
|
||||
{delayed && <span>{`+ ${millisToMinutes(delay)}`}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import style from './EditableText.module.css';
|
||||
|
||||
export default function EditableText(props) {
|
||||
const { label, defaultValue, placeholder, submitHandler } = props;
|
||||
const [text, setText] = useState('');
|
||||
const [text, setText] = useState(defaultValue || '');
|
||||
|
||||
useEffect(() => {
|
||||
if (defaultValue == null) setText('');
|
||||
@@ -19,14 +19,11 @@ export default function EditableText(props) {
|
||||
|
||||
const handleChange = (val) => {
|
||||
if (val.length < 40) setText(val);
|
||||
console.log(val.length);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={style.block}>
|
||||
<span className={props.underlined ? style.titleUnderlined : style.title}>
|
||||
{label}
|
||||
</span>
|
||||
<span className={style.title}>{label}</span>
|
||||
<Editable
|
||||
onChange={(v) => handleChange(v)}
|
||||
onSubmit={(v) => handleSubmit(v)}
|
||||
@@ -34,8 +31,11 @@ export default function EditableText(props) {
|
||||
placeholder={placeholder}
|
||||
className={style.inline}
|
||||
>
|
||||
<EditablePreview />
|
||||
<EditableInput />
|
||||
<EditablePreview
|
||||
color={text === '' ? '#666' : 'inherit'}
|
||||
maxWidth='20em'
|
||||
/>
|
||||
<EditableInput overflowX='hidden' maxWidth='20em' />
|
||||
</Editable>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
.title,
|
||||
.titleUnderlined {
|
||||
.title {
|
||||
padding-left: 1em;
|
||||
font-size: 0.8em;
|
||||
font-size: 0.75em;
|
||||
color: #aaa;
|
||||
display: inline-block;
|
||||
width: 6em;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.titleUnderlined {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.block {
|
||||
display: 'block';
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: inline;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { showErrorToast } from '../helpers/toastManager';
|
||||
import style from './EditableTimer.module.css';
|
||||
|
||||
export default function EditableTimer(props) {
|
||||
const { name, updateValues, time, delay, validate } = props;
|
||||
const { name, actionHandler, time, delay, validate } = props;
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
// prepare time fields
|
||||
@@ -46,7 +46,7 @@ export default function EditableTimer(props) {
|
||||
if (!validate(name, millis)) return false;
|
||||
|
||||
// update entry
|
||||
updateValues(name, millis);
|
||||
actionHandler('update', { field: name, value: millis });
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -56,19 +56,17 @@ export default function EditableTimer(props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={style.time}>
|
||||
<Editable
|
||||
onFocus={() => showOriginal()}
|
||||
onEdit={() => showOriginal}
|
||||
onChange={(v) => setValue(v)}
|
||||
onSubmit={(v) => validateValue(v)}
|
||||
value={value}
|
||||
placeholder='--:--'
|
||||
className={delay > 0 ? style.delayedEditable : style.editable}
|
||||
>
|
||||
<EditablePreview />
|
||||
<EditableInput type='time' min='00:00' max='23:59' />
|
||||
</Editable>
|
||||
</div>
|
||||
<Editable
|
||||
onFocus={() => showOriginal}
|
||||
onEdit={() => showOriginal}
|
||||
onChange={(v) => setValue(v)}
|
||||
onSubmit={(v) => validateValue(v)}
|
||||
value={value}
|
||||
placeholder='--:--'
|
||||
className={delay > 0 ? style.delayedEditable : style.editable}
|
||||
>
|
||||
<EditablePreview />
|
||||
<EditableInput type='time' min='00:00' max='23:59' />
|
||||
</Editable>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
.time {
|
||||
.editable,
|
||||
.delayedEditable {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: 4px;
|
||||
width: 5em;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.editable,
|
||||
.delayedEditable {
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.delayedEditable {
|
||||
|
||||
@@ -94,7 +94,7 @@ export default function MessageControl() {
|
||||
</Editable>
|
||||
<VisibleIconBtn
|
||||
active={pres.visible || undefined}
|
||||
clickhandler={() => messageControl('toggle-pres-visible')}
|
||||
actionHandler={() => messageControl('toggle-pres-visible')}
|
||||
{...inputProps}
|
||||
/>
|
||||
</div>
|
||||
@@ -114,7 +114,7 @@ export default function MessageControl() {
|
||||
</Editable>
|
||||
<VisibleIconBtn
|
||||
active={publ.visible || undefined}
|
||||
clickhandler={() => messageControl('toggle-publ-visible')}
|
||||
actionHandler={() => messageControl('toggle-publ-visible')}
|
||||
{...inputProps}
|
||||
/>
|
||||
</div>
|
||||
@@ -135,7 +135,7 @@ export default function MessageControl() {
|
||||
|
||||
<VisibleIconBtn
|
||||
active={lower.visible || undefined}
|
||||
clickhandler={() => messageControl('toggle-lower-visible')}
|
||||
actionHandler={() => messageControl('toggle-lower-visible')}
|
||||
{...inputProps}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { IconButton } from '@chakra-ui/button';
|
||||
import { FiZap, FiPlus, FiMinusCircle, FiClock } from 'react-icons/fi';
|
||||
|
||||
export default function ActionButtons(props) {
|
||||
const { showAdd, showDelay, showBlock } = props;
|
||||
const { showAdd, showDelay, showBlock, actionHandler } = props;
|
||||
|
||||
const menuStyle = {
|
||||
color: '#000000',
|
||||
@@ -25,7 +25,7 @@ export default function ActionButtons(props) {
|
||||
<MenuList style={menuStyle}>
|
||||
<MenuItem
|
||||
icon={<FiPlus />}
|
||||
onClick={props.addHandler}
|
||||
onClick={() => actionHandler('event')}
|
||||
isDisabled={!showAdd}
|
||||
>
|
||||
Event next
|
||||
@@ -33,14 +33,14 @@ export default function ActionButtons(props) {
|
||||
|
||||
<MenuItem
|
||||
icon={<FiClock />}
|
||||
onClick={props.delayHandler}
|
||||
onClick={() => actionHandler('delay')}
|
||||
isDisabled={!showDelay}
|
||||
>
|
||||
Delay next
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<FiMinusCircle />}
|
||||
onClick={props.blockHandler}
|
||||
onClick={() => actionHandler('block')}
|
||||
isDisabled={!showBlock}
|
||||
>
|
||||
Block next
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
/* ============= COMMON ============= */
|
||||
|
||||
.eventRow,
|
||||
.eventRowActive,
|
||||
.block,
|
||||
.delay {
|
||||
position: relative;
|
||||
margin: 0.2em 0;
|
||||
padding: 0.2em 0.5em;
|
||||
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
font-size: 15px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.block,
|
||||
.delay {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.actionOverlay {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding-top: 0.2em;
|
||||
gap: 0.5em;
|
||||
align-content: center;
|
||||
align-self: flex-start;
|
||||
|
||||
opacity: 0.8;
|
||||
transition: linear 0.1s;
|
||||
}
|
||||
|
||||
.eventRow:hover > .actionOverlay,
|
||||
.eventRowActive:hover > .actionOverlay,
|
||||
.block:hover > .actionOverlay,
|
||||
.delay:hover > .actionOverlay {
|
||||
opacity: 1;
|
||||
transition: linear 0.1s;
|
||||
}
|
||||
|
||||
/* NA */
|
||||
.actionOverlay:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
/* ============= EVENT ITEM ============= */
|
||||
|
||||
.eventRow,
|
||||
.eventRowActive {
|
||||
display: grid;
|
||||
grid-template-columns: 2em 2em auto auto 1fr auto;
|
||||
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
align-items: baseline;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.eventRow {
|
||||
background-color: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.eventRowActive {
|
||||
background-color: rgba(255, 255, 255, 0.36);
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
#00000000 10%,
|
||||
#4bffab10 89%,
|
||||
#227c52aa 90%,
|
||||
#4bffabcc 100%
|
||||
);
|
||||
}
|
||||
|
||||
.drag {
|
||||
color: rgba(255, 255, 255, 0.37);
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.indicators {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: flex-start;
|
||||
font-size: 0.75em;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.next,
|
||||
.nextDisabled {
|
||||
width: max-content;
|
||||
padding: 0 0.2em;
|
||||
color: #4bffab;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.next {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.nextDisabled {
|
||||
opacity: 0.05;
|
||||
}
|
||||
|
||||
.delayValue {
|
||||
color: #d69e2eaa;
|
||||
padding: 0 0.2em;
|
||||
text-align: center;
|
||||
align-self: flex-start;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.next:after,
|
||||
.delayValue:after {
|
||||
content: '\200b';
|
||||
}
|
||||
|
||||
.titleContainer,
|
||||
.detailedContainer {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.rowDetailed {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.titleContainer,
|
||||
.detailedContainer {
|
||||
width: 90%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.more {
|
||||
position: absolute;
|
||||
right: 0.25em;
|
||||
top: 0.5em;
|
||||
}
|
||||
|
||||
/* ============= BLOCK BLOCK ============= */
|
||||
.block {
|
||||
background-color: #805ad5;
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(107, 70, 193, 0.4) 0%,
|
||||
rgba(128, 90, 213, 0.4) 20%,
|
||||
rgba(107, 70, 193, 0.15) 21%
|
||||
);
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 2em auto;
|
||||
align-content: center;
|
||||
align-items: baseline;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.block > .drag {
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.block > .actionOverlay {
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
/* ============= DELAY BLOCK ============= */
|
||||
.delay {
|
||||
background-color: #ecc94b;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(214, 158, 46, 0.4) 0%,
|
||||
rgba(236, 201, 75, 0.4) 20%,
|
||||
rgba(214, 158, 46, 0.07) 21%
|
||||
);
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 2em 1fr auto;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
align-items: baseline;
|
||||
gap: 0.5em;
|
||||
}
|
||||
@@ -2,20 +2,10 @@ import { Draggable } from 'react-beautiful-dnd';
|
||||
import { FiMoreVertical } from 'react-icons/fi';
|
||||
import DeleteIconBtn from '../../../common/components/buttons/DeleteIconBtn';
|
||||
import ActionButtons from './ActionButtons';
|
||||
import style from './Block.module.css';
|
||||
import style from './BlockBlock.module.css';
|
||||
|
||||
export default function BlockBlock(props) {
|
||||
const { eventsHandler, index, data } = props;
|
||||
|
||||
const addHandler = () => {
|
||||
eventsHandler('add', { type: 'event', order: index + 1 });
|
||||
};
|
||||
const deleteHandler = () => {
|
||||
eventsHandler('delete', data.id);
|
||||
};
|
||||
const delayHandler = () => {
|
||||
eventsHandler('add', { type: 'delay', order: index + 1 });
|
||||
};
|
||||
const { index, data, actionHandler } = props;
|
||||
|
||||
return (
|
||||
<Draggable key={data.id} draggableId={data.id} index={index}>
|
||||
@@ -29,13 +19,8 @@ export default function BlockBlock(props) {
|
||||
<FiMoreVertical />
|
||||
</span>
|
||||
<div className={style.actionOverlay}>
|
||||
<DeleteIconBtn clickhandler={deleteHandler} />
|
||||
<ActionButtons
|
||||
showAdd
|
||||
addHandler={addHandler}
|
||||
showDelay
|
||||
delayHandler={delayHandler}
|
||||
/>
|
||||
<DeleteIconBtn actionHandler={actionHandler} />
|
||||
<ActionButtons showAdd showDelay actionHandler={actionHandler} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/* ============= COMMON ============= */
|
||||
|
||||
.block {
|
||||
margin: 0.2em 0;
|
||||
padding: 0.2em 0.5em;
|
||||
box-sizing: border-box;
|
||||
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
font-size: 15px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 2em auto;
|
||||
align-content: center;
|
||||
align-items: baseline;
|
||||
gap: 0.5em;
|
||||
|
||||
background-color: #805ad5;
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(107, 70, 193, 0.4) 0%,
|
||||
rgba(128, 90, 213, 0.4) 20%,
|
||||
rgba(107, 70, 193, 0.15) 21%
|
||||
);
|
||||
}
|
||||
|
||||
/* ================ DRAG ================ */
|
||||
.drag {
|
||||
color: rgba(255, 255, 255, 0.67);
|
||||
align-self: center;
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
/* ============== ACTION ================ */
|
||||
.actionOverlay {
|
||||
display: flex;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-content: center;
|
||||
align-self: flex-start;
|
||||
padding-top: 0.2em;
|
||||
gap: 0.5em;
|
||||
opacity: 0.8;
|
||||
transition: linear 0.1s;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.block:hover > .actionOverlay {
|
||||
opacity: 1;
|
||||
transition: linear 0.1s;
|
||||
}
|
||||
|
||||
/* NA */
|
||||
.actionOverlay:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -1,27 +1,14 @@
|
||||
import { Draggable } from 'react-beautiful-dnd';
|
||||
import { FiMoreVertical } from 'react-icons/fi';
|
||||
import { millisToMinutes } from '../../../common/dateConfig';
|
||||
import TimeInput from '../../../common/input/TimeInput';
|
||||
import style from './Block.module.css';
|
||||
import ActionButtons from './ActionButtons';
|
||||
import DeleteIconBtn from '../../../common/components/buttons/DeleteIconBtn';
|
||||
import ApplyIconBtn from '../../../common/components/buttons/ApplyIconBtn';
|
||||
import DelayInput from '../../../common/input/DelayInput';
|
||||
import style from './DelayBlock.module.css';
|
||||
|
||||
export default function DelayBlock(props) {
|
||||
const { data, eventsHandler, index } = props;
|
||||
|
||||
const addHandler = () => {
|
||||
eventsHandler('add', { type: 'event', order: index + 1 });
|
||||
};
|
||||
|
||||
const submitHandler = (value) => {
|
||||
// convert to ms and patch
|
||||
eventsHandler('patch', { id: data.id, duration: value * 1000 * 60 });
|
||||
};
|
||||
|
||||
const deleteHandler = () => {
|
||||
eventsHandler('delete', data.id);
|
||||
};
|
||||
const { eventsHandler, data, index, actionHandler } = props;
|
||||
|
||||
const applyDelayHandler = () => {
|
||||
eventsHandler('applyDelay', { id: data.id, duration: data.duration });
|
||||
@@ -29,6 +16,7 @@ export default function DelayBlock(props) {
|
||||
|
||||
let delayValue =
|
||||
data.duration != null ? millisToMinutes(data.duration) : undefined;
|
||||
|
||||
return (
|
||||
<Draggable key={data.id} draggableId={data.id} index={index}>
|
||||
{(provided) => (
|
||||
@@ -40,11 +28,15 @@ export default function DelayBlock(props) {
|
||||
<span className={style.drag} {...provided.dragHandleProps}>
|
||||
<FiMoreVertical />
|
||||
</span>
|
||||
<TimeInput value={delayValue} submitHandler={submitHandler} />
|
||||
<DelayInput
|
||||
className={style.input}
|
||||
value={delayValue}
|
||||
actionHandler={actionHandler}
|
||||
/>
|
||||
<div className={style.actionOverlay}>
|
||||
<ApplyIconBtn clickhandler={applyDelayHandler} />
|
||||
<DeleteIconBtn clickhandler={deleteHandler} />
|
||||
<ActionButtons showAdd addHandler={addHandler} />
|
||||
<DeleteIconBtn actionHandler={actionHandler} />
|
||||
<ActionButtons showAdd actionHandler={actionHandler} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/* ============= COMMON ============= */
|
||||
|
||||
.delay {
|
||||
margin: 0.2em 0;
|
||||
padding: 0.2em 0.5em;
|
||||
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
font-size: 15px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 2em 1fr auto;
|
||||
grid-template-areas: 'drag inpt btns';
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
align-items: baseline;
|
||||
gap: 0.5em;
|
||||
|
||||
background-color: #ecc94b;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(214, 158, 46, 0.4) 0%,
|
||||
rgba(236, 201, 75, 0.4) 20%,
|
||||
rgba(214, 158, 46, 0.07) 21%
|
||||
);
|
||||
}
|
||||
|
||||
/* ================ DRAG ================ */
|
||||
|
||||
.drag {
|
||||
grid-area: drag;
|
||||
color: rgba(255, 255, 255, 0.67);
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* =============== INPUT ================ */
|
||||
|
||||
.input {
|
||||
grid-area: inpt;
|
||||
}
|
||||
|
||||
/* ============== ACTION ================ */
|
||||
|
||||
.actionOverlay {
|
||||
grid-area: btns;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-content: center;
|
||||
align-self: flex-start;
|
||||
padding-top: 0.2em;
|
||||
gap: 0.5em;
|
||||
opacity: 0.8;
|
||||
transition: linear 0.1s;
|
||||
}
|
||||
|
||||
.delay:hover > .actionOverlay {
|
||||
opacity: 1;
|
||||
transition: linear 0.1s;
|
||||
}
|
||||
|
||||
/* NA */
|
||||
.actionOverlay:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
import { FiChevronDown, FiChevronUp, FiMoreVertical } from 'react-icons/fi';
|
||||
import { memo, useCallback, useEffect, useState } from 'react';
|
||||
import { showErrorToast } from '../../../common/helpers/toastManager';
|
||||
import { Draggable } from 'react-beautiful-dnd';
|
||||
import EventTimes from '../../../common/components/eventTimes/EventTimes';
|
||||
// import common from './Block.module.css';
|
||||
import style from './EventBlock.module.css';
|
||||
import EditableText from '../../../common/input/EditableText';
|
||||
import DelayValue from '../../../common/input/DelayValue';
|
||||
import ActionButtons from './ActionButtons';
|
||||
import VisibleIconBtn from '../../../common/components/buttons/VisibleIconBtn';
|
||||
import DeleteIconBtn from '../../../common/components/buttons/DeleteIconBtn';
|
||||
|
||||
const areEqual = (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.data.revision === nextProps.data.revision &&
|
||||
prevProps.selected === nextProps.selected &&
|
||||
prevProps.next === nextProps.next &&
|
||||
prevProps.index === nextProps.index &&
|
||||
prevProps.delay === nextProps.delay
|
||||
);
|
||||
};
|
||||
|
||||
const EventBlock = (props) => {
|
||||
const { data, selected, next, delay, index, eventsHandler } = props;
|
||||
|
||||
const [more, setMore] = useState(true);
|
||||
const [visible, setVisible] = useState(data.isPublic || false);
|
||||
|
||||
// Set visibility indicator
|
||||
useEffect(() => {
|
||||
setVisible(data.isPublic);
|
||||
}, [data.isPublic]);
|
||||
|
||||
// Create / delete new events
|
||||
const actionHandler = (action) => {
|
||||
switch (action) {
|
||||
case 'event':
|
||||
eventsHandler('add', { type: 'event', order: index + 1 });
|
||||
break;
|
||||
case 'delay':
|
||||
eventsHandler('add', { type: 'delay', order: index + 1 });
|
||||
break;
|
||||
case 'block':
|
||||
eventsHandler('add', { type: 'block', order: index + 1 });
|
||||
break;
|
||||
case 'delete':
|
||||
eventsHandler('delete', data.id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Update data in fields
|
||||
const updateValues = (field, value) => {
|
||||
// validate field
|
||||
if (field in data) {
|
||||
// create object with new field
|
||||
const newData = { id: data.id, [field]: value };
|
||||
|
||||
// request update in parent
|
||||
eventsHandler('patch', newData);
|
||||
} else {
|
||||
showErrorToast('Field Error: ' + field);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTitleSubmit = (v) => {
|
||||
updateValues('title', v);
|
||||
};
|
||||
|
||||
const handleSubtitleSubmit = (v) => {
|
||||
updateValues('subtitle', v);
|
||||
};
|
||||
|
||||
const handlePresenterSubmit = (v) => {
|
||||
updateValues('presenter', v);
|
||||
};
|
||||
|
||||
const handleNoteSubmit = (v) => {
|
||||
updateValues('note', v);
|
||||
};
|
||||
|
||||
const handleVisibleToggle = useCallback(() => {
|
||||
const viz = !data.isPublic || !visible;
|
||||
updateValues('isPublic', viz);
|
||||
}, [data.isPublic, visible, updateValues]);
|
||||
|
||||
// TODO: inside useEffect()
|
||||
const isSelected = selected ? style.active : '';
|
||||
const isExpanded = more ? style.expanded : style.collapsed;
|
||||
const classSelect = ` ${style.event} ${isExpanded} ${isSelected}`;
|
||||
|
||||
return (
|
||||
<Draggable key={data.id} draggableId={data.id} index={index}>
|
||||
{(provided) => (
|
||||
<div
|
||||
className={classSelect}
|
||||
{...provided.draggableProps}
|
||||
ref={provided.innerRef}
|
||||
>
|
||||
<span className={style.drag} {...provided.dragHandleProps}>
|
||||
<FiMoreVertical />
|
||||
</span>
|
||||
<div className={style.indicators}>
|
||||
<div className={next ? style.next : style.nextDisabled}>Next</div>
|
||||
<DelayValue delay={delay} />
|
||||
</div>
|
||||
<EventTimes
|
||||
updateValues={updateValues}
|
||||
timeStart={data.timeStart}
|
||||
timeEnd={data.timeEnd}
|
||||
delay={delay}
|
||||
className={style.time}
|
||||
/>
|
||||
{more && <div className={style.start}>Start Time</div>}
|
||||
{more && <div className={style.end}>End Time</div>}
|
||||
{more && <div className={style.duration}>Duration</div>}
|
||||
|
||||
<div className={style.rowDetailed}>
|
||||
{more ? (
|
||||
<div className={style.titleContainer}>
|
||||
<EditableText
|
||||
label='Title'
|
||||
defaultValue={data.title}
|
||||
placeholder='Add Title'
|
||||
submitHandler={handleTitleSubmit}
|
||||
underlined
|
||||
/>
|
||||
<EditableText
|
||||
label='Presenter'
|
||||
defaultValue={data.presenter}
|
||||
placeholder='Add Presenter name'
|
||||
submitHandler={handlePresenterSubmit}
|
||||
underlined
|
||||
/>
|
||||
<EditableText
|
||||
label='Subtitle'
|
||||
defaultValue={data.subtitle}
|
||||
placeholder='Add Subtitle'
|
||||
submitHandler={handleSubtitleSubmit}
|
||||
underlined
|
||||
/>
|
||||
<EditableText
|
||||
label='note'
|
||||
defaultValue={data.note}
|
||||
placeholder='Add note'
|
||||
submitHandler={handleNoteSubmit}
|
||||
underlined
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className={style.titleContainer}>
|
||||
<EditableText
|
||||
label='Title'
|
||||
defaultValue={data.title}
|
||||
placeholder='Add Title'
|
||||
submitHandler={handleTitleSubmit}
|
||||
isTight
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={style.more} onClick={() => setMore(!more)}>
|
||||
{more ? <FiChevronUp /> : <FiChevronDown />}
|
||||
</div>
|
||||
</div>
|
||||
<div className={style.actionOverlay}>
|
||||
<VisibleIconBtn
|
||||
clickhandler={handleVisibleToggle}
|
||||
active={visible}
|
||||
/>
|
||||
<DeleteIconBtn actionHandler={actionHandler} />
|
||||
<ActionButtons
|
||||
showAdd
|
||||
showDelay
|
||||
showBlock
|
||||
actionHandler={actionHandler}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(EventBlock, areEqual);
|
||||
// export default EventBlock;
|
||||
@@ -1,14 +1,15 @@
|
||||
import Icon from '@chakra-ui/icon';
|
||||
import { FiChevronDown, FiChevronUp, FiMoreVertical } from 'react-icons/fi';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { showErrorToast } from '../../../common/helpers/toastManager';
|
||||
import { memo, useState } from 'react';
|
||||
import { Draggable } from 'react-beautiful-dnd';
|
||||
import EventTimes from '../../../common/components/eventTimes/EventTimes';
|
||||
import style from './Block.module.css';
|
||||
import EventTimesVertical from '../../../common/components/eventTimes/EventTimesVertical';
|
||||
import EditableText from '../../../common/input/EditableText';
|
||||
import DelayValue from '../../../common/input/DelayValue';
|
||||
import ActionButtons from './ActionButtons';
|
||||
import VisibleIconBtn from '../../../common/components/buttons/VisibleIconBtn';
|
||||
import DeleteIconBtn from '../../../common/components/buttons/DeleteIconBtn';
|
||||
import { millisToMinutes } from '../../../common/dateConfig';
|
||||
import style from './EventBlock.module.css';
|
||||
|
||||
const areEqual = (prevProps, nextProps) => {
|
||||
return (
|
||||
@@ -20,147 +21,181 @@ const areEqual = (prevProps, nextProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const ExpandedBlock = (props) => {
|
||||
const { provided, data, next, delay, delayValue, actionHandler } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={style.drag} {...provided.dragHandleProps}>
|
||||
<FiMoreVertical />
|
||||
</span>
|
||||
|
||||
<div className={style.indicators}>
|
||||
<span className={next ? style.next : style.nextDisabled}>Next</span>
|
||||
{delayValue != null && (
|
||||
<span className={style.delayValue}>+ {delayValue}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className={style.timeExpanded}>
|
||||
<EventTimesVertical
|
||||
actionHandler={actionHandler}
|
||||
timeStart={data.timeStart}
|
||||
timeEnd={data.timeEnd}
|
||||
delay={delay}
|
||||
className={style.time}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={style.titleContainer}>
|
||||
<EditableText
|
||||
label='Title'
|
||||
defaultValue={data.title}
|
||||
placeholder='Add Title'
|
||||
submitHandler={(v) =>
|
||||
actionHandler('update', { field: 'title', value: v })
|
||||
}
|
||||
/>
|
||||
<EditableText
|
||||
label='Presenter'
|
||||
defaultValue={data.presenter}
|
||||
placeholder='Add Presenter name'
|
||||
submitHandler={(v) =>
|
||||
actionHandler('update', { field: 'presenter', value: v })
|
||||
}
|
||||
/>
|
||||
<EditableText
|
||||
label='Subtitle'
|
||||
defaultValue={data.subtitle}
|
||||
placeholder='Add Subtitle'
|
||||
submitHandler={(v) =>
|
||||
actionHandler('update', { field: 'subtitle', value: v })
|
||||
}
|
||||
/>
|
||||
<EditableText
|
||||
label='Note'
|
||||
defaultValue={data.note}
|
||||
placeholder='Add Note'
|
||||
submitHandler={(v) =>
|
||||
actionHandler('update', { field: 'note', value: v })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Icon
|
||||
className={style.more}
|
||||
as={FiChevronUp}
|
||||
marginTop='0.2em'
|
||||
gridArea='more'
|
||||
onClick={() => props.setExpanded(false)}
|
||||
/>
|
||||
<div className={style.actionOverlay}>
|
||||
<VisibleIconBtn actionHandler={actionHandler} active={data.isPublic} />
|
||||
<ActionButtons
|
||||
showAdd
|
||||
showDelay
|
||||
showBlock
|
||||
actionHandler={actionHandler}
|
||||
/>
|
||||
<DeleteIconBtn actionHandler={actionHandler} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const CollapsedBlock = (props) => {
|
||||
const { provided, data, next, delay, delayValue, actionHandler } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={style.drag} {...provided.dragHandleProps}>
|
||||
<FiMoreVertical />
|
||||
</span>
|
||||
|
||||
<div className={style.indicators}>
|
||||
<span className={next ? style.next : style.nextDisabled}>Next</span>
|
||||
{delayValue != null && (
|
||||
<span className={style.delayValue}>+ {delayValue}</span>
|
||||
)}
|
||||
</div>
|
||||
<EventTimes
|
||||
actionHandler={actionHandler}
|
||||
timeStart={data.timeStart}
|
||||
timeEnd={data.timeEnd}
|
||||
delay={delay}
|
||||
className={style.time}
|
||||
/>
|
||||
<div className={style.titleContainer}>
|
||||
<EditableText
|
||||
label='Title'
|
||||
defaultValue={data.title}
|
||||
placeholder='Add Title'
|
||||
submitHandler={(v) =>
|
||||
actionHandler('update', { field: 'title', value: v })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Icon
|
||||
className={style.more}
|
||||
as={FiChevronDown}
|
||||
marginTop='0.2em'
|
||||
gridArea='more'
|
||||
onClick={() => props.setExpanded(true)}
|
||||
/>
|
||||
<div className={style.actionOverlay}>
|
||||
<VisibleIconBtn actionHandler={actionHandler} active={data.isPublic} />
|
||||
<ActionButtons
|
||||
showAdd
|
||||
showDelay
|
||||
showBlock
|
||||
actionHandler={actionHandler}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const EventBlock = (props) => {
|
||||
const { data, selected, next, delay, index, eventsHandler } = props;
|
||||
const { data, selected, delay, index, actionHandler } = props;
|
||||
|
||||
const [more, setMore] = useState(true);
|
||||
const [visible, setVisible] = useState(data.isPublic || false);
|
||||
const [expanded, setExpanded] = useState(true);
|
||||
|
||||
// Set visibility indicator
|
||||
useEffect(() => {
|
||||
setVisible(data.isPublic);
|
||||
}, [data.isPublic]);
|
||||
// TODO: should this go inside useEffect()
|
||||
// Would I then need to add this to state?
|
||||
const isSelected = selected ? style.active : '';
|
||||
const isExpanded = expanded ? style.expanded : style.collapsed;
|
||||
const classSelect = `${style.event} ${isExpanded} ${isSelected}`;
|
||||
|
||||
const updateValues = (field, value) => {
|
||||
// validate field
|
||||
if (field in data) {
|
||||
// create object with new field
|
||||
const newData = { id: data.id, [field]: value };
|
||||
|
||||
// request update in parent
|
||||
eventsHandler('patch', newData);
|
||||
} else {
|
||||
showErrorToast('Field Error: ' + field);
|
||||
}
|
||||
};
|
||||
|
||||
const addHandler = () => {
|
||||
eventsHandler('add', { type: 'event', order: index + 1 });
|
||||
};
|
||||
const delayHandler = () => {
|
||||
eventsHandler('add', { type: 'delay', order: index + 1 });
|
||||
};
|
||||
const blockHandler = () => {
|
||||
eventsHandler('add', { type: 'block', order: index + 1 });
|
||||
};
|
||||
const deleteHandler = () => {
|
||||
eventsHandler('delete', data.id);
|
||||
};
|
||||
|
||||
const handleTitleSubmit = (v) => {
|
||||
updateValues('title', v);
|
||||
};
|
||||
|
||||
const handleSubtitleSubmit = (v) => {
|
||||
updateValues('subtitle', v);
|
||||
};
|
||||
|
||||
const handlePresenterSubmit = (v) => {
|
||||
updateValues('presenter', v);
|
||||
};
|
||||
|
||||
const handleNoteSubmit = (v) => {
|
||||
updateValues('note', v);
|
||||
};
|
||||
|
||||
const handleVisibleToggle = () => {
|
||||
let viz = !data.isPublic || !visible;
|
||||
updateValues('isPublic', viz);
|
||||
};
|
||||
// Calculate delay in min
|
||||
const delayValue = delay > 0 ? millisToMinutes(delay) : null;
|
||||
|
||||
return (
|
||||
<Draggable key={data.id} draggableId={data.id} index={index}>
|
||||
{(provided) => (
|
||||
<div
|
||||
className={selected ? style.eventRowActive : style.eventRow}
|
||||
className={classSelect}
|
||||
{...provided.draggableProps}
|
||||
ref={provided.innerRef}
|
||||
>
|
||||
<span className={style.drag} {...provided.dragHandleProps}>
|
||||
<FiMoreVertical />
|
||||
</span>
|
||||
<div className={style.indicators}>
|
||||
<div className={next ? style.next : style.nextDisabled}>Next</div>
|
||||
<DelayValue delay={delay} />
|
||||
</div>
|
||||
<EventTimes
|
||||
updateValues={updateValues}
|
||||
timeStart={data.timeStart}
|
||||
timeEnd={data.timeEnd}
|
||||
delay={delay}
|
||||
/>
|
||||
<div className={style.rowDetailed}>
|
||||
{more ? (
|
||||
<div className={style.detailedContainer}>
|
||||
<EditableText
|
||||
label='Title'
|
||||
defaultValue={data.title}
|
||||
placeholder='Add Title'
|
||||
submitHandler={handleTitleSubmit}
|
||||
underlined
|
||||
/>
|
||||
<EditableText
|
||||
label='Presenter'
|
||||
defaultValue={data.presenter}
|
||||
placeholder='Add Presenter name'
|
||||
submitHandler={handlePresenterSubmit}
|
||||
underlined
|
||||
/>
|
||||
<EditableText
|
||||
label='Subtitle'
|
||||
defaultValue={data.subtitle}
|
||||
placeholder='Add Subtitle'
|
||||
submitHandler={handleSubtitleSubmit}
|
||||
underlined
|
||||
/>
|
||||
<EditableText
|
||||
label='note'
|
||||
defaultValue={data.note}
|
||||
placeholder='Add note'
|
||||
submitHandler={handleNoteSubmit}
|
||||
underlined
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className={style.titleContainer}>
|
||||
<EditableText
|
||||
label='Title'
|
||||
defaultValue={data.title}
|
||||
placeholder='Add Title'
|
||||
submitHandler={handleTitleSubmit}
|
||||
isTight
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={style.more} onClick={() => setMore(!more)}>
|
||||
{more ? <FiChevronUp /> : <FiChevronDown />}
|
||||
</div>
|
||||
</div>
|
||||
<div className={style.actionOverlay}>
|
||||
<VisibleIconBtn
|
||||
clickhandler={handleVisibleToggle}
|
||||
active={visible}
|
||||
{expanded ? (
|
||||
<ExpandedBlock
|
||||
provided={provided}
|
||||
data={data}
|
||||
next={props.next}
|
||||
delay={delay}
|
||||
delayValue={delayValue}
|
||||
actionHandler={actionHandler}
|
||||
setExpanded={setExpanded}
|
||||
/>
|
||||
<DeleteIconBtn clickhandler={deleteHandler} />
|
||||
<ActionButtons
|
||||
showAdd
|
||||
addHandler={addHandler}
|
||||
showDelay
|
||||
delayHandler={delayHandler}
|
||||
showBlock
|
||||
blockHandler={blockHandler}
|
||||
) : (
|
||||
<CollapsedBlock
|
||||
provided={provided}
|
||||
data={data}
|
||||
next={props.next}
|
||||
delay={delay}
|
||||
delayValue={delayValue}
|
||||
actionHandler={actionHandler}
|
||||
setExpanded={setExpanded}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
@@ -168,4 +203,3 @@ const EventBlock = (props) => {
|
||||
};
|
||||
|
||||
export default memo(EventBlock, areEqual);
|
||||
// export default EventBlock;
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
/* ============= COMMON ============= */
|
||||
.event {
|
||||
margin: 0.2em 0;
|
||||
padding: 0.2em 0.5em;
|
||||
box-sizing: border-box;
|
||||
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
font-size: 15px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
|
||||
display: grid;
|
||||
gap: 0.5em;
|
||||
|
||||
background-color: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: rgba(255, 255, 255, 0.36);
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
#00000000 10%,
|
||||
#4bffab10 97%,
|
||||
#227c52aa 98%,
|
||||
#4bffabcc 100%
|
||||
);
|
||||
}
|
||||
|
||||
.collapsed {
|
||||
grid-template-columns: 2em 2em auto auto 1fr auto 3.7em;
|
||||
grid-template-areas: 'drag indi time time text more btns';
|
||||
justify-content: center;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.expanded {
|
||||
grid-template-columns: 2em 2em 2em auto 1fr auto 3.7em;
|
||||
grid-template-rows: repeat(4, 1fr);
|
||||
grid-template-areas:
|
||||
'drag indi time time text more btns'
|
||||
'.... indi time time text .... btns'
|
||||
'.... indi time time text .... btns'
|
||||
'.... indi time time text .... btns';
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* ================ DRAG ================ */
|
||||
|
||||
.drag {
|
||||
grid-area: drag;
|
||||
color: rgba(255, 255, 255, 0.67);
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* ============= INDICATORS ============= */
|
||||
.indicators {
|
||||
grid-area: indi;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: flex-start;
|
||||
font-size: 0.75em;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.next,
|
||||
.nextDisabled {
|
||||
width: max-content;
|
||||
padding: 0 0.2em;
|
||||
color: #4bffab;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.next {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.nextDisabled {
|
||||
opacity: 0.05;
|
||||
}
|
||||
|
||||
.delayValue {
|
||||
color: #d69e2eaa;
|
||||
padding: 0 0.2em;
|
||||
text-align: center;
|
||||
align-self: flex-start;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.next:after,
|
||||
.delayValue:after {
|
||||
content: '\200b';
|
||||
}
|
||||
/* =============== TIMES ================ */
|
||||
|
||||
.time {
|
||||
grid-area: time;
|
||||
}
|
||||
|
||||
.start {
|
||||
grid-area: star;
|
||||
}
|
||||
|
||||
.end {
|
||||
grid-area: end;
|
||||
}
|
||||
|
||||
.duration {
|
||||
grid-area: dura;
|
||||
}
|
||||
|
||||
.timeExpanded {
|
||||
grid-area: time;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-items: flex-start;
|
||||
|
||||
background-color: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: 4px;
|
||||
padding: 0 0.5em 0.5em 0.5em;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 0.75em;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.calculatedTime {
|
||||
grid-area: time;
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
/* =============== TITLES =============== */
|
||||
|
||||
.titleContainer {
|
||||
grid-area: text;
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
align-self: flex-start;
|
||||
height: 100%;
|
||||
|
||||
background-color: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
/* ================ MORE ================ */
|
||||
|
||||
.more {
|
||||
cursor: pointer;
|
||||
}
|
||||
/* ============== ACTION ================ */
|
||||
|
||||
.actionOverlay {
|
||||
grid-area: btns;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-content: center;
|
||||
align-self: flex-start;
|
||||
padding-top: 0.2em;
|
||||
gap: 0.5em;
|
||||
opacity: 0.8;
|
||||
transition: linear 0.1s;
|
||||
}
|
||||
|
||||
.expanded > .actionOverlay {
|
||||
flex-direction: column;
|
||||
justify-self: flex-end;
|
||||
}
|
||||
|
||||
.event:hover > .actionOverlay {
|
||||
opacity: 1;
|
||||
transition: linear 0.1s;
|
||||
}
|
||||
|
||||
/* NA */
|
||||
.actionOverlay:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import DelayBlock from './DelayBlock';
|
||||
import BlockBlock from './BlockBlock';
|
||||
import EventBlock from './EventBlock';
|
||||
import { showErrorToast } from '../../../common/helpers/toastManager';
|
||||
|
||||
export default function EventListItem(props) {
|
||||
const {
|
||||
@@ -14,6 +15,46 @@ export default function EventListItem(props) {
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
// Create / delete new events
|
||||
const actionHandler = (action, payload) => {
|
||||
switch (action) {
|
||||
case 'event':
|
||||
eventsHandler('add', { type: 'event', order: index + 1 });
|
||||
break;
|
||||
case 'delay':
|
||||
eventsHandler('add', { type: 'delay', order: index + 1 });
|
||||
break;
|
||||
case 'block':
|
||||
eventsHandler('add', { type: 'block', order: index + 1 });
|
||||
break;
|
||||
case 'delete':
|
||||
eventsHandler('delete', data.id);
|
||||
break;
|
||||
case 'update':
|
||||
// Handles and filters update requests
|
||||
const { field, value } = payload;
|
||||
if (field === 'durationOverride') {
|
||||
// duration defines timeEnd
|
||||
let end = (data.timeStart += value);
|
||||
const newData = { id: data.id, timeEnd: end };
|
||||
|
||||
// request update in parent
|
||||
eventsHandler('patch', newData);
|
||||
} else if (field in data) {
|
||||
// create object with new field
|
||||
const newData = { id: data.id, [field]: value };
|
||||
|
||||
// request update in parent
|
||||
eventsHandler('patch', newData);
|
||||
} else {
|
||||
showErrorToast('Field Error: ' + field);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case 'event':
|
||||
return (
|
||||
@@ -22,17 +63,22 @@ export default function EventListItem(props) {
|
||||
data={data}
|
||||
selected={selected}
|
||||
next={next}
|
||||
eventsHandler={eventsHandler}
|
||||
actionHandler={actionHandler}
|
||||
delay={delay}
|
||||
/>
|
||||
);
|
||||
case 'block':
|
||||
return (
|
||||
<BlockBlock index={index} data={data} eventsHandler={eventsHandler} />
|
||||
<BlockBlock index={index} data={data} actionHandler={actionHandler} />
|
||||
);
|
||||
case 'delay':
|
||||
return (
|
||||
<DelayBlock index={index} data={data} eventsHandler={eventsHandler} />
|
||||
<DelayBlock
|
||||
index={index}
|
||||
data={data}
|
||||
eventsHandler={eventsHandler}
|
||||
actionHandler={actionHandler}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user