mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-17 21:24:11 +00:00
bump version to alpha release
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@getontime/cli",
|
"name": "@getontime/cli",
|
||||||
"version": "3.3.2",
|
"version": "3.4.0-alpha-timeline.2",
|
||||||
"author": "Carlos Valente",
|
"author": "Carlos Valente",
|
||||||
"description": "Time keeping for live events",
|
"description": "Time keeping for live events",
|
||||||
"repository": "https://github.com/cpvalente/ontime",
|
"repository": "https://github.com/cpvalente/ontime",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ontime-ui",
|
"name": "ontime-ui",
|
||||||
"version": "3.3.2",
|
"version": "3.4.0-alpha-timeline.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -511,5 +511,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];
|
||||||
|
|
||||||
export const getProgressOptions = (timeFormat: string): ParamField[] => {
|
export const getProgressOptions = (timeFormat: string): ParamField[] => {
|
||||||
return [getTimeOption(timeFormat)];
|
return [
|
||||||
|
getTimeOption(timeFormat),
|
||||||
|
{
|
||||||
|
id: 'fullHeight',
|
||||||
|
title: 'Full height columns',
|
||||||
|
description: 'Whether the columns in the timeline should be the full height ',
|
||||||
|
type: 'boolean',
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,19 @@ export const getAccessibleColour = (bgColour?: string): ColourCombination => {
|
|||||||
return { backgroundColor: '#1a1a1a', color: '#fffffa' };
|
return { backgroundColor: '#1a1a1a', color: '#fffffa' };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds opacity to a given colour, returns undefined if invalid
|
||||||
|
*/
|
||||||
|
export function alpha(colour: string, amount: number): string | undefined {
|
||||||
|
try {
|
||||||
|
const withAlpha = Color(colour).alpha(amount).hexa();
|
||||||
|
return withAlpha;
|
||||||
|
} catch (_error) {
|
||||||
|
/* we do not handle errors here */
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Creates a list of classnames from array of css module conditions
|
* @description Creates a list of classnames from array of css module conditions
|
||||||
* @param classNames - css modules objects
|
* @param classNames - css modules objects
|
||||||
|
|||||||
@@ -6,40 +6,43 @@ $timeline-entry-height: 20px;
|
|||||||
|
|
||||||
.timelineEvents {
|
.timelineEvents {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entryIndicator {
|
.entryColumn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: var(--color, $ui-white);
|
background-color: var(--lighter, $gray-500);
|
||||||
height: $timeline-entry-height;
|
border-bottom: 0.25rem solid var(--color, $ui-white);
|
||||||
|
border-right: 1px solid $ui-black;
|
||||||
|
min-height: 80px;
|
||||||
|
|
||||||
|
&.fullHeight {
|
||||||
|
height: calc(100% - 4rem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.entryContent {
|
.entryContent {
|
||||||
position: absolute;
|
|
||||||
margin-top: $timeline-entry-height;
|
|
||||||
padding-top: var(--top, 0);
|
padding-top: var(--top, 0);
|
||||||
border-left: 2px solid var(--color, $ui-white);
|
|
||||||
|
|
||||||
color: $ui-white;
|
|
||||||
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
min-width: 100%;
|
|
||||||
padding-inline: 0.5em;
|
|
||||||
width: fit-content;
|
|
||||||
background-color: $ui-black;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.lastElement {
|
&.lastElement {
|
||||||
border-left: none;
|
|
||||||
border-right: 2px solid var(--color, $ui-black);
|
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.entryText {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
color: $ui-white;
|
||||||
|
padding-inline: 0.5em;
|
||||||
|
width: fit-content;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&.textBg {
|
||||||
|
background-color: var(--bg, $black-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.start,
|
.start,
|
||||||
.title {
|
.title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -49,7 +52,7 @@ $timeline-entry-height: 20px;
|
|||||||
[data-status='finished'] {
|
[data-status='finished'] {
|
||||||
color: $gray-500;
|
color: $gray-500;
|
||||||
.status {
|
.status {
|
||||||
display: none
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { useViewportSize } from '@mantine/hooks';
|
import { useViewportSize } from '@mantine/hooks';
|
||||||
import { isOntimeEvent, MaybeNumber } from 'ontime-types';
|
import { isOntimeEvent, MaybeNumber } from 'ontime-types';
|
||||||
import { dayInMs, getFirstEventNormal, getLastEventNormal, MILLIS_PER_HOUR } from 'ontime-utils';
|
import { dayInMs, getFirstEventNormal, getLastEventNormal, MILLIS_PER_HOUR } from 'ontime-utils';
|
||||||
|
|
||||||
import useRundown from '../../../common/hooks-query/useRundown';
|
import useRundown from '../../../common/hooks-query/useRundown';
|
||||||
|
import { isStringBoolean } from '../../viewers/common/viewUtils';
|
||||||
|
|
||||||
import { type ProgressStatus, TimelineEntry } from './TimelineEntry';
|
import { type ProgressStatus, TimelineEntry } from './TimelineEntry';
|
||||||
import { TimelineMarkers } from './TimelineMarkers';
|
import { TimelineMarkers } from './TimelineMarkers';
|
||||||
@@ -46,6 +48,8 @@ function Timeline(props: TimelineProps) {
|
|||||||
const { selectedEventId } = props;
|
const { selectedEventId } = props;
|
||||||
const { width: screenWidth } = useViewportSize();
|
const { width: screenWidth } = useViewportSize();
|
||||||
const timelineData = useTimeline();
|
const timelineData = useTimeline();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const fullHeight = isStringBoolean(searchParams.get('fullHeight'));
|
||||||
|
|
||||||
if (timelineData === null) {
|
if (timelineData === null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -62,6 +66,7 @@ function Timeline(props: TimelineProps) {
|
|||||||
return (
|
return (
|
||||||
<div className={style.timeline}>
|
<div className={style.timeline}>
|
||||||
<TimelineMarkers />
|
<TimelineMarkers />
|
||||||
|
<ProgressBar startHour={startHour} endHour={endHour} />
|
||||||
<div className={style.timelineEvents}>
|
<div className={style.timelineEvents}>
|
||||||
{order.map((eventId) => {
|
{order.map((eventId) => {
|
||||||
// for now we dont render delays and blocks
|
// for now we dont render delays and blocks
|
||||||
@@ -92,7 +97,8 @@ function Timeline(props: TimelineProps) {
|
|||||||
event.duration,
|
event.duration,
|
||||||
screenWidth,
|
screenWidth,
|
||||||
);
|
);
|
||||||
const estimatedRightPosition = elementLeftPosition + getEstimatedWidth(event.title);
|
const estimatedWidth = getEstimatedWidth(event.title);
|
||||||
|
const estimatedRightPosition = elementLeftPosition + estimatedWidth;
|
||||||
const laneLevel = getLaneLevel(rightMostElements, elementLeftPosition);
|
const laneLevel = getLaneLevel(rightMostElements, elementLeftPosition);
|
||||||
|
|
||||||
if (rightMostElements[laneLevel] === undefined || rightMostElements[laneLevel] < estimatedRightPosition) {
|
if (rightMostElements[laneLevel] === undefined || rightMostElements[laneLevel] < estimatedRightPosition) {
|
||||||
@@ -111,11 +117,12 @@ function Timeline(props: TimelineProps) {
|
|||||||
start={event.timeStart}
|
start={event.timeStart}
|
||||||
title={event.title}
|
title={event.title}
|
||||||
width={elementWidth}
|
width={elementWidth}
|
||||||
|
mayGrow={elementWidth < estimatedWidth}
|
||||||
|
fullHeight={fullHeight}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<ProgressBar startHour={startHour} endHour={endHour} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useClock } from '../../../common/hooks/useSocket';
|
import { useClock } from '../../../common/hooks/useSocket';
|
||||||
import { cx } from '../../../common/utils/styleUtils';
|
import { alpha, cx } from '../../../common/utils/styleUtils';
|
||||||
import { formatDuration, formatTime } from '../../../common/utils/time';
|
import { formatDuration, formatTime } from '../../../common/utils/time';
|
||||||
|
|
||||||
import { getStatusLabel } from './timelineUtils';
|
import { getStatusLabel } from './timelineUtils';
|
||||||
@@ -18,6 +18,9 @@ interface TimelineEntry {
|
|||||||
start: number;
|
start: number;
|
||||||
title: string;
|
title: string;
|
||||||
width: number;
|
width: number;
|
||||||
|
|
||||||
|
mayGrow: boolean;
|
||||||
|
fullHeight: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const laneHeight = 120;
|
const laneHeight = 120;
|
||||||
@@ -27,39 +30,48 @@ const formatOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function TimelineEntry(props: TimelineEntry) {
|
export function TimelineEntry(props: TimelineEntry) {
|
||||||
const { colour, duration, isLast, lane, left, status, start, title, width } = props;
|
const { colour, duration, isLast, lane, left, status, start, title, width, mayGrow, fullHeight } = props;
|
||||||
|
|
||||||
const formattedStartTime = formatTime(start, formatOptions);
|
const formattedStartTime = formatTime(start, formatOptions);
|
||||||
const formattedDuration = `Dur ${formatDuration(duration)}`;
|
const formattedDuration = formatDuration(duration);
|
||||||
|
|
||||||
|
const lighterColour = alpha(colour, 0.7);
|
||||||
|
const alphaColour = alpha(colour, 0.6);
|
||||||
|
const columnClasses = cx([style.entryColumn, fullHeight && style.fullHeight]);
|
||||||
const contentClasses = cx([style.entryContent, isLast && style.lastElement]);
|
const contentClasses = cx([style.entryContent, isLast && style.lastElement]);
|
||||||
|
const textBgClasses = cx([style.entryText, mayGrow && style.textBg]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<div
|
className={columnClasses}
|
||||||
className={style.entryIndicator}
|
style={{
|
||||||
style={{
|
'--color': colour,
|
||||||
'--color': colour,
|
'--lighter': lighterColour ?? '',
|
||||||
left: `${left}px`,
|
left: `${left}px`,
|
||||||
width: `${width}px`,
|
width: `${width}px`,
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
<div
|
<div
|
||||||
className={contentClasses}
|
className={contentClasses}
|
||||||
data-status={status}
|
data-status={status}
|
||||||
style={{
|
style={{
|
||||||
'--color': colour,
|
'--color': colour,
|
||||||
'--top': `${lane * laneHeight}px`,
|
'--top': `${lane * laneHeight}px`,
|
||||||
zIndex: 5 - lane,
|
|
||||||
left: `${left}px`,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={style.start}>{formattedStartTime}</div>
|
<div
|
||||||
<div className={style.title}>{title}</div>
|
className={textBgClasses}
|
||||||
<div className={style.duration}>{formattedDuration}</div>
|
style={{
|
||||||
<TimelineEntryStatus status={status} start={start} />
|
'--bg': alphaColour ?? '',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className={style.start}>{formattedStartTime}</div>
|
||||||
|
<div className={style.title}>{title}</div>
|
||||||
|
<div className={style.duration}>{formattedDuration}</div>
|
||||||
|
<TimelineEntryStatus status={status} start={start} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
.markers {
|
.markers {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: $ui-white;
|
color: $ui-white;
|
||||||
background-color: $white-10;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
line-height: 1rem;
|
line-height: 1rem;
|
||||||
font-size: calc(1rem - 2px);
|
font-size: calc(1rem - 2px);
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
& > * {
|
& > span {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
border-left: 1px solid black;
|
||||||
|
height: 100vh;
|
||||||
:nth-child(odd) {
|
|
||||||
background-color: $white-20;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
.progressBar {
|
.progressBar {
|
||||||
position: relative;
|
width: 100%;
|
||||||
height: 100%;
|
height: 0.75rem;
|
||||||
border-right: 2px solid $active-red;
|
|
||||||
|
|
||||||
transition-duration: 0.3s;
|
transition-duration: 0.3s;
|
||||||
transition-property: left;
|
transition-property: left;
|
||||||
|
background-color: $gray-900;
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
height: 100%;
|
||||||
|
background-color: $active-red;;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ export function ProgressBar(props: ProgressBarProps) {
|
|||||||
const { startHour, endHour } = props;
|
const { startHour, endHour } = props;
|
||||||
const { clock } = useClock();
|
const { clock } = useClock();
|
||||||
|
|
||||||
const left = getRelativePositionX(startHour, endHour, clock);
|
const width = getRelativePositionX(startHour, endHour, clock);
|
||||||
|
|
||||||
return <div className={style.progressBar} style={{ width: `${left}%` }} />;
|
return (
|
||||||
|
<div className={style.progressBar}>
|
||||||
|
<div className={style.progress} style={{ width: `${width}%` }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
background-color: $ui-black;
|
background-color: $ui-black;
|
||||||
color: $ui-white;
|
color: $ui-white;
|
||||||
text-transform: uppercase;
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -38,11 +37,13 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
||||||
&.now {
|
&.now {
|
||||||
color: $green-500;
|
color: $red-500;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.next {
|
&.next {
|
||||||
color: $red-500;
|
color: $green-500;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.subdue {
|
&.subdue {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ontime",
|
"name": "ontime",
|
||||||
"version": "3.3.2",
|
"version": "3.4.0-alpha-timeline.2",
|
||||||
"author": "Carlos Valente",
|
"author": "Carlos Valente",
|
||||||
"description": "Time keeping for live events",
|
"description": "Time keeping for live events",
|
||||||
"repository": "https://github.com/cpvalente/ontime",
|
"repository": "https://github.com/cpvalente/ontime",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "ontime-server",
|
"name": "ontime-server",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"version": "3.3.2",
|
"version": "3.4.0-alpha-timeline.2",
|
||||||
"exports": "./src/index.js",
|
"exports": "./src/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@googleapis/sheets": "^5.0.5",
|
"@googleapis/sheets": "^5.0.5",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ontime",
|
"name": "ontime",
|
||||||
"version": "3.3.2",
|
"version": "3.4.0-alpha-timeline.2",
|
||||||
"description": "Time keeping for live events",
|
"description": "Time keeping for live events",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"ontime",
|
"ontime",
|
||||||
|
|||||||
Reference in New Issue
Block a user