major: event block redesign

This commit is contained in:
cv
2021-05-10 21:31:16 +02:00
parent 3f598f3a7a
commit d24fb406af
23 changed files with 921 additions and 428 deletions
@@ -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;
}
+11 -19
View File
@@ -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;
+166 -132
View File
@@ -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;