v2 styling (#263)

* feat: implement progress bar on running event

* refactor: use event action hook

* refactor: align backend data

* style: v2 style tweaks

* style: v2 style tweaks + ts <Info>

* fix: fix delay increment calls

* style: v2 style tweaks + ts <Playback>

* style: v2 style tweaks + ts <EventEditor>

* style: v2 style tweaks + ts <MessageControl>

* fix: naming issue with message control socket endpoint

* chore: upgrade style dependencies

* style: v2 style tweaks + ts <MenuBar>

* style: v2 style tweaks + ts <RundownMenu>

* style: v2 style tweaks + ts <Playback>

* fix: add selected and next information to EventTimer

* style: v2 style tweaks + ts <EventBlock>

* style: v2 style tweaks + ts <DelayBlock>

* style: v2 style tweaks + ts <BlockBlock>

* style: v2 style tweaks + ts <QuickEntry>

* refactor: extract <TextInput> logic

* chore: upgrade build dependencies

* chore: folder structure refactor

* fix: issue with dependency path in electron

* chore: update version in demo db
This commit is contained in:
Carlos Valente
2022-11-27 14:56:09 +01:00
committed by GitHub
parent 3626b5b357
commit d486d78594
104 changed files with 3027 additions and 3217 deletions
@@ -1,6 +1,130 @@
@use '../../../theme/main' as *;
@use '../../../theme/v2Styles' as *;
@use '../blockMixins' as *;
.eventBlock {
@include block-styling;
display: grid;
grid-template-areas:
"binder ... ... ..."
"binder pb-actions times actions"
"binder pb-actions title title"
"binder pb-actions estatus estatus"
"binder ... ... ...";
grid-template-columns: $block-binder-width auto 1fr auto;
grid-template-rows: 4px 36px 36px 36px 4px;
align-items: center;
margin: 4px 2px;
padding-right: $block-clearance;
gap: 2px;
&.selected {
background-color: #101010;
}
&.hasCursor {
outline: 1px solid #779BE7;
}
&.skip {
border: 1px solid rgba(white, 0.03);
box-shadow: none;
.delayNote,
.eventTitle,
.eventNote,
.binder,
.eventTimers,
.eventStatus {
opacity: $opacity-disabled;
}
}
}
.binder {
grid-area: binder;
height: 100%;
display: grid;
place-content: center;
position: relative;
cursor: pointer;
border-radius: $block-border-radius 0 0 $block-border-radius;
background-color: #303030; // to override inline
color: $section-white;
font-size: 17px;
.drag {
@include drag-style;
position: absolute;
margin-top: 4px;
}
}
.playbackActions {
grid-area: pb-actions;
display: flex;
flex-direction: column;
margin: 0 8px;
gap: 6px;
}
.eventTimers {
grid-area: times;
display: flex;
align-items: center;
gap: $block-clearance;
height: 100%;
.delayNote {
font-size: 12px;
line-height: 14px;
color: #E69056;
}
}
.eventTitle {
grid-area: title;
display: block;
font-size: 18px;
&.noTitle {
.preview {
opacity: $opacity-disabled;
}
}
}
.eventActions {
grid-area: actions;
display: flex;
gap: $block-clearance;
justify-content: flex-end;
}
.eventOptions {
margin: $element-spacing 16px $element-spacing 0;
}
.progressBg {
grid-area: progb;
border-radius: 2px;
background-color: #282828;
opacity: 1;
height: 100%;
}
.progressBg.hidden {
opacity: 0;
}
.flip {
transform: rotateY(180deg);
}
.statusElements {
grid-area: estatus;
display: grid;
@@ -9,189 +133,40 @@
"progb progb";
gap: 2px;
grid-template-rows: auto 4px;
.eventNote {
grid-area: notes;
display: block;
font-size: 15px;
color: $notes-color;
line-height: 15px;
}
.eventStatus {
grid-area: status;
display: flex;
justify-content: flex-end;
gap: $element-spacing;
.statusIcon {
width: 24px;
height: 24px;
border-radius: 12px;
padding: 4px;
color: $text-gray-disabled;
}
.statusIcon.statusNext {
border: 1px solid transparent;
&.enabled {
color: $text-white;
background-color: $ontime-accent;
}
}
.statusIcon.statusDelay {
&.enabled {
color: $text-white;
background-color: $ontime-delay;
}
}
.statusIcon.statusPublic {
&.enabled {
color: $text-white;
background-color: $ontime-roll;
}
}
}
}
.eventBlock {
// layout
display: grid;
grid-template-areas:
"binder pb-actions times actions"
"binder pb-actions title title"
"binder pb-actions estatus estatus";
grid-template-columns: $binder-width auto 1fr auto;
grid-template-rows: 36px 36px 36px;
align-items: center;
margin: 4px 2px;
padding-right: $clearance;
// style - general
border-radius: $block-border-radius;
gap: 2px;
// style - colour
background-color: $bg-container-l2;
border: $border-l3;
// variant
&.selected {
background-color: $bg-container-l3;
}
&.skip {
background-color: $bg-gray-1000;
border: 1px solid transparent;
.eventTimers > .delayNote,
.eventTitle,
.eventNote {
color: $text-gray-disabled;
}
.binder,
.eventTimers,
.eventStatus {
opacity: $opacity-disabled;
}
}
.binder {
// layout
grid-area: binder;
height: 100%;
display: grid;
place-content: center;
position: relative;
cursor: pointer;
// style - general
border-radius: 3px 0 0 3px;
border-right: 1px solid $bg-gray-900;
// style - colour
background-color: $bg-container-l3; // override inline
color: $text-white;
.drag {
@include drag-style;
position: absolute;
}
}
.playbackActions {
// layout
grid-area: pb-actions;
display: flex;
flex-direction: column;
margin: $element-spacing $clearance $element-spacing $element-spacing;
// style - general
gap: 6px;
}
.eventTimers {
grid-area: times;
display: flex;
align-items: center;
gap: $clearance;
height: 100%;
.delayNote {
font-size: 11px;
color: $text-delay;
}
}
.eventTitle {
grid-area: title;
display: block;
font-size: 18px;
.eventTitle__preview {
width: 100%;
}
&.noTitle {
span {
opacity: $opacity-disabled;
}
}
input {
border-radius: 2px;
border: 1px;
}
}
.eventActions {
grid-area: actions;
display: flex;
gap: $element-spacing;
justify-content: flex-end;
}
.eventOptions {
margin: $element-spacing 16px $element-spacing 0;
}
}
.progressBg {
grid-area: progb;
border-radius: 1px;
background-color: $bg-container-l1;
opacity: 1;
// layout
height: 100%;
padding: 2px 0;
}
.progressBg.hidden {
opacity: 0;
}
.eventNote {
grid-area: notes;
display: block;
font-size: 13px;
color: $block-text-color;
line-height: 13px;
}
.eventStatus {
grid-area: status;
display: flex;
justify-content: flex-end;
gap: 8px;
.statusIcon {
width: 16px;
height: 16px;
color: #404040;
}
.statusNext.enabled {
color: $playback-start;
}
.statusDelay.enabled {
color: $ontime-delay;
}
.statusPublic.enabled {
color: $action-blue;
}
}
@@ -4,17 +4,16 @@ import { Editable, EditableInput, EditablePreview, Tooltip } from '@chakra-ui/re
import { FiUsers } from '@react-icons/all-files/fi/FiUsers';
import { IoOptions } from '@react-icons/all-files/io5/IoOptions';
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
import { IoPlayBackOutline } from '@react-icons/all-files/io5/IoPlayBackOutline';
import { IoPlayOutline } from '@react-icons/all-files/io5/IoPlayOutline';
import { IoPlaySkipForward } from '@react-icons/all-files/io5/IoPlaySkipForward';
import { IoReload } from '@react-icons/all-files/io5/IoReload';
import { IoRemoveCircle } from '@react-icons/all-files/io5/IoRemoveCircle';
import { IoRemoveCircleOutline } from '@react-icons/all-files/io5/IoRemoveCircleOutline';
import { IoReorderTwo } from '@react-icons/all-files/io5/IoReorderTwo';
import { IoReturnDownForward } from '@react-icons/all-files/io5/IoReturnDownForward';
import { IoTimerOutline } from '@react-icons/all-files/io5/IoTimerOutline';
import { editorEventId } from 'common/atoms/LocalEventSettings';
import TooltipActionBtn from 'common/components/buttons/TooltipActionBtn';
import { getAccessibleColour } from 'common/utils/styleUtils';
import { cx, getAccessibleColour } from 'common/utils/styleUtils';
import { useAtom } from 'jotai';
import { useEventAction } from '../../../common/hooks/useEventAction';
@@ -23,7 +22,7 @@ import { Playstate } from '../../../common/models/OntimeTypes';
import { tooltipDelayMid } from '../../../ontimeConfig';
import { EventItemActions } from '../RundownEntry';
import EventBlockActionMenu from './composite/EventBlockActionMenu';
import BlockActionMenu from './composite/BlockActionMenu';
import EventBlockProgressBar from './composite/EventBlockProgressBar';
import EventBlockTimers from './composite/EventBlockTimers';
@@ -31,9 +30,6 @@ import style from './EventBlock.module.scss';
const blockBtnStyle = {
size: 'sm',
colorScheme: 'white',
variant: 'ghost',
fontSize: '20px',
};
const tooltipProps = {
@@ -56,8 +52,9 @@ interface EventBlockProps {
next: boolean;
skip: boolean;
selected: boolean;
hasCursor: boolean;
playback?: Playstate;
actionHandler: (action: EventItemActions, payload: any) => void;
actionHandler: (action: EventItemActions, payload?: any) => void;
}
export default function EventBlock(props: EventBlockProps) {
@@ -77,6 +74,7 @@ export default function EventBlock(props: EventBlockProps) {
next,
skip = false,
selected,
hasCursor,
playback,
actionHandler,
} = props;
@@ -89,6 +87,7 @@ export default function EventBlock(props: EventBlockProps) {
const hasDelay = delay !== 0 && delay !== null;
// Todo: could I re-render the item without causing a state change here?
// ?? use refs instead?
useEffect(() => {
setBlockTitle(title);
}, [title]);
@@ -115,13 +114,18 @@ export default function EventBlock(props: EventBlockProps) {
playBtnStyles._hover = {};
}
const blockClasses = cx([
style.eventBlock,
skip ? style.skip : null,
selected ? style.selected : null,
hasCursor ? style.hasCursor : null,
]);
return (
<Draggable key={eventId} draggableId={eventId} index={index}>
{(provided) => (
<div
className={`${style.eventBlock} ${skip ? style.skip : ''} ${
selected ? style.selected : ''
}`}
className={blockClasses}
{...provided.draggableProps}
ref={provided.innerRef}
>
@@ -138,36 +142,38 @@ export default function EventBlock(props: EventBlockProps) {
</div>
<div className={style.playbackActions}>
<TooltipActionBtn
variant='ontime-subtle-white'
aria-label='Skip event'
tooltip='Skip event'
openDelay={tooltipDelayMid}
icon={skip ? <IoRemoveCircle /> : <IoRemoveCircleOutline />}
{...blockBtnStyle}
variant={skip ? 'solid' : 'ghost'}
clickHandler={() => actionHandler('update', { field: 'skip', value: !skip })}
tabIndex={-1}
disabled={selected}
/>
<TooltipActionBtn
variant='ontime-subtle-white'
aria-label='Load event'
tooltip='Load event'
openDelay={tooltipDelayMid}
icon={selected ? <IoPlayBackOutline /> : <IoReload />}
icon={<IoReload className={style.flip} />}
disabled={skip}
{...blockBtnStyle}
clickHandler={() => setEventPlayback.loadEvent(eventId)}
tabIndex={-1}
/>
<TooltipActionBtn
variant='ontime-subtle-white'
aria-label='Start event'
tooltip='Start event'
openDelay={tooltipDelayMid}
icon={eventIsPlaying ? <IoPlay /> : <IoPlayOutline />}
disabled={skip}
{...blockBtnStyle}
variant={eventIsPlaying ? 'solid' : 'ghost'}
clickHandler={() => setEventPlayback.startEvent(eventId)}
backgroundColor={eventIsPlaying ? '#58A151' : undefined}
_hover={{backgroundColor: eventIsPlaying ? '#58A151' : undefined}}
tabIndex={-1}
/>
</div>
@@ -186,7 +192,7 @@ export default function EventBlock(props: EventBlockProps) {
onChange={(value) => setBlockTitle(value)}
onSubmit={(value) => handleTitle(value)}
>
<EditablePreview className={style.eventTitle__preview} />
<EditablePreview className={style.preview} />
<EditableInput />
</Editable>
<div className={style.statusElements}>
@@ -195,54 +201,53 @@ export default function EventBlock(props: EventBlockProps) {
<EventBlockProgressBar playback={playback} />
</div>
<div className={style.eventStatus}>
<Tooltip label='Next event' isDisabled={!next} {...tooltipProps}>
<span
className={`${style.statusIcon} ${style.statusNext} ${next ? style.enabled : ''}`}
<Tooltip
label='Next event'
isDisabled={!next}
shouldWrapChildren {...tooltipProps}
>
<IoReturnDownForward />
</span>
<IoPlaySkipForward
className={`${style.statusIcon} ${style.statusNext} ${next ? style.enabled : ''}`} />
</Tooltip>
<Tooltip label='Event has delay' isDisabled={!hasDelay} {...tooltipProps}>
<span
className={`${style.statusIcon} ${style.statusDelay} ${
hasDelay ? style.enabled : ''
}`}
<Tooltip
label='Event has delay'
isDisabled={!hasDelay}
shouldWrapChildren {...tooltipProps}
>
<IoTimerOutline />
</span>
<IoTimerOutline className={`${style.statusIcon} ${style.statusDelay} ${
hasDelay ? style.enabled : ''
}`} />
</Tooltip>
<Tooltip
label={`${isPublic ? 'Event is public' : 'Event is private'}`}
{...tooltipProps}
shouldWrapChildren
>
<span
className={`${style.statusIcon} ${style.statusPublic} ${
<FiUsers className={`${style.statusIcon} ${style.statusPublic} ${
isPublic ? style.enabled : ''
}`}
>
<FiUsers />
</span>
}`} />
</Tooltip>
</div>
</div>
<div className={style.eventActions}>
<TooltipActionBtn
{...blockBtnStyle}
variant='ontime-subtle-white'
size='sm'
icon={<IoOptions />}
clickHandler={() => setOpenId((prev) => prev === eventId ? null : eventId)}
tooltip='Event options'
aria-label='Event options'
tabIndex={-1}
backgroundColor={openId === eventId ? '#ebedf0' : 'transparent'}
color={openId === eventId ? '#333' : '#ebedf0'}
_hover={{ bg: '#ebedf0', color: '#333' }}
backgroundColor={openId === eventId ? '#2B5ABC' : undefined}
color={openId === eventId ? 'white' : '#f6f6f6'}
/>
<EventBlockActionMenu
<BlockActionMenu
showAdd
showDelay
showBlock
showClone
enableDelete
actionHandler={actionHandler}
/>
</div>
@@ -0,0 +1,84 @@
import {
IconButton,
Menu,
MenuButton,
MenuDivider,
MenuItem,
MenuList,
Tooltip,
} from '@chakra-ui/react';
import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
import { IoDuplicateOutline } from '@react-icons/all-files/io5/IoDuplicateOutline';
import { IoRemoveCircleOutline } from '@react-icons/all-files/io5/IoRemoveCircleOutline';
import { IoTimerOutline } from '@react-icons/all-files/io5/IoTimerOutline';
import { IoTrashBinSharp } from '@react-icons/all-files/io5/IoTrashBinSharp';
import { tooltipDelayMid } from '../../../../ontimeConfig';
import { EventItemActions } from '../../RundownEntry';
interface BlockActionMenuProps {
showAdd?: boolean;
showDelay?: boolean;
showBlock?: boolean;
enableDelete?: boolean;
showClone?: boolean;
actionHandler: (action: EventItemActions, payload?: any) => void;
className?: string;
}
export default function BlockActionMenu(props: BlockActionMenuProps) {
const { showAdd, showDelay, showBlock, enableDelete, showClone, actionHandler, className } = props;
return (
<Menu isLazy lazyBehavior='unmount' variant='ontime-on-dark'>
<Tooltip label='Add ...' openDelay={tooltipDelayMid}>
<MenuButton
as={IconButton}
aria-label='Event options'
icon={<IoAdd />}
tabIndex={-1}
variant='ontime-subtle'
color='#f6f6f6'
size='sm'
className={className}
/>
</Tooltip>
<MenuList>
<MenuItem icon={<IoAdd />} onClick={() => actionHandler('event')} isDisabled={!showAdd}>
Add Event after
</MenuItem>
<MenuItem
icon={<IoTimerOutline />}
onClick={() => actionHandler('delay')}
isDisabled={!showDelay}
>
Add Delay after
</MenuItem>
<MenuItem
icon={<IoRemoveCircleOutline />}
onClick={() => actionHandler('block')}
isDisabled={!showBlock}
>
Add Block after
</MenuItem>
{showClone && (
<MenuItem
icon={<IoDuplicateOutline />}
onClick={() => actionHandler('clone')}
isDisabled={!showBlock}
>
Clone event
</MenuItem>
)}
<MenuDivider />
<MenuItem
icon={<IoTrashBinSharp />}
onClick={() => actionHandler('delete')}
isDisabled={!enableDelete}
color='#D20300'
>
Delete event
</MenuItem>
</MenuList>
</Menu>
);
}
@@ -1,87 +0,0 @@
import {
Divider,
IconButton,
Menu,
MenuButton,
MenuItem,
MenuList,
Tooltip,
} from '@chakra-ui/react';
import { FiMinusCircle } from '@react-icons/all-files/fi/FiMinusCircle';
import { FiPlus } from '@react-icons/all-files/fi/FiPlus';
import { FiTrash2 } from '@react-icons/all-files/fi/FiTrash2';
import { IoDuplicateOutline } from '@react-icons/all-files/io5/IoDuplicateOutline';
import { IoTimerOutline } from '@react-icons/all-files/io5/IoTimerOutline';
import PropTypes from 'prop-types';
import { tooltipDelayMid } from '../../../../ontimeConfig';
export default function EventBlockActionMenu(props) {
const { showAdd, showDelay, showBlock, showClone, actionHandler } = props;
const menuStyle = {
color: '#000000',
backgroundColor: 'rgba(255,255,255,1)',
};
const blockBtnStyle = {
size: 'sm',
variant: 'outline',
colorScheme: 'whiteAlpha',
};
return (
<Menu isLazy lazyBehavior='unmount'>
<Tooltip label='Add ...' delay={tooltipDelayMid}>
<MenuButton as={IconButton} aria-label='Options' icon={<FiPlus />}
tabIndex={-1} {...blockBtnStyle} />
</Tooltip>
<MenuList style={menuStyle}>
<MenuItem icon={<FiPlus />} onClick={() => actionHandler('event')} isDisabled={!showAdd}>
Add Event after
</MenuItem>
<MenuItem
icon={<IoTimerOutline />}
onClick={() => actionHandler('delay')}
isDisabled={!showDelay}
>
Add Delay after
</MenuItem>
<MenuItem
icon={<FiMinusCircle />}
onClick={() => actionHandler('block')}
isDisabled={!showBlock}
>
Add Block after
</MenuItem>
{showClone && (
<MenuItem
icon={<IoDuplicateOutline />}
onClick={() => actionHandler('clone')}
isDisabled={!showBlock}
>
Clone event
</MenuItem>
)}
<Divider />
<MenuItem
icon={<FiTrash2 />}
onClick={() => actionHandler('delete')}
isDisabled={!showBlock}
color='red.500'
>
Delete event
</MenuItem>
</MenuList>
</Menu>
);
}
EventBlockActionMenu.propTypes = {
showAdd: PropTypes.bool,
showDelay: PropTypes.bool,
showBlock: PropTypes.bool,
showClone: PropTypes.bool,
actionHandler: PropTypes.func,
};
@@ -1,5 +1,5 @@
import { useCallback, useContext } from 'react';
import TimeInput from 'common/components/input/TimeInput';
import TimeInput from 'common/components/input/time-input/TimeInput';
import { LoggingContext } from 'common/context/LoggingContext';
import { millisToMinutes } from 'common/utils/dateConfig';
import { stringFromMillis } from 'common/utils/time';