Skip to content

Commit

Permalink
refactor: compound timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
cpvalente committed Oct 7, 2024
1 parent 6a55d13 commit 7917f8f
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 132 deletions.
40 changes: 21 additions & 19 deletions apps/client/src/features/viewers/timeline/Timeline.module.scss
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
@use '../../../theme/viewerDefs' as *;

$timeline-entry-height: 20px;
$lane-height: 120px;
$timeline-height: 1rem;
$view-background: $ui-black;

.timeline {
flex: 1;
font-weight: 600;
color: $ui-white;
background-color: $ui-black;
}

.timelineEvents {
background-color: $view-background;
box-sizing: content-box;
// create progress background
box-shadow: inset 0 1rem 0 0 $gray-1100;
position: relative;
height: 100%;
}

.column {
display: flex;
flex-direction: column;
position: absolute;
border-left: 1px solid $ui-black;
border-left: 1px solid $view-background;
// avoiding content being larger than the view
height: calc(100% - 3rem);
}

// generate combined timeline
.timelineBlock {
height: $timeline-height;
background: $white-60;
width: 100%;

// decorate timeline element
&::before {
content: '';
position: absolute;
box-sizing: content-box;
top: -$timeline-height;
left: 0;
right: 0;
height: $timeline-height;
background-color: $white-40;
&[data-status='done'] {
background: $active-red;
}

&[data-status='live'] {
background: linear-gradient(to right, $active-red var(--progress, 0%), $white-60 var(--progress, 0%));
}
}

Expand Down Expand Up @@ -66,7 +67,8 @@ $timeline-height: 1rem;
line-height: 1rem;

background-color: var(--lighter, $viewer-card-bg-color);
border-bottom: 2px solid $ui-black;
border-bottom: 2px solid $view-background;
border-top: 2px solid $view-background;
box-shadow: 0 0.25rem 0 0 var(--color, $gray-300);

&[data-status='done'] {
Expand Down
90 changes: 43 additions & 47 deletions apps/client/src/features/viewers/timeline/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { isOntimeEvent, isPlayableEvent, MaybeNumber, OntimeRundown } from 'onti
import { checkIsNextDay, dayInMs, getLastEvent, MILLIS_PER_HOUR } from 'ontime-utils';

import TimelineMarkers from './timeline-markers/TimelineMarkers';
import TimelineProgressBar from './timeline-progress-bar/TimelineProgressBar';
import { getElementPosition, getEndHour, getStartHour } from './timeline.utils';
import { ProgressStatus, TimelineEntry } from './TimelineEntry';

Expand Down Expand Up @@ -39,57 +38,54 @@ function Timeline(props: TimelineProps) {
return (
<div className={style.timeline}>
<TimelineMarkers startHour={startHour} endHour={endHour} />
<TimelineProgressBar startHour={startHour} endHour={endHour} />
<div className={style.timelineEvents}>
{rundown.map((event) => {
// for now we dont render delays and blocks
if (!isOntimeEvent(event) || !isPlayableEvent(event)) {
return null;
}
{rundown.map((event) => {
// for now we dont render delays and blocks
if (!isOntimeEvent(event) || !isPlayableEvent(event)) {
return null;
}

// keep track of progress of rundown
if (eventStatus === 'live') {
eventStatus = 'future';
}
if (event.id === selectedEventId) {
eventStatus = 'live';
}
// keep track of progress of rundown
if (eventStatus === 'live') {
eventStatus = 'future';
}
if (event.id === selectedEventId) {
eventStatus = 'live';
}

// we only need to check for next day if we have a previous event
if (
previousEventStartTime !== null &&
checkIsNextDay(previousEventStartTime, event.timeStart, event.duration)
) {
elapsedDays++;
}
const normalisedStart = event.timeStart + elapsedDays * dayInMs;
// we only need to check for next day if we have a previous event
if (
previousEventStartTime !== null &&
checkIsNextDay(previousEventStartTime, event.timeStart, event.duration)
) {
elapsedDays++;
}
const normalisedStart = event.timeStart + elapsedDays * dayInMs;

const { left: elementLeftPosition, width: elementWidth } = getElementPosition(
startHour * MILLIS_PER_HOUR,
endHour * MILLIS_PER_HOUR,
normalisedStart + (event.delay ?? 0),
event.duration,
screenWidth,
);
const { left: elementLeftPosition, width: elementWidth } = getElementPosition(
startHour * MILLIS_PER_HOUR,
endHour * MILLIS_PER_HOUR,
normalisedStart + (event.delay ?? 0),
event.duration,
screenWidth,
);

// prepare values for next iteration
previousEventStartTime = normalisedStart;
// prepare values for next iteration
previousEventStartTime = normalisedStart;

return (
<TimelineEntry
key={event.id}
colour={event.colour}
delay={event.delay ?? 0}
duration={event.duration}
left={elementLeftPosition}
status={eventStatus}
start={normalisedStart} // dataset solves issues related to crossing midnight
title={event.title}
width={elementWidth}
/>
);
})}
</div>
return (
<TimelineEntry
key={event.id}
colour={event.colour}
delay={event.delay ?? 0}
duration={event.duration}
left={elementLeftPosition}
status={eventStatus}
start={normalisedStart} // dataset solves issues related to crossing midnight
title={event.title}
width={elementWidth}
/>
);
})}
</div>
);
}
13 changes: 11 additions & 2 deletions apps/client/src/features/viewers/timeline/TimelineEntry.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useTimelineStatus } from '../../../common/hooks/useSocket';
import { useTimelineStatus, useTimer } from '../../../common/hooks/useSocket';
import { getProgress } from '../../../common/utils/getProgress';
import { alpha, cx } from '../../../common/utils/styleUtils';
import { formatDuration, formatTime } from '../../../common/utils/time';
import { useTranslation } from '../../../translation/TranslationProvider';
Expand Down Expand Up @@ -48,6 +49,7 @@ export function TimelineEntry(props: TimelineEntryProps) {
width: `${width}px`,
}}
>
{status === 'live' ? <ActiveBlock /> : <div data-status={status} className={style.timelineBlock} />}
<div
className={contentClasses}
data-status={status}
Expand Down Expand Up @@ -76,7 +78,7 @@ interface TimelineEntryStatusProps {
start: number;
}

