@centreon/ui 23.10.17 → 23.10.18-MON-17793-create-graph-component-centreon-ui.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/package.json +2 -1
  2. package/src/Graph/Axes/UnitLabel.tsx +20 -0
  3. package/src/Graph/Axes/index.tsx +99 -0
  4. package/src/Graph/Axes/models.ts +26 -0
  5. package/src/Graph/Axes/useAxisY.ts +100 -0
  6. package/src/Graph/Graph.tsx +231 -0
  7. package/src/Graph/Grids/index.tsx +20 -0
  8. package/src/Graph/Header/index.tsx +34 -0
  9. package/src/Graph/InteractionWithGraph/AnchorPoint/RegularAnchorPoint.tsx +88 -0
  10. package/src/Graph/InteractionWithGraph/AnchorPoint/StackedAnchorPoint.tsx +97 -0
  11. package/src/Graph/InteractionWithGraph/AnchorPoint/index.tsx +56 -0
  12. package/src/Graph/InteractionWithGraph/AnchorPoint/models.ts +25 -0
  13. package/src/Graph/InteractionWithGraph/AnchorPoint/useAnchorPoint.ts +109 -0
  14. package/src/Graph/InteractionWithGraph/Bar.tsx +21 -0
  15. package/src/Graph/InteractionWithGraph/ZoomPreview/index.tsx +38 -0
  16. package/src/Graph/InteractionWithGraph/ZoomPreview/models.ts +17 -0
  17. package/src/Graph/InteractionWithGraph/ZoomPreview/useZoomPreview.ts +101 -0
  18. package/src/Graph/InteractionWithGraph/ZoomPreview/zoomPreviewAtoms.ts +5 -0
  19. package/src/Graph/InteractionWithGraph/index.tsx +93 -0
  20. package/src/Graph/InteractionWithGraph/interactionWithGraphAtoms.ts +7 -0
  21. package/src/Graph/InteractionWithGraph/mouseTimeValueAtoms.ts +49 -0
  22. package/src/Graph/Lines/RegularLines/index.tsx +99 -0
  23. package/src/Graph/Lines/RegularLines/useRegularLines.ts +15 -0
  24. package/src/Graph/Lines/StackedLines/index.tsx +74 -0
  25. package/src/Graph/Lines/StackedLines/useStackedLines.ts +39 -0
  26. package/src/Graph/Lines/index.tsx +153 -0
  27. package/src/Graph/Lines/models.ts +32 -0
  28. package/src/Graph/LoadingProgress.tsx +46 -0
  29. package/src/Graph/common/index.ts +33 -0
  30. package/src/Graph/data.json +1338 -0
  31. package/src/Graph/helpers/doc.ts +323 -0
  32. package/src/Graph/helpers/index.ts +15 -0
  33. package/src/Graph/index.stories.tsx +30 -0
  34. package/src/Graph/index.tsx +66 -0
  35. package/src/Graph/models.ts +68 -0
  36. package/src/Graph/timeSeries/index.test.ts +484 -0
  37. package/src/Graph/timeSeries/index.ts +512 -0
  38. package/src/Graph/timeSeries/models.ts +119 -0
  39. package/src/Graph/useGraphData.ts +117 -0
  40. package/src/TimePeriods/CustomTimePeriod/CompactCustomTimePeriod.styles.ts +57 -0
  41. package/src/TimePeriods/CustomTimePeriod/CompactCustomTimePeriod.tsx +78 -0
  42. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/ErrorText.tsx +14 -0
  43. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +169 -0
  44. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/index.tsx +76 -0
  45. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +61 -0
  46. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/usePickersStartEndDate.ts +91 -0
  47. package/src/TimePeriods/CustomTimePeriod/index.tsx +63 -0
  48. package/src/TimePeriods/DateTimePickerInput.tsx +99 -0
  49. package/src/TimePeriods/SelectedTimePeriod.tsx +99 -0
  50. package/src/TimePeriods/TimePeriods.cypress.spec.tsx +497 -0
  51. package/src/TimePeriods/helpers/index.ts +25 -0
  52. package/src/TimePeriods/index.stories.tsx +74 -0
  53. package/src/TimePeriods/index.tsx +77 -0
  54. package/src/TimePeriods/models.ts +103 -0
  55. package/src/TimePeriods/timePeriods.styles.ts +20 -0
  56. package/src/TimePeriods/timePeriodsAtoms.ts +113 -0
  57. package/src/TimePeriods/useDateTimePickerAdapter.ts +329 -0
  58. package/src/TimePeriods/useSortTimePeriods.ts +24 -0
