mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-22 15:39:11 +00:00
refactor: small style tweaks
refactor: consistent font size on inputs refactor: style tweaks for block spacing refactor: improve skipped styles fix: improve responsiveness in extracted rundown refactor: style tweaks to overview refactor: simplify group target duration
This commit is contained in:
committed by
Carlos Valente
parent
524721002a
commit
a358ec0f17
@@ -9,7 +9,7 @@ import style from './TimeInput.module.scss';
|
|||||||
interface NullableTimeInputProps<T extends string> {
|
interface NullableTimeInputProps<T extends string> {
|
||||||
id?: T;
|
id?: T;
|
||||||
name: T;
|
name: T;
|
||||||
submitHandler: (field: T, value: string) => void;
|
submitHandler: (field: T, value: number) => void;
|
||||||
time?: number | null;
|
time?: number | null;
|
||||||
emptyDisplay: string;
|
emptyDisplay: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
@@ -71,7 +71,7 @@ export default function NullableTimeInput<T extends string>({
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
submitHandler(name, newValue);
|
submitHandler(name, valueInMillis);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[name, submitHandler, time],
|
[name, submitHandler, time],
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
|
||||||
padding-inline: 0.5rem;
|
padding-inline: 0.5rem;
|
||||||
font-size: calc(1rem - 2px);
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
&:hover:not([data-disabled]) {
|
&:hover:not([data-disabled]) {
|
||||||
@@ -57,7 +56,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.popup {
|
.popup {
|
||||||
font-size: calc(1rem - 2px);
|
font-size: 1rem;
|
||||||
background-color: $gray-1200;
|
background-color: $gray-1200;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export function getOffsetText(offset: MaybeNumber): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getOffsetState(offset: MaybeNumber): 'over' | 'under' | 'muted' | null {
|
export function getOffsetState(offset: MaybeNumber): 'over' | 'under' | 'muted' | null {
|
||||||
if (offset === null) return null;
|
if (offset === null) return 'muted';
|
||||||
if (offset === 0) return 'muted';
|
if (offset === 0) return null;
|
||||||
return offset < 0 ? 'over' : 'under';
|
return offset < 0 ? 'over' : 'under';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ export function OffsetOverview() {
|
|||||||
|
|
||||||
const isPlaying = isPlaybackActive(playback);
|
const isPlaying = isPlaybackActive(playback);
|
||||||
const correctedOffset = offset * -1;
|
const correctedOffset = offset * -1;
|
||||||
const offsetState = getOffsetState(offset);
|
const offsetState = getOffsetState(isPlaying ? offset : null);
|
||||||
const offsetText = getOffsetText(isPlaying ? correctedOffset : null);
|
const offsetText = getOffsetText(isPlaying ? correctedOffset : null);
|
||||||
|
|
||||||
return <OverUnder state={offsetState} value={offsetText} testId='offset' />;
|
return <OverUnder state={offsetState} value={offsetText} testId='offset' />;
|
||||||
|
|||||||
@@ -9,14 +9,11 @@
|
|||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
min-width: 5em;
|
min-width: 5em;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: $ui-white;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: '\200b';
|
content: '\200b';
|
||||||
}
|
}
|
||||||
|
|
||||||
&.muted {
|
|
||||||
color: $muted-gray;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.column {
|
.column {
|
||||||
@@ -32,7 +29,7 @@
|
|||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-state="over"] {
|
&[data-state='over'] {
|
||||||
.label .over {
|
.label .over {
|
||||||
color: $playback-over;
|
color: $playback-over;
|
||||||
}
|
}
|
||||||
@@ -41,7 +38,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-state="under"] {
|
&[data-state='under'] {
|
||||||
.label .under {
|
.label .under {
|
||||||
color: $playback-under;
|
color: $playback-under;
|
||||||
}
|
}
|
||||||
@@ -50,7 +47,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-state="muted"] {
|
&[data-state='muted'] {
|
||||||
.clock {
|
.clock {
|
||||||
color: $muted-gray;
|
color: $muted-gray;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ interface TimeLayoutProps {
|
|||||||
|
|
||||||
export function TimeColumn({ label, value, muted, className, testId }: TimeLayoutProps) {
|
export function TimeColumn({ label, value, muted, className, testId }: TimeLayoutProps) {
|
||||||
return (
|
return (
|
||||||
<div className={style.column}>
|
<div className={style.column} data-state={muted ? 'muted' : 'active'}>
|
||||||
<span className={style.label}>{label}</span>
|
<span className={style.label}>{label}</span>
|
||||||
<span className={cx([style.clock, muted && style.muted, className])} data-testid={testId}>
|
<span className={cx([style.clock, className])} data-testid={testId}>
|
||||||
{value}
|
{value}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ import { AppMode, sessionKeys } from '../../ontimeConfig';
|
|||||||
|
|
||||||
import QuickAddButtons from './entry-editor/quick-add-buttons/QuickAddButtons';
|
import QuickAddButtons from './entry-editor/quick-add-buttons/QuickAddButtons';
|
||||||
import QuickAddInline from './entry-editor/quick-add-cursor/QuickAddInline';
|
import QuickAddInline from './entry-editor/quick-add-cursor/QuickAddInline';
|
||||||
import BlockEnd from './rundown-block/BlockEnd';
|
|
||||||
import RundownBlock from './rundown-block/RundownBlock';
|
import RundownBlock from './rundown-block/RundownBlock';
|
||||||
|
import RundownBlockEnd from './rundown-block/RundownBlockEnd';
|
||||||
import { canDrop, makeRundownMetadata, makeSortableList } from './rundown.utils';
|
import { canDrop, makeRundownMetadata, makeSortableList } from './rundown.utils';
|
||||||
import RundownEmpty from './RundownEmpty';
|
import RundownEmpty from './RundownEmpty';
|
||||||
import { useEventSelection } from './useEventSelection';
|
import { useEventSelection } from './useEventSelection';
|
||||||
@@ -444,20 +444,23 @@ export default function Rundown({ data }: RundownProps) {
|
|||||||
|
|
||||||
if (isBlockCollapsed) {
|
if (isBlockCollapsed) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
|
||||||
const parentColour = (entries[parentId] as OntimeBlock | undefined)?.colour;
|
|
||||||
// if the previous element is selected, it will have its own QuickAddInline
|
|
||||||
// we use thisId instead of previousEntryId because the block end does not process
|
|
||||||
// and it does not cause the reassignment of the iteration id to the previous entry
|
|
||||||
return (
|
|
||||||
<Fragment key={entryId}>
|
|
||||||
{isEditMode && rundownMetadata.groupEntries === 0 && (
|
|
||||||
<QuickAddButtons previousEventId={null} parentBlock={parentId} backgroundColor={parentColour} />
|
|
||||||
)}
|
|
||||||
<BlockEnd key={entryId} id={entryId} colour={parentColour} />
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the previous element is selected, it will have its own QuickAddInline
|
||||||
|
// we use thisId instead of previousEntryId because the block end does not process
|
||||||
|
// and it does not cause the reassignment of the iteration id to the previous entry
|
||||||
|
return (
|
||||||
|
<Fragment key={entryId}>
|
||||||
|
{isEditMode && rundownMetadata.groupEntries === 0 && (
|
||||||
|
<QuickAddButtons
|
||||||
|
previousEventId={null}
|
||||||
|
parentBlock={parentId}
|
||||||
|
backgroundColor={rundownMetadata.groupColour}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<RundownBlockEnd key={entryId} id={entryId} colour={rundownMetadata.groupColour} />
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we iterate through a stateful copy of order to make the dnd operations smoother
|
// we iterate through a stateful copy of order to make the dnd operations smoother
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ function RundownExport() {
|
|||||||
>
|
>
|
||||||
<FinderPlacement />
|
<FinderPlacement />
|
||||||
<ViewNavigationMenu suppressSettings />
|
<ViewNavigationMenu suppressSettings />
|
||||||
<div className={style.content}>
|
<div className={style.rundown}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<RundownContextMenu>
|
<RundownContextMenu>
|
||||||
<RundownWrapper isSmallDevice />
|
<RundownWrapper isSmallDevice />
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { OntimeBlock } from 'ontime-types';
|
import { MaybeNumber, OntimeBlock } from 'ontime-types';
|
||||||
import { millisToString, parseUserTime } from 'ontime-utils';
|
import { millisToString } from 'ontime-utils';
|
||||||
|
|
||||||
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
||||||
import SwatchSelect from '../../../common/components/input/colour-input/SwatchSelect';
|
import SwatchSelect from '../../../common/components/input/colour-input/SwatchSelect';
|
||||||
import NullableTimeInput from '../../../common/components/input/time-input/NullableTimeInput';
|
|
||||||
import AppLink from '../../../common/components/link/app-link/AppLink';
|
import AppLink from '../../../common/components/link/app-link/AppLink';
|
||||||
import { useEntryActions } from '../../../common/hooks/useEntryAction';
|
import { useEntryActions } from '../../../common/hooks/useEntryAction';
|
||||||
import useCustomFields from '../../../common/hooks-query/useCustomFields';
|
import useCustomFields from '../../../common/hooks-query/useCustomFields';
|
||||||
import { getOffsetState } from '../../../common/utils/offset';
|
import { getOffsetState } from '../../../common/utils/offset';
|
||||||
import { enDash, timerPlaceholder } from '../../../common/utils/styleUtils';
|
import { cx, enDash, timerPlaceholder } from '../../../common/utils/styleUtils';
|
||||||
import TextLikeInput from '../../../views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput';
|
import TextLikeInput from '../../../views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput';
|
||||||
|
|
||||||
import EntryEditorCustomFields from './composite/EventEditorCustomFields';
|
import EntryEditorCustomFields from './composite/EventEditorCustomFields';
|
||||||
import EventTextArea from './composite/EventTextArea';
|
import EventTextArea from './composite/EventTextArea';
|
||||||
import EntryEditorTextInput from './composite/EventTextInput';
|
import EntryEditorTextInput from './composite/EventTextInput';
|
||||||
|
import TargetDurationInput from './composite/TargetDurationInput';
|
||||||
|
|
||||||
import style from './EntryEditor.module.scss';
|
import style from './EntryEditor.module.scss';
|
||||||
|
|
||||||
// title + colour + custom field labels
|
// title + colour + custom field labels
|
||||||
export type BlockEditorUpdateTextFields = 'targetDuration' | 'title' | 'colour' | string;
|
export type BlockEditorUpdateTextFields = 'title' | 'colour' | string;
|
||||||
export type BlockEditorUpdateMaybeNumberFields = 'targetDuration';
|
export type BlockEditorUpdateMaybeNumberFields = 'targetDuration';
|
||||||
|
|
||||||
interface BlockEditorProps {
|
interface BlockEditorProps {
|
||||||
@@ -31,7 +31,7 @@ export default function BlockEditor({ block }: BlockEditorProps) {
|
|||||||
const { updateEntry } = useEntryActions();
|
const { updateEntry } = useEntryActions();
|
||||||
|
|
||||||
const handleSubmit = useCallback(
|
const handleSubmit = useCallback(
|
||||||
(field: BlockEditorUpdateTextFields | BlockEditorUpdateMaybeNumberFields, value: string | boolean) => {
|
(field: BlockEditorUpdateTextFields | BlockEditorUpdateMaybeNumberFields, value: string | MaybeNumber) => {
|
||||||
// Handle custom fields
|
// Handle custom fields
|
||||||
if (typeof field === 'string' && field.startsWith('custom-')) {
|
if (typeof field === 'string' && field.startsWith('custom-')) {
|
||||||
const fieldLabel = field.split('custom-')[1];
|
const fieldLabel = field.split('custom-')[1];
|
||||||
@@ -40,11 +40,7 @@ export default function BlockEditor({ block }: BlockEditorProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (field === 'targetDuration') {
|
if (field === 'targetDuration') {
|
||||||
if (value === '') {
|
return updateEntry({ id: block.id, targetDuration: value as MaybeNumber });
|
||||||
return updateEntry({ id: block.id, targetDuration: null });
|
|
||||||
}
|
|
||||||
|
|
||||||
return updateEntry({ id: block.id, targetDuration: parseUserTime(value as string) });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// all other strings are text fields
|
// all other strings are text fields
|
||||||
@@ -85,22 +81,22 @@ export default function BlockEditor({ block }: BlockEditorProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={style.inline}>
|
<div className={style.inline}>
|
||||||
<div>
|
|
||||||
<Editor.Label htmlFor='targetDuration'>Target duration</Editor.Label>
|
|
||||||
<NullableTimeInput
|
|
||||||
name='targetDuration'
|
|
||||||
time={block.targetDuration}
|
|
||||||
submitHandler={handleSubmit}
|
|
||||||
emptyDisplay={enDash}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<Editor.Label htmlFor='eventId'>Plan offset</Editor.Label>
|
<Editor.Label htmlFor='eventId'>Plan offset</Editor.Label>
|
||||||
<TextLikeInput offset={planOffsetLabel} className={style.textLikeInput} tabIndex={-1}>
|
<TextLikeInput
|
||||||
|
offset={planOffsetLabel}
|
||||||
|
className={cx([style.textLikeInput, planOffset === null && style.inactive])}
|
||||||
|
tabIndex={-1}
|
||||||
|
>
|
||||||
{planOffset !== null && planOffset > 0 ? '+' : ''}
|
{planOffset !== null && planOffset > 0 ? '+' : ''}
|
||||||
{millisToString(planOffset, { fallback: enDash })}
|
{millisToString(planOffset, { fallback: enDash })}
|
||||||
</TextLikeInput>
|
</TextLikeInput>
|
||||||
</div>
|
</div>
|
||||||
|
<TargetDurationInput
|
||||||
|
duration={block.duration}
|
||||||
|
targetDuration={block.targetDuration}
|
||||||
|
submitHandler={handleSubmit}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -94,16 +94,21 @@
|
|||||||
|
|
||||||
/* approximating the style of a disabled input */
|
/* approximating the style of a disabled input */
|
||||||
.textLikeInput {
|
.textLikeInput {
|
||||||
background-color: rgba($gray-1200, 0.4);
|
background: transparent;
|
||||||
font-weight: 400;
|
justify-content: start;
|
||||||
color: $gray-200;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
width: 7.5em;
|
width: 7.5em;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-bottom: 1px solid $gray-1100;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba($gray-1200, 0.4);
|
background: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inactive {
|
||||||
|
color: $muted-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
color: $active-indicator;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { IoLockClosed, IoLockOpenOutline } from 'react-icons/io5';
|
||||||
|
import { MaybeNumber } from 'ontime-types';
|
||||||
|
|
||||||
|
import IconButton from '../../../../common/components/buttons/IconButton';
|
||||||
|
import * as Editor from '../../../../common/components/editor-utils/EditorUtils';
|
||||||
|
import NullableTimeInput from '../../../../common/components/input/time-input/NullableTimeInput';
|
||||||
|
import Tooltip from '../../../../common/components/tooltip/Tooltip';
|
||||||
|
import { cx, enDash } from '../../../../common/utils/styleUtils';
|
||||||
|
import TimeInputGroup from '../../time-input-flow/TimeInputGroup';
|
||||||
|
|
||||||
|
import style from '../EntryEditor.module.scss';
|
||||||
|
|
||||||
|
interface TargetDurationInputProps {
|
||||||
|
duration: MaybeNumber;
|
||||||
|
targetDuration: MaybeNumber;
|
||||||
|
submitHandler: (field: 'targetDuration', value: MaybeNumber) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TargetDurationInput({ duration, targetDuration, submitHandler }: TargetDurationInputProps) {
|
||||||
|
const isBlocked = targetDuration !== null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Editor.Label htmlFor='targetDuration'>Target duration</Editor.Label>
|
||||||
|
<TimeInputGroup hasDelay={isBlocked && targetDuration !== duration}>
|
||||||
|
<NullableTimeInput
|
||||||
|
name='targetDuration'
|
||||||
|
time={targetDuration}
|
||||||
|
submitHandler={submitHandler}
|
||||||
|
emptyDisplay={enDash}
|
||||||
|
className={isBlocked ? '' : style.inactive}
|
||||||
|
/>
|
||||||
|
<Tooltip
|
||||||
|
text='Lock to target duration'
|
||||||
|
className={cx([style.timeAction, isBlocked && style.active])}
|
||||||
|
onClick={() => submitHandler('targetDuration', isBlocked ? null : duration)}
|
||||||
|
data-testid='lock__duration'
|
||||||
|
render={<IconButton variant='subtle-white' className={isBlocked ? style.active : style.inactive} />}
|
||||||
|
>
|
||||||
|
{isBlocked ? <IoLockClosed /> : <IoLockOpenOutline />}
|
||||||
|
</Tooltip>
|
||||||
|
</TimeInputGroup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
.block {
|
.block {
|
||||||
@include block-styling;
|
@include block-styling;
|
||||||
|
|
||||||
margin-block: 1rem 0.25rem;
|
margin-block: 0.5rem;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 2rem 1fr;
|
grid-template-columns: 2rem 1fr;
|
||||||
grid-template-areas: 'binder header';
|
grid-template-areas: 'binder header';
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.expanded {
|
&.expanded {
|
||||||
margin-block: 1rem 0;
|
margin-block: 0.5rem 0;
|
||||||
border-radius: $block-border-radius $block-border-radius 0 0;
|
border-radius: $block-border-radius $block-border-radius 0 0;
|
||||||
border-bottom: 0.25rem solid color-mix(in srgb, transparent 90%, var(--user-bg, transparent) 10%);
|
border-bottom: 0.25rem solid color-mix(in srgb, transparent 90%, var(--user-bg, transparent) 10%);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -6,5 +6,5 @@
|
|||||||
background-color: var(--user-bg, $gray-1050);
|
background-color: var(--user-bg, $gray-1050);
|
||||||
|
|
||||||
border-radius: 0 0 $block-border-radius $block-border-radius;
|
border-radius: 0 0 $block-border-radius $block-border-radius;
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
+2
-2
@@ -1,14 +1,14 @@
|
|||||||
import { useSortable } from '@dnd-kit/sortable';
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
|
||||||
import style from './BlockEnd.module.scss';
|
import style from './RundownBlockEnd.module.scss';
|
||||||
|
|
||||||
interface BlockEndProps {
|
interface BlockEndProps {
|
||||||
id: string;
|
id: string;
|
||||||
colour?: string;
|
colour?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BlockEnd({ id, colour }: BlockEndProps) {
|
export default function RundownBlockEnd({ id, colour }: BlockEndProps) {
|
||||||
const {
|
const {
|
||||||
attributes: dragAttributes,
|
attributes: dragAttributes,
|
||||||
listeners: dragListeners,
|
listeners: dragListeners,
|
||||||
@@ -37,10 +37,10 @@ function RundownHeader() {
|
|||||||
return (
|
return (
|
||||||
<Toolbar.Root className={style.header}>
|
<Toolbar.Root className={style.header}>
|
||||||
<ToggleGroup value={[editorMode]} onValueChange={toggleAppMode} className={style.group}>
|
<ToggleGroup value={[editorMode]} onValueChange={toggleAppMode} className={style.group}>
|
||||||
<Toolbar.Button render={<Toggle />} value={AppMode.Run} className={style.button}>
|
<Toolbar.Button render={<Toggle />} value={AppMode.Run} className={style.radioButton}>
|
||||||
Run
|
Run
|
||||||
</Toolbar.Button>
|
</Toolbar.Button>
|
||||||
<Toolbar.Button render={<Toggle />} value={AppMode.Edit} className={style.button}>
|
<Toolbar.Button render={<Toggle />} value={AppMode.Edit} className={style.radioButton}>
|
||||||
Edit
|
Edit
|
||||||
</Toolbar.Button>
|
</Toolbar.Button>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
@@ -48,10 +48,10 @@ function RundownHeader() {
|
|||||||
<Editor.Separator className={style.separator} />
|
<Editor.Separator className={style.separator} />
|
||||||
|
|
||||||
<ToggleGroup value={[offsetMode]} onValueChange={toggleOffsetMode} className={style.group}>
|
<ToggleGroup value={[offsetMode]} onValueChange={toggleOffsetMode} className={style.group}>
|
||||||
<Toolbar.Button render={<Toggle />} value={OffsetMode.Absolute} className={style.button}>
|
<Toolbar.Button render={<Toggle />} value={OffsetMode.Absolute} className={style.radioButton}>
|
||||||
Absolute
|
Absolute
|
||||||
</Toolbar.Button>
|
</Toolbar.Button>
|
||||||
<Toolbar.Button render={<Toggle />} value={OffsetMode.Relative} className={style.button}>
|
<Toolbar.Button render={<Toggle />} value={OffsetMode.Relative} className={style.radioButton}>
|
||||||
Relative
|
Relative
|
||||||
</Toolbar.Button>
|
</Toolbar.Button>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
|
|||||||
@@ -1,31 +1,3 @@
|
|||||||
.timeAction {
|
|
||||||
background: $gray-1050;
|
|
||||||
color: $gray-500;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 2rem;
|
|
||||||
width: 2rem;
|
|
||||||
display: grid;
|
|
||||||
place-content: center;
|
|
||||||
aspect-ratio: 1;
|
|
||||||
|
|
||||||
&:hover:not(:disabled):not(:active) {
|
|
||||||
background: $gray-1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active:not(:disabled) {
|
|
||||||
background: $gray-1300;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: 1px solid $blue-500;
|
|
||||||
outline-offset: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: var(--status-color-active-override, $active-indicator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fourtyfive {
|
.fourtyfive {
|
||||||
transform: rotate(-45deg);
|
transform: rotate(-45deg);
|
||||||
}
|
}
|
||||||
@@ -36,28 +8,10 @@
|
|||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputGroup {
|
.inactive {
|
||||||
border: 1px solid transparent;
|
color: $muted-gray;
|
||||||
width: fit-content;
|
}
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
.active {
|
||||||
border-radius: $component-border-radius-md;
|
color: $active-indicator;
|
||||||
|
|
||||||
&.delayed {
|
|
||||||
border: 1px solid $ontime-delay-text;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
max-width: 6.5em;
|
|
||||||
border-radius: $component-border-radius-md 0 0 $component-border-radius-md;
|
|
||||||
border: none;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 1.5rem;
|
|
||||||
height: 2rem;
|
|
||||||
border-radius: 0 $component-border-radius-md $component-border-radius-md 0;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ import { IoAlertCircleOutline, IoLink, IoLockClosed, IoLockOpenOutline, IoUnlink
|
|||||||
import { TimeField, TimeStrategy } from 'ontime-types';
|
import { TimeField, TimeStrategy } from 'ontime-types';
|
||||||
import { dayInMs } from 'ontime-utils';
|
import { dayInMs } from 'ontime-utils';
|
||||||
|
|
||||||
|
import IconButton from '../../../common/components/buttons/IconButton';
|
||||||
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
import * as Editor from '../../../common/components/editor-utils/EditorUtils';
|
||||||
import TimeInput from '../../../common/components/input/time-input/TimeInput';
|
import TimeInput from '../../../common/components/input/time-input/TimeInput';
|
||||||
import Tooltip from '../../../common/components/tooltip/Tooltip';
|
import Tooltip from '../../../common/components/tooltip/Tooltip';
|
||||||
import { useEntryActions } from '../../../common/hooks/useEntryAction';
|
import { useEntryActions } from '../../../common/hooks/useEntryAction';
|
||||||
import { cx } from '../../../common/utils/styleUtils';
|
|
||||||
|
import TimeInputGroup from './TimeInputGroup';
|
||||||
|
|
||||||
import style from './TimeInputFlow.module.scss';
|
import style from './TimeInputFlow.module.scss';
|
||||||
|
|
||||||
@@ -67,7 +69,7 @@ function TimeInputFlow({
|
|||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
{showLabels && <Editor.Label className={style.sectionTitle}>Start time</Editor.Label>}
|
{showLabels && <Editor.Label className={style.sectionTitle}>Start time</Editor.Label>}
|
||||||
<div className={cx([style.inputGroup, hasDelay && style.delayed])}>
|
<TimeInputGroup hasDelay={hasDelay}>
|
||||||
<TimeInput
|
<TimeInput
|
||||||
name='timeStart'
|
name='timeStart'
|
||||||
submitHandler={handleSubmit}
|
submitHandler={handleSubmit}
|
||||||
@@ -78,17 +80,17 @@ function TimeInputFlow({
|
|||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
text='Link start to previous end'
|
text='Link start to previous end'
|
||||||
className={cx([style.timeAction, linkStart && style.active])}
|
|
||||||
onClick={() => handleLink(!linkStart)}
|
onClick={() => handleLink(!linkStart)}
|
||||||
|
render={<IconButton variant='subtle-white' className={linkStart ? style.active : style.inactive} />}
|
||||||
>
|
>
|
||||||
<span className={style.fourtyfive}>{linkStart ? <IoLink /> : <IoUnlink />}</span>
|
<span className={style.fourtyfive}>{linkStart ? <IoLink /> : <IoUnlink />}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</TimeInputGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{showLabels && <Editor.Label>End time</Editor.Label>}
|
{showLabels && <Editor.Label>End time</Editor.Label>}
|
||||||
<div className={cx([style.inputGroup, hasDelay && style.delayed])}>
|
<TimeInputGroup hasDelay={hasDelay}>
|
||||||
<TimeInput
|
<TimeInput
|
||||||
name='timeEnd'
|
name='timeEnd'
|
||||||
submitHandler={handleSubmit}
|
submitHandler={handleSubmit}
|
||||||
@@ -99,18 +101,18 @@ function TimeInputFlow({
|
|||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
text='Lock end'
|
text='Lock end'
|
||||||
className={cx([style.timeAction, isLockedEnd && style.active])}
|
render={<IconButton variant='subtle-white' className={isLockedEnd ? style.active : style.inactive} />}
|
||||||
onClick={() => handleChangeStrategy(TimeStrategy.LockEnd)}
|
onClick={() => handleChangeStrategy(TimeStrategy.LockEnd)}
|
||||||
data-testid='lock__end'
|
data-testid='lock__end'
|
||||||
>
|
>
|
||||||
{isLockedEnd ? <IoLockClosed /> : <IoLockOpenOutline />}
|
{isLockedEnd ? <IoLockClosed /> : <IoLockOpenOutline />}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</TimeInputGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{showLabels && <Editor.Label>Duration</Editor.Label>}
|
{showLabels && <Editor.Label>Duration</Editor.Label>}
|
||||||
<div className={cx([style.inputGroup, hasDelay && style.delayed])}>
|
<TimeInputGroup hasDelay={hasDelay}>
|
||||||
<TimeInput
|
<TimeInput
|
||||||
name='duration'
|
name='duration'
|
||||||
submitHandler={handleSubmit}
|
submitHandler={handleSubmit}
|
||||||
@@ -121,13 +123,13 @@ function TimeInputFlow({
|
|||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
text='Lock duration'
|
text='Lock duration'
|
||||||
className={cx([style.timeAction, isLockedDuration && style.active])}
|
render={<IconButton variant='subtle-white' className={isLockedDuration ? style.active : style.inactive} />}
|
||||||
onClick={() => handleChangeStrategy(TimeStrategy.LockDuration)}
|
onClick={() => handleChangeStrategy(TimeStrategy.LockDuration)}
|
||||||
data-testid='lock__duration'
|
data-testid='lock__duration'
|
||||||
>
|
>
|
||||||
{isLockedDuration ? <IoLockClosed /> : <IoLockOpenOutline />}
|
{isLockedDuration ? <IoLockClosed /> : <IoLockOpenOutline />}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</TimeInputGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{warnings.length > 0 && (
|
{warnings.length > 0 && (
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
.inputGroup {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
width: fit-content;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: $component-border-radius-md;
|
||||||
|
|
||||||
|
&.delayed {
|
||||||
|
border: 1px solid $ontime-delay-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
max-width: 6.5em;
|
||||||
|
border-radius: $component-border-radius-md 0 0 $component-border-radius-md;
|
||||||
|
border: none;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 2rem;
|
||||||
|
border-radius: 0 $component-border-radius-md $component-border-radius-md 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
import { cx } from '../../../common/utils/styleUtils';
|
||||||
|
|
||||||
|
import style from './TimeInputGroup.module.scss';
|
||||||
|
|
||||||
|
interface TimeInputGroupProps {
|
||||||
|
hasDelay?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TimeInputGroup({ hasDelay, children }: PropsWithChildren<TimeInputGroupProps>) {
|
||||||
|
return <div className={cx([style.inputGroup, hasDelay && style.delayed])}>{children}</div>;
|
||||||
|
}
|
||||||
+5
@@ -18,6 +18,11 @@
|
|||||||
&.skip {
|
&.skip {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
.indexColumn {
|
||||||
|
text-decoration: line-through;
|
||||||
|
opacity: $opacity-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
Reference in New Issue
Block a user