more comprihensive disable logic and better description + icons

This commit is contained in:
alex-arc
2026-07-17 18:26:52 +02:00
parent a4f60c702a
commit b8628e569e
4 changed files with 25 additions and 8 deletions
@@ -1,5 +1,5 @@
import { MaybeNumber } from 'ontime-types'; import { MaybeNumber } from 'ontime-types';
import { IoLockClosed, IoLockOpenOutline } from 'react-icons/io5'; import { TbTargetArrow, TbTarget } from 'react-icons/tb';
import IconButton from '../../../../common/components/buttons/IconButton'; 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';
@@ -37,7 +37,7 @@ export default function TargetDurationInput({ duration, targetDuration, submitHa
data-testid='lock__duration' data-testid='lock__duration'
render={<IconButton variant='subtle-white' className={isLocked ? style.active : style.inactive} />} render={<IconButton variant='subtle-white' className={isLocked ? style.active : style.inactive} />}
> >
{isLocked ? <IoLockClosed /> : <IoLockOpenOutline />} {isLocked ? <TbTargetArrow /> : <TbTarget />}
</Tooltip> </Tooltip>
</TimeInputGroup> </TimeInputGroup>
</div> </div>
@@ -1,6 +1,6 @@
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 { Day, EndAction, EntryId, Playback, TimeStrategy, TimerType } from 'ontime-types'; import { Day, EndAction, EntryId, Maybe, OntimeGroup, Playback, TimeStrategy, TimerType } from 'ontime-types';
import { isPlaybackActive } from 'ontime-utils'; import { isPlaybackActive } from 'ontime-utils';
import { MouseEvent, useEffect, useRef } from 'react'; import { MouseEvent, useEffect, useRef } from 'react';
import { import {
@@ -16,6 +16,7 @@ import {
import { TbClockPin, TbFlagFilled, TbListNumbers } from 'react-icons/tb'; import { TbClockPin, TbFlagFilled, TbListNumbers } from 'react-icons/tb';
import { useEntryActionsContext } from '../../../common/context/EntryActionsContext'; import { useEntryActionsContext } from '../../../common/context/EntryActionsContext';
import { useEntry } from '../../../common/hooks-query/useRundown';
import { useContextMenu } from '../../../common/hooks/useContextMenu'; import { useContextMenu } from '../../../common/hooks/useContextMenu';
import { useEntryCopy } from '../../../common/stores/entryCopyStore'; import { useEntryCopy } from '../../../common/stores/entryCopyStore';
import { deviceAlt, deviceMod } from '../../../common/utils/deviceUtils'; import { deviceAlt, deviceMod } from '../../../common/utils/deviceUtils';
@@ -102,6 +103,8 @@ export default function RundownEvent({
const clearSelectedEventId = useEventIdSwapping((state) => state.clearSelectedEventId); const clearSelectedEventId = useEventIdSwapping((state) => state.clearSelectedEventId);
const openRenumberDialog = useRenumberCuesDialogStore((state) => state.onOpen); const openRenumberDialog = useRenumberCuesDialogStore((state) => state.onOpen);
const parentGroup = useEntry(parent) as Maybe<OntimeGroup>;
const { updateEntry, batchUpdateEvents, clone, deleteEntry, groupEntries, swapEvents, matchGroupDuration } = const { updateEntry, batchUpdateEvents, clone, deleteEntry, groupEntries, swapEvents, matchGroupDuration } =
useEntryActionsContext(); useEntryActionsContext();
@@ -176,12 +179,16 @@ export default function RundownEvent({
{ {
type: 'item', type: 'item',
label: 'Match Group Target Duration', label: 'Match Group Target Duration',
description: 'Change event duration to fill the group target',
icon: TbClockPin, icon: TbClockPin,
onClick: () => { onClick: () => {
if (!parent) return; if (!parent) return;
matchGroupDuration(eventId, parent); matchGroupDuration(eventId, parent);
}, },
disabled: !parent, disabled:
!parentGroup ||
parentGroup.targetDuration === null ||
parentGroup.duration === parentGroup.targetDuration,
}, },
{ type: 'divider' }, { type: 'divider' },
{ {
@@ -90,7 +90,12 @@
} }
.lockIcon { .lockIcon {
opacity: 0.6; &.inactive {
color: $muted-gray;
}
&.active {
color: $active-indicator;
}
} }
.over { .over {
@@ -8,11 +8,10 @@ import {
IoChevronUp, IoChevronUp,
IoDuplicateOutline, IoDuplicateOutline,
IoFolderOpenOutline, IoFolderOpenOutline,
IoLockClosed,
IoReorderTwo, IoReorderTwo,
IoTrash, IoTrash,
} from 'react-icons/io5'; } from 'react-icons/io5';
import { TbClockPin } from 'react-icons/tb'; import { TbTargetArrow, TbClockPin } from 'react-icons/tb';
import IconButton from '../../../common/components/buttons/IconButton'; import IconButton from '../../../common/components/buttons/IconButton';
import Tag from '../../../common/components/tag/Tag'; import Tag from '../../../common/components/tag/Tag';
@@ -47,6 +46,8 @@ export default function RundownGroup({ data, hasCursor, collapsed, onCollapse }:
const selectedEvents = useEventSelection((state) => state.selectedEvents); const selectedEvents = useEventSelection((state) => state.selectedEvents);
const entryCopyId = useEntryCopy((state) => state.entryCopyId); const entryCopyId = useEntryCopy((state) => state.entryCopyId);
const isDurationMatching = data.targetDuration !== null && data.targetDuration === data.duration;
const matchDuration = useCallback(() => { const matchDuration = useCallback(() => {
updateEntry({ id: data.id, targetDuration: data.duration }); updateEntry({ id: data.id, targetDuration: data.duration });
}, [data.duration, data.id, updateEntry]); }, [data.duration, data.id, updateEntry]);
@@ -72,6 +73,8 @@ export default function RundownGroup({ data, hasCursor, collapsed, onCollapse }:
label: 'Match Content Duration', label: 'Match Content Duration',
icon: TbClockPin, icon: TbClockPin,
onClick: matchDuration, onClick: matchDuration,
disabled: isDurationMatching,
description: "Change group target duration to match it's contents",
}, },
{ type: 'divider' }, { type: 'divider' },
{ {
@@ -198,7 +201,9 @@ export default function RundownGroup({ data, hasCursor, collapsed, onCollapse }:
<Tag className={style.offsetLabel}>{planOffset}</Tag> <Tag className={style.offsetLabel}>{planOffset}</Tag>
</span> </span>
)} )}
{data.targetDuration !== null && <IoLockClosed className={style.lockIcon} />} {data.targetDuration !== null && (
<TbTargetArrow className={cx([style.lockIcon, isDurationMatching ? style.active : style.inactive])} />
)}
</div> </div>
</div> </div>
</div> </div>