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