// we isolate this component to avoid isolate re-renders provoked by the clock changes
// extract component to isolate re-renders provoked by the clock changes
function TimelineEntryStatus(props: TimelineEntryStatusProps) {
const { status, start } = props;
const { clock, offset } = useTimelineStatus();
Expand All @@ -92,3 +94,10 @@ function TimelineEntryStatus(props: TimelineEntryStatusProps) {

return <div className={style.status}>{statusText}</div>;
}

/** Generates a block level progress bar */
function ActiveBlock() {
const { current, duration } = useTimer();
const progress = getProgress(current, duration);
return <div data-status='live' className={style.timelineBlock} style={{ '--progress': `${progress}%` }} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,9 @@ describe('getCSSPosition()', () => {
});
});

describe('makeTmelineSections', () => {
describe('makeTimelineSections', () => {
it('creates an array between the hours given, end excluded', () => {
const result = makeTimelineSections(11, 17);
expect(result).toEqual(['11:00', '12:00', '13:00', '14:00', '15:00', '16:00']);
});

it('wraps around midnight', () => {
const result = makeTimelineSections(22, 26);
expect(result).toEqual(['22:00', '23:00', '00:00', '01:00']);
expect(result).toEqual([11, 12, 13, 14, 15, 16]);
});
});
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
.markers {
position: absolute;
left: 0;
width: 100%;
color: $ui-white;
height: 100%;

display: flex;
height: 1rem;
line-height: 1rem;
margin-bottom: 0.25rem;
font-size: calc(1rem - 2px);
justify-content: space-evenly;

& > span {
flex-grow: 1;
border-left: 1px solid $white-7;
height: 100vh;

&:not(:first-child) {
border-left: 1px solid $white-7;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ interface TimelineMarkersProps {
endHour: number;
}

/** Creates a line for every hour in the timeline */
export default function TimelineMarkers(props: TimelineMarkersProps) {
const { startHour, endHour } = props;

const elements = makeTimelineSections(startHour, endHour);

return (
<div className={style.markers}>
{elements.map((tag, index) => {
return <span key={`${index}-${tag}`}>{tag}</span>;
})}
{elements.map((tag) => (
<span key={tag} />
))}
</div>
);
}

This file was deleted.

This file was deleted.

4 changes: 1 addition & 3 deletions apps/client/src/features/viewers/timeline/timeline.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import {
getTimeFromPrevious,
isNewLatest,
MILLIS_PER_HOUR,
millisToString,
removeSeconds,
} from 'ontime-utils';

import { clamp } from '../../../common/utils/math';
Expand Down Expand Up @@ -71,7 +69,7 @@ export function getEndHour(endTime: number): number {
export function makeTimelineSections(firstHour: number, lastHour: number) {
const timelineSections = [];
for (let i = firstHour; i < lastHour; i++) {
timelineSections.push(removeSeconds(millisToString((i % 24) * MILLIS_PER_HOUR)));
timelineSections.push(i);
}
return timelineSections;
}
Expand Down

0 comments on commit 7917f8f

Please sign in to comment.