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:
Carlos Valente
2025-08-02 06:49:51 +02:00
committed by Carlos Valente
parent 524721002a
commit a358ec0f17
20 changed files with 175 additions and 131 deletions
@@ -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;
+2 -2
View File
@@ -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>
+17 -14
View File
@@ -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%);
} }
@@ -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;
} }
@@ -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>;
}
@@ -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;