@@ -0,0 +1,97 @@
1
+ import { memo } from 'react';
2
+
3
+ import { ScaleLinear, ScaleTime } from 'd3-scale';
4
+ import { equals, isNil, map, not, pipe } from 'ramda';
5
+
6
+ import { bisectDate } from '../../timeSeries';
7
+
8
+ import { StackValue } from './models';
9
+
10
+ import AnchorPoint from '.';
11
+
12
+ interface Props {
13
+ areaColor: string;
14
+ displayTimeValues: boolean;
15
+ graphHeight: number;
16
+ graphWidth: number;
17
+ lineColor: string;
18
+ positionX?: number;
19
+ positionY?: number;
20
+ stackValues: Array<StackValue>;
21
+ timeTick?: Date;
22
+ transparency: number;
23
+ xScale: ScaleTime<number, number>;
24
+ yScale: ScaleLinear<number, number>;
25
+ }
26
+
27
+ const key = 'data';
28
+
29
+ const getStackedDates = (stackValues: Array<StackValue>): Array<Date> => {
30
+ const toTimeTick = (stackValue: StackValue): string => {
31
+ return key in stackValue ? stackValue[key].timeTick : '';
32
+ };
33
+ const toDate = (tick: string): Date => new Date(tick);
34
+
35
+ return pipe(map(toTimeTick), map(toDate))(stackValues);
36
+ };
37
+
38
+ const getYAnchorPoint = ({
39
+ timeTick,
40
+ stackValues,
41
+ yScale
42
+ }: Pick<Props, 'timeTick' | 'stackValues' | 'yScale'>): number => {
43
+ const index = bisectDate(getStackedDates(stackValues), timeTick);
44
+ const timeValue = stackValues[index];
45
+
46
+ return yScale(timeValue[1] as number);
47
+ };
48
+
49
+ const StackedAnchorPoint = ({
50
+ xScale,
51
+ yScale,
52
+ stackValues,
53
+ timeTick,
54
+ areaColor,
55
+ transparency,
56
+ lineColor,
57
+ displayTimeValues,
58
+ ...rest
59
+ }: Props): JSX.Element | null => {
60
+ if (isNil(timeTick) || not(displayTimeValues)) {
61
+ return null;
62
+ }
63
+ if (isNil(timeTick)) {
64
+ return null;
65
+ }
66
+ const xAnchorPoint = xScale(timeTick);
67
+
68
+ const yAnchorPoint = getYAnchorPoint({
69
+ stackValues,
70
+ timeTick,
71
+ yScale
72
+ });
73
+
74
+ if (isNil(yAnchorPoint)) {
75
+ return null;
76
+ }
77
+
78
+ return (
79
+ <AnchorPoint
80
+ areaColor={areaColor}
81
+ lineColor={lineColor}
82
+ transparency={transparency}
83
+ x={xAnchorPoint}
84
+ y={yAnchorPoint}
85
+ {...rest}
86
+ />
87
+ );
88
+ };
89
+
90
+ export default memo(
91
+ StackedAnchorPoint,
92
+ (prevProps, nextProps) =>
93
+ equals(prevProps.timeTick, nextProps.timeTick) &&
94
+ equals(prevProps.stackValues, nextProps.stackValues) &&
95
+ equals(prevProps.positionX, nextProps.positionX) &&
96
+ equals(prevProps.positionY, nextProps.positionY)
97
+ );
@@ -0,0 +1,56 @@
1
+ import { Shape } from '@visx/visx';
2
+
3
+ import { grey } from '@mui/material/colors';
4
+
5
+ interface Props {
6
+ areaColor: string;
7
+ graphHeight: number;
8
+ graphWidth: number;
9
+ lineColor: string;
10
+ positionX?: number;
11
+ positionY?: number;
12
+ transparency: number;
13
+ x: number;
14
+ y: number;
15
+ }
16
+
17
+ const Point = ({
18
+ areaColor,
19
+ lineColor,
20
+ transparency,
21
+ x,
22
+ y,
23
+ ...rest
24
+ }: Props): JSX.Element => {
25
+ const { positionX, positionY, graphHeight, graphWidth } = rest;
26
+
27
+ return (
28
+ <g>
29
+ <circle
30
+ cx={x}
31
+ cy={y}
32
+ fill={areaColor}
33
+ fillOpacity={(1 - transparency * 0.01).toString()}
34
+ r={3}
35
+ stroke={lineColor}
36
+ />
37
+
38
+ <Shape.Line
39
+ from={{ x: positionX, y: 0 }}
40
+ pointerEvents="none"
41
+ stroke={grey[400]}
42
+ strokeWidth={1}
43
+ to={{ x: positionX, y: graphHeight }}
44
+ />
45
+ <Shape.Line
46
+ from={{ x: 0, y: positionY }}
47
+ pointerEvents="none"
48
+ stroke={grey[400]}
49
+ strokeWidth={1}
50
+ to={{ x: graphWidth, y: positionY }}
51
+ />
52
+ </g>
53
+ );
54
+ };
55
+
56
+ export default Point;
@@ -0,0 +1,25 @@
1
+ import { ScaleLinear, ScaleTime } from 'd3-scale';
2
+
3
+ import { TimeValue } from '../../timeSeries/models';
4
+
5
+ interface AnchorPoint {
6
+ areaColor: string;
7
+ lineColor: string;
8
+ transparency: number;
9
+ xScale: ScaleTime<number, number>;
10
+ yScale: ScaleLinear<number, number>;
11
+ }
12
+
13
+ export interface RegularLinesAnchorPoint extends AnchorPoint {
14
+ metric: string;
15
+ timeSeries: Array<TimeValue>;
16
+ }
17
+
18
+ export interface StackedAnchorPoint extends AnchorPoint {
19
+ stack;
20
+ }
21
+
22
+ export interface StackData {
23
+ data: TimeValue;
24
+ }
25
+ export type StackValue = [number, number, StackData];
@@ -0,0 +1,109 @@
1
+ import { MutableRefObject, useEffect, useState } from 'react';
2
+
3
+ import { Event } from '@visx/visx';
4
+ import { ScaleLinear } from 'd3-scale';
5
+ import { useAtomValue, useSetAtom } from 'jotai';
6
+ import { equals, isEmpty, isNil, not } from 'ramda';
7
+
8
+ import { margin } from '../../common';
9
+ import { getMetrics, getTimeValue } from '../../timeSeries';
10
+ import { TimeValue } from '../../timeSeries/models';
11
+ import {
12
+ MousePosition,
13
+ changeMousePositionAndTimeValueDerivedAtom,
14
+ mousePositionAtom,
15
+ timeValueAtom
16
+ } from '../mouseTimeValueAtoms';
17
+ import {
18
+ eventMouseDownAtom,
19
+ eventMouseMovingAtom
20
+ } from '../interactionWithGraphAtoms';
21
+
22
+ interface AnchorPointResult {
23
+ positionX?: number;
24
+ positionY?: number;
25
+ timeTick?: Date;
26
+ }
27
+
28
+ interface Props {
29
+ graphSvgRef: MutableRefObject<SVGSVGElement | null>;
30
+ timeSeries: Array<TimeValue>;
31
+ xScale: ScaleLinear<number, number>;
32
+ }
33
+
34
+ const useAnchorPoint = ({
35
+ graphSvgRef,
36
+ timeSeries,
37
+ xScale
38
+ }: Props): AnchorPointResult => {
39
+ const [position, setPosition] = useState<null | MousePosition>(null);
40
+
41
+ const eventMouseMoving = useAtomValue(eventMouseMovingAtom);
42
+ const eventMouseDown = useAtomValue(eventMouseDownAtom);
43
+
44
+ const mousePosition = useAtomValue(mousePositionAtom);
45
+ const timeValueData = useAtomValue(timeValueAtom);
46
+
47
+ const changeMousePositionAndTimeValue = useSetAtom(
48
+ changeMousePositionAndTimeValueDerivedAtom
49
+ );
50
+
51
+ const metrics = getMetrics(timeValueData as TimeValue);
52
+
53
+ const containsMetrics = not(isNil(metrics)) && not(isEmpty(metrics));
54
+
55
+ const mousePositionTimeTick = position
56
+ ? getTimeValue({ timeSeries, x: position[0], xScale }).timeTick
57
+ : 0;
58
+ const timeTick = containsMetrics
59
+ ? new Date(mousePositionTimeTick)
60
+ : undefined;
61
+
62
+ const positionX = position ? position[0] - margin.left : undefined;
63
+ const positionY = position ? position[1] - margin.top : undefined;
64
+
65
+ const updateMousePosition = (pointPosition: MousePosition): void => {
66
+ if (isNil(pointPosition)) {
67
+ changeMousePositionAndTimeValue({
68
+ position: null,
69
+ timeValue: null
70
+ });
71
+
72
+ return;
73
+ }
74
+ const timeValue = getTimeValue({
75
+ timeSeries,
76
+ x: pointPosition[0],
77
+ xScale
78
+ });
79
+
80
+ changeMousePositionAndTimeValue({ position: pointPosition, timeValue });
81
+ };
82
+
83
+ useEffect(() => {
84
+ if (eventMouseDown) {
85
+ return;
86
+ }
87
+ const mousePoint = eventMouseMoving
88
+ ? Event.localPoint(
89
+ graphSvgRef?.current as SVGSVGElement,
90
+ eventMouseMoving
91
+ )
92
+ : null;
93
+ setPosition(mousePoint ? [mousePoint.x, mousePoint.y] : null);
94
+ }, [eventMouseDown, eventMouseMoving]);
95
+
96
+ useEffect(() => {
97
+ if (equals(position, mousePosition) && position) {
98
+ return;
99
+ }
100
+ updateMousePosition(position);
101
+ }, [position]);
102
+
103
+ return {
104
+ positionX,
105
+ positionY,
106
+ timeTick
107
+ };
108
+ };
109
+ export default useAnchorPoint;
@@ -0,0 +1,21 @@
1
+ import { Shape } from '@visx/visx';
2
+ import { AddSVGProps } from '@visx/shape/lib/types';
3
+
4
+ interface BarProps {
5
+ className?: string;
6
+ innerRef?: React.Ref<SVGRectElement>;
7
+ open?: boolean;
8
+ }
9
+
10
+ const Bar = ({
11
+ open = true,
12
+ ...restProps
13
+ }: AddSVGProps<BarProps, SVGRectElement>): JSX.Element | null => {
14
+ if (!open) {
15
+ return null;
16
+ }
17
+
18
+ return <Shape.Bar {...restProps} />;
19
+ };
20
+
21
+ export default Bar;
@@ -0,0 +1,38 @@
1
+ import { omit } from 'ramda';
2
+
3
+ import { alpha, useTheme } from '@mui/system';
4
+
5
+ import Bar from '../Bar';
6
+
7
+ import useZoomPreview from './useZoomPreview';
8
+ import { ZoomPreviewData } from './models';
9
+
10
+ const ZoomPreview = (data: ZoomPreviewData): JSX.Element => {
11
+ const theme = useTheme();
12
+
13
+ const { graphHeight, xScale, graphWidth, graphSvgRef, ...rest } = data;
14
+
15
+ const { zoomBarWidth, zoomBoundaries } = useZoomPreview({
16
+ graphSvgRef,
17
+ graphWidth,
18
+ xScale
19
+ });
20
+
21
+ const restData = omit(['display'], { ...rest });
22
+
23
+ return (
24
+ <g>
25
+ <Bar
26
+ fill={alpha(theme.palette.primary.main, 0.2)}
27
+ height={graphHeight}
28
+ stroke={alpha(theme.palette.primary.main, 0.5)}
29
+ width={zoomBarWidth}
30
+ x={zoomBoundaries?.start || 0}
31
+ y={0}
32
+ {...restData}
33
+ />
34
+ </g>
35
+ );
36
+ };
37
+
38
+ export default ZoomPreview;
@@ -0,0 +1,17 @@
1
+ import { MutableRefObject } from 'react';
2
+
3
+ import { ScaleTime } from 'd3-scale';
4
+
5
+ import { ZoomPreview } from '../../models';
6
+
7
+ export interface ZoomBoundaries {
8
+ end: string;
9
+ start: string;
10
+ }
11
+
12
+ export interface ZoomPreviewData extends ZoomPreview {
13
+ graphHeight: number;
14
+ graphSvgRef: MutableRefObject<SVGSVGElement | null>;
15
+ graphWidth: number;
16
+ xScale: ScaleTime<number, number>;
17
+ }
@@ -0,0 +1,101 @@
1
+ import { MutableRefObject, useEffect, useState } from 'react';
2
+
3
+ import { Event } from '@visx/visx';
4
+ import { ScaleTime } from 'd3-scale';
5
+ import { useAtomValue, useSetAtom } from 'jotai';
6
+ import { equals, gte, isNil, lt } from 'ramda';
7
+
8
+ import { margin } from '../../common';
9
+ import {
10
+ eventMouseDownAtom,
11
+ eventMouseMovingAtom,
12
+ eventMouseUpAtom
13
+ } from '../interactionWithGraphAtoms';
14
+
15
+ import { zoomParametersAtom } from './zoomPreviewAtoms';
16
+
17
+ interface Boundaries {
18
+ end: number;
19
+ start: number;
20
+ }
21
+ interface ZoomPreview {
22
+ zoomBarWidth: number;
23
+ zoomBoundaries: Boundaries | null;
24
+ }
25
+
26
+ interface Props {
27
+ graphSvgRef: MutableRefObject<SVGSVGElement | null>;
28
+ graphWidth: number;
29
+ xScale: ScaleTime<number, number>;
30
+ }
31
+
32
+ const useZoomPreview = ({
33
+ graphSvgRef,
34
+ xScale,
35
+ graphWidth
36
+ }: Props): ZoomPreview => {
37
+ const [zoomBoundaries, setZoomBoundaries] = useState<Boundaries | null>(null);
38
+ const eventMouseMoving = useAtomValue(eventMouseMovingAtom);
39
+ const eventMouseDown = useAtomValue(eventMouseDownAtom);
40
+ const eventMouseUp = useAtomValue(eventMouseUpAtom);
41
+
42
+ const setZoomParameters = useSetAtom(zoomParametersAtom);
43
+
44
+ const mousePointDown = eventMouseDown
45
+ ? Event.localPoint(eventMouseDown)
46
+ : null;
47
+
48
+ const mouseDownPositionX = mousePointDown
49
+ ? mousePointDown.x - margin.left
50
+ : null;
51
+
52
+ const mousePositionMoving = eventMouseMoving
53
+ ? Event.localPoint(graphSvgRef.current as SVGSVGElement, eventMouseMoving)
54
+ : null;
55
+
56
+ const movingMousePositionX = mousePositionMoving
57
+ ? mousePositionMoving.x - margin.left
58
+ : null;
59
+
60
+ const applyZoom = (): void => {
61
+ if (equals(zoomBoundaries?.start, zoomBoundaries?.end)) {
62
+ return;
63
+ }
64
+
65
+ setZoomParameters({
66
+ end: xScale?.invert(zoomBoundaries?.end || graphWidth)?.toISOString(),
67
+ start: xScale?.invert(zoomBoundaries?.start || 0)?.toISOString()
68
+ });
69
+ };
70
+
71
+ useEffect(() => {
72
+ if (isNil(mouseDownPositionX) || isNil(movingMousePositionX)) {
73
+ return;
74
+ }
75
+
76
+ setZoomBoundaries({
77
+ end: gte(movingMousePositionX, mouseDownPositionX)
78
+ ? movingMousePositionX
79
+ : mouseDownPositionX,
80
+ start: lt(movingMousePositionX, mouseDownPositionX)
81
+ ? movingMousePositionX
82
+ : mouseDownPositionX
83
+ });
84
+ }, [movingMousePositionX, mouseDownPositionX]);
85
+
86
+ useEffect(() => {
87
+ if (isNil(eventMouseUp) || isNil(zoomBoundaries)) {
88
+ return;
89
+ }
90
+ applyZoom();
91
+ setZoomBoundaries(null);
92
+ }, [eventMouseUp]);
93
+
94
+ const zoomBarWidth = Math.abs(
95
+ (zoomBoundaries?.end || 0) - (zoomBoundaries?.start || 0)
96
+ );
97
+
98
+ return { zoomBarWidth, zoomBoundaries };
99
+ };
100
+
101
+ export default useZoomPreview;
@@ -0,0 +1,5 @@
1
+ import { atom } from 'jotai';
2
+
3
+ import { ZoomBoundaries } from './models';
4
+
5
+ export const zoomParametersAtom = atom<ZoomBoundaries | null>(null);
@@ -0,0 +1,93 @@
1
+ import { MutableRefObject } from 'react';
2
+
3
+ import { useSetAtom } from 'jotai';
4
+ import { makeStyles } from 'tss-react/mui';
5
+ import { ScaleTime } from 'd3-scale';
6
+
7
+ import { ZoomPreview as ZoomPreviewModel } from '../models';
8
+
9
+ import Bar from './Bar';
10
+ import ZoomPreview from './ZoomPreview';
11
+ import {
12
+ eventMouseDownAtom,
13
+ eventMouseMovingAtom,
14
+ eventMouseUpAtom
15
+ } from './interactionWithGraphAtoms';
16
+
17
+ const useStyles = makeStyles()(() => ({
18
+ overlay: {
19
+ cursor: 'crosshair'
20
+ }
21
+ }));
22
+
23
+ interface CommonData {
24
+ graphHeight: number;
25
+ graphSvgRef: MutableRefObject<SVGSVGElement | null>;
26
+ graphWidth: number;
27
+ }
28
+
29
+ interface ZoomData extends ZoomPreviewModel {
30
+ xScale: ScaleTime<number, number>;
31
+ }
32
+
33
+ interface Props {
34
+ commonData: CommonData;
35
+ zoomData: ZoomData;
36
+ }
37
+
38
+ const InteractionWithGraph = ({ zoomData, commonData }: Props): JSX.Element => {
39
+ const { classes } = useStyles();
40
+
41
+ const setEventMouseMoving = useSetAtom(eventMouseMovingAtom);
42
+ const setEventMouseDown = useSetAtom(eventMouseDownAtom);
43
+ const setEventMouseUp = useSetAtom(eventMouseUpAtom);
44
+
45
+ const { graphHeight, graphWidth, graphSvgRef } = commonData;
46
+
47
+ const displayZoomPreview = zoomData?.display ?? true;
48
+
49
+ const mouseLeave = (): void => {
50
+ setEventMouseMoving(null);
51
+ setEventMouseDown(null);
52
+ };
53
+
54
+ const mouseUp = (event): void => {
55
+ setEventMouseUp(event);
56
+ setEventMouseDown(null);
57
+ };
58
+
59
+ const mouseMove = (event): void => {
60
+ setEventMouseMoving(event);
61
+ };
62
+
63
+ const mouseDown = (event): void => {
64
+ setEventMouseDown(event);
65
+ };
66
+
67
+ return (
68
+ <g>
69
+ {displayZoomPreview && (
70
+ <ZoomPreview
71
+ {...zoomData}
72
+ graphHeight={graphHeight}
73
+ graphSvgRef={graphSvgRef}
74
+ graphWidth={graphWidth}
75
+ />
76
+ )}
77
+ <Bar
78
+ className={classes.overlay}
79
+ fill="transparent"
80
+ height={graphHeight}
81
+ width={graphWidth}
82
+ x={0}
83
+ y={0}
84
+ onMouseDown={mouseDown}
85
+ onMouseLeave={mouseLeave}
86
+ onMouseMove={mouseMove}
87
+ onMouseUp={mouseUp}
88
+ />
89
+ </g>
90
+ );
91
+ };
92
+
93
+ export default InteractionWithGraph;
@@ -0,0 +1,7 @@
1
+ import { atom } from 'jotai';
2
+
3
+ export const eventMouseMovingAtom = atom<null | MouseEvent>(null);
4
+
5
+ export const eventMouseDownAtom = atom<null | MouseEvent>(null);
6
+
7
+ export const eventMouseUpAtom = atom<null | MouseEvent>(null);
@@ -0,0 +1,49 @@
1
+ import { atom } from 'jotai';
2
+ import { not, isNil } from 'ramda';
3
+
4
+ import { TimeValue } from '../timeSeries/models';
5
+
6
+ export type MousePosition = [number, number] | null;
7
+
8
+ interface PositionTimeValue {
9
+ position: MousePosition;
10
+ timeValue: TimeValue | null;
11
+ }
12
+
13
+ interface NewTimeValueInViewportState {
14
+ isInViewport?: boolean;
15
+ newTimeValue: TimeValue | null;
16
+ }
17
+
18
+ export const timeValueAtom = atom<TimeValue | null>(null);
19
+ export const mousePositionAtom = atom<MousePosition>(null);
20
+ export const isListingGraphOpenAtom = atom(false);
21
+
22
+ export const changeTimeValueDerivedAtom = atom(
23
+ null,
24
+ (
25
+ _,
26
+ set,
27
+ { newTimeValue, isInViewport }: NewTimeValueInViewportState
28
+ ): void => {
29
+ if (not(isInViewport)) {
30
+ return;
31
+ }
32
+ set(timeValueAtom, newTimeValue);
33
+ }
34
+ );
35
+
36
+ export const changeMousePositionAndTimeValueDerivedAtom = atom(
37
+ null,
38
+ (_, set, { position, timeValue }: PositionTimeValue): void => {
39
+ if (isNil(position) || isNil(timeValue)) {
40
+ set(mousePositionAtom, null);
41
+ set(timeValueAtom, null);
42
+
43
+ return;
44
+ }
45
+ set(mousePositionAtom, position);
46
+
47
+ set(timeValueAtom, timeValue);
48
+ }
49
+ );