mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-13 19:33:46 +00:00
styles: v2 (#229)
* style: revise selected style * style: revise editor styles * style: revise viewer styles * chore: update tests
This commit is contained in:
@@ -26,9 +26,10 @@ export default function ActionButtons(props: ActionButtonProps) {
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
aria-label='Options'
|
||||
size='xs'
|
||||
size='sm'
|
||||
icon={<FiPlus />}
|
||||
colorScheme='blue'
|
||||
variant='ghost'
|
||||
/>
|
||||
</Tooltip>
|
||||
<MenuList style={menuStyle}>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function TooltipLoadingActionBtn(props) {
|
||||
const { clickHandler, icon, color, size = 'xs', tooltip, ...rest } = props;
|
||||
const { clickHandler, icon, size = 'xs', tooltip, ...rest } = props;
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
@@ -29,7 +29,6 @@ export default function TooltipLoadingActionBtn(props) {
|
||||
TooltipLoadingActionBtn.propTypes = {
|
||||
clickHandler: PropTypes.func,
|
||||
icon: PropTypes.element,
|
||||
color: PropTypes.string,
|
||||
size: PropTypes.oneOf(['xs', 'sm', 'md', 'lg']),
|
||||
tooltip: PropTypes.string,
|
||||
};
|
||||
|
||||
@@ -5,10 +5,10 @@ import PropTypes from 'prop-types';
|
||||
import style from './CollapseBar.module.scss';
|
||||
|
||||
export default function CollapseBar(props) {
|
||||
const { title = 'Collapse bar', isCollapsed, onClick, roll } = props;
|
||||
const { title = 'Collapse bar', isCollapsed, onClick } = props;
|
||||
|
||||
return (
|
||||
<div className={roll ? style.headerRoll : style.header}>
|
||||
<div className={style.header}>
|
||||
{title}
|
||||
<Icon
|
||||
className={isCollapsed ? style.moreCollapsed : style.moreExpanded}
|
||||
@@ -22,5 +22,4 @@ CollapseBar.propTypes = {
|
||||
title: PropTypes.string,
|
||||
isCollapsed: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
roll: PropTypes.bool,
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
input[type="color"] {
|
||||
appearance: none;
|
||||
background-color: $action-blue;
|
||||
cursor: pointer;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
|
||||
@@ -133,7 +133,7 @@ export default function TimeInput(props) {
|
||||
size='sm'
|
||||
icon={<IoLink style={{ transform: 'rotate(-45deg)' }} />}
|
||||
aria-label='automate'
|
||||
colorScheme='blue'
|
||||
colorScheme='whiteAlpha'
|
||||
style={{ borderRadius: '2px', width: 'min-content' }}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
.emptyContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
color: $bg-black-300;
|
||||
color: $bg-black-200;
|
||||
|
||||
.empty {
|
||||
width: 100%;
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('Message Control input blocks', () => {
|
||||
// Presenter dialog and button
|
||||
// substring match, ignore case
|
||||
render(<MockMessageControl />);
|
||||
expect(screen.getByPlaceholderText(/presenter/i)).toBeInTheDocument();
|
||||
expect(screen.getByPlaceholderText(/stage/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Public dialog', async () => {
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function InputRow(props: InputRowProps) {
|
||||
value={inputText}
|
||||
placeholder={placeholder}
|
||||
className={style.inline}
|
||||
color={text === '' ? '#666' : 'inherit'}
|
||||
color={text === '' ? '#505050' : 'inherit'}
|
||||
>
|
||||
<EditablePreview className={`${style.padleft} ${style.fullWidth}`} />
|
||||
<EditableInput className={style.padleft} />
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function MessageControl() {
|
||||
<div className={style.messageContainer}>
|
||||
<InputRow
|
||||
label='Timer screen message'
|
||||
placeholder='only the presenter screens see this'
|
||||
placeholder='Shown in stage timer'
|
||||
text={data.presenter.text}
|
||||
visible={data.presenter.visible}
|
||||
changeHandler={(newValue) => setMessage.presenterText(newValue)}
|
||||
@@ -25,7 +25,7 @@ export default function MessageControl() {
|
||||
/>
|
||||
<InputRow
|
||||
label='Public screen message'
|
||||
placeholder='public screens will render this'
|
||||
placeholder='Shown in public screens'
|
||||
text={data.public.text}
|
||||
visible={data.public.visible}
|
||||
changeHandler={(newValue) => setMessage.publicText(newValue)}
|
||||
@@ -33,7 +33,7 @@ export default function MessageControl() {
|
||||
/>
|
||||
<InputRow
|
||||
label='Lower third message'
|
||||
placeholder='visible in lower third screen'
|
||||
placeholder='Shown in lower third'
|
||||
text={data.lower.text}
|
||||
visible={data.lower.visible}
|
||||
changeHandler={(newValue) => setMessage.lowerText(newValue)}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
.messageContainer,
|
||||
.onAirToggle {
|
||||
@include main-container;
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
padding: 0.5em;
|
||||
@@ -47,7 +46,6 @@
|
||||
}
|
||||
|
||||
.inline {
|
||||
background-color: $light-bg-transparent;
|
||||
border-color: $action-blue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ import style from '../../editors/Editor.module.scss';
|
||||
|
||||
export default function MessageControlExport() {
|
||||
return (
|
||||
<Box className={style.messages}>
|
||||
<h1>Messages Control</h1>
|
||||
<Box className={style.messages} data-testid="panel-messages-control">
|
||||
<FiArrowUpRight className={style.corner} onClick={(event) => handleLinks(event, 'messagecontrol')} />
|
||||
<div className={style.content}>
|
||||
<ErrorBoundary>
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.timeContainer,
|
||||
.playbackContainer {
|
||||
@include second-container;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,7 @@ import style from '../../editors/Editor.module.scss';
|
||||
|
||||
export default function TimerControlExport() {
|
||||
return (
|
||||
<Box className={style.playback}>
|
||||
<h1>Timer Control</h1>
|
||||
<Box className={style.playback} data-testid="panel-timer-control">
|
||||
<FiArrowUpRight className={style.corner} onClick={(event) => handleLinks(event, 'timercontrol')} />
|
||||
<div className={style.content}>
|
||||
<ErrorBoundary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IoArrowUndo } from '@react-icons/all-files/io5/IoArrowUndo';
|
||||
import { IoPlayBack } from '@react-icons/all-files/io5/IoPlayBack';
|
||||
import { IoPlaySkipBack } from '@react-icons/all-files/io5/IoPlaySkipBack';
|
||||
import { IoPlaySkipForward } from '@react-icons/all-files/io5/IoPlaySkipForward';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -30,7 +30,7 @@ export default function Transport(props) {
|
||||
clickHandler={() => playbackControl.reload()}
|
||||
disabled={selectedId == null || isRolling || noEvents}
|
||||
tooltip='Reload event'
|
||||
icon={<IoArrowUndo size='22px' />}
|
||||
icon={<IoPlayBack size='22px' />}
|
||||
/>
|
||||
<UnloadIconBtn
|
||||
clickHandler={() => playbackControl.stop()}
|
||||
|
||||
@@ -114,12 +114,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: max(1.5em, 16px);
|
||||
color: $bg-gray-100;
|
||||
padding-bottom: 0.25em;
|
||||
}
|
||||
|
||||
.mainContainer {
|
||||
.settings,
|
||||
.editor,
|
||||
@@ -128,9 +122,8 @@ h1 {
|
||||
.info {
|
||||
border-radius: 8px;
|
||||
height: 100%;
|
||||
background-color: $bg-gray-1000;
|
||||
background-color: $bg-container-l1;
|
||||
padding: 0.8em 1.5em;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -138,9 +131,8 @@ h1 {
|
||||
|
||||
.eventEditor {
|
||||
border-radius: 3px 3px 0 0;
|
||||
background-color: $bg-gray-950;
|
||||
border-top: 1px solid $bg-gray-900;
|
||||
padding: 0.8em 1.5em;
|
||||
background-color: $bg-container-l1;
|
||||
border-top: $border-l1;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100vw;
|
||||
@@ -155,14 +147,14 @@ h1 {
|
||||
transition: bottom 0.7s;
|
||||
}
|
||||
|
||||
.header {
|
||||
.eventEditorLayout {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.header {
|
||||
h1 {
|
||||
margin-right: auto;
|
||||
}
|
||||
background-color: $bg-container-l2;
|
||||
padding: 8px;
|
||||
border-left: 1px solid $bg-container-l3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,8 +186,8 @@ h1 {
|
||||
|
||||
.playback {
|
||||
grid-area: play;
|
||||
min-height: 320px;
|
||||
max-height: 320px;
|
||||
min-height: 270px;
|
||||
max-height: 270px;
|
||||
min-width: 480px;
|
||||
}
|
||||
|
||||
@@ -211,5 +203,5 @@ h1 {
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-top: 0.5em;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export default function Editor() {
|
||||
<ErrorBoundary>
|
||||
<ModalManager isOpen={isSettingsOpen} onClose={onSettingsClose} />
|
||||
</ErrorBoundary>
|
||||
<div className={styles.mainContainer}>
|
||||
<div className={styles.mainContainer} data-testid="event-editor">
|
||||
<Box id='settings' className={styles.settings}>
|
||||
<ErrorBoundary>
|
||||
<MenuBar
|
||||
|
||||
@@ -23,9 +23,11 @@ export default function BlockBlock(props) {
|
||||
<TooltipLoadingActionBtn
|
||||
clickHandler={() => actionHandler('delete')}
|
||||
icon={<IoRemove />}
|
||||
colorScheme='red'
|
||||
tooltip='Delete'
|
||||
_hover={{ bg: 'red.400' }}
|
||||
variant='ghost'
|
||||
_hover={{ bg: 'red.400', color: 'white' }}
|
||||
color='red.500'
|
||||
size='sm'
|
||||
/>
|
||||
<ActionButtons showAdd showDelay actionHandler={actionHandler} />
|
||||
</HStack>
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function DelayBlock(props) {
|
||||
<HStack spacing='4px' className={style.actionOverlay}>
|
||||
<Button
|
||||
onClick={applyDelayHandler}
|
||||
size='xs'
|
||||
size='sm'
|
||||
colorScheme='orange'
|
||||
_hover={{ bg: 'orange.400' }}
|
||||
leftIcon={<FiCheck />}
|
||||
@@ -64,9 +64,11 @@ export default function DelayBlock(props) {
|
||||
<TooltipLoadingActionBtn
|
||||
clickHandler={deleteHandler}
|
||||
icon={<IoRemove />}
|
||||
colorScheme='red'
|
||||
tooltip='Delete'
|
||||
_hover={{ bg: 'red.400' }}
|
||||
variant='ghost'
|
||||
_hover={{ bg: 'red.400', color: 'white' }}
|
||||
color='red.500'
|
||||
size='sm'
|
||||
/>
|
||||
<ActionButtons showAdd actionHandler={actionHandler} />
|
||||
</HStack>
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
}
|
||||
|
||||
.create {
|
||||
background-color: rgba($bg-gray-1100, 0.85);
|
||||
box-sizing: border-box;
|
||||
|
||||
width: 100%;
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
// layout
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"binder progb progb progb"
|
||||
"binder pb-actions times actions"
|
||||
"binder pb-actions title title"
|
||||
"binder pb-actions notes status"
|
||||
"binder ... ... ...";
|
||||
"binder progb progb progb";
|
||||
grid-template-columns: $binder-width auto 1fr auto;
|
||||
grid-template-rows: $element-spacing 36px 36px 36px $element-spacing;
|
||||
grid-template-rows: 36px 36px 36px $element-spacing;
|
||||
align-items: center;
|
||||
margin: 2px 1px;
|
||||
padding-right: $clearance;
|
||||
@@ -21,16 +20,13 @@
|
||||
gap: 2px;
|
||||
|
||||
// style - colour
|
||||
background-color: $bg-gray-950;
|
||||
border: $block-border;
|
||||
|
||||
// style - text
|
||||
|
||||
background-color: $bg-container-l2;
|
||||
border: $border-l3;
|
||||
|
||||
// variant
|
||||
&.selected {
|
||||
background-color: $bg-gray-900;
|
||||
border: 1px solid $ontime-accent;
|
||||
background-color: $bg-container-l3;
|
||||
box-shadow: 0 0 1px 2px $ontime-accent;
|
||||
}
|
||||
|
||||
&.skip {
|
||||
@@ -39,8 +35,7 @@
|
||||
|
||||
.eventTimers > .delayNote,
|
||||
.eventTitle,
|
||||
.eventNote,
|
||||
{
|
||||
.eventNote {
|
||||
color: $text-gray-disabled;
|
||||
}
|
||||
|
||||
@@ -65,11 +60,9 @@
|
||||
border-right: 1px solid $bg-gray-900;
|
||||
|
||||
// style - colour
|
||||
background-color: $bg-gray-950; // override inline
|
||||
background-color: $bg-container-l3; // override inline
|
||||
color: $text-white;
|
||||
|
||||
// style - text
|
||||
|
||||
.drag {
|
||||
@include drag-style;
|
||||
position: absolute;
|
||||
@@ -184,7 +177,9 @@
|
||||
.progressBg {
|
||||
grid-area: progb;
|
||||
border-radius: 1px;
|
||||
background-color: $bg-black-100;
|
||||
background-color: $bg-container-l1;
|
||||
margin-bottom: 4px;
|
||||
margin-left: 4px;
|
||||
|
||||
// layout
|
||||
height: 100%;
|
||||
@@ -194,8 +189,6 @@
|
||||
height: 100%;
|
||||
width: 0;
|
||||
border-radius: 1px 0 0 1px;
|
||||
margin-top: 2px;
|
||||
margin-left: 4px;
|
||||
|
||||
// animations
|
||||
transition: 1s linear;
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { Draggable } from 'react-beautiful-dnd';
|
||||
import { Editable, EditableInput, EditablePreview, IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import { Editable, EditableInput, EditablePreview, Tooltip } from '@chakra-ui/react';
|
||||
import { FiUsers } from '@react-icons/all-files/fi/FiUsers';
|
||||
import { IoOptions } from '@react-icons/all-files/io5/IoOptions';
|
||||
import { IoPause } from '@react-icons/all-files/io5/IoPause';
|
||||
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 { IoReload } from '@react-icons/all-files/io5/IoReload';
|
||||
import { IoRemoveCircleSharp } from '@react-icons/all-files/io5/IoRemoveCircleSharp';
|
||||
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 { IoSettingsSharp } from '@react-icons/all-files/io5/IoSettingsSharp';
|
||||
import { IoTimerOutline } from '@react-icons/all-files/io5/IoTimerOutline';
|
||||
import { editorEventId } from 'common/atoms/LocalEventSettings';
|
||||
import TooltipActionBtn from 'common/components/buttons/TooltipActionBtn';
|
||||
@@ -27,9 +29,8 @@ import style from './EventBlock.module.scss';
|
||||
|
||||
const blockBtnStyle = {
|
||||
size: 'sm',
|
||||
colorScheme: 'blue',
|
||||
variant: 'outline',
|
||||
borderRadius: '3px',
|
||||
colorScheme: 'white',
|
||||
variant: 'ghost',
|
||||
fontSize: '20px',
|
||||
};
|
||||
|
||||
@@ -103,6 +104,12 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
// Todo: data should come from socket
|
||||
const progress = `${Math.random() * 100}%`;
|
||||
const eventIsPlaying = selected && playback === 'start';
|
||||
const playBtnStyles = { _hover: {} };
|
||||
if (!skip && eventIsPlaying) {
|
||||
playBtnStyles._hover = { bg: '#c05621' };
|
||||
} else if (!skip && !eventIsPlaying) {
|
||||
playBtnStyles._hover = { bg: '#4bffab' };
|
||||
}
|
||||
|
||||
return (
|
||||
<Draggable key={eventId} draggableId={eventId} index={index}>
|
||||
@@ -136,37 +143,37 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
aria-label='Skip event'
|
||||
tooltip='Skip event'
|
||||
openDelay={tooltipDelayMid}
|
||||
icon={<IoRemoveCircleSharp />}
|
||||
icon={skip ? <IoRemoveCircle /> : <IoRemoveCircleOutline />}
|
||||
{...blockBtnStyle}
|
||||
variant={skip ? 'solid' : 'outline'}
|
||||
variant={skip ? 'solid' : 'ghost'}
|
||||
clickHandler={() => actionHandler('update', { field: 'skip', value: !skip })}
|
||||
tabIndex={-1}
|
||||
disabled={selected}
|
||||
/>
|
||||
<TooltipActionBtn
|
||||
aria-label='Start event'
|
||||
tooltip='Start event'
|
||||
aria-label='Load event'
|
||||
tooltip='Load event'
|
||||
openDelay={tooltipDelayMid}
|
||||
icon={eventIsPlaying ? <IoPause /> : <IoPlay />}
|
||||
icon={selected ? <IoPlayBackOutline /> : <IoReload />}
|
||||
disabled={skip}
|
||||
{...blockBtnStyle}
|
||||
variant={eventIsPlaying ? 'solid' : 'outline'}
|
||||
clickHandler={() => setPlayback.loadEvent()}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<TooltipActionBtn
|
||||
aria-label='Toggle start / pause event'
|
||||
tooltip={eventIsPlaying ? 'Pause event' : 'Start event'}
|
||||
openDelay={tooltipDelayMid}
|
||||
icon={eventIsPlaying ? <IoPause /> : <IoPlayOutline />}
|
||||
disabled={skip}
|
||||
{...blockBtnStyle}
|
||||
variant={eventIsPlaying ? 'solid' : 'ghost'}
|
||||
clickHandler={
|
||||
eventIsPlaying
|
||||
? () => setPlayback.pause()
|
||||
: () => setPlayback.startEvent()
|
||||
}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<TooltipActionBtn
|
||||
aria-label='Load event'
|
||||
tooltip='Load event'
|
||||
openDelay={tooltipDelayMid}
|
||||
icon={selected ? <IoReload /> : <IoReload />}
|
||||
disabled={skip}
|
||||
{...blockBtnStyle}
|
||||
variant={selected ? 'solid' : 'outline'}
|
||||
clickHandler={() => setPlayback.loadEvent()}
|
||||
{...playBtnStyles}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
</div>
|
||||
@@ -190,13 +197,17 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
</Editable>
|
||||
<span className={style.eventNote}>{note}</span>
|
||||
<div className={style.eventActions}>
|
||||
<IconButton
|
||||
icon={<IoSettingsSharp />}
|
||||
aria-label='event options'
|
||||
onClick={() => setOpenId((prev) => prev === eventId ? null : eventId)}
|
||||
<TooltipActionBtn
|
||||
{...blockBtnStyle}
|
||||
variant={openId === eventId ? 'solid' : 'outline'}
|
||||
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' }}
|
||||
/>
|
||||
<EventBlockActionMenu
|
||||
showAdd
|
||||
|
||||
@@ -27,9 +27,8 @@ export default function EventBlockActionMenu(props) {
|
||||
|
||||
const blockBtnStyle = {
|
||||
size: 'sm',
|
||||
variant: 'ghost',
|
||||
colorScheme: 'blue',
|
||||
variant: 'outline',
|
||||
borderRadius: '3px',
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createRef, useCallback, useContext, useEffect } from 'react';
|
||||
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
|
||||
import { Button } from '@chakra-ui/react';
|
||||
import {
|
||||
defaultPublicAtom,
|
||||
showQuickEntryAtom,
|
||||
@@ -166,7 +167,12 @@ export default function EventList(props) {
|
||||
);
|
||||
|
||||
if (events.length < 1) {
|
||||
return <Empty text='No Events' style={{ marginTop: '10vh' }} />;
|
||||
return (
|
||||
<div className={style.alignCenter}>
|
||||
<Empty text='No Events' style={{ marginTop: '7vh' }} />
|
||||
<Button variant='solid' colorScheme='blue'>Create Event</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
let cumulativeDelay = 0;
|
||||
let eventIndex = -1;
|
||||
|
||||
@@ -11,8 +11,7 @@ import style from '../Editor.module.scss';
|
||||
export default function EventListExport() {
|
||||
return (
|
||||
<CursorProvider>
|
||||
<Box className={style.editor}>
|
||||
<h1>Event List</h1>
|
||||
<Box className={style.editor} data-testid="panel-event-list">
|
||||
<FiArrowUpRight
|
||||
className={style.corner}
|
||||
onClick={(event) => handleLinks(event, 'eventlist')}
|
||||
|
||||
@@ -20,15 +20,13 @@ export default function EventListWrapper() {
|
||||
}, [emitError, isError]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.content}>
|
||||
<EventListMenu />
|
||||
<div className={styles.content}>
|
||||
{status === 'success' && data ? (
|
||||
<EventList events={data} />
|
||||
) : (
|
||||
<Empty text='Connecting to server' />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
{status === 'success' && data ? (
|
||||
<EventList events={data} />
|
||||
) : (
|
||||
<Empty text='Connecting to server' />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
@use '../../../theme/mixins' as *;
|
||||
|
||||
.eventContainer {
|
||||
@include second-container;
|
||||
margin-top: 1em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -22,8 +21,8 @@
|
||||
}
|
||||
|
||||
.cursor {
|
||||
box-shadow: 2px 2px 0 $ontime-pink;
|
||||
border-radius: 3px;
|
||||
outline: 1px solid $action-blue;
|
||||
}
|
||||
|
||||
.bgElement {
|
||||
@@ -31,3 +30,11 @@
|
||||
background: rgba(214, 158, 46, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.alignCenter {
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
button {
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ export default function EventEditor() {
|
||||
leftIcon={<FiUsers />}
|
||||
size='sm'
|
||||
colorScheme='blue'
|
||||
variant={event.isPublic ? 'solid' : 'outline'}
|
||||
variant={event.isPublic ? 'solid' : 'ghost'}
|
||||
onClick={() => togglePublic(event.isPublic)}
|
||||
>
|
||||
{event.isPublic ? 'Event is Public' : 'Make event public'}
|
||||
@@ -180,7 +180,7 @@ export default function EventEditor() {
|
||||
<Button
|
||||
leftIcon={<IoBan />}
|
||||
onClick={() => handleSubmit('colour', '')}
|
||||
variant='outline'
|
||||
variant='ghost'
|
||||
colorScheme='blue'
|
||||
borderRadius='3px'
|
||||
size='sm'
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
@use '../../theme/main' as *;
|
||||
|
||||
.eventEditor {
|
||||
padding: 16px 32px;
|
||||
width: 100%;
|
||||
overflow-y: scroll;
|
||||
max-height: 33vh;
|
||||
max-height: 40vh;
|
||||
gap: max(16px, 2vh);
|
||||
|
||||
display: grid;
|
||||
@@ -11,7 +13,7 @@
|
||||
|
||||
.timers,
|
||||
.titles {
|
||||
background-color: $bg-gray-1100;
|
||||
background-color: $bg-container-l2;
|
||||
border-radius: 2px;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
@@ -22,6 +24,9 @@
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.timers label:nth-child(1) {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.titles {
|
||||
grid-area: titles;
|
||||
@@ -36,6 +41,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.left {
|
||||
@@ -46,8 +52,7 @@
|
||||
.right {
|
||||
padding-left: 16px;
|
||||
grid-area: right;
|
||||
border-left: 1px solid $bg-gray-900;
|
||||
|
||||
border-left: 1px solid $bg-container-l3;
|
||||
}
|
||||
|
||||
.osc {
|
||||
|
||||
@@ -22,17 +22,18 @@ export default function InfoExport() {
|
||||
return (
|
||||
<Box className={`${style.eventEditor} ${!openId ? style.noEvent : ''}`}>
|
||||
<ErrorBoundary>
|
||||
<div className={style.header}>
|
||||
<h1>Event Editor</h1>
|
||||
<IconButton
|
||||
aria-label='Close Menu'
|
||||
icon={<FiX />}
|
||||
onClick={() => setOpenId(null)}
|
||||
{...closeBtnStyle}
|
||||
/>
|
||||
</div>
|
||||
<div className={style.content}>
|
||||
<div className={style.eventEditorLayout}>
|
||||
<EventEditor />
|
||||
<div className={style.header}>
|
||||
<IconButton
|
||||
aria-label='Close Menu'
|
||||
size='lg'
|
||||
icon={<FiX />}
|
||||
onClick={() => setOpenId(null)}
|
||||
{...closeBtnStyle}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</Box>
|
||||
|
||||
@@ -36,8 +36,8 @@ export default function Info() {
|
||||
<span>{selected}</span>
|
||||
</div>
|
||||
<InfoNif />
|
||||
<InfoTitle title='Now' data={titlesNow} roll={data.playback === 'roll'} />
|
||||
<InfoTitle title='Next' data={titlesNext} roll={data.playback === 'roll'} />
|
||||
<InfoTitle title='Now' data={titlesNow} />
|
||||
<InfoTitle title='Next' data={titlesNext} />
|
||||
<InfoLogger />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
@use '../../theme/mixins' as *;
|
||||
|
||||
@mixin container {
|
||||
margin-top: 1em;
|
||||
@include second-container;
|
||||
margin-top: 1em;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.container {
|
||||
@include container;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
|
||||
.main {
|
||||
font-size: 0.9em;
|
||||
color: $ontime-pink;
|
||||
@@ -32,10 +34,6 @@
|
||||
color: $header-gray;
|
||||
}
|
||||
|
||||
.headerRoll {
|
||||
color: $ontime-roll;
|
||||
}
|
||||
|
||||
.collapsedTitle {
|
||||
font-size: inherit;
|
||||
|
||||
@@ -78,10 +76,18 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.if {
|
||||
font-size: 0.8em;
|
||||
color: $ontime-accent;
|
||||
background-color: $bg-gray-900;
|
||||
padding: 0 0.5em;
|
||||
margin: 0 0.5em;
|
||||
.interfaceList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.interface {
|
||||
font-size: 0.8em;
|
||||
background-color: $bg-container-l3;
|
||||
padding: 0 8px;
|
||||
margin: 0 2px;
|
||||
border-radius: 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ import style from '../editors/Editor.module.scss';
|
||||
|
||||
export default function InfoExport() {
|
||||
return (
|
||||
<Box className={style.info}>
|
||||
<h1>Info</h1>
|
||||
<Box className={style.info} data-testid="panel-info">
|
||||
<FiArrowUpRight className={style.corner} onClick={(event) => handleLinks(event, 'info')} />
|
||||
<div className={style.content}>
|
||||
<ErrorBoundary>
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
padding: 0 0.5em;
|
||||
margin: 0 0.5em;
|
||||
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
margin-bottom: 2px;
|
||||
|
||||
@@ -19,13 +19,13 @@ export default function InfoNif() {
|
||||
onClick={() => setCollapsed((c) => !c)}
|
||||
/>
|
||||
{!collapsed && (status === 'success') &&(
|
||||
<div>
|
||||
<div className={style.interfaceList}>
|
||||
{data?.networkInterfaces.map((e) => (
|
||||
<a
|
||||
key={e.address}
|
||||
href='#!'
|
||||
onClick={() => openLink(baseURL.replace('__IP__', e.address))}
|
||||
className={style.if}
|
||||
className={style.interface}
|
||||
>
|
||||
{`${e.name} - ${e.address}`}
|
||||
</a>
|
||||
|
||||
@@ -6,10 +6,9 @@ import CollapseBar from '../../common/components/collapseBar/CollapseBar';
|
||||
import style from './Info.module.scss';
|
||||
|
||||
export default function InfoTitle(props) {
|
||||
const { title, data } = props;
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
|
||||
const { title, data, roll } = props;
|
||||
|
||||
const noTitl = data.title == null || data.title === '';
|
||||
const noPres = data.presenter == null || data.presenter === '';
|
||||
const noSubt = data.subtitle == null || data.subtitle === '';
|
||||
@@ -21,7 +20,6 @@ export default function InfoTitle(props) {
|
||||
title={title}
|
||||
isCollapsed={collapsed}
|
||||
onClick={() => setCollapsed((c) => !c)}
|
||||
roll={roll}
|
||||
/>
|
||||
{!collapsed && (
|
||||
<>
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
import { memo, useCallback, useContext } from 'react';
|
||||
import { ButtonGroup, HStack } from '@chakra-ui/react';
|
||||
import { FiTarget } from '@react-icons/all-files/fi/FiTarget';
|
||||
import { IoCaretDown } from '@react-icons/all-files/io5/IoCaretDown';
|
||||
import { IoCaretUp } from '@react-icons/all-files/io5/IoCaretUp';
|
||||
import TooltipActionBtn from 'common/components/buttons/TooltipActionBtn';
|
||||
import { CursorContext } from 'common/context/CursorContext';
|
||||
import { useEventAction } from 'common/hooks/useEventAction';
|
||||
|
||||
import MenuActionButtons from './MenuActionButtons';
|
||||
|
||||
import style from './EventListMenu.module.css';
|
||||
|
||||
const cursorBtnProps = {
|
||||
size: 'sm',
|
||||
color: 'pink.300',
|
||||
borderColor: 'pink.300',
|
||||
variant: 'outline',
|
||||
_hover: { bg: 'pink.400', color: 'white' },
|
||||
};
|
||||
|
||||
const EventListMenu = () => {
|
||||
const { isCursorLocked, toggleCursorLocked, moveCursorUp, moveCursorDown } =
|
||||
useContext(CursorContext);
|
||||
const { addEvent, deleteAllEvents } = useEventAction();
|
||||
|
||||
const actionHandler = useCallback(
|
||||
(action) => {
|
||||
switch (action) {
|
||||
case 'event':
|
||||
addEvent({ type: action });
|
||||
break;
|
||||
case 'delay':
|
||||
addEvent({ type: action });
|
||||
break;
|
||||
case 'block':
|
||||
addEvent({ type: action });
|
||||
break;
|
||||
case 'cursorUp':
|
||||
moveCursorUp();
|
||||
break;
|
||||
case 'cursorDown':
|
||||
moveCursorDown();
|
||||
break;
|
||||
case 'togglelock':
|
||||
toggleCursorLocked();
|
||||
break;
|
||||
case 'deleteall':
|
||||
deleteAllEvents();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
[addEvent, deleteAllEvents, moveCursorDown, moveCursorUp, toggleCursorLocked],
|
||||
);
|
||||
|
||||
return (
|
||||
<HStack className={style.headerButtons}>
|
||||
<ButtonGroup isAttached>
|
||||
<TooltipActionBtn
|
||||
{...cursorBtnProps}
|
||||
clickHandler={() => actionHandler('cursorUp')}
|
||||
icon={<IoCaretUp />}
|
||||
tooltip='Move cursor up Alt + ↑'
|
||||
/>
|
||||
<TooltipActionBtn
|
||||
{...cursorBtnProps}
|
||||
clickHandler={() => actionHandler('cursorDown')}
|
||||
icon={<IoCaretDown />}
|
||||
tooltip='Move cursor down Alt + ↓'
|
||||
/>
|
||||
<TooltipActionBtn
|
||||
{...cursorBtnProps}
|
||||
clickHandler={() => actionHandler('togglelock')}
|
||||
icon={<FiTarget />}
|
||||
tooltip='Lock cursor to current'
|
||||
width='3em'
|
||||
backgroundColor={isCursorLocked && 'pink.400'}
|
||||
color={isCursorLocked ? 'white' : 'pink.400'}
|
||||
variant={isCursorLocked ? 'solid' : 'outline'}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<MenuActionButtons actionHandler={actionHandler} size='sm' />
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(EventListMenu);
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
.headerButtons {
|
||||
align-content: center;
|
||||
justify-content: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { memo, useCallback, useContext } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
HStack,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Tooltip,
|
||||
} from '@chakra-ui/react';
|
||||
import { FiClock } from '@react-icons/all-files/fi/FiClock';
|
||||
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 { CursorContext } from 'common/context/CursorContext';
|
||||
|
||||
import { useEventAction } from '../../common/hooks/useEventAction';
|
||||
|
||||
import style from './EventListMenu.module.scss';
|
||||
|
||||
const menuStyle = {
|
||||
color: '#000000',
|
||||
backgroundColor: 'rgba(255,255,255,1)',
|
||||
};
|
||||
|
||||
const EventListMenu = () => {
|
||||
const { isCursorLocked, toggleCursorLocked } = useContext(CursorContext);
|
||||
const { addEvent, deleteAllEvents } = useEventAction();
|
||||
|
||||
type ActionTypes = 'event' | 'delay' | 'block' | 'delete-all' | 'toggle-lock';
|
||||
const actionHandler = useCallback(
|
||||
(action: ActionTypes) => {
|
||||
switch (action) {
|
||||
case 'toggle-lock':
|
||||
toggleCursorLocked();
|
||||
break;
|
||||
case 'event':
|
||||
addEvent({ type: action });
|
||||
break;
|
||||
case 'delay':
|
||||
addEvent({ type: action });
|
||||
break;
|
||||
case 'block':
|
||||
addEvent({ type: action });
|
||||
break;
|
||||
case 'delete-all':
|
||||
deleteAllEvents();
|
||||
break;
|
||||
}
|
||||
},
|
||||
[toggleCursorLocked],
|
||||
);
|
||||
|
||||
return (
|
||||
<HStack className={style.headerButtons}>
|
||||
<Button
|
||||
size='sm'
|
||||
variant={isCursorLocked ? 'solid' : 'ghost'}
|
||||
onClick={() => actionHandler('toggle-lock')}
|
||||
colorScheme='blue'
|
||||
>
|
||||
Lock cursor to current
|
||||
</Button>
|
||||
<Menu isLazy lazyBehavior='unmount'>
|
||||
<Tooltip label='Add / Delete ...'>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
aria-label='Create Menu'
|
||||
size='sm'
|
||||
icon={<FiPlus />}
|
||||
colorScheme='blue'
|
||||
variant='outline'
|
||||
/>
|
||||
</Tooltip>
|
||||
<MenuList style={menuStyle}>
|
||||
<MenuItem icon={<FiPlus />} onClick={() => actionHandler('event')}>
|
||||
Add Event first
|
||||
</MenuItem>
|
||||
<MenuItem icon={<FiClock />} onClick={() => actionHandler('delay')}>
|
||||
Add Delay first
|
||||
</MenuItem>
|
||||
<MenuItem icon={<FiMinusCircle />} onClick={() => actionHandler('block')}>
|
||||
Add Block first
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem icon={<FiTrash2 />} onClick={() => actionHandler('delete-all')} color='red.500'>
|
||||
Delete All
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(EventListMenu);
|
||||
@@ -1,61 +0,0 @@
|
||||
import {
|
||||
IconButton,
|
||||
Divider,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Tooltip,
|
||||
} from '@chakra-ui/react';
|
||||
import { FiClock } from '@react-icons/all-files/fi/FiClock';
|
||||
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 { useEventAction } from 'common/hooks/useEventAction';
|
||||
import { Size } from 'common/models/UtilTypes';
|
||||
|
||||
|
||||
interface MenuActionButtonsProps {
|
||||
actionHandler: (action: string) => void;
|
||||
size?: Size;
|
||||
}
|
||||
|
||||
// Todo: add useEventActionsHook
|
||||
export default function MenuActionButtons(props: MenuActionButtonsProps) {
|
||||
const { actionHandler, size = 'xs' } = props;
|
||||
const { deleteAllEvents } = useEventAction();
|
||||
const menuStyle = {
|
||||
color: '#000000',
|
||||
backgroundColor: 'rgba(255,255,255,1)',
|
||||
};
|
||||
|
||||
return (
|
||||
<Menu isLazy lazyBehavior='unmount'>
|
||||
<Tooltip label='Add / Delete ...'>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
aria-label='Create Menu'
|
||||
size={size}
|
||||
icon={<FiPlus />}
|
||||
colorScheme='blue'
|
||||
/>
|
||||
</Tooltip>
|
||||
<MenuList style={menuStyle}>
|
||||
<MenuItem icon={<FiPlus />} onClick={() => actionHandler('event')}>
|
||||
Add Event first
|
||||
</MenuItem>
|
||||
<MenuItem icon={<FiClock />} onClick={() => actionHandler('delay')}>
|
||||
Add Delay first
|
||||
</MenuItem>
|
||||
<MenuItem icon={<FiMinusCircle />} onClick={() => actionHandler('block')}>
|
||||
Add Block first
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem icon={<FiTrash2 />} onClick={() => deleteAllEvents()} color='red.500'>
|
||||
Delete All
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import { FiHelpCircle } from '@react-icons/all-files/fi/FiHelpCircle';
|
||||
import { FiMaximize } from '@react-icons/all-files/fi/FiMaximize';
|
||||
import { FiMinimize } from '@react-icons/all-files/fi/FiMinimize';
|
||||
import { FiSave } from '@react-icons/all-files/fi/FiSave';
|
||||
import { FiSettings } from '@react-icons/all-files/fi/FiSettings';
|
||||
import { FiUpload } from '@react-icons/all-files/fi/FiUpload';
|
||||
import { IoSettingsOutline } from '@react-icons/all-files/io5/IoSettingsOutline';
|
||||
import { downloadEvents } from 'common/api/ontimeApi';
|
||||
|
||||
import QuitIconBtn from '../../common/components/buttons/QuitIconBtn';
|
||||
@@ -116,7 +116,7 @@ export default function MenuBar(props: MenuBarProps) {
|
||||
/>
|
||||
<TooltipActionBtn
|
||||
{...buttonStyle}
|
||||
icon={<FiSettings />}
|
||||
icon={<IoSettingsOutline />}
|
||||
className={isSettingsOpen ? style.open : ''}
|
||||
clickHandler={onSettingsOpen}
|
||||
tooltip='Settings'
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
|
||||
import MenuActionButtons from '../MenuActionButtons';
|
||||
|
||||
const actionHandler = vi.fn();
|
||||
const renderInMock = () => {
|
||||
render(
|
||||
<QueryClientProvider client={queryClientMock}>
|
||||
<MenuActionButtons actionHandler={actionHandler} />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
};
|
||||
|
||||
test('check that menu bar renders correctly', () => {
|
||||
// need to inject the react query provider
|
||||
renderInMock();
|
||||
|
||||
const b = screen.getByRole('button', {
|
||||
name: /create menu/i,
|
||||
});
|
||||
|
||||
expect(b).toBeInTheDocument();
|
||||
});
|
||||
@@ -83,7 +83,10 @@ export default function TableWrapper() {
|
||||
return <span>loading...</span>;
|
||||
}
|
||||
return (
|
||||
<div className={theme === 'dark' ? style.tableWrapper__dark : style.tableWrapper}>
|
||||
<div
|
||||
className={theme === 'dark' ? style.tableWrapper__dark : style.tableWrapper}
|
||||
data-testid="cuesheet"
|
||||
>
|
||||
<TableHeader handleCSVExport={exportHandler} featureData={featureData} />
|
||||
<OntimeTable
|
||||
tableData={events}
|
||||
|
||||
@@ -58,7 +58,7 @@ export default function Backstage(props) {
|
||||
const clock = formatTime(time.clock, formatOptions);
|
||||
|
||||
return (
|
||||
<div className='backstage'>
|
||||
<div className='backstage' data-testid="backstage-view">
|
||||
<NavLogo />
|
||||
|
||||
<div className='event-title'>{general.title}</div>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
.today-container {
|
||||
background-color: var(--outdent-background-color-override, $viewer-outdent-bg-color);
|
||||
padding: 1vh 1vw;
|
||||
border-radius: 1vw;
|
||||
border-radius: $element-border-radius;
|
||||
|
||||
.label {
|
||||
font-size: 1.3vw;
|
||||
@@ -50,7 +50,7 @@
|
||||
}
|
||||
|
||||
.event {
|
||||
border-radius: 0 2vw 2vw 0;
|
||||
border-radius: 0 $element-border-radius $element-border-radius 0;
|
||||
margin-left: -1vw;
|
||||
padding: 1vh 2vw;
|
||||
overflow: hidden;
|
||||
@@ -115,12 +115,12 @@
|
||||
|
||||
.clock-container {
|
||||
grid-area: time;
|
||||
border-radius: 0 0 1vw 1vw;
|
||||
border-radius: 0 0 $element-border-radius $element-border-radius;
|
||||
}
|
||||
|
||||
.timer-container {
|
||||
grid-area: clck;
|
||||
border-radius: 1vw 1vw 0 0;
|
||||
border-radius: $element-border-radius $element-border-radius 0 0;
|
||||
}
|
||||
|
||||
.info {
|
||||
|
||||
@@ -90,10 +90,10 @@ export default function Countdown(props) {
|
||||
: formatTime(follow.timeEnd + delay, formatOptions);
|
||||
|
||||
return (
|
||||
<div className='countdown'>
|
||||
<div className='countdown' data-testid="countdown-view">
|
||||
<NavLogo />
|
||||
{follow === null ? (
|
||||
<div className='event-select'>
|
||||
<div className='event-select' data-testid="countdown-select">
|
||||
<span className='event-select__title'>Select an event to follow</span>
|
||||
<ul className='event-select__events'>
|
||||
{backstageEvents.length === 0 ? (
|
||||
@@ -112,7 +112,7 @@ export default function Countdown(props) {
|
||||
</ul>
|
||||
</div>
|
||||
) : (
|
||||
<div className='countdown-container'>
|
||||
<div className='countdown-container' data-testid="countdown-event">
|
||||
<div className='timer-group'>
|
||||
<div className='aux-timers'>
|
||||
<div className='aux-timers__label'>Time Now</div>
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
color: var(--accent-color-override, $accent-color);
|
||||
background-color: var(--outdent-background-color-override, $viewer-outdent-bg-color);
|
||||
padding: 1vh 2vw;
|
||||
border-radius: 1vw;
|
||||
border-radius: $element-border-radius;
|
||||
}
|
||||
|
||||
.status {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
.lower-container {
|
||||
position: absolute;
|
||||
padding: 1vh 2vh;
|
||||
border-radius: 1vh;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.message-container {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 45vw;
|
||||
border-radius: 0 1vh 1vh 0;
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.message-container {
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function Pip(props) {
|
||||
const clock = formatTime(time.clock, formatOptions);
|
||||
|
||||
return (
|
||||
<div className='pip'>
|
||||
<div className='pip' data-testid="pip-view">
|
||||
<NavLogo />
|
||||
|
||||
<div className='event-title'>{general.title}</div>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
.timer-container {
|
||||
background-color: var(--outdent-background-color-override, $viewer-outdent-bg-color);
|
||||
padding: 1vh 1vw;
|
||||
border-radius: 1vw;
|
||||
border-radius: $element-border-radius;
|
||||
|
||||
.label {
|
||||
font-size: 1.3vw;
|
||||
@@ -126,11 +126,11 @@
|
||||
|
||||
.clock-container {
|
||||
grid-area: time;
|
||||
border-radius: 0 0 1vw 1vw;
|
||||
border-radius: 0 0 $element-border-radius $element-border-radius;
|
||||
}
|
||||
|
||||
.timer-container {
|
||||
grid-area: clck;
|
||||
border-radius: 1vw 1vw 0 0;
|
||||
border-radius: $element-border-radius $element-border-radius 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function Public(props) {
|
||||
const clock = formatTime(time.clock, formatOptions);
|
||||
|
||||
return (
|
||||
<div className='public-screen'>
|
||||
<div className='public-screen' data-testid="public-view">
|
||||
<NavLogo />
|
||||
|
||||
<div className='event-title'>{general.title}</div>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
.today-container {
|
||||
background-color: var(--outdent-background-color-override, $viewer-outdent-bg-color);
|
||||
padding: 1vh 1vw;
|
||||
border-radius: 1vw;
|
||||
border-radius: $element-border-radius;
|
||||
|
||||
.label {
|
||||
font-size: 1.3vw;
|
||||
@@ -50,7 +50,7 @@
|
||||
}
|
||||
|
||||
.event {
|
||||
border-radius: 0 2vw 2vw 0;
|
||||
border-radius: 0 $element-border-radius $element-border-radius 0;
|
||||
margin-left: -1vw;
|
||||
padding: 1vh 2vw;
|
||||
overflow: hidden;
|
||||
@@ -114,7 +114,7 @@
|
||||
|
||||
.clock-container {
|
||||
grid-area: time;
|
||||
border-radius: 1vw;
|
||||
border-radius: $element-border-radius;
|
||||
}
|
||||
|
||||
.info {
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function StudioClock(props) {
|
||||
const [, , secondsNow] = stringFromMillis(time.clock).split(':');
|
||||
|
||||
return (
|
||||
<div className='studio-clock'>
|
||||
<div className='studio-clock' data-testid="studio-view">
|
||||
<NavLogo />
|
||||
<div className='clock-container'>
|
||||
<div className='studio-timer'>{clock}</div>
|
||||
|
||||
@@ -66,7 +66,10 @@ export default function Timer(props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={time.finished ? 'stage-timer stage-timer--finished' : 'stage-timer'}>
|
||||
<div
|
||||
className={time.finished ? 'stage-timer stage-timer--finished' : 'stage-timer'}
|
||||
data-testid="timer-view"
|
||||
>
|
||||
<div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}>
|
||||
<div className='message'>{pres.text}</div>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
.event {
|
||||
background-color: var(--outdent-background-color-override, $viewer-outdent-bg-color);
|
||||
padding: 1vh 2vw;
|
||||
border-radius: 1vw;
|
||||
border-radius: $element-border-radius;
|
||||
max-width: 100%;
|
||||
|
||||
&.now {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
scrollbar-color: rgba(255, 255, 255, 0.15) rgba(255, 255, 255, 0.01);
|
||||
scrollbar-color: #2b2b2b rgba(255, 255, 255, 0.01);
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ $bg-black-100: #070707;
|
||||
$bg-black-200: #1a1a1a; // container borders
|
||||
$bg-black-300: #1f1f1f; // container text
|
||||
$bg-gray-1100: #232323; // container borders
|
||||
$bg-gray-1050: #242424; // container borders
|
||||
$bg-gray-1000: #262626; // container borders
|
||||
$bg-gray-950: #292929;
|
||||
$bg-gray-900: #303030;
|
||||
@@ -42,7 +43,7 @@ $opacity-disabled: 0.4;
|
||||
$notes-color: #d69e2e;
|
||||
|
||||
$text-white: #fffffa;
|
||||
$text-gray-disabled: #999;
|
||||
$text-gray-disabled: #505050;
|
||||
$header-gray: #ccc;
|
||||
$label-gray: #aaa;
|
||||
$clocks: #ddd;
|
||||
@@ -64,11 +65,17 @@ $title-gray: #ddd;
|
||||
$subtitle-gray: #aaa;
|
||||
|
||||
//////////////////////////////////// block elements
|
||||
$bg-container-l1: #202020;
|
||||
$bg-container-l2: #232323;
|
||||
$bg-container-l3: #2b2b2b;
|
||||
$border-l1: 1px solid $bg-gray-1000;
|
||||
$border-l3: 1px solid $bg-gray-900;
|
||||
|
||||
$block-delay-color: #ecc94b;
|
||||
$delay-text: #d69e2e;
|
||||
$block-delay-border: #d69e2e55;
|
||||
$block-block-color: #805ad5;
|
||||
$block-border: 1px solid $bg-gray-900;
|
||||
$block-border: 1px solid $bg-gray-1100;
|
||||
|
||||
//////////////////////////////////// viewer cards
|
||||
@mixin card-title {
|
||||
|
||||
@@ -9,13 +9,12 @@
|
||||
}
|
||||
|
||||
@mixin second-container {
|
||||
background-color: $bg-gray-1100;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
background-color: $bg-container-l2;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@mixin third-container {
|
||||
background-color: $bg-black-300;
|
||||
background-color: $bg-gray-1100;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ $delay-color: $delay-text;
|
||||
// Main Properties of a viewer
|
||||
$viewer-background-color: $bg-black; // --background-color-override
|
||||
$viewer-color: $title-white; // --color-override
|
||||
$viewer-outdent-bg-color: $bg-gray-1100; // --outdent-background-color-override
|
||||
$viewer-outdent-bg-color: $bg-container-l1; // --outdent-background-color-override
|
||||
$viewer-overlay-bg-color: $bg-overlay;
|
||||
$element-border-radius: 8px;
|
||||
|
||||
// Properties related to timer
|
||||
$timer-finished-color: $ontime-pink-variant;
|
||||
|
||||
@@ -3,38 +3,37 @@ describe('validate routes', () => {
|
||||
describe('viewer routes', () => {
|
||||
it('default to stage timer', () => {
|
||||
cy.visit('http://localhost:4001/');
|
||||
cy.get('[data-testid="timer-view"]').should('exist');
|
||||
cy.contains('Time Now');
|
||||
});
|
||||
|
||||
it('renders stage timer', () => {
|
||||
it('renders stage timer routes', () => {
|
||||
cy.visit('http://localhost:4001/timer');
|
||||
cy.get('[data-testid="timer-view"]').should('exist');
|
||||
cy.contains('Time Now');
|
||||
});
|
||||
|
||||
it('renders presenter', () => {
|
||||
cy.visit('http://localhost:4001/presenter');
|
||||
cy.get('[data-testid="timer-view"]').should('exist');
|
||||
cy.contains('Time Now');
|
||||
});
|
||||
|
||||
it('renders speaker', () => {
|
||||
cy.visit('http://localhost:4001/speaker');
|
||||
cy.get('[data-testid="timer-view"]').should('exist');
|
||||
cy.contains('Time Now');
|
||||
});
|
||||
|
||||
it('renders backstage view /stage', () => {
|
||||
cy.visit('http://localhost:4001/stage');
|
||||
cy.get('[data-testid="timer-view"]').should('exist');
|
||||
cy.contains('Time Now');
|
||||
});
|
||||
|
||||
it('renders backstage view /backstage', () => {
|
||||
it('renders backstage routes', () => {
|
||||
cy.visit('http://localhost:4001/backstage');
|
||||
cy.get('[data-testid="backstage-view"]').should('exist');
|
||||
cy.contains('Today');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Info');
|
||||
});
|
||||
|
||||
it('renders backstage view /sm', () => {
|
||||
cy.visit('http://localhost:4001/sm');
|
||||
cy.get('[data-testid="backstage-view"]').should('exist');
|
||||
cy.contains('Today');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Info');
|
||||
@@ -42,6 +41,7 @@ describe('validate routes', () => {
|
||||
|
||||
it('renders public view', () => {
|
||||
cy.visit('http://localhost:4001/public');
|
||||
cy.get('[data-testid="public-view"]').should('exist');
|
||||
cy.contains('Today');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Info');
|
||||
@@ -49,8 +49,8 @@ describe('validate routes', () => {
|
||||
|
||||
it('renders pip view', () => {
|
||||
cy.visit('http://localhost:4001/pip');
|
||||
cy.get('[data-testid="pip-view"]').should('exist');
|
||||
cy.contains('Today');
|
||||
cy.contains('Info');
|
||||
});
|
||||
|
||||
it('renders lower third with no errors', () => {
|
||||
@@ -60,16 +60,21 @@ describe('validate routes', () => {
|
||||
|
||||
it('renders studio clock', () => {
|
||||
cy.visit('http://localhost:4001/studio');
|
||||
cy.get('[data-testid="studio-view"]').should('exist');
|
||||
cy.contains('ON AIR');
|
||||
});
|
||||
|
||||
it('renders countdown selection', () => {
|
||||
cy.visit('http://localhost:4001/countdown');
|
||||
cy.get('[data-testid="countdown-view"]').should('exist');
|
||||
cy.get('[data-testid="countdown-select"]').should('exist');
|
||||
cy.contains('Select an event to follow');
|
||||
});
|
||||
|
||||
it('renders countdown with event', () => {
|
||||
cy.visit('http://localhost:4001/countdown?event=1');
|
||||
cy.get('[data-testid="countdown-view"]').should('exist');
|
||||
cy.get('[data-testid="countdown-event"]').should('exist');
|
||||
cy.contains('Time Now');
|
||||
cy.contains('Start Time');
|
||||
cy.contains('End Time');
|
||||
@@ -78,38 +83,30 @@ describe('validate routes', () => {
|
||||
|
||||
describe('clock view timer', () => {
|
||||
it('renders base route', () => {
|
||||
// base route
|
||||
cy.visit('http://localhost:4001/clock');
|
||||
cy.get('[data-testid="clock-view"]').should('exist');
|
||||
// ontime fallsback to stage timer on errors, so we check for that
|
||||
cy.get('.App').should('not.contain', 'Time Now');
|
||||
});
|
||||
});
|
||||
|
||||
describe('minimal timer and options', () => {
|
||||
it('renders base route', () => {
|
||||
// base route
|
||||
cy.visit('http://localhost:4001/minimal');
|
||||
cy.get('[data-testid="minimal-timer"]').should('exist');
|
||||
// ontime fallsback to stage timer on errors, so we check for that
|
||||
cy.get('.App').should('not.contain', 'Time Now');
|
||||
});
|
||||
|
||||
it('renders with defined options', () => {
|
||||
// route with options
|
||||
cy.visit(
|
||||
'http://localhost:4001/minimal?font=arial=1&size=1.5&text=0f0&key=ff0&hideovertime=true&alignx=start&aligny=end&offsetx=100&offsety=-100&hidemessages=true'
|
||||
);
|
||||
cy.get('[data-testid="minimal-timer"]').should('exist');
|
||||
// ontime fallback to stage timer on errors, so we check for that
|
||||
cy.get('.App').should('not.contain', 'Time Now');
|
||||
});
|
||||
|
||||
it('hides nav on given option', () => {
|
||||
// route with options for no nav
|
||||
cy.visit('http://localhost:4001/minimal?hidenav=true');
|
||||
cy.get('[data-testid="minimal-timer"]').should('exist');
|
||||
// ontime fallback to stage timer on errors, so we check for that
|
||||
cy.get('.App').should('not.contain', 'Time Now');
|
||||
cy.get('[data-testid="nav-logo"]').should('not.exist');
|
||||
});
|
||||
@@ -118,49 +115,53 @@ describe('validate routes', () => {
|
||||
describe('editor routes', () => {
|
||||
it('app editor', () => {
|
||||
cy.visit('http://localhost:4001/editor');
|
||||
cy.contains('Event List');
|
||||
cy.contains('Timer Control');
|
||||
cy.contains('Messages Control');
|
||||
cy.contains('Info');
|
||||
cy.get('[data-testid="event-editor"]').should('exist');
|
||||
cy.get('[data-testid="panel-event-list"]').should('exist');
|
||||
cy.get('[data-testid="panel-timer-control"]').should('exist');
|
||||
cy.get('[data-testid="panel-messages-control"]').should('exist');
|
||||
cy.get('[data-testid="panel-info"]').should('exist');
|
||||
});
|
||||
it('self contained eventlist', () => {
|
||||
cy.visit('http://localhost:4001/eventlist');
|
||||
cy.get('.App').should('contain', 'Event List');
|
||||
cy.get('.App').should('not.contain', 'Timer Control');
|
||||
cy.get('.App').should('not.contain', 'Messages Control');
|
||||
cy.get('.App').should('not.contain', 'Info');
|
||||
cy.get('[data-testid="panel-event-list"]').should('exist');
|
||||
cy.get('[data-testid="panel-timer-control"]').should('not.exist');
|
||||
cy.get('[data-testid="panel-messages-control"]').should('not.exist');
|
||||
cy.get('[data-testid="panel-info"]').should('not.exist');
|
||||
});
|
||||
it('self contained timercontrol', () => {
|
||||
cy.visit('http://localhost:4001/timercontrol');
|
||||
cy.get('.App').should('not.contain', 'Event List');
|
||||
cy.get('.App').should('contain', 'Timer Control');
|
||||
cy.get('.App').should('not.contain', 'Messages Control');
|
||||
cy.get('.App').should('not.contain', 'Info');
|
||||
cy.get('[data-testid="panel-event-list"]').should('not.exist');
|
||||
cy.get('[data-testid="panel-timer-control"]').should('exist');
|
||||
cy.get('[data-testid="panel-messages-control"]').should('not.exist');
|
||||
cy.get('[data-testid="panel-info"]').should('not.exist');
|
||||
});
|
||||
it('self contained messagecontrol', () => {
|
||||
cy.visit('http://localhost:4001/messagecontrol');
|
||||
cy.get('.App').should('not.contain', 'Event List');
|
||||
cy.get('.App').should('not.contain', 'Timer Control');
|
||||
cy.get('.App').should('contain', 'Messages Control');
|
||||
cy.get('.App').should('not.contain', 'Info');
|
||||
cy.get('[data-testid="panel-event-list"]').should('not.exist');
|
||||
cy.get('[data-testid="panel-timer-control"]').should('not.exist');
|
||||
cy.get('[data-testid="panel-messages-control"]').should('exist');
|
||||
cy.get('[data-testid="panel-info"]').should('not.exist');
|
||||
});
|
||||
it('self contained info', () => {
|
||||
cy.visit('http://localhost:4001/info');
|
||||
cy.get('.App').should('not.contain', 'Event List');
|
||||
cy.get('.App').should('not.contain', 'Timer Control');
|
||||
cy.get('.App').should('not.contain', 'Messages Control');
|
||||
cy.get('.App').should('contain', 'Info');
|
||||
cy.get('[data-testid="panel-event-list"]').should('not.exist');
|
||||
cy.get('[data-testid="panel-timer-control"]').should('not.exist');
|
||||
cy.get('[data-testid="panel-messages-control"]').should('not.exist');
|
||||
cy.get('[data-testid="panel-info"]').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
it('table routes', () => {
|
||||
cy.visit('http://localhost:4001/table');
|
||||
cy.contains('Running Timer');
|
||||
cy.get('[data-testid="cuesheet"]').should('exist');
|
||||
|
||||
cy.visit('http://localhost:4001/cuesheet');
|
||||
cy.contains('Running Timer');
|
||||
cy.get('[data-testid="cuesheet"]').should('exist');
|
||||
|
||||
cy.visit('http://localhost:4001/cuelist');
|
||||
cy.contains('Running Timer');
|
||||
cy.get('[data-testid="cuesheet"]').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user