mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-05 07:28:01 +00:00
bump version to alpha release
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getontime/cli",
|
||||
"version": "3.3.2",
|
||||
"version": "3.4.0-alpha-timeline.2",
|
||||
"author": "Carlos Valente",
|
||||
"description": "Time keeping for live events",
|
||||
"repository": "https://github.com/cpvalente/ontime",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ontime-ui",
|
||||
"version": "3.3.2",
|
||||
"version": "3.4.0-alpha-timeline.2",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -511,5 +511,14 @@ export const getOperatorOptions = (customFields: CustomFields, timeFormat: strin
|
||||
export const getCountdownOptions = (timeFormat: string): ParamField[] => [getTimeOption(timeFormat), hideTimerSeconds];
|
||||
|
||||
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' };
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param classNames - css modules objects
|
||||
|
||||
@@ -6,40 +6,43 @@ $timeline-entry-height: 20px;
|
||||
|
||||
.timelineEvents {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.entryIndicator {
|
||||
.entryColumn {
|
||||
position: absolute;
|
||||
background-color: var(--color, $ui-white);
|
||||
height: $timeline-entry-height;
|
||||
background-color: var(--lighter, $gray-500);
|
||||
border-bottom: 0.25rem solid var(--color, $ui-white);
|
||||
border-right: 1px solid $ui-black;
|
||||
min-height: 80px;
|
||||
|
||||
&.fullHeight {
|
||||
height: calc(100% - 4rem);
|
||||
}
|
||||
}
|
||||
|
||||
.entryContent {
|
||||
position: absolute;
|
||||
margin-top: $timeline-entry-height;
|
||||
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 {
|
||||
border-left: none;
|
||||
border-right: 2px solid var(--color, $ui-black);
|
||||
text-align: right;
|
||||
|
||||
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,
|
||||
.title {
|
||||
font-weight: 600;
|
||||
@@ -49,7 +52,7 @@ $timeline-entry-height: 20px;
|
||||
[data-status='finished'] {
|
||||
color: $gray-500;
|
||||
.status {
|
||||
display: none
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { memo } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useViewportSize } from '@mantine/hooks';
|
||||
import { isOntimeEvent, MaybeNumber } from 'ontime-types';
|
||||
import { dayInMs, getFirstEventNormal, getLastEventNormal, MILLIS_PER_HOUR } from 'ontime-utils';
|
||||
|
||||
import useRundown from '../../../common/hooks-query/useRundown';
|
||||
import { isStringBoolean } from '../../viewers/common/viewUtils';
|
||||
|
||||
import { type ProgressStatus, TimelineEntry } from './TimelineEntry';
|
||||
import { TimelineMarkers } from './TimelineMarkers';
|
||||
@@ -46,6 +48,8 @@ function Timeline(props: TimelineProps) {
|
||||
const { selectedEventId } = props;
|
||||
const { width: screenWidth } = useViewportSize();
|
||||
const timelineData = useTimeline();
|
||||
const [searchParams] = useSearchParams();
|
||||
const fullHeight = isStringBoolean(searchParams.get('fullHeight'));
|
||||
|
||||
if (timelineData === null) {
|
||||
return null;
|
||||
@@ -62,6 +66,7 @@ function Timeline(props: TimelineProps) {
|
||||
return (
|
||||
<div className={style.timeline}>
|
||||
<TimelineMarkers />
|
||||
<ProgressBar startHour={startHour} endHour={endHour} />
|
||||
<div className={style.timelineEvents}>
|
||||
{order.map((eventId) => {
|
||||
// for now we dont render delays and blocks
|
||||
@@ -92,7 +97,8 @@ function Timeline(props: TimelineProps) {
|
||||
event.duration,
|
||||
screenWidth,
|
||||
);
|
||||
const estimatedRightPosition = elementLeftPosition + getEstimatedWidth(event.title);
|
||||
const estimatedWidth = getEstimatedWidth(event.title);
|
||||
const estimatedRightPosition = elementLeftPosition + estimatedWidth;
|
||||
const laneLevel = getLaneLevel(rightMostElements, elementLeftPosition);
|
||||
|
||||
if (rightMostElements[laneLevel] === undefined || rightMostElements[laneLevel] < estimatedRightPosition) {
|
||||
@@ -111,11 +117,12 @@ function Timeline(props: TimelineProps) {
|
||||
start={event.timeStart}
|
||||
title={event.title}
|
||||
width={elementWidth}
|
||||
mayGrow={elementWidth < estimatedWidth}
|
||||
fullHeight={fullHeight}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<ProgressBar startHour={startHour} endHour={endHour} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 { getStatusLabel } from './timelineUtils';
|
||||
@@ -18,6 +18,9 @@ interface TimelineEntry {
|
||||
start: number;
|
||||
title: string;
|
||||
width: number;
|
||||
|
||||
mayGrow: boolean;
|
||||
fullHeight: boolean;
|
||||
}
|
||||
|
||||
const laneHeight = 120;
|
||||
@@ -27,39 +30,48 @@ const formatOptions = {
|
||||
};
|
||||
|
||||
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 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 textBgClasses = cx([style.entryText, mayGrow && style.textBg]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={style.entryIndicator}
|
||||
style={{
|
||||
'--color': colour,
|
||||
left: `${left}px`,
|
||||
width: `${width}px`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={columnClasses}
|
||||
style={{
|
||||
'--color': colour,
|
||||
'--lighter': lighterColour ?? '',
|
||||
left: `${left}px`,
|
||||
width: `${width}px`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={contentClasses}
|
||||
data-status={status}
|
||||
style={{
|
||||
'--color': colour,
|
||||
'--top': `${lane * laneHeight}px`,
|
||||
zIndex: 5 - lane,
|
||||
left: `${left}px`,
|
||||
}}
|
||||
>
|
||||
<div className={style.start}>{formattedStartTime}</div>
|
||||
<div className={style.title}>{title}</div>
|
||||
<div className={style.duration}>{formattedDuration}</div>
|
||||
<TimelineEntryStatus status={status} start={start} />
|
||||
<div
|
||||
className={textBgClasses}
|
||||
style={{
|
||||
'--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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
.markers {
|
||||
width: 100%;
|
||||
color: $ui-white;
|
||||
background-color: $white-10;
|
||||
display: flex;
|
||||
height: 1rem;
|
||||
line-height: 1rem;
|
||||
font-size: calc(1rem - 2px);
|
||||
justify-content: space-evenly;
|
||||
text-align: center;
|
||||
|
||||
& > * {
|
||||
& > span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
:nth-child(odd) {
|
||||
background-color: $white-20;
|
||||
border-left: 1px solid black;
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
.progressBar {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
border-right: 2px solid $active-red;
|
||||
width: 100%;
|
||||
height: 0.75rem;
|
||||
|
||||
transition-duration: 0.3s;
|
||||
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 { 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;
|
||||
color: $ui-white;
|
||||
text-transform: uppercase;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -38,11 +37,13 @@
|
||||
font-weight: 600;
|
||||
|
||||
&.now {
|
||||
color: $green-500;
|
||||
color: $red-500;
|
||||
|
||||
}
|
||||
|
||||
&.next {
|
||||
color: $red-500;
|
||||
color: $green-500;
|
||||
|
||||
}
|
||||
|
||||
&.subdue {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ontime",
|
||||
"version": "3.3.2",
|
||||
"version": "3.4.0-alpha-timeline.2",
|
||||
"author": "Carlos Valente",
|
||||
"description": "Time keeping for live events",
|
||||
"repository": "https://github.com/cpvalente/ontime",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "ontime-server",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"version": "3.3.2",
|
||||
"version": "3.4.0-alpha-timeline.2",
|
||||
"exports": "./src/index.js",
|
||||
"dependencies": {
|
||||
"@googleapis/sheets": "^5.0.5",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ontime",
|
||||
"version": "3.3.2",
|
||||
"version": "3.4.0-alpha-timeline.2",
|
||||
"description": "Time keeping for live events",
|
||||
"keywords": [
|
||||
"ontime",
|
||||
|
||||
Reference in New Issue
Block a user