mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-11 10:23:54 +00:00
V2 styles part2 (#233)
* style: create tag to display network messages * style: create button style for playback * style: review style for QuickEntry * style: review style for Info panel * style: review style for Event blocks * style: review style for Message control panel * style: review style for Playback control * style: review application styles * style: review block styles * refactor: TapButton has active state * style: small style tweaks * refactor: type checks
This commit is contained in:
@@ -3,8 +3,6 @@ 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 { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
interface ActionButtonProps {
|
||||
showAdd?: boolean;
|
||||
showDelay?: boolean;
|
||||
@@ -22,14 +20,14 @@ export default function ActionButtons(props: ActionButtonProps) {
|
||||
|
||||
return (
|
||||
<Menu isLazy lazyBehavior='unmount'>
|
||||
<Tooltip label='Add ...' openDelay={tooltipDelayMid}>
|
||||
<Tooltip label='Add ...'>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
aria-label='Options'
|
||||
size='sm'
|
||||
icon={<FiPlus />}
|
||||
colorScheme='blue'
|
||||
variant='ghost'
|
||||
colorScheme='white'
|
||||
variant='outline'
|
||||
/>
|
||||
</Tooltip>
|
||||
<MenuList style={menuStyle}>
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function TooltipLoadingActionBtn(props) {
|
||||
const { clickHandler, icon, size = 'xs', tooltip, ...rest } = props;
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
setLoading(true);
|
||||
clickHandler();
|
||||
}, [clickHandler, setLoading]);
|
||||
|
||||
return (
|
||||
<Tooltip label={tooltip} shouldWrapChildren={loading}>
|
||||
<IconButton
|
||||
aria-label={tooltip}
|
||||
size={size}
|
||||
icon={icon}
|
||||
onClick={handleClick}
|
||||
disabled={loading}
|
||||
isLoading={loading}
|
||||
{...rest}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
TooltipLoadingActionBtn.propTypes = {
|
||||
clickHandler: PropTypes.func,
|
||||
icon: PropTypes.element,
|
||||
size: PropTypes.oneOf(['xs', 'sm', 'md', 'lg']),
|
||||
tooltip: PropTypes.string,
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { IconButton, IconButtonProps, Tooltip } from '@chakra-ui/react';
|
||||
|
||||
interface TooltipLoadingActionBtnProps extends IconButtonProps {
|
||||
clickHandler: () => void;
|
||||
tooltip: string;
|
||||
openDelay?: number;
|
||||
}
|
||||
|
||||
export default function TooltipLoadingActionBtn(props: TooltipLoadingActionBtnProps) {
|
||||
const { clickHandler, icon, size = 'xs', tooltip, openDelay = 0, ...rest } = props;
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
setLoading(true);
|
||||
clickHandler();
|
||||
}, [clickHandler, setLoading]);
|
||||
|
||||
return (
|
||||
<Tooltip label={tooltip} shouldWrapChildren={loading} openDelay={openDelay}>
|
||||
<IconButton
|
||||
{...rest}
|
||||
aria-label={tooltip}
|
||||
size={size}
|
||||
icon={icon}
|
||||
onClick={handleClick}
|
||||
disabled={loading}
|
||||
isLoading={loading}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
@use '../../../theme/main' as *;
|
||||
|
||||
.header,
|
||||
.headerRoll {
|
||||
.header {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 0.9em;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header {
|
||||
color: $header-gray;
|
||||
}
|
||||
|
||||
.headerRoll {
|
||||
color: $ontime-roll;
|
||||
}
|
||||
|
||||
.moreExpanded,
|
||||
.moreCollapsed {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { IconButton, Input, InputGroup, InputLeftElement } from '@chakra-ui/react';
|
||||
import { IoLink } from '@react-icons/all-files/io5/IoLink';
|
||||
import { Button, Input, InputGroup, InputLeftElement, Tooltip } from '@chakra-ui/react';
|
||||
import { LoggingContext } from 'common/context/LoggingContext';
|
||||
import { forgivingStringToMillis } from 'common/utils/dateConfig';
|
||||
import { stringFromMillis } from 'common/utils/time';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { tooltipDelayFast } from '../../../ontimeConfig';
|
||||
|
||||
import style from './TimeInput.module.scss';
|
||||
|
||||
export default function TimeInput(props) {
|
||||
@@ -126,18 +127,46 @@ export default function TimeInput(props) {
|
||||
|
||||
const isDelayed = delay != null && delay !== 0;
|
||||
|
||||
/*
|
||||
<IconButton
|
||||
size='sm'
|
||||
icon="s"
|
||||
aria-label='automate'
|
||||
colorScheme='white'
|
||||
style={{ borderRadius: '2px', width: 'min-content' }}
|
||||
tabIndex={-1}
|
||||
variant='ghost'
|
||||
/>
|
||||
*/
|
||||
|
||||
const buttonInitial = () => {
|
||||
if (name === 'timeStart') return 'S';
|
||||
if (name === 'timeEnd') return 'E';
|
||||
if (name === 'duration') return 'D';
|
||||
}
|
||||
|
||||
const buttonTooltip = () => {
|
||||
if (name === 'timeStart') return 'Start';
|
||||
if (name === 'timeEnd') return 'End';
|
||||
if (name === 'duration') return 'Duration';
|
||||
}
|
||||
|
||||
return (
|
||||
<InputGroup size='sm' className={`${style.timeInput} ${isDelayed ? style.delayed : ''}`}>
|
||||
<InputLeftElement width='fit-content'>
|
||||
<IconButton
|
||||
<Tooltip label={buttonTooltip()} openDelay={tooltipDelayFast}>
|
||||
<Button
|
||||
size='sm'
|
||||
icon={<IoLink style={{ transform: 'rotate(-45deg)' }} />}
|
||||
aria-label='automate'
|
||||
colorScheme='blue'
|
||||
style={{ borderRadius: '2px', width: 'min-content' }}
|
||||
variant='filled'
|
||||
tabIndex={-1}
|
||||
variant='ghost'
|
||||
/>
|
||||
backgroundColor='#303030'
|
||||
color='#fffffa'
|
||||
borderRadius='2px 0 0 2px'
|
||||
border={isDelayed ? "1px solid #d69e2e55" : "1px solid transparent"}
|
||||
>
|
||||
{buttonInitial()}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
@use '../../../theme/main' as *;
|
||||
|
||||
.copyTag {
|
||||
display: flex;
|
||||
border-radius: 2px;
|
||||
border: 1px solid $action-blue;
|
||||
cursor: pointer;
|
||||
font-size: 0.75em;
|
||||
width: max-content;
|
||||
|
||||
&:active {
|
||||
border: 1px solid $text-white;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: $action-blue;
|
||||
color: $text-white;
|
||||
padding: 0 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: $label-gray;
|
||||
padding: 0 4px;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { Tooltip } from '@chakra-ui/react';
|
||||
|
||||
import { tooltipDelayFast } from '../../../ontimeConfig';
|
||||
|
||||
import style from './CopyTag.module.scss';
|
||||
|
||||
interface CopyTagProps {
|
||||
label?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function CopyTag(props: PropsWithChildren<CopyTagProps>) {
|
||||
const { label, className, children } = props;
|
||||
|
||||
return (
|
||||
<Tooltip label='Click to copy' openDelay={tooltipDelayFast}>
|
||||
<button
|
||||
className={`${style.copyTag} ${className}`}
|
||||
onClick={() => navigator.clipboard.writeText(children as string)}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{label && (
|
||||
<span className={style.label}>
|
||||
{label}
|
||||
</span>
|
||||
)}
|
||||
<span className={style.text}>
|
||||
{children}
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@@ -28,10 +28,13 @@ export function formatDisplay(seconds, hideZero = false) {
|
||||
|
||||
/**
|
||||
* @description Converts milliseconds to seconds
|
||||
* @param {number} millis - time in seconds
|
||||
* @param {number | null} millis - time in seconds
|
||||
* @returns {number} Amount in seconds
|
||||
*/
|
||||
export const millisToSeconds = (millis) => {
|
||||
if (millis === null) {
|
||||
return 0;
|
||||
}
|
||||
return millis < 0 ? Math.ceil(millis / mts) : Math.floor(millis / mts);
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export const nowInMillis = () => {
|
||||
|
||||
/**
|
||||
* @description Converts milliseconds to string representing time
|
||||
* @param {number} ms - time in milliseconds
|
||||
* @param {number | null} ms - time in milliseconds
|
||||
* @param {boolean} showSeconds - weather to show the seconds
|
||||
* @param {string} delim - character between HH MM SS
|
||||
* @param {string} ifNull - what to return if value is null
|
||||
|
||||
@@ -2,6 +2,7 @@ import { IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import { IoMicOffOutline } from '@react-icons/all-files/io5/IoMicOffOutline';
|
||||
import { IoMicSharp } from '@react-icons/all-files/io5/IoMicSharp';
|
||||
|
||||
import CopyTag from '../../../common/components/osc-tag/CopyTag';
|
||||
import { useMessageControlProvider } from '../../../common/hooks/useSocketProvider';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
@@ -52,8 +53,11 @@ export default function MessageControl() {
|
||||
aria-label='Toggle On Air'
|
||||
/>
|
||||
</Tooltip>
|
||||
<span className={style.onAirLabel}>On Air</span>
|
||||
<span className={style.oscLabel}>{`/ontime/offAir << OSC >> /ontime/onAir`}</span>
|
||||
<div className={style.onAirLabel}>On Air</div>
|
||||
<div className={style.oscLabel}>
|
||||
<CopyTag label='OSC'>/ontime/offAir</CopyTag>
|
||||
<CopyTag label='OSC'>/ontime/offAir</CopyTag>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
@use '../../../theme/main' as *;
|
||||
@use '../../../theme/mixins' as *;
|
||||
|
||||
|
||||
@mixin message-control-label() {
|
||||
font-size: 0.9em;
|
||||
color: $label-gray;
|
||||
}
|
||||
.messageContainer,
|
||||
.onAirToggle {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.messageContainer {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
.inputItems {
|
||||
display: grid;
|
||||
@@ -20,8 +25,7 @@
|
||||
.label {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 0.9em;
|
||||
color: $label-gray;
|
||||
@include message-control-label;
|
||||
}
|
||||
|
||||
.inputRowActive {
|
||||
@@ -33,25 +37,30 @@
|
||||
|
||||
.onAirToggle {
|
||||
margin-top: 1em;
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'btn label'
|
||||
'btn osc';
|
||||
grid-template-columns: 2.5em 1fr;
|
||||
grid-template-rows: 1.2em 0.8em;
|
||||
'btn label'
|
||||
'btn osc';
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
align-items: center;
|
||||
|
||||
.btn {
|
||||
aspect-ratio: 1;
|
||||
grid-area: btn;
|
||||
height: 100%;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.onAirLabel {
|
||||
grid-area: label;
|
||||
font-size: 1.2em;
|
||||
@include message-control-label;
|
||||
}
|
||||
|
||||
.oscLabel {
|
||||
@include osc-label;
|
||||
grid-area: osc;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import PauseIconBtn from '../../../common/components/buttons/PauseIconBtn';
|
||||
import RollIconBtn from '../../../common/components/buttons/RollIconBtn';
|
||||
import StartIconBtn from '../../../common/components/buttons/StartIconBtn';
|
||||
|
||||
import style from './PlaybackControl.module.scss';
|
||||
|
||||
export default function Playback(props) {
|
||||
const { playback, selectedId, playbackControl, noEvents } = props;
|
||||
const isRolling = playback === 'roll';
|
||||
|
||||
return (
|
||||
<div className={style.playbackContainer}>
|
||||
<StartIconBtn
|
||||
active={playback === 'start'}
|
||||
clickhandler={() => playbackControl.start()}
|
||||
disabled={!selectedId || isRolling || noEvents}
|
||||
/>
|
||||
<PauseIconBtn
|
||||
active={playback === 'pause'}
|
||||
clickhandler={() => playbackControl.pause()}
|
||||
disabled={!selectedId || isRolling || noEvents || playback !== 'start'}
|
||||
/>
|
||||
<RollIconBtn
|
||||
active={playback === 'roll'}
|
||||
disabled={playback === 'roll' || noEvents}
|
||||
clickhandler={() => playbackControl.roll()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Playback.propTypes = {
|
||||
playback: PropTypes.string,
|
||||
selectedId: PropTypes.string,
|
||||
playbackControl: PropTypes.shape({
|
||||
start: PropTypes.func,
|
||||
pause: PropTypes.func,
|
||||
roll: PropTypes.func,
|
||||
}).isRequired,
|
||||
noEvents: PropTypes.bool.isRequired,
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
import { Tooltip } from '@chakra-ui/react';
|
||||
import { IoPause } from '@react-icons/all-files/io5/IoPause';
|
||||
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
|
||||
import { IoTimeOutline } from '@react-icons/all-files/io5/IoTimeOutline';
|
||||
|
||||
import { usePlaybackControlProvider } from '../../../common/hooks/useSocketProvider';
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
import TapButton from './TapButton';
|
||||
|
||||
import style from './PlaybackControl.module.scss';
|
||||
|
||||
interface PlaybackProps {
|
||||
playback: Playstate;
|
||||
selectedId: string;
|
||||
noEvents: boolean;
|
||||
}
|
||||
|
||||
export default function Playback(props: PlaybackProps) {
|
||||
const { playback, selectedId, noEvents } = props;
|
||||
const isRolling = playback === 'roll';
|
||||
const { setPlayback } = usePlaybackControlProvider();
|
||||
|
||||
return (
|
||||
<div className={style.playbackContainer}>
|
||||
<Tooltip label='Start playback' openDelay={100}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.start()}
|
||||
disabled={!selectedId || isRolling || noEvents}
|
||||
theme='start'
|
||||
active={playback === 'start'}
|
||||
>
|
||||
<IoPlay />
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label='Pause playback' openDelay={tooltipDelayMid}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.pause()}
|
||||
disabled={!selectedId || isRolling || noEvents || playback !== 'start'}
|
||||
theme='pause'
|
||||
active={playback === 'pause'}
|
||||
>
|
||||
<IoPause />
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label='Start roll mode' openDelay={tooltipDelayMid}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.roll()}
|
||||
disabled={playback === 'roll' || noEvents}
|
||||
theme='roll'
|
||||
active={isRolling}
|
||||
>
|
||||
<IoTimeOutline />
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -8,9 +8,6 @@
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.playbackContainer {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.timeContainer {
|
||||
display: grid;
|
||||
@@ -65,7 +62,7 @@
|
||||
}
|
||||
|
||||
.indNegativeActive {
|
||||
background-color: $ontime-pink;
|
||||
background-color: $ontime-pink-variant;
|
||||
}
|
||||
|
||||
.indDelayActive {
|
||||
@@ -74,11 +71,11 @@
|
||||
|
||||
.btn {
|
||||
grid-area: btn;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
width: 100%;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.minus {
|
||||
@@ -87,6 +84,12 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.start,
|
||||
.finish,
|
||||
.roll {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.start {
|
||||
grid-area: sta;
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,4 +1,5 @@
|
||||
import { usePlaybackControlProvider } from '../../../common/hooks/useSocketProvider';
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
|
||||
import PlaybackButtons from './PlaybackButtons';
|
||||
import PlaybackTimer from './PlaybackTimer';
|
||||
@@ -6,20 +7,18 @@ import PlaybackTimer from './PlaybackTimer';
|
||||
import style from './PlaybackControl.module.scss';
|
||||
|
||||
export default function PlaybackControl() {
|
||||
const { data, setPlayback } = usePlaybackControlProvider();
|
||||
const { data } = usePlaybackControlProvider();
|
||||
|
||||
return (
|
||||
<div className={style.mainContainer}>
|
||||
<PlaybackTimer
|
||||
playback={data.playback}
|
||||
playback={data.playback as Playstate}
|
||||
selectedId={data.selectedEventId}
|
||||
handleIncrement={(amount) => setPlayback.delay(amount)}
|
||||
/>
|
||||
<PlaybackButtons
|
||||
playback={data.playback}
|
||||
selectedId={data.selectedEventId}
|
||||
noEvents={data.numEvents < 1}
|
||||
playbackControl={setPlayback}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
+48
-65
@@ -1,39 +1,34 @@
|
||||
import { memo } from 'react';
|
||||
import { Button, Tooltip } from '@chakra-ui/react';
|
||||
import { Tooltip } from '@chakra-ui/react';
|
||||
import TimerDisplay from 'common/components/countdown/TimerDisplay';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useTimerProvider } from '../../../common/hooks/useSocketProvider';
|
||||
import {
|
||||
usePlaybackControlProvider,
|
||||
useTimerProvider,
|
||||
} from '../../../common/hooks/useSocketProvider';
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
import { millisToSeconds } from '../../../common/utils/dateConfig';
|
||||
import { stringFromMillis } from '../../../common/utils/time';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
import TapButton from './TapButton';
|
||||
|
||||
import style from './PlaybackControl.module.scss';
|
||||
|
||||
const areEqual = (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.playback === nextProps.playback &&
|
||||
prevProps.selectedId === nextProps.selectedId
|
||||
);
|
||||
};
|
||||
interface PlaybackTimerProps {
|
||||
playback: Playstate;
|
||||
selectedId: string | null;
|
||||
}
|
||||
|
||||
const incrementProps = {
|
||||
size: 'sm',
|
||||
width: '2em',
|
||||
colorScheme: 'white',
|
||||
variant: 'outline',
|
||||
fontSize: '12px',
|
||||
};
|
||||
|
||||
const PlaybackTimer = (props) => {
|
||||
const { playback, handleIncrement, selectedId } = props;
|
||||
export default function PlaybackTimer(props: PlaybackTimerProps) {
|
||||
const { playback, selectedId } = props;
|
||||
const { setPlayback } = usePlaybackControlProvider();
|
||||
const timerData = useTimerProvider();
|
||||
const started = stringFromMillis(timerData.startedAt, true);
|
||||
const finish = stringFromMillis(timerData.expectedFinish, true);
|
||||
const isRolling = playback === 'roll';
|
||||
const isWaiting = timerData.secondaryTimer > 0 && timerData.current == null;
|
||||
const isWaiting = timerData.secondaryTimer !== null && timerData.secondaryTimer > 0 && timerData.current === null;
|
||||
const disableButtons = selectedId == null || isRolling;
|
||||
const isOvertime = timerData.current < 0;
|
||||
const isOvertime = timerData.current !== null && timerData.current < 0;
|
||||
|
||||
return (
|
||||
<div className={style.timeContainer}>
|
||||
@@ -68,55 +63,43 @@ const PlaybackTimer = (props) => {
|
||||
</>
|
||||
)}
|
||||
<div className={style.btn}>
|
||||
<Tooltip label='Remove 1 minute' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
||||
<Button
|
||||
{...incrementProps}
|
||||
<Tooltip label='Remove 1 minute' openDelay={tooltipDelayMid}
|
||||
shouldWrapChildren={disableButtons}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.delay(-5)}
|
||||
disabled={disableButtons}
|
||||
onClick={() => handleIncrement(-1)}
|
||||
_hover={!disableButtons && { bg: '#ebedf0', color: '#333' }}
|
||||
>
|
||||
-1
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip label='Add 1 minute' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
||||
<Button
|
||||
{...incrementProps}
|
||||
disabled={disableButtons}
|
||||
onClick={() => handleIncrement(1)}
|
||||
_hover={!disableButtons && { bg: '#ebedf0', color: '#333' }}
|
||||
>
|
||||
+1
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip label='Remove 5 minutes' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
||||
<Button
|
||||
{...incrementProps}
|
||||
disabled={disableButtons}
|
||||
onClick={() => handleIncrement(-5)}
|
||||
_hover={!disableButtons && { bg: '#ebedf0', color: '#333' }}
|
||||
>
|
||||
square>
|
||||
-5
|
||||
</Button>
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
<Tooltip label='Add 5 minutes' openDelay={tooltipDelayMid} shouldWrapChildren={disableButtons}>
|
||||
<Button
|
||||
{...incrementProps}
|
||||
<Tooltip label='Remove 1 minute' openDelay={tooltipDelayMid}
|
||||
shouldWrapChildren={disableButtons}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.delay(-5)}
|
||||
disabled={disableButtons}
|
||||
onClick={() => handleIncrement(5)}
|
||||
_hover={!disableButtons && { bg: '#ebedf0', color: '#333' }}
|
||||
>
|
||||
+5
|
||||
</Button>
|
||||
square>
|
||||
-1
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
<Tooltip label='Remove 1 minute' openDelay={tooltipDelayMid}
|
||||
shouldWrapChildren={disableButtons}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.delay(-5)}
|
||||
disabled={disableButtons}
|
||||
square>
|
||||
1
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
<Tooltip label='Remove 1 minute' openDelay={tooltipDelayMid}
|
||||
shouldWrapChildren={disableButtons}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.delay(-5)}
|
||||
disabled={disableButtons}
|
||||
square>
|
||||
5
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(PlaybackTimer, areEqual);
|
||||
|
||||
PlaybackTimer.propTypes = {
|
||||
playback: PropTypes.string,
|
||||
handleIncrement: PropTypes.func.isRequired,
|
||||
selectedId: PropTypes.string,
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
@use '../../../theme/main' as *;
|
||||
|
||||
@mixin tap-factory($theme-front, $theme-bg, $theme-high) {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
font-size: 22px;
|
||||
background-color: $theme-bg;
|
||||
color: $theme-front;
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
aspect-ratio: 3/1;
|
||||
box-shadow: $theme-high 0 0 2px;
|
||||
transition: background-color 0.3s;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
background-color: $opacity-disabled;
|
||||
box-shadow: none;
|
||||
opacity: $opacity-disabled;
|
||||
}
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: $theme-high;
|
||||
}
|
||||
|
||||
&:active:not(:disabled) {
|
||||
color: $theme-bg;
|
||||
background-color: $theme-front;
|
||||
transition: background-color 0.15s;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $theme-high;
|
||||
}
|
||||
}
|
||||
|
||||
.tapButton.neutral{
|
||||
@include tap-factory(rgba(255, 255, 255, 0.867), #303030, #363636);
|
||||
}
|
||||
|
||||
.tapButton.start {
|
||||
@include tap-factory(rgba(255, 255, 255, 0.867), #303030, $ontime-accent);
|
||||
}
|
||||
|
||||
.tapButton.roll {
|
||||
@include tap-factory(rgba(255, 255, 255, 0.867), #303030, $ontime-roll);
|
||||
}
|
||||
|
||||
.tapButton.pause {
|
||||
@include tap-factory(rgba(255, 255, 255, 0.867), #303030, $ontime-paused);
|
||||
}
|
||||
|
||||
.tapButton.ontime {
|
||||
@include tap-factory(rgba(255, 255, 255, 0.867), #303030, $ontime-pink);
|
||||
}
|
||||
|
||||
.tapButton.stop {
|
||||
@include tap-factory(rgba(255, 255, 255, 0.867), #303030, $ontime-red);
|
||||
}
|
||||
|
||||
.tapButton.square {
|
||||
aspect-ratio: 1;
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ForwardedRef, forwardRef, PropsWithChildren } from 'react';
|
||||
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
|
||||
import style from './TapButton.module.scss';
|
||||
|
||||
interface TapButtonProps {
|
||||
disabled?: boolean;
|
||||
square?: boolean;
|
||||
onClick: () => void;
|
||||
theme?: Playstate | 'neutral';
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
const TapButton = forwardRef((props: PropsWithChildren<TapButtonProps>, ref: ForwardedRef<HTMLButtonElement> ) => {
|
||||
const { children, disabled, onClick, theme = 'neutral', square, active } = props;
|
||||
return (
|
||||
<button
|
||||
className={`${style.tapButton} ${style[theme]} ${square ? style.square : ''} ${active ? style.active : ''}`}
|
||||
disabled={disabled}
|
||||
type='button'
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
TapButton.displayName = "TabButton";
|
||||
export default TapButton;
|
||||
@@ -1,53 +0,0 @@
|
||||
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';
|
||||
|
||||
import TransportIconBtn from '../../../common/components/buttons/TransportIconBtn';
|
||||
import UnloadIconBtn from '../../../common/components/buttons/UnloadIconBtn';
|
||||
|
||||
import style from './PlaybackControl.module.scss';
|
||||
|
||||
export default function Transport(props) {
|
||||
const { playback, selectedId, playbackControl, noEvents } = props;
|
||||
const isRolling = playback === 'roll';
|
||||
|
||||
return (
|
||||
<div className={style.playbackContainer}>
|
||||
<TransportIconBtn
|
||||
clickHandler={() => playbackControl.previous()}
|
||||
disabled={isRolling || noEvents}
|
||||
tooltip='Previous event'
|
||||
icon={<IoPlaySkipBack size='22px' />}
|
||||
/>
|
||||
<TransportIconBtn
|
||||
clickHandler={() => playbackControl.next()}
|
||||
disabled={isRolling || noEvents}
|
||||
tooltip='Next event'
|
||||
icon={<IoPlaySkipForward size='22px' />}
|
||||
/>
|
||||
<TransportIconBtn
|
||||
clickHandler={() => playbackControl.reload()}
|
||||
disabled={selectedId == null || isRolling || noEvents}
|
||||
tooltip='Reload event'
|
||||
icon={<IoPlayBack size='22px' />}
|
||||
/>
|
||||
<UnloadIconBtn
|
||||
clickHandler={() => playbackControl.stop()}
|
||||
disabled={(selectedId == null && !isRolling) || noEvents}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Transport.propTypes = {
|
||||
playback: PropTypes.string,
|
||||
selectedId: PropTypes.string,
|
||||
playbackControl: PropTypes.shape({
|
||||
previous: PropTypes.func,
|
||||
next: PropTypes.func,
|
||||
reload: PropTypes.func,
|
||||
stop: PropTypes.func,
|
||||
}).isRequired,
|
||||
noEvents: PropTypes.bool.isRequired,
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import { Tooltip } from '@chakra-ui/react';
|
||||
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 { IoStop } from '@react-icons/all-files/io5/IoStop';
|
||||
|
||||
import { usePlaybackControlProvider } from '../../../common/hooks/useSocketProvider';
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
import TapButton from './TapButton';
|
||||
|
||||
import style from './PlaybackControl.module.scss';
|
||||
|
||||
interface TransportProps {
|
||||
playback: Playstate;
|
||||
selectedId: string;
|
||||
noEvents: boolean;
|
||||
}
|
||||
|
||||
export default function Transport(props: TransportProps) {
|
||||
const { playback, selectedId, noEvents } = props;
|
||||
const { setPlayback } = usePlaybackControlProvider();
|
||||
const isRolling = playback === 'roll';
|
||||
|
||||
return (
|
||||
<div className={style.playbackContainer}>
|
||||
<Tooltip label='Previous event' openDelay={tooltipDelayMid}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.previous()}
|
||||
disabled={isRolling || noEvents}
|
||||
>
|
||||
<IoPlaySkipBack />
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
<Tooltip label='Next event' openDelay={tooltipDelayMid}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.next()}
|
||||
disabled={isRolling || noEvents}
|
||||
>
|
||||
<IoPlaySkipForward />
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
<Tooltip label='Reload event' openDelay={tooltipDelayMid}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.reload()}
|
||||
disabled={selectedId == null || isRolling || noEvents}
|
||||
>
|
||||
<IoPlayBack />
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
<Tooltip label='Unload Event' openDelay={100}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.stop()}
|
||||
disabled={(selectedId == null && !isRolling) || noEvents}
|
||||
theme='stop'
|
||||
>
|
||||
<IoStop />
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -131,7 +131,7 @@
|
||||
|
||||
.eventEditor {
|
||||
border-radius: 3px 3px 0 0;
|
||||
background-color: $bg-container-l1;
|
||||
background-color: $bg-black;
|
||||
border-top: 1px solid $bg-gray-900;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
@@ -152,7 +152,7 @@
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: $bg-container-l2;
|
||||
background-color: $bg-black;
|
||||
padding: 8px;
|
||||
border-left: 1px solid $bg-container-l3;
|
||||
}
|
||||
@@ -186,8 +186,8 @@
|
||||
|
||||
.playback {
|
||||
grid-area: play;
|
||||
min-height: 270px;
|
||||
max-height: 270px;
|
||||
min-height: 275px;
|
||||
max-height: 380px;
|
||||
min-width: 480px;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ $block-border-radius: 3px;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-content: center;
|
||||
opacity: 0.8;
|
||||
opacity: 0.6;
|
||||
transition: linear 0.1s;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ $block-border-radius: 3px;
|
||||
@mixin drag-style() {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
opacity: 0.1;
|
||||
opacity: 0.3;
|
||||
cursor: grab;
|
||||
transition: opacity 0.3s;
|
||||
&:hover {
|
||||
|
||||
@@ -24,10 +24,10 @@ export default function BlockBlock(props) {
|
||||
clickHandler={() => actionHandler('delete')}
|
||||
icon={<IoRemove />}
|
||||
tooltip='Delete'
|
||||
variant='ghost'
|
||||
_hover={{ bg: 'red.400', color: 'white' }}
|
||||
color='red.500'
|
||||
variant='outline'
|
||||
colorScheme='white'
|
||||
size='sm'
|
||||
aria-label='Delete'
|
||||
/>
|
||||
<ActionButtons showAdd showDelay actionHandler={actionHandler} />
|
||||
</HStack>
|
||||
|
||||
@@ -6,24 +6,21 @@
|
||||
.block {
|
||||
@include block-spacing;
|
||||
|
||||
box-sizing: content-box;
|
||||
display: grid;
|
||||
grid-template-columns: 40px 1fr;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
|
||||
border-radius: 2px 2px $block-border-radius $block-border-radius;
|
||||
background-color: rgba(107, 70, 193, 0.4);
|
||||
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%
|
||||
);
|
||||
border-radius: 2px;
|
||||
border: $border-l3;
|
||||
border-bottom: 4px solid $block-block-color;
|
||||
background-color: $bg-container-l2;
|
||||
}
|
||||
|
||||
/* ================ DRAG ================ */
|
||||
.drag {
|
||||
@include drag-style;
|
||||
color: $block-block-color;
|
||||
}
|
||||
|
||||
/* ============== ACTION ================ */
|
||||
|
||||
@@ -55,9 +55,10 @@ export default function DelayBlock(props) {
|
||||
<Button
|
||||
onClick={applyDelayHandler}
|
||||
size='sm'
|
||||
colorScheme='orange'
|
||||
_hover={{ bg: 'orange.400' }}
|
||||
color="#F57C13"
|
||||
borderColor="#F57C13"
|
||||
leftIcon={<FiCheck />}
|
||||
variant='outline'
|
||||
>
|
||||
Apply delay
|
||||
</Button>
|
||||
@@ -65,9 +66,8 @@ export default function DelayBlock(props) {
|
||||
clickHandler={deleteHandler}
|
||||
icon={<IoRemove />}
|
||||
tooltip='Delete'
|
||||
variant='ghost'
|
||||
_hover={{ bg: 'red.400', color: 'white' }}
|
||||
color='red.500'
|
||||
variant='outline'
|
||||
colorScheme='white'
|
||||
size='sm'
|
||||
/>
|
||||
<ActionButtons showAdd actionHandler={actionHandler} />
|
||||
|
||||
@@ -6,20 +6,17 @@
|
||||
.delay {
|
||||
@include block-spacing;
|
||||
|
||||
box-sizing: content-box;
|
||||
display: grid;
|
||||
grid-template-columns: 32px 1fr auto;
|
||||
grid-template-areas: 'drag inpt btns';
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
border-radius: 2px;
|
||||
border: $border-l3;
|
||||
border-top: 4px solid $block-delay-color;
|
||||
background-color: $bg-container-l2;
|
||||
|
||||
border-radius: $block-border-radius $block-border-radius 2px 2px;
|
||||
background-color: rgba(214, 158, 46, 0.4);
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(214, 158, 46, 0.4) 0%,
|
||||
rgba(214, 158, 46, 0.4) 20%,
|
||||
rgba(214, 158, 46, 0.17) 21%
|
||||
);
|
||||
}
|
||||
|
||||
/* ================ DRAG ================ */
|
||||
@@ -27,6 +24,7 @@
|
||||
.drag {
|
||||
grid-area: drag;
|
||||
@include drag-style;
|
||||
color: $block-delay-color;
|
||||
}
|
||||
|
||||
/* =============== INPUT ================ */
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
grid-template-columns: $binder-width auto 1fr auto;
|
||||
grid-template-rows: 36px 36px 36px $element-spacing;
|
||||
align-items: center;
|
||||
margin: 2px 2px;
|
||||
margin:4px 2px;
|
||||
padding-right: $clearance;
|
||||
|
||||
// style - general
|
||||
@@ -26,7 +26,6 @@
|
||||
// variant
|
||||
&.selected {
|
||||
background-color: $bg-container-l3;
|
||||
box-shadow: 0 0 1px 2px $ontime-accent;
|
||||
}
|
||||
|
||||
&.skip {
|
||||
@@ -87,7 +86,7 @@
|
||||
height: 100%;
|
||||
|
||||
.delayNote {
|
||||
font-size: 10px;
|
||||
font-size: 11px;
|
||||
color: $text-delay;
|
||||
}
|
||||
}
|
||||
@@ -148,9 +147,8 @@
|
||||
border: 1px solid transparent;
|
||||
|
||||
&.enabled {
|
||||
color: $ontime-accent;
|
||||
border: 1px solid $ontime-accent;
|
||||
background-color: $bg-gray-1100;
|
||||
color: $text-white;
|
||||
background-color: $ontime-accent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Draggable } from 'react-beautiful-dnd';
|
||||
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';
|
||||
@@ -108,7 +108,7 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
if (!skip && eventIsPlaying) {
|
||||
playBtnStyles._hover = { bg: '#c05621' };
|
||||
} else if (!skip && !eventIsPlaying) {
|
||||
playBtnStyles._hover = { bg: '#4bffab' };
|
||||
playBtnStyles._hover = { };
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -161,19 +161,15 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<TooltipActionBtn
|
||||
aria-label='Toggle start / pause event'
|
||||
tooltip={eventIsPlaying ? 'Pause event' : 'Start event'}
|
||||
aria-label='Start event'
|
||||
tooltip='Start event'
|
||||
openDelay={tooltipDelayMid}
|
||||
icon={eventIsPlaying ? <IoPause /> : <IoPlayOutline />}
|
||||
icon={eventIsPlaying ? <IoPlay /> : <IoPlayOutline />}
|
||||
disabled={skip}
|
||||
{...blockBtnStyle}
|
||||
variant={eventIsPlaying ? 'solid' : 'ghost'}
|
||||
clickHandler={
|
||||
eventIsPlaying
|
||||
? () => setPlayback.pause()
|
||||
: () => setPlayback.startEvent()
|
||||
}
|
||||
{...playBtnStyles}
|
||||
clickHandler={() => setPlayback.startEvent()}
|
||||
backgroundColor={eventIsPlaying ? '#58A151' : undefined}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -27,8 +27,8 @@ export default function EventBlockActionMenu(props) {
|
||||
|
||||
const blockBtnStyle = {
|
||||
size: 'sm',
|
||||
variant: 'ghost',
|
||||
colorScheme: 'blue',
|
||||
variant: 'outline',
|
||||
colorScheme: 'whiteAlpha',
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useAtomValue } from 'jotai';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import useSubscription from '../../../common/hooks/useSubscription';
|
||||
import EntryBlock from '../entry-block/EntryBlock';
|
||||
import QuickAddBlock from '../quick-add-block/QuickAddBlock';
|
||||
|
||||
import EventListItem from './EventListItem';
|
||||
|
||||
@@ -231,7 +231,7 @@ export default function EventList(props) {
|
||||
/>
|
||||
</div>
|
||||
{((showQuickEntry && index === cursor) || isLast) && (
|
||||
<EntryBlock
|
||||
<QuickAddBlock
|
||||
showKbd={index === cursor}
|
||||
previousId={e.id}
|
||||
previousEventId={previousEventId}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
.bgElement {
|
||||
&.delayed {
|
||||
background: rgba(214, 158, 46, 0.3);
|
||||
background: rgba($block-delay-color, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
@use '../../../theme/main' as *;
|
||||
|
||||
.create {
|
||||
.quickAdd {
|
||||
background-color: $bg-container-l2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -81,4 +81,5 @@
|
||||
border-color: $text-gray-disabled;
|
||||
color: $text-gray-disabled;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
+4
-4
@@ -8,9 +8,9 @@ import { useAtomValue } from 'jotai';
|
||||
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
import style from './EntryBlock.module.scss';
|
||||
import style from './QuickAddBlock.module.scss';
|
||||
|
||||
interface EntryBlockProps {
|
||||
interface QuickAddBlockProps {
|
||||
showKbd: boolean;
|
||||
previousId?: string;
|
||||
previousEventId: string | null;
|
||||
@@ -18,7 +18,7 @@ interface EntryBlockProps {
|
||||
disableAddBlock: boolean;
|
||||
}
|
||||
|
||||
export default function EntryBlock(props: EntryBlockProps) {
|
||||
export default function QuickAddBlock(props: QuickAddBlockProps) {
|
||||
const {
|
||||
showKbd,
|
||||
previousId,
|
||||
@@ -61,7 +61,7 @@ export default function EntryBlock(props: EntryBlockProps) {
|
||||
}, [addEvent, doPublic, doStartTime, emitError, previousId, previousEventId]);
|
||||
|
||||
return (
|
||||
<div className={style.create}>
|
||||
<div className={style.quickAdd}>
|
||||
<Tooltip label='Add Event' openDelay={tooltipDelayMid}>
|
||||
<span
|
||||
className={style.createEvent}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { Button } from '@chakra-ui/react';
|
||||
import { Button, Select } from '@chakra-ui/react';
|
||||
import { FiUsers } from '@react-icons/all-files/fi/FiUsers';
|
||||
import { IoBan } from '@react-icons/all-files/io5/IoBan';
|
||||
import { editorEventId } from 'common/atoms/LocalEventSettings';
|
||||
@@ -17,6 +17,7 @@ import { useAtom } from 'jotai';
|
||||
import useEventsList from '../../common/hooks-query/useEventsList';
|
||||
|
||||
import style from './EventEditor.module.scss';
|
||||
import CopyTag from '../../common/components/osc-tag/CopyTag';
|
||||
|
||||
export default function EventEditor() {
|
||||
const [openId] = useAtom(editorEventId);
|
||||
@@ -104,40 +105,57 @@ export default function EventEditor() {
|
||||
|
||||
return (
|
||||
<div className={style.eventEditor}>
|
||||
<div className={style.timers}>
|
||||
<label className={style.inputLabel}>
|
||||
Start time {delayed && <span className={style.delayLabel}>{addedTime}</span>}
|
||||
{delayed && <div className={style.delayLabel}>{newStart}</div>}
|
||||
</label>
|
||||
<TimeInput
|
||||
name='timeStart'
|
||||
submitHandler={handleSubmit}
|
||||
validationHandler={timerValidationHandler}
|
||||
time={event.timeStart}
|
||||
delay={0}
|
||||
placeholder='Start'
|
||||
/>
|
||||
<label className={style.inputLabel}>
|
||||
End time {delayed && <span className={style.delayLabel}>{addedTime}</span>}
|
||||
{delayed && <div className={style.delayLabel}>{newEnd}</div>}
|
||||
</label>
|
||||
<TimeInput
|
||||
name='timeEnd'
|
||||
submitHandler={handleSubmit}
|
||||
validationHandler={timerValidationHandler}
|
||||
time={event.timeEnd}
|
||||
delay={0}
|
||||
placeholder='End'
|
||||
/>
|
||||
<label className={style.inputLabel}>Duration</label>
|
||||
<TimeInput
|
||||
name='duration'
|
||||
submitHandler={handleSubmit}
|
||||
validationHandler={timerValidationHandler}
|
||||
time={event.duration}
|
||||
delay={0}
|
||||
placeholder='Duration'
|
||||
/>
|
||||
<div className={style.timeOptions}>
|
||||
<div className={style.timers}>
|
||||
<label className={style.inputLabel}>
|
||||
Start time {delayed && <span className={style.delayLabel}>{addedTime}</span>}
|
||||
{delayed && <div className={style.delayLabel}>{newStart}</div>}
|
||||
</label>
|
||||
<TimeInput
|
||||
name='timeStart'
|
||||
submitHandler={handleSubmit}
|
||||
validationHandler={timerValidationHandler}
|
||||
time={event.timeStart}
|
||||
delay={0}
|
||||
placeholder='Start'
|
||||
/>
|
||||
<label className={style.inputLabel}>
|
||||
End time {delayed && <span className={style.delayLabel}>{addedTime}</span>}
|
||||
{delayed && <div className={style.delayLabel}>{newEnd}</div>}
|
||||
</label>
|
||||
<TimeInput
|
||||
name='timeEnd'
|
||||
submitHandler={handleSubmit}
|
||||
validationHandler={timerValidationHandler}
|
||||
time={event.timeEnd}
|
||||
delay={0}
|
||||
placeholder='End'
|
||||
/>
|
||||
<label className={style.inputLabel}>Duration</label>
|
||||
<TimeInput
|
||||
name='duration'
|
||||
submitHandler={handleSubmit}
|
||||
validationHandler={timerValidationHandler}
|
||||
time={event.duration}
|
||||
delay={0}
|
||||
placeholder='Duration'
|
||||
/>
|
||||
</div>
|
||||
<div className={style.timeSettings}>
|
||||
<label className={style.inputLabel}>Timer type</label>
|
||||
<Select size='sm' variant='filled' color='black'>
|
||||
<option value='option1'>Start to end</option>
|
||||
<option value='option2'>Duration</option>
|
||||
<option value='option3'>Follow previous</option>
|
||||
<option value='option3'>Start only</option>
|
||||
</Select>
|
||||
<label className={style.inputLabel}>Countdown style</label>
|
||||
<Select size='sm' variant='filled' color='black'>
|
||||
<option value='option1'>Count down</option>
|
||||
<option value='option2'>Count up</option>
|
||||
<option value='option3'>Clock</option>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div className={style.titles}>
|
||||
<div className={style.left}>
|
||||
@@ -199,7 +217,7 @@ export default function EventEditor() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={style.osc}>{`OSC Trigger /ontime/gotoid/${event.id}`}</div>
|
||||
<CopyTag label='OSC trigger' className={style.osc}>{`/ontime/gotoid/${event.id}`}</CopyTag>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,26 +8,39 @@
|
||||
gap: max(16px, 2vh);
|
||||
|
||||
display: grid;
|
||||
grid-template-areas: 'timers titles';
|
||||
grid-template-areas: 'timeOptions titles';
|
||||
grid-template-columns: auto 1fr;
|
||||
|
||||
.timers,
|
||||
|
||||
.timeOptions,
|
||||
.titles {
|
||||
background-color: $bg-container-l2;
|
||||
background-color: $bg-container-over;
|
||||
border-radius: 2px;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.timers {
|
||||
grid-area: timers;
|
||||
.timeOptions {
|
||||
grid-area: timeOptions;
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.timers, .timeSettings {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.timers label:nth-child(1) {
|
||||
.timers label:nth-child(1),
|
||||
.timeSettings label:nth-child(1),
|
||||
{
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.timeSettings {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.titles {
|
||||
grid-area: titles;
|
||||
display: grid;
|
||||
@@ -57,10 +70,7 @@
|
||||
|
||||
.osc {
|
||||
grid-area: tag;
|
||||
font-size: 14px;
|
||||
color: $ontime-accent;
|
||||
text-align: right;
|
||||
user-select: text;
|
||||
justify-self: end;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ export default function Info() {
|
||||
<span>{selected}</span>
|
||||
</div>
|
||||
<InfoNif />
|
||||
<InfoTitle title='Now' data={titlesNow} />
|
||||
<InfoTitle title='Next' data={titlesNext} />
|
||||
<InfoTitle title='Playing Now' data={titlesNow} />
|
||||
<InfoTitle title='Playing Next' data={titlesNext} />
|
||||
<InfoLogger />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
@include container;
|
||||
}
|
||||
|
||||
|
||||
.main {
|
||||
font-size: 0.9em;
|
||||
color: $label-gray;
|
||||
@@ -19,20 +18,6 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header,
|
||||
.headerRoll {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 0.9em;
|
||||
color: $header-gray;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header {
|
||||
color: $header-gray;
|
||||
}
|
||||
|
||||
.collapsedTitle {
|
||||
font-size: inherit;
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
overflow-y: scroll;
|
||||
font-size: 0.8em;
|
||||
user-select: text;
|
||||
@include third-container;
|
||||
padding: 0 0.5em;
|
||||
margin: 0 0.5em;
|
||||
|
||||
@@ -78,8 +77,8 @@
|
||||
}
|
||||
|
||||
div.active {
|
||||
background: $ontime-accent;
|
||||
color: darken($ontime-accent, 70%);
|
||||
background: $ontime-roll;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.clear {
|
||||
|
||||
@@ -4,3 +4,9 @@
|
||||
gap: 8px;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.labelledSwitch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { memo, useCallback, useContext } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
HStack,
|
||||
IconButton,
|
||||
@@ -8,6 +7,7 @@ import {
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Switch,
|
||||
Tooltip,
|
||||
} from '@chakra-ui/react';
|
||||
import { FiClock } from '@react-icons/all-files/fi/FiClock';
|
||||
@@ -29,13 +29,10 @@ const EventListMenu = () => {
|
||||
const { isCursorLocked, toggleCursorLocked } = useContext(CursorContext);
|
||||
const { addEvent, deleteAllEvents } = useEventAction();
|
||||
|
||||
type ActionTypes = 'event' | 'delay' | 'block' | 'delete-all' | 'toggle-lock';
|
||||
const actionHandler = useCallback(
|
||||
type ActionTypes = 'event' | 'delay' | 'block' | 'delete-all';
|
||||
const eventAction = useCallback(
|
||||
(action: ActionTypes) => {
|
||||
switch (action) {
|
||||
case 'toggle-lock':
|
||||
toggleCursorLocked();
|
||||
break;
|
||||
case 'event':
|
||||
addEvent({ type: action });
|
||||
break;
|
||||
@@ -55,14 +52,14 @@ const EventListMenu = () => {
|
||||
|
||||
return (
|
||||
<HStack className={style.headerButtons}>
|
||||
<Button
|
||||
size='sm'
|
||||
variant={isCursorLocked ? 'solid' : 'ghost'}
|
||||
onClick={() => actionHandler('toggle-lock')}
|
||||
colorScheme='blue'
|
||||
>
|
||||
<label className={style.labelledSwitch}>
|
||||
<Switch
|
||||
defaultChecked={isCursorLocked}
|
||||
onChange={(event) => toggleCursorLocked(event.target.checked)}
|
||||
colorScheme='blue'
|
||||
/>
|
||||
Lock cursor to current
|
||||
</Button>
|
||||
</label>
|
||||
<Menu isLazy lazyBehavior='unmount'>
|
||||
<Tooltip label='Add / Delete ...'>
|
||||
<MenuButton
|
||||
@@ -70,22 +67,22 @@ const EventListMenu = () => {
|
||||
aria-label='Create Menu'
|
||||
size='sm'
|
||||
icon={<FiPlus />}
|
||||
colorScheme='blue'
|
||||
colorScheme='white'
|
||||
variant='outline'
|
||||
/>
|
||||
</Tooltip>
|
||||
<MenuList style={menuStyle}>
|
||||
<MenuItem icon={<FiPlus />} onClick={() => actionHandler('event')}>
|
||||
Add Event first
|
||||
<MenuItem icon={<FiPlus />} onClick={() => eventAction('event')}>
|
||||
Add Event at start
|
||||
</MenuItem>
|
||||
<MenuItem icon={<FiClock />} onClick={() => actionHandler('delay')}>
|
||||
Add Delay first
|
||||
<MenuItem icon={<FiClock />} onClick={() => eventAction('delay')}>
|
||||
Add Delay at start
|
||||
</MenuItem>
|
||||
<MenuItem icon={<FiMinusCircle />} onClick={() => actionHandler('block')}>
|
||||
Add Block first
|
||||
<MenuItem icon={<FiMinusCircle />} onClick={() => eventAction('block')}>
|
||||
Add Block at start
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem icon={<FiTrash2 />} onClick={() => actionHandler('delete-all')} color='red.500'>
|
||||
<MenuItem icon={<FiTrash2 />} onClick={() => eventAction('delete-all')} color='red.500'>
|
||||
Delete All
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
|
||||
@@ -17,15 +17,17 @@ $bg-gray-100: #c0c0c0; // borders and whatnot
|
||||
|
||||
$bg-overlay: rgba(0, 0, 0, 0.85);
|
||||
|
||||
$ontime-accent: #4bffab;
|
||||
// $ontime-accent: #4bffab);
|
||||
$ontime-accent: #58A151;
|
||||
$ontime-accent-text: mix($bg-black, $ontime-accent, 10%);
|
||||
$ontime-pink: #ff7597;
|
||||
$ontime-pink-variant: #ff6969;
|
||||
$ontime-roll: #2b6cb0;
|
||||
$ontime-delay: #dd6b20;
|
||||
$ontime-roll: #0274B6;
|
||||
$ontime-delay: #F57C13;
|
||||
$action-blue: #3182ce;
|
||||
$ontime-paused: #c05621;
|
||||
$opacity-disabled: 0.4;
|
||||
$ontime-red: #E4281E;
|
||||
|
||||
//rgba(255, 255, 255, 0.39); - $bg-gray-700
|
||||
//rgba(255, 255, 255, 0.13); - $bg-gray-900
|
||||
@@ -40,15 +42,15 @@ $opacity-disabled: 0.4;
|
||||
// outdent in level2 - $bg-gray-950
|
||||
//////////////////////////////////// editor
|
||||
|
||||
$notes-color: #d69e2e;
|
||||
$notes-color: #9fd8ff;
|
||||
|
||||
$text-white: #fffffa;
|
||||
$text-gray-disabled: #505050;
|
||||
$header-gray: #ccc;
|
||||
$label-gray: #aaa;
|
||||
$header-gray: $label-gray;
|
||||
$clocks: #ddd;
|
||||
$bg-gray: #f4f4f8;
|
||||
$text-delay: #a97d24;
|
||||
$text-delay: #F57C13;
|
||||
|
||||
$light-bg: #2b6cb0;
|
||||
$light-bg-transparent: #2b6cb055;
|
||||
@@ -65,16 +67,18 @@ $title-gray: #ddd;
|
||||
$subtitle-gray: #aaa;
|
||||
|
||||
//////////////////////////////////// block elements
|
||||
$bg-container-over: #0b1521;
|
||||
$bg-container-over-l1: #132337;
|
||||
$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;
|
||||
$block-delay-color: #E2720D;
|
||||
$delay-text: #d69e2e;
|
||||
$block-delay-border: #d69e2e55;
|
||||
$block-block-color: #805ad5;
|
||||
$block-block-color: #7347AD;
|
||||
$block-border: 1px solid $bg-gray-1100;
|
||||
|
||||
//////////////////////////////////// viewer cards
|
||||
|
||||
@@ -18,10 +18,3 @@
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
@mixin osc-label {
|
||||
color: $ontime-accent-text;
|
||||
font-size: 0.8em;
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user