From 9334460f5a6902403d2773f4e9ce14ab63ef9103 Mon Sep 17 00:00:00 2001 From: Alexis Jacomy Date: Fri, 28 Feb 2025 10:15:37 +0100 Subject: [PATCH 1/2] ui-spacetimechart: imports proper style in stories Signed-off-by: Alexis Jacomy --- ui-spacetimechart/src/stories/additional-data.stories.tsx | 1 + ui-spacetimechart/src/stories/base-rendering.stories.tsx | 1 + ui-spacetimechart/src/stories/custom-styles.stories.tsx | 1 + ui-spacetimechart/src/stories/layers.stories.tsx | 1 + ui-spacetimechart/src/stories/measuring.stories.tsx | 1 + ui-spacetimechart/src/stories/options.stories.tsx | 3 +++ ui-spacetimechart/src/stories/paths-interactions.stories.tsx | 1 + ui-spacetimechart/src/stories/performances.stories.tsx | 1 + ui-spacetimechart/src/stories/scroll-navigation.stories.tsx | 1 + ui-spacetimechart/src/stories/stage-interactions.stories.tsx | 3 +++ ui-spacetimechart/src/stories/work-schedules.stories.tsx | 5 +++-- 11 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ui-spacetimechart/src/stories/additional-data.stories.tsx b/ui-spacetimechart/src/stories/additional-data.stories.tsx index f4a8053b5..0c6591f30 100644 --- a/ui-spacetimechart/src/stories/additional-data.stories.tsx +++ b/ui-spacetimechart/src/stories/additional-data.stories.tsx @@ -20,6 +20,7 @@ import { AMBIANT_A10, ERROR_30, ERROR_60, HOUR, KILOMETER, MINUTE } from '../lib import { type DrawingFunction, type Point } from '../lib/types'; import { getDiff } from '../utils/vectors'; +import '@osrd-project/ui-core/dist/theme.css'; import '@osrd-project/ui-spacetimechart/dist/theme.css'; const MONO_TRACK_SPACES = [ diff --git a/ui-spacetimechart/src/stories/base-rendering.stories.tsx b/ui-spacetimechart/src/stories/base-rendering.stories.tsx index fa722264b..a68e559c4 100644 --- a/ui-spacetimechart/src/stories/base-rendering.stories.tsx +++ b/ui-spacetimechart/src/stories/base-rendering.stories.tsx @@ -6,6 +6,7 @@ import { SpaceTimeChart, PathLayer } from '../'; import { OPERATIONAL_POINTS, PATHS } from './lib/paths'; import { X_ZOOM_LEVEL, Y_ZOOM_LEVEL } from './lib/utils'; +import '@osrd-project/ui-core/dist/theme.css'; import '@osrd-project/ui-spacetimechart/dist/theme.css'; type WrapperProps = { diff --git a/ui-spacetimechart/src/stories/custom-styles.stories.tsx b/ui-spacetimechart/src/stories/custom-styles.stories.tsx index 657942ef4..6ee9c8acb 100644 --- a/ui-spacetimechart/src/stories/custom-styles.stories.tsx +++ b/ui-spacetimechart/src/stories/custom-styles.stories.tsx @@ -16,6 +16,7 @@ import { import { type Point } from '../lib/types'; import { getDiff } from '../utils/vectors'; +import '@osrd-project/ui-core/dist/theme.css'; import '@osrd-project/ui-spacetimechart/dist/theme.css'; const DEFAULT_COLOR_1 = '#FF511A'; diff --git a/ui-spacetimechart/src/stories/layers.stories.tsx b/ui-spacetimechart/src/stories/layers.stories.tsx index 5888d9ebe..fe69dc61d 100644 --- a/ui-spacetimechart/src/stories/layers.stories.tsx +++ b/ui-spacetimechart/src/stories/layers.stories.tsx @@ -21,6 +21,7 @@ import { } from '../lib/consts'; import type { Point } from '../lib/types'; +import '@osrd-project/ui-core/dist/theme.css'; import '@osrd-project/ui-spacetimechart/dist/theme.css'; const CONFLICTS = [ diff --git a/ui-spacetimechart/src/stories/measuring.stories.tsx b/ui-spacetimechart/src/stories/measuring.stories.tsx index c81141fef..196c54778 100644 --- a/ui-spacetimechart/src/stories/measuring.stories.tsx +++ b/ui-spacetimechart/src/stories/measuring.stories.tsx @@ -10,6 +10,7 @@ import { X_ZOOM_LEVEL, Y_ZOOM_LEVEL, zoom } from './lib/utils'; import { type DataPoint, type Point } from '../lib/types'; import { getDiff } from '../utils/vectors'; +import '@osrd-project/ui-core/dist/theme.css'; import '@osrd-project/ui-spacetimechart/dist/theme.css'; type WrapperProps = { diff --git a/ui-spacetimechart/src/stories/options.stories.tsx b/ui-spacetimechart/src/stories/options.stories.tsx index a9d5eaebb..8952d6c61 100644 --- a/ui-spacetimechart/src/stories/options.stories.tsx +++ b/ui-spacetimechart/src/stories/options.stories.tsx @@ -19,6 +19,9 @@ import { CanvasContext } from '../lib/context'; import { type Point } from '../lib/types'; import { getDiff } from '../utils/vectors'; +import '@osrd-project/ui-core/dist/theme.css'; +import '@osrd-project/ui-spacetimechart/dist/theme.css'; + const ScreenshotButton = () => { const { captureCanvases } = useContext(CanvasContext); diff --git a/ui-spacetimechart/src/stories/paths-interactions.stories.tsx b/ui-spacetimechart/src/stories/paths-interactions.stories.tsx index 2eb8ba09c..a65a5e020 100644 --- a/ui-spacetimechart/src/stories/paths-interactions.stories.tsx +++ b/ui-spacetimechart/src/stories/paths-interactions.stories.tsx @@ -10,6 +10,7 @@ import { X_ZOOM_LEVEL, Y_ZOOM_LEVEL, zoom } from './lib/utils'; import { type PathData, type Point } from '../lib/types'; import { getDiff } from '../utils/vectors'; +import '@osrd-project/ui-core/dist/theme.css'; import '@osrd-project/ui-spacetimechart/dist/theme.css'; function delayPath(path: T, newTimeOrigin: number): T { diff --git a/ui-spacetimechart/src/stories/performances.stories.tsx b/ui-spacetimechart/src/stories/performances.stories.tsx index 2d0a21611..cdf8e2f47 100644 --- a/ui-spacetimechart/src/stories/performances.stories.tsx +++ b/ui-spacetimechart/src/stories/performances.stories.tsx @@ -11,6 +11,7 @@ import { KILOMETER, MINUTE } from '../lib/consts'; import { type OperationalPoint, type Point } from '../lib/types'; import { getDiff } from '../utils/vectors'; +import '@osrd-project/ui-core/dist/theme.css'; import '@osrd-project/ui-spacetimechart/dist/theme.css'; const DATE_OFFSET = +new Date('2024/01/01'); diff --git a/ui-spacetimechart/src/stories/scroll-navigation.stories.tsx b/ui-spacetimechart/src/stories/scroll-navigation.stories.tsx index 25a17d5b1..cadc6cbde 100644 --- a/ui-spacetimechart/src/stories/scroll-navigation.stories.tsx +++ b/ui-spacetimechart/src/stories/scroll-navigation.stories.tsx @@ -12,6 +12,7 @@ import { isPathOnScreen } from '../utils/geometry'; import { getSpaceAtTime } from '../utils/scales'; import { getDiff } from '../utils/vectors'; +import '@osrd-project/ui-core/dist/theme.css'; import '@osrd-project/ui-spacetimechart/dist/theme.css'; const PATHS_DICT = keyBy(PATHS, 'id'); diff --git a/ui-spacetimechart/src/stories/stage-interactions.stories.tsx b/ui-spacetimechart/src/stories/stage-interactions.stories.tsx index 0923103ee..fb36cda35 100644 --- a/ui-spacetimechart/src/stories/stage-interactions.stories.tsx +++ b/ui-spacetimechart/src/stories/stage-interactions.stories.tsx @@ -16,6 +16,9 @@ import { import { type Point } from '../lib/types'; import { getDiff } from '../utils/vectors'; +import '@osrd-project/ui-core/dist/theme.css'; +import '@osrd-project/ui-spacetimechart/dist/theme.css'; + type WrapperProps = { xPan: boolean; yPan: boolean; diff --git a/ui-spacetimechart/src/stories/work-schedules.stories.tsx b/ui-spacetimechart/src/stories/work-schedules.stories.tsx index 9b3e97517..e8dbdca02 100644 --- a/ui-spacetimechart/src/stories/work-schedules.stories.tsx +++ b/ui-spacetimechart/src/stories/work-schedules.stories.tsx @@ -1,7 +1,5 @@ import React, { useState } from 'react'; -import '@osrd-project/ui-core/dist/theme.css'; - import type { Meta } from '@storybook/react'; import { OPERATIONAL_POINTS, PATHS } from './lib/paths'; @@ -13,6 +11,9 @@ import { type Point, type PathData, type OperationalPoint } from '../lib/types'; import { type WorkSchedule } from '../types'; import { getDiff } from '../utils/vectors'; +import '@osrd-project/ui-core/dist/theme.css'; +import '@osrd-project/ui-spacetimechart/dist/theme.css'; + const SAMPLE_WORK_SCHEDULES: WorkSchedule[] = [ { type: 'TRACK', From fab80350b941aa8f7201045736ca3a4a0649a1ae Mon Sep 17 00:00:00 2001 From: Alexis Jacomy Date: Fri, 28 Feb 2025 10:21:13 +0100 Subject: [PATCH 2/2] ui-spacetimechart: upgrades time captions This commit implements new designs from @thibautsailly, and addresses issue OpenRailAssociation/osrd#10663. Details: - Updates styles matrices for time captions and graduations - Adds date labels - Adds new option `hideDates` to hide date labels, adds it to the options story - Adds some margin around the time captions stage, so that captions of times right outside the stage are still visible while panning - Fixes path labels when axis are swapped Signed-off-by: Alexis Jacomy --- .../src/components/PathLayer.tsx | 20 +-- .../src/components/SpaceTimeChart.tsx | 7 ++ .../src/components/TimeCaptions.tsx | 115 ++++++++++++------ ui-spacetimechart/src/lib/consts.ts | 58 +++++---- ui-spacetimechart/src/lib/types.ts | 35 ++++-- .../src/stories/options.stories.tsx | 9 ++ 6 files changed, 157 insertions(+), 87 deletions(-) diff --git a/ui-spacetimechart/src/components/PathLayer.tsx b/ui-spacetimechart/src/components/PathLayer.tsx index f562d0af3..56354d0c4 100644 --- a/ui-spacetimechart/src/components/PathLayer.tsx +++ b/ui-spacetimechart/src/components/PathLayer.tsx @@ -2,7 +2,6 @@ import { useCallback } from 'react'; import { inRange, last } from 'lodash'; -import { CAPTION_SIZE } from './TimeCaptions'; import { useDraw, usePicking } from '../hooks/useCanvas'; import { type DataPoint, @@ -199,6 +198,7 @@ export const PathLayer = ({ width, height, swapAxis, + captionSize, theme: { background, pathsStyles: { fontSize, fontFamily }, @@ -211,10 +211,12 @@ export const PathLayer = ({ ) => { if (!label) return; - const firstPointOnScreenIndex = points.findIndex(({ x, y }) => - !swapAxis - ? inRange(x, 0, width) && inRange(y, 0, height - CAPTION_SIZE) - : inRange(x, CAPTION_SIZE, width) && inRange(y, 0, height) + const minX = swapAxis ? captionSize : 0; + const minY = 0; + const maxX = width; + const maxY = swapAxis ? height : height - captionSize; + const firstPointOnScreenIndex = points.findIndex( + ({ x, y }) => inRange(x, minX, maxX) && inRange(y, minY, maxY) ); if (firstPointOnScreenIndex < 0) return; @@ -230,11 +232,11 @@ export const PathLayer = ({ if (next) angle = Math.atan2(next.y - curr.y, next.x - curr.x); } else { const slope = (curr.y - prev.y) / (curr.x - prev.x); - const yOnYAxisIntersect = curr.y - curr.x * slope; - const xOnXAxisIntersect = curr.x - curr.y / slope; - if (yOnYAxisIntersect >= 0) { + const yOnYAxisIntersect = curr.y - minY - (curr.x - minX) * slope; + const xOnXAxisIntersect = curr.x - minX - (curr.y - minY) / slope; + if (yOnYAxisIntersect >= minY) { position = { - x: 0, + x: minX, y: yOnYAxisIntersect, }; } else { diff --git a/ui-spacetimechart/src/components/SpaceTimeChart.tsx b/ui-spacetimechart/src/components/SpaceTimeChart.tsx index 175222a54..b378f14cd 100644 --- a/ui-spacetimechart/src/components/SpaceTimeChart.tsx +++ b/ui-spacetimechart/src/components/SpaceTimeChart.tsx @@ -45,6 +45,7 @@ export const SpaceTimeChart = (props: SpaceTimeChartProps) => { hideGrid, hidePathsLabels, showTicks, + hideDates, theme, /* eslint-disable @typescript-eslint/no-unused-vars */ onPan, @@ -76,6 +77,7 @@ export const SpaceTimeChart = (props: SpaceTimeChartProps) => { hideGrid, hidePathsLabels, showTicks, + hideDates, }), [ operationalPoints, @@ -91,6 +93,7 @@ export const SpaceTimeChart = (props: SpaceTimeChartProps) => { hideGrid, hidePathsLabels, showTicks, + hideDates, ] ); @@ -154,7 +157,11 @@ export const SpaceTimeChart = (props: SpaceTimeChartProps) => { hideGrid: !!hideGrid, hidePathsLabels: !!hidePathsLabels, showTicks: !!showTicks, + hideDates: !!hideDates, theme: fullTheme, + captionSize: hideDates + ? fullTheme.timeCaptionsSize + : fullTheme.dateCaptionsSize + fullTheme.timeCaptionsSize, }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [fingerprint]); diff --git a/ui-spacetimechart/src/components/TimeCaptions.tsx b/ui-spacetimechart/src/components/TimeCaptions.tsx index 3ce00c0ed..03896e251 100644 --- a/ui-spacetimechart/src/components/TimeCaptions.tsx +++ b/ui-spacetimechart/src/components/TimeCaptions.tsx @@ -1,10 +1,13 @@ import { useCallback } from 'react'; +import { map } from 'lodash'; + import { useDraw } from '../hooks/useCanvas'; -import { MINUTE } from '../lib/consts'; +import { HOUR, MINUTE } from '../lib/consts'; import { type DrawingFunction } from '../lib/types'; import { computeVisibleTimeMarkers, getCrispLineCoordinate } from '../utils/canvas'; +const MARGIN = 100; const MINUTES_FORMATTER = (t: number) => `:${new Date(t).getMinutes().toString().padStart(2, '0')}`; const HOURS_FORMATTER = (t: number, pixelsPerMinute: number) => { const date = new Date(t); @@ -16,8 +19,15 @@ const HOURS_FORMATTER = (t: number, pixelsPerMinute: number) => { return date.getHours().toString().padStart(2, '0'); } }; +const DATES_FORMATER = (t: number) => { + const date = new Date(t); + return [ + date.getDate().toString().padStart(2, '0'), + (date.getMonth() + 1).toString().padStart(2, '0'), + date.getFullYear().toString(), + ].join('/'); +}; -export const CAPTION_SIZE = 33; const RANGES_FORMATER: ((t: number, pixelsPerMinute: number) => string)[] = [ () => '', () => '', @@ -51,14 +61,19 @@ const TimeCaptions = () => { timeCaptionsPriorities, timeCaptionsStyles, timeGraduationsStyles, + dateCaptionsStyle, }, + captionSize, + hideDates, showTicks, } ) => { const timeAxisSize = !swapAxis ? width : height; const spaceAxisSize = !swapAxis ? height : width; - const minT = timeOrigin - timeScale * timePixelOffset; - const maxT = minT + timeScale * width; + + // Add some margin, so that captions of times right outside the stage are still visible: + const minT = timeOrigin - timeScale * (timePixelOffset + MARGIN); + const maxT = minT + timeScale * (width + MARGIN * 2); // Find which styles to apply, relatively to the timescale (i.e. horizontal zoom level): const pixelsPerMinute = (1 / timeScale) * MINUTE; @@ -72,71 +87,91 @@ const TimeCaptions = () => { return false; }); - const labelMarkFormatter = (labelLevel: number, i: number) => ({ - level: labelLevel, - rangeIndex: i, - }); const labelMarks = computeVisibleTimeMarkers( minT, maxT, timeRanges, labelLevels, - labelMarkFormatter + (level: number, i: number) => ({ + level, + styles: timeCaptionsStyles[level], + formatter: RANGES_FORMATER[i], + }) ); - + let allMarksArray = map(labelMarks, (mark, t) => ({ + ...mark, + time: +t, + })); + if (!hideDates) + allMarksArray = allMarksArray.concat( + map( + computeVisibleTimeMarkers(minT, maxT, [24 * HOUR], [1], (level: number) => ({ + level, + styles: dateCaptionsStyle, + formatter: DATES_FORMATER, + })), + (mark, t) => ({ + ...mark, + time: +t, + }) + ) + ); // Render caption background: ctx.fillStyle = background; if (!swapAxis) { - ctx.fillRect(0, spaceAxisSize - CAPTION_SIZE, timeAxisSize, CAPTION_SIZE); + ctx.fillRect(0, spaceAxisSize - captionSize, timeAxisSize, captionSize); } else { - ctx.fillRect(0, 0, CAPTION_SIZE, timeAxisSize); - } - - // Render caption top border: - ctx.strokeStyle = timeGraduationsStyles[1].color; - ctx.lineWidth = timeGraduationsStyles[1].width; - if (!showTicks) { - ctx.beginPath(); - if (!swapAxis) { - const y = getCrispLineCoordinate(spaceAxisSize - CAPTION_SIZE, ctx.lineWidth); - ctx.moveTo(0, y); - ctx.lineTo(timeAxisSize, y); - } else { - const x = getCrispLineCoordinate(CAPTION_SIZE, ctx.lineWidth); - ctx.moveTo(x, 0); - ctx.lineTo(x, timeAxisSize); - } - ctx.stroke(); + ctx.fillRect(0, 0, captionSize, timeAxisSize); } // Render time captions: - for (const t in labelMarks) { - const { level, rangeIndex } = labelMarks[t]; - const styles = timeCaptionsStyles[level]; - const formatter = RANGES_FORMATER[rangeIndex]; - const text = formatter(+t, pixelsPerMinute); + allMarksArray.forEach(({ styles, formatter, time }) => { + const text = formatter(time, pixelsPerMinute); - ctx.textAlign = 'center'; + ctx.textAlign = styles.textAlign || 'center'; ctx.textBaseline = 'top'; ctx.fillStyle = styles.color; + ctx.lineWidth = 5; + ctx.strokeStyle = background; + ctx.lineCap = 'butt'; ctx.font = `${styles.fontWeight || 'normal'} ${styles.font}`; - const timePixel = getCrispLineCoordinate(getTimePixel(+t), ctx.lineWidth); + const timePixel = getCrispLineCoordinate(getTimePixel(time), ctx.lineWidth); if (!swapAxis) { if (showTicks) { ctx.strokeStyle = timeCaptionsStyles[1].color; - ctx.moveTo(timePixel, spaceAxisSize - CAPTION_SIZE); - ctx.lineTo(timePixel, +t % 180000 === 0 ? 8 : 4); + ctx.moveTo(timePixel, spaceAxisSize - captionSize); + ctx.lineTo(timePixel, time % 180000 === 0 ? 8 : 4); ctx.stroke(); } - ctx.fillText(text, timePixel, spaceAxisSize - CAPTION_SIZE + (styles.topOffset || 0)); + + ctx.strokeText(text, timePixel, spaceAxisSize - captionSize + (styles.topOffset || 0)); + ctx.fillText(text, timePixel, spaceAxisSize - captionSize + (styles.topOffset || 0)); } else { ctx.save(); - ctx.translate(CAPTION_SIZE - (styles.topOffset || 0), timePixel); + ctx.translate(captionSize - (styles.topOffset || 0), timePixel); ctx.rotate(Math.PI / 2); + ctx.strokeText(text, 0, 0); ctx.fillText(text, 0, 0); ctx.restore(); } + }); + + // Render caption top border: + ctx.strokeStyle = timeGraduationsStyles[1].color; + ctx.lineWidth = timeGraduationsStyles[1].width; + if (!showTicks) { + ctx.beginPath(); + if (!swapAxis) { + const y = getCrispLineCoordinate(spaceAxisSize - captionSize, ctx.lineWidth); + ctx.moveTo(0, y); + ctx.lineTo(timeAxisSize, y); + } else { + const x = getCrispLineCoordinate(captionSize, ctx.lineWidth); + ctx.moveTo(x, 0); + ctx.lineTo(x, timeAxisSize); + } + ctx.stroke(); } }, [] diff --git a/ui-spacetimechart/src/lib/consts.ts b/ui-spacetimechart/src/lib/consts.ts index 9f2dade8a..348316f9e 100644 --- a/ui-spacetimechart/src/lib/consts.ts +++ b/ui-spacetimechart/src/lib/consts.ts @@ -6,6 +6,7 @@ export const BLUE = '#2170B9'; export const GREY_10 = '#EDEDED'; export const GREY_30 = '#B6B2AF'; export const GREY_50 = '#797671'; +export const WHITE = '#FFFFFF'; export const WHITE_75 = '#FFFFFFC0'; export const AMBIANT_A10 = '#EFF3F5'; export const ERROR_30 = '#FF6868'; @@ -19,7 +20,7 @@ export const OCCUPANCY_WARNING = '#FFEABF'; // Fonts: export const FONT_SIZE = 10; -export const FONT = 'IBM Plex Sans'; +export const FONT = 'IBM Plex Sans, sans-serif'; // Here are some helpers to write code about time in ms that is humanly readable: export const SECOND = 1000; @@ -31,7 +32,7 @@ export const KILOMETER = 1000; export const DEFAULT_THEME: SpaceTimeChartTheme = { background: 'white', - breakpoints: [0.2, 0.4, 0.8, 2.4, 12, 32, 72, Infinity], + breakpoints: [0.2, 0.5, 1, 4, 16, 48, 72, Infinity], timeRanges: [ 10 * SECOND, 30 * SECOND, @@ -69,84 +70,89 @@ export const DEFAULT_THEME: SpaceTimeChartTheme = { // and with which priority level: // - Each line corresponds to a breakpoint, in the same order as in the BREAKPOINTS array // - Each column corresponds to a time range, in the same order as in the TIME_RANGES array + timeCaptionsSize: 40, timeCaptionsPriorities: [ [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1], [0, 0, 0, 0, 0, 3, 1, 1, 1, 1, 1], [0, 0, 0, 0, 3, 1, 1, 1, 1, 1, 1], [0, 0, 0, 3, 2, 1, 1, 1, 1, 1, 1], [0, 0, 3, 2, 1, 1, 1, 1, 1, 1, 1], - [0, 0, 3, 2, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1], ], timeCaptionsStyles: { 1: { color: GREY_50, font: `12px ${FONT}`, - topOffset: 11, + topOffset: 12, }, 2: { color: GREY_30, font: `12px ${FONT}`, - topOffset: 9, + topOffset: 12, }, 3: { color: GREY_30, font: `10px ${FONT}`, topOffset: 6, }, - 4: { - color: GREY_30, - font: `8px ${FONT}`, - topOffset: 8, - }, + }, + dateCaptionsSize: 0, + dateCaptionsStyle: { + color: GREY_30, + font: `10px ${FONT}`, + topOffset: 28, + textAlign: 'left', }, // The following matrix indicate, for various zoom levels, what time marks should be represented, // and with which priority level: // - Each line corresponds to a breakpoint, in the same order as in the BREAKPOINTS array // - Each column corresponds to a time range, in the same order as in the TIME_RANGES array timeGraduationsPriorities: [ - [0, 0, 0, 0, 0, 0, 0, 4, 3, 2, 1], - [0, 0, 0, 0, 0, 0, 4, 3, 3, 2, 1], + [0, 0, 0, 0, 0, 0, 0, 6, 4, 2, 1], + [0, 0, 0, 0, 0, 0, 6, 5, 5, 2, 1], [0, 0, 0, 0, 0, 6, 4, 3, 3, 2, 1], - [0, 0, 0, 0, 6, 5, 4, 3, 3, 2, 1], + [0, 0, 0, 0, 6, 5, 4, 3, 2, 2, 1], [0, 0, 0, 6, 5, 4, 3, 2, 2, 2, 1], [0, 0, 6, 5, 4, 4, 3, 2, 2, 2, 1], - [0, 6, 5, 4, 4, 4, 3, 2, 2, 2, 1], - [6, 0, 5, 4, 4, 4, 3, 2, 2, 2, 1], + [0, 6, 5, 4, 3, 3, 2, 2, 2, 2, 1], + [6, 5, 4, 3, 3, 3, 2, 2, 2, 2, 1], ], timeGraduationsStyles: { 1: { - width: 0.5, + width: 0.75, color: BLACK, + opacity: 0.5, }, 2: { width: 0.5, color: BLUE, - opacity: 0.77, + opacity: 0.7, }, 3: { width: 0.5, color: BLUE, - opacity: 0.5, + opacity: 0.7, + dashArray: [80, 8], }, 4: { width: 0.5, color: BLUE, - opacity: 0.5, - dashArray: [6, 6], + opacity: 0.6, + dashArray: [16, 4], }, 5: { width: 0.5, color: BLUE, - opacity: 0.5, - dashArray: [6, 18], + opacity: 0.6, + dashArray: [8, 8], }, 6: { - width: 1, + width: 0.75, color: BLUE, - opacity: 0.5, - dashArray: [1, 12], + opacity: 0.6, + dashArray: [2, 8], }, }, }; diff --git a/ui-spacetimechart/src/lib/types.ts b/ui-spacetimechart/src/lib/types.ts index ed89d662c..5c193b9e4 100644 --- a/ui-spacetimechart/src/lib/types.ts +++ b/ui-spacetimechart/src/lib/types.ts @@ -144,12 +144,26 @@ export type MouseContextType = MouseState & { hoveredItem: HoveredItem | null; }; -export type LineStyle = { width: number; color: string; opacity?: number; dashArray?: number[] }; +export type LineStyle = { + width: number; + color: string; + opacity?: number; + dashArray?: number[]; +}; + +export type CaptionStyle = { + color: string; + font: string; + fontWeight?: string; + fontSize?: string; + topOffset?: number; + textAlign?: CanvasTextAlign; +}; // STYLES: export type SpaceTimeChartTheme = { background: string; - breakpoints: number[]; + breakpoints: number[]; // in pixels/minute timeRanges: number[]; pathsStyles: { fontSize: number; @@ -157,18 +171,12 @@ export type SpaceTimeChartTheme = { }; spaceGraduationsStyles: Record; timeCaptionsPriorities: number[][]; - timeCaptionsStyles: Record< - number, - { - color: string; - font: string; - fontWeight?: string; - fontSize?: string; - topOffset?: number; - } - >; + timeCaptionsStyles: Record; + timeCaptionsSize: number; timeGraduationsPriorities: number[][]; timeGraduationsStyles: Record; + dateCaptionsStyle: CaptionStyle; + dateCaptionsSize: number; }; // CORE COMPONENT MAIN TYPES: @@ -200,6 +208,7 @@ export type SpaceTimeChartProps = { // Additional options to show/hide context information: hideGrid?: boolean; hidePathsLabels?: boolean; + hideDates?: boolean; showTicks?: boolean; // Custom styles: @@ -279,10 +288,12 @@ export type SpaceTimeChartContextType = { // Full theme: theme: SpaceTimeChartTheme; + captionSize: number; // Other options: enableSnapping: boolean; hideGrid: boolean; hidePathsLabels: boolean; + hideDates: boolean; showTicks: boolean; }; diff --git a/ui-spacetimechart/src/stories/options.stories.tsx b/ui-spacetimechart/src/stories/options.stories.tsx index 8952d6c61..91c2609f5 100644 --- a/ui-spacetimechart/src/stories/options.stories.tsx +++ b/ui-spacetimechart/src/stories/options.stories.tsx @@ -48,6 +48,7 @@ type WrapperProps = { enableSnapping: boolean; hideGrid: boolean; hidePathsLabels: boolean; + hideDates: boolean; swapAxis: boolean; spaceScaleType: 'linear' | 'proportional'; }; @@ -61,6 +62,7 @@ const Wrapper = ({ enableSnapping, hideGrid, hidePathsLabels, + hideDates, swapAxis, spaceScaleType, }: WrapperProps) => { @@ -85,6 +87,7 @@ const Wrapper = ({ enableSnapping={enableSnapping} hideGrid={hideGrid} hidePathsLabels={hidePathsLabels} + hideDates={hideDates} swapAxis={swapAxis} operationalPoints={OPERATIONAL_POINTS} spaceOrigin={0} @@ -179,6 +182,11 @@ export default { defaultValue: false, control: { type: 'boolean' }, }, + hideDates: { + name: 'Hide dates?', + defaultValue: false, + control: { type: 'boolean' }, + }, swapAxis: { name: 'Swap time and space axis?', defaultValue: false, @@ -199,6 +207,7 @@ export const DefaultArgs = { enableSnapping: true, hideGrid: false, hidePathsLabels: false, + hideDates: false, swapAxis: false, spaceScaleType: 'linear', },