feat(op): add group visual relation

This commit is contained in:
Carlos Valente
2026-07-14 10:56:44 +02:00
committed by Carlos Valente
parent a75d35bdc0
commit 5001dda284
5 changed files with 86 additions and 11 deletions
@@ -180,7 +180,13 @@ function Operator({ rundown, rundownMetadata, customFields, settings }: Operator
return (
<Fragment key={entry.id}>
<OperatorGroup key={entry.id} title={entry.title} />
<OperatorGroup
key={entry.id}
title={entry.title}
colour={entry.colour}
count={entry.entries.length}
duration={entry.duration}
/>
{entry.entries.map((nestedEntryId) => {
const nestedEntry = rundown.entries[nestedEntryId];
if (!isOntimeEvent(nestedEntry)) {
@@ -217,6 +223,7 @@ function Operator({ rundown, rundownMetadata, customFields, settings }: Operator
isLinkedToLoaded={isLinkedToLoaded}
isSelected={isLoaded}
isPast={isPast}
groupColour={entry.colour}
selectedRef={isLoaded ? selectedRef : undefined}
showStart={showStart}
subscribed={subscribedData}
@@ -22,17 +22,39 @@
background-color: $gray-1250;
}
&.grouped {
position: relative;
padding-left: 0.35rem;
background:
linear-gradient(90deg, color-mix(in srgb, transparent 88%, var(--group-colour, $gray-500) 12%), transparent 8rem),
$viewer-card-bg-color;
}
&.running {
border-top: 1px solid $gray-1300;
background-color: var(--operator-running-bg-override, $active-green);
}
&.grouped.running {
background:
linear-gradient(90deg, color-mix(in srgb, transparent 82%, var(--group-colour, $gray-500) 18%), transparent 8rem),
var(--operator-running-bg-override, $active-green);
}
&.past {
border-top: 1px solid transparent;
opacity: 0.2;
}
}
.groupRail {
position: absolute;
inset-block: 0;
left: 0;
width: 0.35rem;
background-color: var(--group-colour, $gray-500);
}
.binder {
grid-area: binder;
color: $section-white;
@@ -25,6 +25,7 @@ interface OperatorEventProps {
isLinkedToLoaded: boolean;
isSelected: boolean;
isPast: boolean;
groupColour?: string;
selectedRef?: RefObject<HTMLDivElement | null>;
showStart: boolean;
subscribed: Subscribed;
@@ -46,6 +47,7 @@ function OperatorEvent({
isLinkedToLoaded,
isSelected,
isPast,
groupColour,
selectedRef,
showStart,
subscribed,
@@ -68,7 +70,12 @@ function OperatorEvent({
const mouseHandlers = useLongPress(handleLongPress);
const cueColours = colour && getAccessibleColour(colour);
const operatorClasses = cx([style.event, isSelected && style.running, isPast && style.past]);
const operatorClasses = cx([
style.event,
groupColour && style.grouped,
isSelected && style.running,
isPast && style.past,
]);
const hasFields = subscribed.some((field) => field.value);
const columnCount = subscribed.length ? Math.min(subscribed.length, 4) : 0;
@@ -85,8 +92,10 @@ function OperatorEvent({
data-testid={cue}
ref={selectedRef}
onContextMenu={handleLongPress}
style={groupColour ? ({ '--group-colour': groupColour } as CSSProperties) : undefined}
{...mouseHandlers}
>
{groupColour && <div className={style.groupRail} />}
<div className={style.binder} style={{ ...cueColours }}>
<span className={style.cue}>{cue}</span>
</div>
@@ -1,13 +1,33 @@
.group {
width: 100%;
padding: 0.25rem 0.5rem;
min-height: 2.5rem;
padding: 0.4rem 0.75rem;
border-left: 0.35rem solid var(--group-colour, $gray-500);
background-color: $gray-1350;
background: color-mix(in srgb, transparent 88%, var(--group-colour, $gray-500) 12%);
font-size: 1.25rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 1rem;
letter-spacing: 0;
}
// tablet
@media (min-width: $min-tablet) {
.group {
padding: 0.25rem 1rem;
}
.title {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.meta {
display: flex;
flex: 0 0 auto;
align-items: center;
gap: 0.5rem;
color: rgba($ui-white, 0.55);
font-size: 0.875rem;
font-weight: 500;
}
@@ -1,12 +1,29 @@
import { memo } from 'react';
import { CSSProperties, memo } from 'react';
import { getAccessibleColour } from '../../../common/utils/styleUtils';
import { formatDuration } from '../../../common/utils/time';
import style from './OperatorGroup.module.scss';
interface OperatorGroup {
title: string;
colour: string;
count: number;
duration: number;
}
export default memo(OperatorGroup);
function OperatorGroup({ title }: OperatorGroup) {
return <div className={style.group}>{title}</div>;
function OperatorGroup({ title, colour, count, duration }: OperatorGroup) {
const groupColour = colour || '#929292';
const groupColours = getAccessibleColour(groupColour);
return (
<div className={style.group} style={{ ...groupColours, '--group-colour': groupColour } as CSSProperties}>
<span className={style.title}>{title}</span>
<span className={style.meta}>
<span>{`${count} ${count === 1 ? 'event' : 'events'}`}</span>
<span>{formatDuration(duration)}</span>
</span>
</div>
);
}