mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-16 21:03:29 +00:00
feat: projected times in countdown
This commit is contained in:
committed by
Carlos Valente
parent
938c5fbb4c
commit
73e23ee323
@@ -508,4 +508,14 @@ export const getOperatorOptions = (customFields: CustomFields, timeFormat: strin
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getCountdownOptions = (timeFormat: string): ParamField[] => [getTimeOption(timeFormat), hideTimerSeconds];
|
export const getCountdownOptions = (timeFormat: string): ParamField[] => [
|
||||||
|
getTimeOption(timeFormat),
|
||||||
|
hideTimerSeconds,
|
||||||
|
{
|
||||||
|
id: 'showProjected',
|
||||||
|
title: 'Show projected time',
|
||||||
|
description: 'Whether to show the projected delay of an event (taken from runtime offset).',
|
||||||
|
type: 'boolean',
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
Message,
|
Message,
|
||||||
OntimeEvent,
|
OntimeEvent,
|
||||||
ProjectData,
|
ProjectData,
|
||||||
|
Runtime,
|
||||||
Settings,
|
Settings,
|
||||||
SupportedEvent,
|
SupportedEvent,
|
||||||
TimerMessage,
|
TimerMessage,
|
||||||
@@ -36,6 +37,7 @@ type WithDataProps = {
|
|||||||
publicEventNext: OntimeEvent | null;
|
publicEventNext: OntimeEvent | null;
|
||||||
publicEventNow: OntimeEvent | null;
|
publicEventNow: OntimeEvent | null;
|
||||||
publicSelectedId: string | null;
|
publicSelectedId: string | null;
|
||||||
|
runtime: Runtime;
|
||||||
selectedId: string | null;
|
selectedId: string | null;
|
||||||
settings: Settings | undefined;
|
settings: Settings | undefined;
|
||||||
time: ViewExtendedTimer;
|
time: ViewExtendedTimer;
|
||||||
@@ -66,7 +68,7 @@ const withData = <P extends WithDataProps>(Component: ComponentType<P>) => {
|
|||||||
}, [rundownData]);
|
}, [rundownData]);
|
||||||
|
|
||||||
// websocket data
|
// websocket data
|
||||||
const { clock, timer, message, onAir, eventNext, publicEventNext, publicEventNow, eventNow } =
|
const { clock, timer, message, onAir, eventNext, publicEventNext, publicEventNow, eventNow, runtime } =
|
||||||
useStore(runtimeStore);
|
useStore(runtimeStore);
|
||||||
const publicSelectedId = publicEventNow?.id ?? null;
|
const publicSelectedId = publicEventNow?.id ?? null;
|
||||||
const selectedId = eventNow?.id ?? null;
|
const selectedId = eventNow?.id ?? null;
|
||||||
@@ -108,6 +110,7 @@ const withData = <P extends WithDataProps>(Component: ComponentType<P>) => {
|
|||||||
publicEventNext={publicEventNext}
|
publicEventNext={publicEventNext}
|
||||||
publicEventNow={publicEventNow}
|
publicEventNow={publicEventNow}
|
||||||
publicSelectedId={publicSelectedId}
|
publicSelectedId={publicSelectedId}
|
||||||
|
runtime={runtime}
|
||||||
selectedId={selectedId}
|
selectedId={selectedId}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
time={TimeManagerType}
|
time={TimeManagerType}
|
||||||
|
|||||||
@@ -21,11 +21,11 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
font-size: clamp(24px, 2vw, 32px);
|
font-size: clamp(1.5rem, 2vw, 2rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__events {
|
&__events {
|
||||||
font-size: clamp(16px, 1.5vw, 24px);
|
font-size: clamp(1rem, 1.5vw, 1.5rem);
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
height: 70vh;
|
height: 70vh;
|
||||||
@@ -38,18 +38,18 @@
|
|||||||
.countdown-container {
|
.countdown-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: min(2vh, 16px);
|
gap: min(2vh, 1rem);
|
||||||
padding: min(2vh, 16px) clamp(16px, 10vw, 64px);
|
padding: min(2vh, 1rem) clamp(1rem, 10vw, 4rem);
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto auto auto auto 1fr;
|
grid-template-rows: auto auto auto auto 1fr;
|
||||||
grid-template-columns: 100%;
|
grid-template-columns: 100%;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
'header'
|
'header'
|
||||||
'status'
|
'status'
|
||||||
'clock'
|
'clock'
|
||||||
'title'
|
'title'
|
||||||
'timers';
|
'timers';
|
||||||
|
|
||||||
/* =================== HEADER + EXTRAS ===================*/
|
/* =================== HEADER + EXTRAS ===================*/
|
||||||
|
|
||||||
@@ -59,13 +59,13 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
font-size: clamp(16px, 1.5vw, 24px);
|
font-size: $timer-label-size;
|
||||||
color: var(--label-color-override, $viewer-label-color);
|
color: var(--label-color-override, $viewer-label-color);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time {
|
.time {
|
||||||
font-size: clamp(32px, 3.5vw, 50px);
|
font-size: $timer-value-size;
|
||||||
color: var(--secondary-color-override, $viewer-secondary-color);
|
color: var(--secondary-color-override, $viewer-secondary-color);
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
line-height: 0.95em;
|
line-height: 0.95em;
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
.status {
|
.status {
|
||||||
grid-area: status;
|
grid-area: status;
|
||||||
color: var(--label-color-override, $viewer-label-color);
|
color: var(--label-color-override, $viewer-label-color);
|
||||||
font-size: clamp(32px, 3.5vw, 50px);
|
font-size: clamp(2rem, 3.5vw, 3.5rem);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
.title {
|
.title {
|
||||||
grid-area: title;
|
grid-area: title;
|
||||||
background-color: var(--card-background-color-override, $viewer-card-bg-color);
|
background-color: var(--card-background-color-override, $viewer-card-bg-color);
|
||||||
padding: 16px 24px;
|
padding: 1rem 1.5rem;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -119,28 +119,49 @@
|
|||||||
|
|
||||||
.timer-group {
|
.timer-group {
|
||||||
grid-area: timers;
|
grid-area: timers;
|
||||||
display: flex;
|
display: grid;
|
||||||
justify-content: space-evenly;
|
grid-template-areas:
|
||||||
align-items: flex-end;
|
'projected-start projected-end'
|
||||||
|
'scheduled-start scheduled-end';
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
justify-items: center;
|
||||||
|
text-align: center;
|
||||||
|
row-gap: clamp(1rem, 5vh, 4rem);
|
||||||
|
|
||||||
|
align-self: flex-end;
|
||||||
|
height: fit-content;
|
||||||
|
|
||||||
.aux-timers {
|
&__projected-start {
|
||||||
text-align: center;
|
grid-area: projected-start;
|
||||||
font-size: clamp(24px, 1.75vw, 32px);
|
}
|
||||||
|
|
||||||
&__label {
|
&__projected-end {
|
||||||
color: var(--label-color-override, $viewer-label-color);
|
grid-area: projected-end;
|
||||||
text-transform: uppercase;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&__value {
|
&__scheduled-start {
|
||||||
font-size: clamp(32px, 3.5vw, 50px);
|
grid-area: scheduled-start;
|
||||||
color: var(--secondary-color-override, $viewer-secondary-color);
|
}
|
||||||
letter-spacing: 0.05em;
|
|
||||||
line-height: 0.95em;
|
|
||||||
|
|
||||||
&--delayed {
|
&__scheduled-end {
|
||||||
color: $delay-color;
|
grid-area: scheduled-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: $timer-label-size;
|
||||||
|
color: var(--label-color-override, $viewer-label-color);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__value {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-size: $timer-value-size;
|
||||||
|
color: var(--secondary-color-override, $viewer-secondary-color);
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
line-height: 0.95em;
|
||||||
|
|
||||||
|
&--delayed {
|
||||||
|
color: $delay-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { OntimeEvent, OntimeRundownEntry, Playback, Settings, SupportedEvent, ViewSettings } from 'ontime-types';
|
import {
|
||||||
|
OntimeEvent,
|
||||||
|
OntimeRundownEntry,
|
||||||
|
Playback,
|
||||||
|
Runtime,
|
||||||
|
Settings,
|
||||||
|
SupportedEvent,
|
||||||
|
ViewSettings,
|
||||||
|
} from 'ontime-types';
|
||||||
|
|
||||||
import { overrideStylesURL } from '../../../common/api/constants';
|
import { overrideStylesURL } from '../../../common/api/constants';
|
||||||
import { getCountdownOptions } from '../../../common/components/view-params-editor/constants';
|
import { getCountdownOptions } from '../../../common/components/view-params-editor/constants';
|
||||||
@@ -13,7 +21,7 @@ import { useTranslation } from '../../../translation/TranslationProvider';
|
|||||||
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
|
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
|
||||||
import { getFormattedTimer, isStringBoolean } from '../common/viewUtils';
|
import { getFormattedTimer, isStringBoolean } from '../common/viewUtils';
|
||||||
|
|
||||||
import { fetchTimerData, TimerMessage } from './countdown.helpers';
|
import { fetchTimerData, getTimerItems, TimerMessage } from './countdown.helpers';
|
||||||
import CountdownSelect from './CountdownSelect';
|
import CountdownSelect from './CountdownSelect';
|
||||||
|
|
||||||
import './Countdown.scss';
|
import './Countdown.scss';
|
||||||
@@ -21,14 +29,15 @@ import './Countdown.scss';
|
|||||||
interface CountdownProps {
|
interface CountdownProps {
|
||||||
isMirrored: boolean;
|
isMirrored: boolean;
|
||||||
backstageEvents: OntimeEvent[];
|
backstageEvents: OntimeEvent[];
|
||||||
time: ViewExtendedTimer;
|
runtime: Runtime;
|
||||||
selectedId: string | null;
|
selectedId: string | null;
|
||||||
viewSettings: ViewSettings;
|
|
||||||
settings: Settings | undefined;
|
settings: Settings | undefined;
|
||||||
|
time: ViewExtendedTimer;
|
||||||
|
viewSettings: ViewSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Countdown(props: CountdownProps) {
|
export default function Countdown(props: CountdownProps) {
|
||||||
const { isMirrored, backstageEvents, time, selectedId, viewSettings, settings } = props;
|
const { isMirrored, backstageEvents, runtime, selectedId, settings, time, viewSettings } = props;
|
||||||
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const { getLocalizedString } = useTranslation();
|
const { getLocalizedString } = useTranslation();
|
||||||
@@ -71,10 +80,10 @@ export default function Countdown(props: CountdownProps) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { message, timer } = fetchTimerData(time, follow, selectedId);
|
const { message, timer } = fetchTimerData(time, follow, selectedId, runtime.offset);
|
||||||
setRunningMessage(message);
|
setRunningMessage(message);
|
||||||
setRunningTimer(timer);
|
setRunningTimer(timer);
|
||||||
}, [follow, selectedId, time]);
|
}, [follow, selectedId, time, runtime.offset]);
|
||||||
|
|
||||||
// defer rendering until we load stylesheets
|
// defer rendering until we load stylesheets
|
||||||
if (!shouldRender) {
|
if (!shouldRender) {
|
||||||
@@ -87,8 +96,12 @@ export default function Countdown(props: CountdownProps) {
|
|||||||
const delayedTimerStyles = delay > 0 ? 'aux-timers__value--delayed' : '';
|
const delayedTimerStyles = delay > 0 ? 'aux-timers__value--delayed' : '';
|
||||||
|
|
||||||
const clock = formatTime(time.clock);
|
const clock = formatTime(time.clock);
|
||||||
const startTime = follow === null ? '...' : formatTime(follow.timeStart + delay);
|
const { scheduledStart, scheduledEnd, projectedStart, projectedEnd } = getTimerItems(
|
||||||
const endTime = follow === null ? '...' : formatTime(follow.timeEnd + delay);
|
follow?.timeStart,
|
||||||
|
follow?.timeEnd,
|
||||||
|
delay,
|
||||||
|
runtime.offset,
|
||||||
|
);
|
||||||
|
|
||||||
const hideSeconds = searchParams.get('hideTimerSeconds');
|
const hideSeconds = searchParams.get('hideTimerSeconds');
|
||||||
const formattedTimer = getFormattedTimer(runningTimer, time.timerType, getLocalizedString('common.minutes'), {
|
const formattedTimer = getFormattedTimer(runningTimer, time.timerType, getLocalizedString('common.minutes'), {
|
||||||
@@ -122,13 +135,25 @@ export default function Countdown(props: CountdownProps) {
|
|||||||
{follow?.title && <div className='title'>{follow.title}</div>}
|
{follow?.title && <div className='title'>{follow.title}</div>}
|
||||||
|
|
||||||
<div className='timer-group'>
|
<div className='timer-group'>
|
||||||
<div className='aux-timers'>
|
{projectedStart && projectedEnd && (
|
||||||
<div className='aux-timers__label'>{getLocalizedString('common.start_time')}</div>
|
<div className='timer-group__projected-start'>
|
||||||
<SuperscriptTime time={startTime} className={`aux-timers__value ${delayedTimerStyles}`} />
|
<div className='timer-group__label'>{getLocalizedString('common.projected_start')}</div>
|
||||||
|
<SuperscriptTime time={projectedStart} className={`timer-group__value ${delayedTimerStyles}`} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{projectedStart && projectedEnd && (
|
||||||
|
<div className='timer-group__projected-end'>
|
||||||
|
<div className='timer-group__label'>{getLocalizedString('common.projected_end')}</div>
|
||||||
|
<SuperscriptTime time={projectedEnd} className={`timer-group__value ${delayedTimerStyles}`} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className='timer-group__scheduled-start'>
|
||||||
|
<div className='timer-group__label'>{getLocalizedString('common.scheduled_start')}</div>
|
||||||
|
<SuperscriptTime time={scheduledStart} className={`timer-group__value ${delayedTimerStyles}`} />
|
||||||
</div>
|
</div>
|
||||||
<div className='aux-timers'>
|
<div className='timer-group__scheduled-end'>
|
||||||
<div className='aux-timers__label'>{getLocalizedString('common.end_time')}</div>
|
<div className='timer-group__label'>{getLocalizedString('common.scheduled_end')}</div>
|
||||||
<SuperscriptTime time={endTime} className={`aux-timers__value ${delayedTimerStyles}`} />
|
<SuperscriptTime time={scheduledEnd} className={`timer-group__value ${delayedTimerStyles}`} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { OntimeEvent, Playback } from 'ontime-types';
|
import { OntimeEvent, Playback } from 'ontime-types';
|
||||||
|
|
||||||
import { ViewExtendedTimer } from '../../../common/models/TimeManager.type';
|
import { ViewExtendedTimer } from '../../../common/models/TimeManager.type';
|
||||||
|
import { formatTime } from '../../../common/utils/time';
|
||||||
|
import { isStringBoolean } from '../common/viewUtils';
|
||||||
|
|
||||||
export enum TimerMessage {
|
export enum TimerMessage {
|
||||||
toStart = 'to_start',
|
toStart = 'to_start',
|
||||||
@@ -22,46 +24,79 @@ export const fetchTimerData = (
|
|||||||
time: ViewExtendedTimer,
|
time: ViewExtendedTimer,
|
||||||
follow: OntimeEvent,
|
follow: OntimeEvent,
|
||||||
selectedId: string | null,
|
selectedId: string | null,
|
||||||
|
offset: number,
|
||||||
): { message: TimerMessage; timer: number } => {
|
): { message: TimerMessage; timer: number } => {
|
||||||
let message;
|
|
||||||
let timer;
|
|
||||||
|
|
||||||
if (selectedId === follow.id) {
|
if (selectedId === follow.id) {
|
||||||
// check that is not running
|
// if it is selected, it may not be running
|
||||||
message = time.playback === Playback.Pause ? TimerMessage.waiting : TimerMessage.running;
|
return {
|
||||||
timer = time.current ?? 0;
|
message: time.playback === Playback.Pause ? TimerMessage.waiting : TimerMessage.running,
|
||||||
} else if (time.clock < follow.timeStart) {
|
timer: time.current ?? 0,
|
||||||
// if it hasnt started, we count to start
|
};
|
||||||
message = TimerMessage.toStart;
|
|
||||||
timer = follow.timeStart - time.clock;
|
|
||||||
} else if (follow.timeStart <= time.clock && time.clock <= follow.timeEnd) {
|
|
||||||
// if it has started, we show running timer
|
|
||||||
message = TimerMessage.waiting;
|
|
||||||
timer = time.current ?? 0;
|
|
||||||
} else {
|
|
||||||
// running timer timer is not the one we are following
|
|
||||||
|
|
||||||
if (follow.timeStart > follow.timeEnd) {
|
|
||||||
// ends day after
|
|
||||||
|
|
||||||
if (follow.timeStart > time.clock) {
|
|
||||||
// if it hasnt started, we count to start
|
|
||||||
message = TimerMessage.toStart;
|
|
||||||
timer = follow.timeStart - time.clock;
|
|
||||||
} else if (follow.timeStart <= time.clock) {
|
|
||||||
// if it has started, we show running timer
|
|
||||||
message = TimerMessage.waiting;
|
|
||||||
timer = time.current ?? 0;
|
|
||||||
} else {
|
|
||||||
// if it has ended, we show how long ago
|
|
||||||
message = TimerMessage.ended;
|
|
||||||
timer = follow.timeEnd;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// if it has ended, we show how long ago
|
|
||||||
message = TimerMessage.ended;
|
|
||||||
timer = follow.timeEnd;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return { message, timer };
|
|
||||||
|
const showProjected = getShouldShowProjected();
|
||||||
|
const addedTime = showProjected ? offset : 0;
|
||||||
|
if (time.clock < follow.timeStart) {
|
||||||
|
// if it hasnt started, we count to start
|
||||||
|
return { message: TimerMessage.toStart, timer: follow.timeStart - time.clock - addedTime };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (follow.timeStart <= time.clock && time.clock <= follow.timeEnd) {
|
||||||
|
// if it has started, we show running timer
|
||||||
|
return { message: TimerMessage.waiting, timer: time.current ?? 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// running timer timer is not the one we are following
|
||||||
|
|
||||||
|
// ends day after
|
||||||
|
if (follow.timeStart > follow.timeEnd) {
|
||||||
|
if (follow.timeStart > time.clock) {
|
||||||
|
// if it hasnt started, we count to start
|
||||||
|
return { message: TimerMessage.toStart, timer: follow.timeStart - time.clock - addedTime };
|
||||||
|
}
|
||||||
|
if (follow.timeStart <= time.clock) {
|
||||||
|
// if it has started, we show running timer
|
||||||
|
return { message: TimerMessage.waiting, timer: time.current ?? 0 };
|
||||||
|
}
|
||||||
|
// if it has ended, we show how long ago
|
||||||
|
return { message: TimerMessage.ended, timer: follow.timeEnd };
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it has ended, we show how long ago
|
||||||
|
return { message: TimerMessage.ended, timer: follow.timeEnd };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets values for the timer items
|
||||||
|
*/
|
||||||
|
export function getTimerItems(start: number | undefined, end: number | undefined, delay: number, offset: number) {
|
||||||
|
if (start == null || end == null) {
|
||||||
|
return {
|
||||||
|
scheduledStart: '',
|
||||||
|
scheduledEnd: '',
|
||||||
|
projectedStart: '',
|
||||||
|
projectedEnd: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const showProjected = getShouldShowProjected();
|
||||||
|
const scheduledStart = formatTime(start + delay);
|
||||||
|
const scheduledEnd = formatTime(end + delay);
|
||||||
|
const projectedStart = showProjected ? formatTime(start + delay - offset) : '';
|
||||||
|
const projectedEnd = showProjected ? formatTime(end + delay - offset) : '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
scheduledStart,
|
||||||
|
scheduledEnd,
|
||||||
|
projectedStart,
|
||||||
|
projectedEnd,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets from the URL whether the showProjected option is active
|
||||||
|
*/
|
||||||
|
function getShouldShowProjected() {
|
||||||
|
const params = new URL(document.location.href).searchParams;
|
||||||
|
return isStringBoolean(params.get('showProjected'));
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
.time {
|
.time {
|
||||||
font-size: clamp(32px, 3.5vw, 50px);
|
font-size: clamp(32px, 3.5vw, 50px);
|
||||||
font-weight: 600;
|
color: var(--secondary-color-override, $viewer-secondary-color);
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
line-height: 0.95em;
|
line-height: 0.95em;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,3 +28,7 @@ $timer-finished-color: $playback-negative;
|
|||||||
$timer-bold-font-family: 'Arial Black', sans-serif; // --card-background-color-override
|
$timer-bold-font-family: 'Arial Black', sans-serif; // --card-background-color-override
|
||||||
|
|
||||||
$external-color: rgba(white, 70%); // --external-color-override
|
$external-color: rgba(white, 70%); // --external-color-override
|
||||||
|
|
||||||
|
// properties of other timers (clock and countdown)
|
||||||
|
$timer-label-size: clamp(16px, 1.5vw, 24px);
|
||||||
|
$timer-value-size: clamp(2rem, 3.5vw, 3.5rem);
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
import { TranslationObject } from './en';
|
import { TranslationObject } from './en';
|
||||||
|
|
||||||
export const langDe: TranslationObject = {
|
export const langDe: TranslationObject = {
|
||||||
'common.end_time': 'Endzeit',
|
'common.expected_finish': 'Erwartetes Ende',
|
||||||
'common.expected_finish': 'Voraussichtliches Ende',
|
|
||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Jetzt',
|
'common.now': 'Jetzt',
|
||||||
'common.next': 'Nächste',
|
'common.next': 'Nächste',
|
||||||
'common.public_message': 'Öffentliche Nachricht',
|
'common.public_message': 'Öffentliche Nachricht',
|
||||||
'common.start_time': 'Startzeit',
|
'common.scheduled_start': 'Geplanter beginn',
|
||||||
|
'common.scheduled_end': 'Geplantes ende',
|
||||||
|
'common.projected_start': 'Erwartetes beginn',
|
||||||
|
'common.projected_end': 'Erwartetes ende',
|
||||||
'common.stage_timer': 'Bühnen-Timer',
|
'common.stage_timer': 'Bühnen-Timer',
|
||||||
'common.started_at': 'Gestartet am',
|
'common.started_at': 'Gestartet am',
|
||||||
'common.time_now': 'Aktuelle Zeit',
|
'common.time_now': 'Aktuelle Zeit',
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
export const langEn = {
|
export const langEn = {
|
||||||
'common.end_time': 'End Time',
|
|
||||||
'common.expected_finish': 'Expected Finish',
|
'common.expected_finish': 'Expected Finish',
|
||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Now',
|
'common.now': 'Now',
|
||||||
'common.next': 'Next',
|
'common.next': 'Next',
|
||||||
'common.public_message': 'Public message',
|
'common.public_message': 'Public message',
|
||||||
'common.start_time': 'Start Time',
|
'common.scheduled_start': 'Scheduled start',
|
||||||
|
'common.scheduled_end': 'Scheduled end',
|
||||||
|
'common.projected_start': 'Projected start',
|
||||||
|
'common.projected_end': 'Projected end',
|
||||||
'common.stage_timer': 'Stage Timer',
|
'common.stage_timer': 'Stage Timer',
|
||||||
'common.started_at': 'Started At',
|
'common.started_at': 'Started At',
|
||||||
'common.time_now': 'Time now',
|
'common.time_now': 'Time now',
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { TranslationObject } from './en';
|
import { TranslationObject } from './en';
|
||||||
|
|
||||||
export const langEs: TranslationObject = {
|
export const langEs: TranslationObject = {
|
||||||
'common.end_time': 'Hora de finalización',
|
|
||||||
'common.expected_finish': 'Finalización esperada',
|
'common.expected_finish': 'Finalización esperada',
|
||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Ahora',
|
'common.now': 'Ahora',
|
||||||
'common.next': 'Siguiente',
|
'common.next': 'Siguiente',
|
||||||
'common.public_message': 'Mensaje público',
|
'common.public_message': 'Mensaje público',
|
||||||
'common.start_time': 'Hora de inicio',
|
'common.scheduled_start': 'Inicio programado',
|
||||||
|
'common.scheduled_end': 'Fin programado',
|
||||||
|
'common.projected_start': 'Inicio previsto',
|
||||||
|
'common.projected_end': 'Fin previsto',
|
||||||
'common.stage_timer': 'Temporizador de presentador',
|
'common.stage_timer': 'Temporizador de presentador',
|
||||||
'common.started_at': 'Iniciado en',
|
'common.started_at': 'Iniciado en',
|
||||||
'common.time_now': 'Ahora',
|
'common.time_now': 'Ahora',
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { TranslationObject } from './en';
|
import { TranslationObject } from './en';
|
||||||
|
|
||||||
export const langFr: TranslationObject = {
|
export const langFr: TranslationObject = {
|
||||||
'common.end_time': 'Termine à',
|
|
||||||
'common.expected_finish': 'Fin estimée à',
|
'common.expected_finish': 'Fin estimée à',
|
||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Maintenant',
|
'common.now': 'Maintenant',
|
||||||
'common.next': 'A suivre',
|
'common.next': 'A suivre',
|
||||||
'common.public_message': 'Message public',
|
'common.public_message': 'Message public',
|
||||||
'common.start_time': 'Heure de début',
|
'common.scheduled_start': 'Début prévu',
|
||||||
|
'common.scheduled_end': 'Fin prévue',
|
||||||
|
'common.projected_start': 'Début projeté',
|
||||||
|
'common.projected_end': 'Fin projetée',
|
||||||
'common.stage_timer': 'Minuteur de scène',
|
'common.stage_timer': 'Minuteur de scène',
|
||||||
'common.started_at': 'Commencé à',
|
'common.started_at': 'Commencé à',
|
||||||
'common.time_now': 'Heure',
|
'common.time_now': 'Heure',
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { TranslationObject } from './en';
|
import { TranslationObject } from './en';
|
||||||
|
|
||||||
export const langIt: TranslationObject = {
|
export const langIt: TranslationObject = {
|
||||||
'common.end_time': 'Ora di Fine',
|
|
||||||
'common.expected_finish': 'Fine Prevista',
|
'common.expected_finish': 'Fine Prevista',
|
||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Adesso',
|
'common.now': 'Adesso',
|
||||||
'common.next': 'Prossimo',
|
'common.next': 'Prossimo',
|
||||||
'common.public_message': 'Messaggio pubblico',
|
'common.public_message': 'Messaggio pubblico',
|
||||||
'common.start_time': 'Ora di Inizio',
|
'common.scheduled_start': 'Inizio programmato',
|
||||||
|
'common.scheduled_end': 'Fine programmata',
|
||||||
|
'common.projected_start': 'Inizio previsto',
|
||||||
|
'common.projected_end': 'Fine prevista',
|
||||||
'common.stage_timer': 'Orologio Palco',
|
'common.stage_timer': 'Orologio Palco',
|
||||||
'common.started_at': 'Iniziato Alle',
|
'common.started_at': 'Iniziato Alle',
|
||||||
'common.time_now': 'Ora attuale',
|
'common.time_now': 'Ora attuale',
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { TranslationObject } from './en';
|
import { TranslationObject } from './en';
|
||||||
|
|
||||||
export const langNo: TranslationObject = {
|
export const langNo: TranslationObject = {
|
||||||
'common.end_time': 'Sluttid',
|
|
||||||
'common.expected_finish': 'Forventet slutt',
|
'common.expected_finish': 'Forventet slutt',
|
||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Nå',
|
'common.now': 'Nå',
|
||||||
'common.next': 'Neste',
|
'common.next': 'Neste',
|
||||||
'common.public_message': 'Offentlig beskjed',
|
'common.public_message': 'Offentlig beskjed',
|
||||||
'common.start_time': 'Starttid',
|
'common.scheduled_start': 'Planlagt start',
|
||||||
|
'common.scheduled_end': 'Planlagt slutt',
|
||||||
|
'common.projected_start': 'Forventet start',
|
||||||
|
'common.projected_end': 'Forventet slutt',
|
||||||
'common.stage_timer': 'Scenetimer',
|
'common.stage_timer': 'Scenetimer',
|
||||||
'common.started_at': 'Startet',
|
'common.started_at': 'Startet',
|
||||||
'common.time_now': 'Klokken nå',
|
'common.time_now': 'Klokken nå',
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { TranslationObject } from './en';
|
import { TranslationObject } from './en';
|
||||||
|
|
||||||
export const langPl: TranslationObject = {
|
export const langPl: TranslationObject = {
|
||||||
'common.end_time': 'Czas zakończenia',
|
|
||||||
'common.expected_finish': 'Zakładany czas zakończenia',
|
'common.expected_finish': 'Zakładany czas zakończenia',
|
||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Teraz',
|
'common.now': 'Teraz',
|
||||||
'common.next': 'Następnie',
|
'common.next': 'Następnie',
|
||||||
'common.public_message': 'Wiadomość publiczna',
|
'common.public_message': 'Wiadomość publiczna',
|
||||||
'common.start_time': 'Czas rozpoczęcia',
|
'common.scheduled_start': 'Planowany początek',
|
||||||
|
'common.scheduled_end': 'Planowany koniec',
|
||||||
|
'common.projected_start': 'Przewidywany początek',
|
||||||
|
'common.projected_end': 'Przewidywany koniec',
|
||||||
'common.stage_timer': 'Timer Scena',
|
'common.stage_timer': 'Timer Scena',
|
||||||
'common.started_at': 'Rozpoczęte o',
|
'common.started_at': 'Rozpoczęte o',
|
||||||
'common.time_now': 'Aktualny czas',
|
'common.time_now': 'Aktualny czas',
|
||||||
@@ -18,4 +20,3 @@ export const langPl: TranslationObject = {
|
|||||||
'countdown.waiting': 'Oczekiwanie na start',
|
'countdown.waiting': 'Oczekiwanie na start',
|
||||||
'countdown.overtime': 'ponad czasem',
|
'countdown.overtime': 'ponad czasem',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { TranslationObject } from './en';
|
import { TranslationObject } from './en';
|
||||||
|
|
||||||
export const langPt: TranslationObject = {
|
export const langPt: TranslationObject = {
|
||||||
'common.end_time': 'Hora de término',
|
|
||||||
'common.expected_finish': 'Término esperado',
|
'common.expected_finish': 'Término esperado',
|
||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Agora',
|
'common.now': 'Agora',
|
||||||
'common.next': 'Próximo',
|
'common.next': 'Próximo',
|
||||||
'common.public_message': 'Mensagem pública',
|
'common.public_message': 'Mensagem pública',
|
||||||
'common.start_time': 'Hora de início',
|
'common.scheduled_start': 'Início programado',
|
||||||
|
'common.scheduled_end': 'Fim programado',
|
||||||
|
'common.projected_start': 'Início previsto',
|
||||||
|
'common.projected_end': 'Fim previsto',
|
||||||
'common.stage_timer': 'Temporizador do presentador',
|
'common.stage_timer': 'Temporizador do presentador',
|
||||||
'common.started_at': 'Iniciado em',
|
'common.started_at': 'Iniciado em',
|
||||||
'common.time_now': 'Hora atual',
|
'common.time_now': 'Hora atual',
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { TranslationObject } from './en';
|
import { TranslationObject } from './en';
|
||||||
|
|
||||||
export const langSv: TranslationObject = {
|
export const langSv: TranslationObject = {
|
||||||
'common.end_time': 'Sluttid',
|
|
||||||
'common.expected_finish': 'Förväntat slut',
|
'common.expected_finish': 'Förväntat slut',
|
||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Nu',
|
'common.now': 'Nu',
|
||||||
'common.next': 'Nästa',
|
'common.next': 'Nästa',
|
||||||
'common.public_message': 'Offentligt meddelande',
|
'common.public_message': 'Offentligt meddelande',
|
||||||
'common.start_time': 'Starttid',
|
'common.scheduled_start': 'Planerad start',
|
||||||
|
'common.scheduled_end': 'Planerad slut',
|
||||||
|
'common.projected_start': 'Beräknad start',
|
||||||
|
'common.projected_end': 'Beräknad slut',
|
||||||
'common.stage_timer': 'Timer för scenen',
|
'common.stage_timer': 'Timer för scenen',
|
||||||
'common.started_at': 'Började vid',
|
'common.started_at': 'Började vid',
|
||||||
'common.time_now': 'Klockan nu',
|
'common.time_now': 'Klockan nu',
|
||||||
|
|||||||
Reference in New Issue
Block a user