mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-07 00:13:53 +00:00
refactor: review styles and implement BEM for user override
This commit is contained in:
committed by
Carlos Valente
parent
dc06a9afc3
commit
b539b2ecd5
@@ -8,6 +8,7 @@ $timeline-height: 1rem;
|
||||
flex: 1;
|
||||
font-weight: 600;
|
||||
color: $ui-white;
|
||||
background-color: $ui-black;
|
||||
}
|
||||
|
||||
.timelineEvents {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<div className={style.timeline}>
|
||||
<div className='timeline'>
|
||||
<ViewParamsEditor viewOptions={progressOptions} />
|
||||
<div className={style.title}>{general.title}</div>
|
||||
<div className={style.sections}>
|
||||
<Section title={getLocalizedString('common.time_now')} content={clock} category='now' />
|
||||
<Section title={getLocalizedString('common.next')} content={nextText} category='next' />
|
||||
<div className='project-header'>
|
||||
{general.title}
|
||||
<div className='clock-container'>
|
||||
<div className='label'>{getLocalizedString('common.time_now')}</div>
|
||||
<SuperscriptTime time={clock} className='time' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='title-grid'>
|
||||
<Section title={getLocalizedString('timeline.live')} content={titleNow} category='now' />
|
||||
<Section title={getLocalizedString('timeline.followedby')} content={followedByText} category='next' />
|
||||
<Section title={getLocalizedString('common.next')} status={nextStatus} content={nextText} category='next' />
|
||||
<Section
|
||||
title={getLocalizedString('timeline.followedby')}
|
||||
status={followedByStatus}
|
||||
content={followedByText}
|
||||
category='next'
|
||||
/>
|
||||
</div>
|
||||
<Timeline selectedEventId={selectedId} rundown={scopedRundown} />
|
||||
</div>
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
.sectionTitle {
|
||||
line-height: 1.2em;
|
||||
font-size: 1.5rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.sectionContent {
|
||||
min-height: 2em;
|
||||
line-height: 1em;
|
||||
font-size: 3rem;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
|
||||
&.now {
|
||||
color: $red-500;
|
||||
}
|
||||
|
||||
&.next {
|
||||
color: $green-500;
|
||||
}
|
||||
|
||||
&.subdue {
|
||||
opacity: $opacity-disabled;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,28 @@
|
||||
import { memo } from 'react';
|
||||
import { MaybeString } from 'ontime-types';
|
||||
|
||||
import { cx } from '../../../../common/utils/styleUtils';
|
||||
|
||||
import style from './TimelineSection.module.scss';
|
||||
|
||||
interface SectionProps {
|
||||
category: 'now' | 'next';
|
||||
content: MaybeString;
|
||||
title: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export default function Section(props: SectionProps) {
|
||||
const { category, content, title } = props;
|
||||
export default memo(Section);
|
||||
|
||||
const contentClasses = cx([style.sectionContent, content != null ? style[category] : style.subdue]);
|
||||
export function Section(props: SectionProps) {
|
||||
const { category, content, title, status } = props;
|
||||
|
||||
const sectionClasses = cx(['section', category === 'now' && 'section--now']);
|
||||
const contentClasses = cx(['section-content', content ? `section-content--${category}` : 'section-content--subdue']);
|
||||
return (
|
||||
<div>
|
||||
<div className={style.sectionTitle}>{title}</div>
|
||||
<div className={sectionClasses}>
|
||||
<div className='section-title'>
|
||||
<span className='section-title__label'>{title}</span>
|
||||
{status && <span className='section-title__status'>{status}</span>}
|
||||
</div>
|
||||
<div className={contentClasses}>{content ?? '-'}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user