mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-12 10:53:51 +00:00
refactor(countdown): drop indirection left over from the row sections
renderRow had a single call site and recomputed activeEntryId, which the caller had already derived to pick the scroll anchor. Inlining it brings the row back to the shape it had before the sections were introduced. The section wrapper no longer carries flex-grow. The list is sized by its content and never fills the column, so there is no free space to distribute and the growth factor resolved to nothing. Sections stay flex containers though, otherwise the row margins collapse and the rows of a section sit closer together than the sections themselves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GjX7D56AW8cL3FXjvtnjtL
This commit is contained in:
@@ -18,11 +18,11 @@
|
||||
padding-bottom: 95vh;
|
||||
}
|
||||
|
||||
/* a group and its events, bounds the sticky group header to its own section */
|
||||
/* a group and its events, bounds the sticky group header to its own section.
|
||||
repeats the gap of the surrounding list, which no longer reaches the rows of a section */
|
||||
.groupSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* the rows used to be direct children of the list, keep the gap they had */
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,12 +103,12 @@ $item-height: 3.5rem;
|
||||
padding-bottom: max(8rem, calc(5rem + env(safe-area-inset-bottom)));
|
||||
}
|
||||
|
||||
/* a subscribed group and its subscribed events, bounds the sticky header to its own section */
|
||||
/* a subscribed group and its subscribed events, bounds the sticky header to its own section.
|
||||
flex rather than block so that the row margins do not collapse, keeping the spacing between
|
||||
rows of a section equal to the spacing between sections */
|
||||
.sub-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* flex-grow is set inline from the row count, so that the rows keep the share they had in the list */
|
||||
flex: 1 1 0;
|
||||
}
|
||||
|
||||
/* scoped to the section, the select view shows the same cards in a flat list */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MaybeNumber, OntimeEvent } from 'ontime-types';
|
||||
import { dayInMs } from 'ontime-utils';
|
||||
import { RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { IoPencil } from 'react-icons/io5';
|
||||
|
||||
import Button from '../../common/components/buttons/Button';
|
||||
@@ -112,44 +112,6 @@ export default function CountdownSubscriptions({ subscribedEvents, goToEditMode
|
||||
throttledHandleScroll();
|
||||
};
|
||||
|
||||
const renderRow = (event: CountdownTarget, rowRef: RefObject<HTMLDivElement | null> | undefined, isLive: boolean) => {
|
||||
// while a group is live, surface the running event's title as the secondary line
|
||||
const liveTitle = event.isGroup && event.liveEntry ? event.liveEntry.title : undefined;
|
||||
const secondaryData = liveTitle ?? getPropertyValue(event, secondarySource);
|
||||
const isGroupedEvent = !event.isGroup && Boolean(event.parent);
|
||||
const activeEntryId = event.isGroup ? (event.liveEntry?.id ?? event.targetId) : event.id;
|
||||
const isArmed = !isLive && activeEntryId === selectedEventId;
|
||||
const countdownEvent = extendEventData(event, currentDay, actualStart, plannedStart, offset, mode, reportData);
|
||||
const displayTitle = getPropertyValue(event, mainSource ?? 'title');
|
||||
|
||||
return (
|
||||
<div
|
||||
key={event.id}
|
||||
ref={rowRef}
|
||||
className={cx([
|
||||
'sub',
|
||||
isLive && 'sub--live',
|
||||
isArmed && 'sub--armed',
|
||||
event.isGroup && 'sub--group',
|
||||
isGroupedEvent && 'sub--in-group',
|
||||
])}
|
||||
data-testid={event.cue}
|
||||
>
|
||||
<div
|
||||
className='sub__binder'
|
||||
style={{ '--user-color': event.colour, '--group-color': event.groupColour ?? 'transparent' }}
|
||||
/>
|
||||
<ScheduleTime event={countdownEvent} showExpected={showExpected} />
|
||||
<SubscriptionStatus event={countdownEvent} />
|
||||
<div className={cx(['sub__title', !displayTitle && 'subdued'])}>
|
||||
{event.isGroup && <span className='sub__eyebrow'>Group</span>}
|
||||
{displayTitle}
|
||||
</div>
|
||||
{secondaryData && <div className='sub__secondary'>{secondaryData}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='list-container' onWheel={handleScroll} onTouchMove={handleScroll} ref={scrollRef}>
|
||||
{sections.map((section) => {
|
||||
@@ -158,21 +120,56 @@ export default function CountdownSubscriptions({ subscribedEvents, goToEditMode
|
||||
const anchorId = section.events.find((event) => getIsLive(event.id, selectedEventId, playback))?.id ?? null;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={section.group?.id ?? rows[0].id}
|
||||
className='sub-section'
|
||||
// the rows used to be direct children of the list, keep their share of the available space
|
||||
style={{ flexGrow: rows.length }}
|
||||
>
|
||||
<div key={section.group?.id ?? rows[0].id} className='sub-section'>
|
||||
{rows.map((event) => {
|
||||
// while a group is live, surface the running event's title as the secondary line
|
||||
const liveTitle = event.isGroup && event.liveEntry ? event.liveEntry.title : undefined;
|
||||
const secondaryData = liveTitle ?? getPropertyValue(event, secondarySource);
|
||||
const isGroupedEvent = !event.isGroup && Boolean(event.parent);
|
||||
const activeEntryId = event.isGroup ? (event.liveEntry?.id ?? event.targetId) : event.id;
|
||||
// a subscribed group is live when any of its children is the selected/running event
|
||||
const isLive = activeEntryId ? getIsLive(activeEntryId, selectedEventId, playback) : false;
|
||||
const isArmed = !isLive && activeEntryId === selectedEventId;
|
||||
// only ever hand the ref to a single row, sharing it would null it out on the next commit
|
||||
const isAnchor = isLive && (anchorId === null || event.id === anchorId);
|
||||
const rowRef = isAnchor ? selectedRef : event.isGroup && anchorId ? stickyHeaderRef : undefined;
|
||||
const countdownEvent = extendEventData(
|
||||
event,
|
||||
currentDay,
|
||||
actualStart,
|
||||
plannedStart,
|
||||
offset,
|
||||
mode,
|
||||
reportData,
|
||||
);
|
||||
const displayTitle = getPropertyValue(event, mainSource ?? 'title');
|
||||
|
||||
return renderRow(event, rowRef, isLive);
|
||||
return (
|
||||
<div
|
||||
key={event.id}
|
||||
ref={rowRef}
|
||||
className={cx([
|
||||
'sub',
|
||||
isLive && 'sub--live',
|
||||
isArmed && 'sub--armed',
|
||||
event.isGroup && 'sub--group',
|
||||
isGroupedEvent && 'sub--in-group',
|
||||
])}
|
||||
data-testid={event.cue}
|
||||
>
|
||||
<div
|
||||
className='sub__binder'
|
||||
style={{ '--user-color': event.colour, '--group-color': event.groupColour ?? 'transparent' }}
|
||||
/>
|
||||
<ScheduleTime event={countdownEvent} showExpected={showExpected} />
|
||||
<SubscriptionStatus event={countdownEvent} />
|
||||
<div className={cx(['sub__title', !displayTitle && 'subdued'])}>
|
||||
{event.isGroup && <span className='sub__eyebrow'>Group</span>}
|
||||
{displayTitle}
|
||||
</div>
|
||||
{secondaryData && <div className='sub__secondary'>{secondaryData}</div>}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user