feat(rundown): clarify offset toggle with labels and info tip

The Absolute/Relative offset toggle in the rundown header gave users no
hint it was even about offsets, and its explanation only lived on
per-button hover (low discoverability, splits a comparative concept).

- Add a small group label to each toolbar toggle (Mode / View / Offset)
  so each control's purpose is self-evident at a glance.
- Add an info icon on the Offset group with a single tooltip explaining
  both modes together, reusing the existing Tooltip + IoInformationCircle
  pattern. Removes the now-redundant per-button offset tooltips.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hj5P4eDgf97zSCzViGHNDy
This commit is contained in:
Claude
2026-07-19 08:51:48 +00:00
parent 5f040092cb
commit a93f2a765e
2 changed files with 71 additions and 38 deletions
@@ -58,8 +58,31 @@
} }
} }
.toggleTooltipTrigger { .controlGroup {
display: flex; display: flex;
align-items: center;
gap: 0.375rem;
}
.groupLabel {
color: $gray-400;
font-size: calc(1rem - 3px);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.02em;
white-space: nowrap;
}
.infoIcon {
display: flex;
align-items: center;
color: $gray-400;
font-size: calc(1rem + 1px);
cursor: help;
&:hover {
color: $gray-200;
}
} }
.apart { .apart {
@@ -3,6 +3,7 @@ import { ToggleGroup } from '@base-ui/react/toggle-group';
import { Toolbar } from '@base-ui/react/toolbar'; import { Toolbar } from '@base-ui/react/toolbar';
import { OffsetMode } from 'ontime-types'; import { OffsetMode } from 'ontime-types';
import { memo } from 'react'; import { memo } from 'react';
import { IoInformationCircle } from 'react-icons/io5';
import Tooltip from '../../../common/components/tooltip/Tooltip'; import Tooltip from '../../../common/components/tooltip/Tooltip';
import { setOffsetMode, useOffsetMode } from '../../../common/hooks/useSocket'; import { setOffsetMode, useOffsetMode } from '../../../common/hooks/useSocket';
@@ -74,52 +75,61 @@ function RundownHeader({ isExtracted, viewMode, setViewMode }: RundownHeaderProp
return ( return (
<Toolbar.Root className={style.header}> <Toolbar.Root className={style.header}>
{showRunEditToggle && ( {showRunEditToggle && (
<ToggleGroup value={[editorMode]} onValueChange={toggleAppMode} className={style.group}> <div className={style.controlGroup}>
<Tooltip <span className={style.groupLabel}>Mode</span>
text='Live playback view with auto-follow' <ToggleGroup value={[editorMode]} onValueChange={toggleAppMode} className={style.group}>
render={<Toolbar.Button render={<Toggle />} value={AppMode.Run} className={style.radioButton} />} <Tooltip
> text='Live playback view with auto-follow'
Run render={<Toolbar.Button render={<Toggle />} value={AppMode.Run} className={style.radioButton} />}
</Tooltip> >
<Tooltip Run
text='Manual editing without playback automation' </Tooltip>
render={<Toolbar.Button render={<Toggle />} value={AppMode.Edit} className={style.radioButton} />} <Tooltip
> text='Manual editing without playback automation'
Edit render={<Toolbar.Button render={<Toggle />} value={AppMode.Edit} className={style.radioButton} />}
</Tooltip> >
</ToggleGroup> Edit
</Tooltip>
</ToggleGroup>
</div>
)} )}
<ToggleGroup value={[viewMode]} onValueChange={toggleViewMode} className={style.group}> <div className={style.controlGroup}>
<Tooltip <span className={style.groupLabel}>View</span>
text='View rundown in list mode' <ToggleGroup value={[viewMode]} onValueChange={toggleViewMode} className={style.group}>
render={<Toolbar.Button render={<Toggle />} value={RundownViewMode.List} className={style.radioButton} />}
>
List
</Tooltip>
<Tooltip
text='View rundown in table mode'
render={<Toolbar.Button render={<Toggle />} value={RundownViewMode.Table} className={style.radioButton} />}
>
Table
</Tooltip>
</ToggleGroup>
{showOffsetToggle && (
<ToggleGroup value={[offsetMode]} onValueChange={toggleOffsetMode} className={style.group}>
<Tooltip <Tooltip
text='Offsets use fixed clock time' text='View rundown in list mode'
render={<Toolbar.Button render={<Toggle />} value={OffsetMode.Absolute} className={style.radioButton} />} render={<Toolbar.Button render={<Toggle />} value={RundownViewMode.List} className={style.radioButton} />}
> >
Absolute List
</Tooltip> </Tooltip>
<Tooltip <Tooltip
text='Offsets follow the rundown relative start' text='View rundown in table mode'
render={<Toolbar.Button render={<Toggle />} value={OffsetMode.Relative} className={style.radioButton} />} render={<Toolbar.Button render={<Toggle />} value={RundownViewMode.Table} className={style.radioButton} />}
> >
Relative Table
</Tooltip> </Tooltip>
</ToggleGroup> </ToggleGroup>
</div>
{showOffsetToggle && (
<div className={style.controlGroup}>
<span className={style.groupLabel}>Offset</span>
<ToggleGroup value={[offsetMode]} onValueChange={toggleOffsetMode} className={style.group}>
<Toolbar.Button render={<Toggle />} value={OffsetMode.Absolute} className={style.radioButton}>
Absolute
</Toolbar.Button>
<Toolbar.Button render={<Toggle />} value={OffsetMode.Relative} className={style.radioButton}>
Relative
</Toolbar.Button>
</ToggleGroup>
<Tooltip
text='Absolute measures offset against the scheduled clock time. Relative measures it from when the rundown actually started.'
render={<span className={style.infoIcon} />}
>
<IoInformationCircle />
</Tooltip>
</div>
)} )}
{showOverflowMenu && <RundownMenu allowNavigation={!isExtracted} />} {showOverflowMenu && <RundownMenu allowNavigation={!isExtracted} />}