diff --git a/apps/client/src/features/viewers/timeline/Timeline.module.scss b/apps/client/src/features/viewers/timeline/Timeline.module.scss index 9e3580deb..430bb9ee4 100644 --- a/apps/client/src/features/viewers/timeline/Timeline.module.scss +++ b/apps/client/src/features/viewers/timeline/Timeline.module.scss @@ -8,6 +8,7 @@ $timeline-height: 1rem; flex: 1; font-weight: 600; color: $ui-white; + background-color: $ui-black; } .timelineEvents { diff --git a/apps/client/src/features/viewers/timeline/TimelinePage.module.scss b/apps/client/src/features/viewers/timeline/TimelinePage.module.scss deleted file mode 100644 index f57864f8e..000000000 --- a/apps/client/src/features/viewers/timeline/TimelinePage.module.scss +++ /dev/null @@ -1,24 +0,0 @@ -.timeline { - width: 100vw; - height: 100vh; - - background-color: $ui-black; - color: $ui-white; - - display: flex; - flex-direction: column; - gap: 2rem; -} - -.title { - padding-inline: 2rem; - font-size: 3.5rem; -} - -.sections { - padding-inline: 2rem; - display: grid; - grid-template-columns: 1fr 1fr; - row-gap: 1rem; - column-gap: 3rem; -} diff --git a/apps/client/src/features/viewers/timeline/TimelinePage.scss b/apps/client/src/features/viewers/timeline/TimelinePage.scss new file mode 100644 index 000000000..fa6edac26 --- /dev/null +++ b/apps/client/src/features/viewers/timeline/TimelinePage.scss @@ -0,0 +1,101 @@ +@use '../../../theme/viewerDefs' as *; + +.timeline { + width: 100vw; + height: 100vh; + padding-top: 0.5rem; + + font-family: var(--font-family-override, $viewer-font-family); + background: var(--background-color-override, $viewer-background-color); + color: var(--color-override, $viewer-color); + + display: flex; + flex-direction: column; + gap: 2rem; + + .project-header { + padding-inline: 2rem; + font-size: clamp(32px, 4.5vw, 64px); + font-weight: 600; + display: flex; + justify-content: space-between; + } + + .clock-container { + .label { + font-size: clamp(16px, 1.5vw, 24px); + font-weight: 600; + color: var(--label-color-override, $viewer-label-color); + text-transform: uppercase; + } + + .time { + font-size: clamp(32px, 3.5vw, 50px); + font-weight: 600; + color: var(--secondary-color-override, $viewer-secondary-color); + letter-spacing: 0.05em; + line-height: 0.95em; + } + } + + .title-grid { + display: grid; + grid-template-columns: 2fr 3fr; + row-gap: 1rem; + column-gap: 2rem; + grid-template-areas: + 'now next' + 'now following'; + padding-inline: 2rem; + } + + .section { + background-color: var(--card-background-color-override, $viewer-card-bg-color); + padding: 0.5rem 1rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + border-radius: $element-border-radius; + } + + .section--now { + grid-area: now; + } + + .section-title { + line-height: 1em; + font-size: 1.5rem; + display: flex; + align-items: center; + justify-content: space-between; + font-weight: 600; + } + + .section-title__label { + text-transform: uppercase; + } + + .section-title__status { + color: $green-500; + } + + .section-content { + min-height: 2em; + line-height: 1em; + font-size: 3rem; + text-transform: uppercase; + font-weight: 600; + } + + .section-content--now { + color: $red-500; + } + + .section-content--next { + color: $green-500; + } + + .section-content--subdue { + opacity: $opacity-disabled; + } +} diff --git a/apps/client/src/features/viewers/timeline/TimelinePage.tsx b/apps/client/src/features/viewers/timeline/TimelinePage.tsx index f94a8a9c3..be339fe09 100644 --- a/apps/client/src/features/viewers/timeline/TimelinePage.tsx +++ b/apps/client/src/features/viewers/timeline/TimelinePage.tsx @@ -1,17 +1,21 @@ import { useMemo } from 'react'; -import { MaybeString, OntimeEvent, ProjectData, Settings } from 'ontime-types'; +import { MaybeString, OntimeEvent, ProjectData, Settings, ViewSettings } from 'ontime-types'; +import { overrideStylesURL } from '../../../common/api/constants'; import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; +import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; +import { useWindowTitle } from '../../../common/hooks/useWindowTitle'; import { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; import { formatTime, getDefaultFormat } from '../../../common/utils/time'; import { useTranslation } from '../../../translation/TranslationProvider'; +import SuperscriptTime from '../common/superscript-time/SuperscriptTime'; import Section from './timeline-section/TimelineSection'; import Timeline from './Timeline'; import { getTimelineOptions } from './timeline.options'; import { getFormattedTimeToStart, getScopedRundown, getUpcomingEvents } from './timeline.utils'; -import style from './TimelinePage.module.scss'; +import './TimelinePage.scss'; interface TimelinePageProps { backstageEvents: OntimeEvent[]; @@ -19,6 +23,7 @@ interface TimelinePageProps { selectedId: MaybeString; settings: Settings | undefined; time: ViewExtendedTimer; + viewSettings: ViewSettings; } /** @@ -27,7 +32,8 @@ interface TimelinePageProps { * There is little point splitting or memoising top level elements */ export default function TimelinePage(props: TimelinePageProps) { - const { backstageEvents, general, selectedId, settings, time } = props; + const { backstageEvents, general, selectedId, settings, time, viewSettings } = props; + const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const { getLocalizedString } = useTranslation(); const clock = formatTime(time.clock); @@ -40,25 +46,44 @@ export default function TimelinePage(props: TimelinePageProps) { return getUpcomingEvents(scopedRundown, selectedId); }, [scopedRundown, selectedId]); + useWindowTitle('Timeline'); + + if (!shouldRender) { + return null; + } + // populate options const defaultFormat = getDefaultFormat(settings?.timeFormat); const progressOptions = getTimelineOptions(defaultFormat); const titleNow = now?.title ?? '-'; - const dueText = getLocalizedString('timeline.due'); - const nextText = next !== null ? `${next.title} · ${getFormattedTimeToStart(next, time.clock, dueText)}` : '-'; - const followedByText = - followedBy !== null ? `${followedBy.title} · ${getFormattedTimeToStart(followedBy, time.clock, dueText)}` : '-'; + const dueText = getLocalizedString('timeline.due').toUpperCase(); + const nextText = next !== null ? next.title : '-'; + const followedByText = followedBy !== null ? followedBy.title : '-'; + + const nextStatus = next !== null ? getFormattedTimeToStart(next, time.clock, dueText) : undefined; + const followedByStatus = followedBy !== null ? getFormattedTimeToStart(followedBy, time.clock, dueText) : undefined; return ( -