@gravity-ui/date-components 2.10.3 → 2.11.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 (103) hide show
  1. package/dist/cjs/components/RangeDateSelection/RangeDateSelection.css +28 -0
  2. package/dist/cjs/components/RangeDateSelection/RangeDateSelection.d.ts +24 -0
  3. package/dist/cjs/components/RangeDateSelection/RangeDateSelection.js +49 -0
  4. package/dist/cjs/components/RangeDateSelection/components/NowLine/NowLine.css +4 -0
  5. package/dist/cjs/components/RangeDateSelection/components/NowLine/NowLine.d.ts +2 -0
  6. package/dist/cjs/components/RangeDateSelection/components/NowLine/NowLine.js +42 -0
  7. package/dist/cjs/components/RangeDateSelection/components/Ruler/Ruler.d.ts +32 -0
  8. package/dist/cjs/components/RangeDateSelection/components/Ruler/Ruler.js +61 -0
  9. package/dist/cjs/components/RangeDateSelection/components/RulerViewport/RulerViewport.css +25 -0
  10. package/dist/cjs/components/RangeDateSelection/components/RulerViewport/RulerViewport.d.ts +13 -0
  11. package/dist/cjs/components/RangeDateSelection/components/RulerViewport/RulerViewport.js +35 -0
  12. package/dist/cjs/components/RangeDateSelection/components/SelectionControl/SelectionControl.css +78 -0
  13. package/dist/cjs/components/RangeDateSelection/components/SelectionControl/SelectionControl.d.ts +8 -0
  14. package/dist/cjs/components/RangeDateSelection/components/SelectionControl/SelectionControl.js +135 -0
  15. package/dist/cjs/components/RangeDateSelection/components/Ticks/MiddleTicks.d.ts +3 -0
  16. package/dist/cjs/components/RangeDateSelection/components/Ticks/MiddleTicks.js +10 -0
  17. package/dist/cjs/components/RangeDateSelection/components/Ticks/SlitTicks.d.ts +5 -0
  18. package/dist/cjs/components/RangeDateSelection/components/Ticks/SlitTicks.js +10 -0
  19. package/dist/cjs/components/RangeDateSelection/components/Ticks/Ticks.css +16 -0
  20. package/dist/cjs/components/RangeDateSelection/components/Ticks/Ticks.d.ts +18 -0
  21. package/dist/cjs/components/RangeDateSelection/components/Ticks/Ticks.js +52 -0
  22. package/dist/cjs/components/RangeDateSelection/components/Ticks/UnavialableTicks.d.ts +15 -0
  23. package/dist/cjs/components/RangeDateSelection/components/Ticks/UnavialableTicks.js +50 -0
  24. package/dist/cjs/components/RangeDateSelection/components/Ticks/utils.d.ts +24 -0
  25. package/dist/cjs/components/RangeDateSelection/components/Ticks/utils.js +166 -0
  26. package/dist/cjs/components/RangeDateSelection/hooks/useRangeDateSelectionState.d.ts +60 -0
  27. package/dist/cjs/components/RangeDateSelection/hooks/useRangeDateSelectionState.js +150 -0
  28. package/dist/cjs/components/RangeDateSelection/i18n/en.json +7 -0
  29. package/dist/cjs/components/RangeDateSelection/i18n/index.d.ts +1 -0
  30. package/dist/cjs/components/RangeDateSelection/i18n/index.js +11 -0
  31. package/dist/cjs/components/RangeDateSelection/i18n/ru.json +7 -0
  32. package/dist/cjs/components/RangeDateSelection/index.d.ts +3 -0
  33. package/dist/cjs/components/RangeDateSelection/index.js +18 -0
  34. package/dist/cjs/components/RangeDateSelection/utils/date.d.ts +2 -0
  35. package/dist/cjs/components/RangeDateSelection/utils/date.js +10 -0
  36. package/dist/cjs/components/RangeDateSelection/utils/span.d.ts +23 -0
  37. package/dist/cjs/components/RangeDateSelection/utils/span.js +27 -0
  38. package/dist/cjs/components/RelativeRangeDatePicker/components/Presets/i18n/index.d.ts +1 -1
  39. package/dist/cjs/components/index.d.ts +1 -0
  40. package/dist/cjs/components/index.js +1 -0
  41. package/dist/cjs/components/mixins.css +0 -0
  42. package/dist/cjs/components/utils/constants.d.ts +7 -0
  43. package/dist/cjs/components/utils/constants.js +10 -0
  44. package/dist/cjs/components/utils/filterDOMProps.d.ts +17 -0
  45. package/dist/cjs/components/utils/filterDOMProps.js +27 -0
  46. package/dist/cjs/hooks/useEventHandler.d.ts +1 -0
  47. package/dist/cjs/hooks/useEventHandler.js +24 -0
  48. package/dist/cjs/hooks/useMove.d.ts +31 -0
  49. package/dist/cjs/hooks/useMove.js +173 -0
  50. package/dist/cjs/hooks/useResizeObserver.d.ts +8 -0
  51. package/dist/cjs/hooks/useResizeObserver.js +32 -0
  52. package/dist/esm/components/RangeDateSelection/RangeDateSelection.css +28 -0
  53. package/dist/esm/components/RangeDateSelection/RangeDateSelection.d.ts +24 -0
  54. package/dist/esm/components/RangeDateSelection/RangeDateSelection.js +43 -0
  55. package/dist/esm/components/RangeDateSelection/components/NowLine/NowLine.css +4 -0
  56. package/dist/esm/components/RangeDateSelection/components/NowLine/NowLine.d.ts +2 -0
  57. package/dist/esm/components/RangeDateSelection/components/NowLine/NowLine.js +36 -0
  58. package/dist/esm/components/RangeDateSelection/components/Ruler/Ruler.d.ts +32 -0
  59. package/dist/esm/components/RangeDateSelection/components/Ruler/Ruler.js +53 -0
  60. package/dist/esm/components/RangeDateSelection/components/RulerViewport/RulerViewport.css +25 -0
  61. package/dist/esm/components/RangeDateSelection/components/RulerViewport/RulerViewport.d.ts +13 -0
  62. package/dist/esm/components/RangeDateSelection/components/RulerViewport/RulerViewport.js +29 -0
  63. package/dist/esm/components/RangeDateSelection/components/SelectionControl/SelectionControl.css +78 -0
  64. package/dist/esm/components/RangeDateSelection/components/SelectionControl/SelectionControl.d.ts +8 -0
  65. package/dist/esm/components/RangeDateSelection/components/SelectionControl/SelectionControl.js +132 -0
  66. package/dist/esm/components/RangeDateSelection/components/Ticks/MiddleTicks.d.ts +3 -0
  67. package/dist/esm/components/RangeDateSelection/components/Ticks/MiddleTicks.js +7 -0
  68. package/dist/esm/components/RangeDateSelection/components/Ticks/SlitTicks.d.ts +5 -0
  69. package/dist/esm/components/RangeDateSelection/components/Ticks/SlitTicks.js +7 -0
  70. package/dist/esm/components/RangeDateSelection/components/Ticks/Ticks.css +16 -0
  71. package/dist/esm/components/RangeDateSelection/components/Ticks/Ticks.d.ts +18 -0
  72. package/dist/esm/components/RangeDateSelection/components/Ticks/Ticks.js +44 -0
  73. package/dist/esm/components/RangeDateSelection/components/Ticks/UnavialableTicks.d.ts +15 -0
  74. package/dist/esm/components/RangeDateSelection/components/Ticks/UnavialableTicks.js +42 -0
  75. package/dist/esm/components/RangeDateSelection/components/Ticks/utils.d.ts +24 -0
  76. package/dist/esm/components/RangeDateSelection/components/Ticks/utils.js +158 -0
  77. package/dist/esm/components/RangeDateSelection/hooks/useRangeDateSelectionState.d.ts +60 -0
  78. package/dist/esm/components/RangeDateSelection/hooks/useRangeDateSelectionState.js +144 -0
  79. package/dist/esm/components/RangeDateSelection/i18n/en.json +7 -0
  80. package/dist/esm/components/RangeDateSelection/i18n/index.d.ts +1 -0
  81. package/dist/esm/components/RangeDateSelection/i18n/index.js +5 -0
  82. package/dist/esm/components/RangeDateSelection/i18n/ru.json +7 -0
  83. package/dist/esm/components/RangeDateSelection/index.d.ts +3 -0
  84. package/dist/esm/components/RangeDateSelection/index.js +2 -0
  85. package/dist/esm/components/RangeDateSelection/utils/date.d.ts +2 -0
  86. package/dist/esm/components/RangeDateSelection/utils/date.js +7 -0
  87. package/dist/esm/components/RangeDateSelection/utils/span.d.ts +23 -0
  88. package/dist/esm/components/RangeDateSelection/utils/span.js +23 -0
  89. package/dist/esm/components/RelativeRangeDatePicker/components/Presets/i18n/index.d.ts +1 -1
  90. package/dist/esm/components/index.d.ts +1 -0
  91. package/dist/esm/components/index.js +1 -0
  92. package/dist/esm/components/mixins.css +0 -0
  93. package/dist/esm/components/utils/constants.d.ts +7 -0
  94. package/dist/esm/components/utils/constants.js +7 -0
  95. package/dist/esm/components/utils/filterDOMProps.d.ts +17 -0
  96. package/dist/esm/components/utils/filterDOMProps.js +24 -0
  97. package/dist/esm/hooks/useEventHandler.d.ts +1 -0
  98. package/dist/esm/hooks/useEventHandler.js +18 -0
  99. package/dist/esm/hooks/useMove.d.ts +31 -0
  100. package/dist/esm/hooks/useMove.js +167 -0
  101. package/dist/esm/hooks/useResizeObserver.d.ts +8 -0
  102. package/dist/esm/hooks/useResizeObserver.js +26 -0
  103. package/package.json +1 -1
@@ -0,0 +1,29 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from 'react';
4
+ import { useMove } from '../../../../hooks/useMove';
5
+ import { block } from '../../../../utils/cn';
6
+ import './RulerViewport.css';
7
+ const b = block('ruler-viewport');
8
+ function RulerViewport({ className, children, onMove, onMoveStart, onMoveEnd, dragDisabled }, ref) {
9
+ const containerRef = React.useRef(null);
10
+ const draggable = typeof onMove === 'function';
11
+ const { moveProps } = useMove({
12
+ onMoveStart: onMoveStart,
13
+ onMove: (e) => {
14
+ const container = containerRef.current;
15
+ if (e.deltaX !== 0 && container) {
16
+ const { width } = container.getBoundingClientRect();
17
+ let delta = (e.deltaX * 100) / width;
18
+ delta = Math.round(delta * 100) / 100;
19
+ if (delta !== 0) {
20
+ onMove === null || onMove === void 0 ? void 0 : onMove(delta, e);
21
+ }
22
+ }
23
+ },
24
+ onMoveEnd: onMoveEnd,
25
+ });
26
+ return (_jsxs("div", { ref: ref, className: b(null, className), children: [_jsxs("svg", { ref: containerRef, className: b('svg'), width: "100%", height: "100%", xmlns: "http://www.w3.org/2000/svg", xmlnsXlink: "http://www.w3.org/1999/xlink", children: [_jsx("defs", { children: _jsxs("filter", { x: "0", y: "0", width: "1", height: "1", id: "g-date-tt-ticklabelbg", children: [_jsx("feFlood", { floodColor: "var(--_--g-date-ruler-viewport-background)" }), _jsx("feComposite", { in: "SourceGraphic" })] }) }), children] }), draggable ? (_jsx("div", Object.assign({ className: b('dragger', { disabled: dragDisabled }) }, moveProps))) : null] }));
27
+ }
28
+ const _RulerViewport = React.forwardRef(RulerViewport);
29
+ export { _RulerViewport as RulerViewport };
@@ -0,0 +1,78 @@
1
+ .g-date-range-date-selection-control {
2
+ --_--g-date-timeline-color-selection: var(
3
+ --g-date-timeline-color-selection,
4
+ var(--g-color-base-info-light)
5
+ );
6
+ --_--g-date-timeline-color-selection-hover: var(
7
+ --g-date-timeline-color-selection-hover,
8
+ var(--g-color-base-info-light-hover)
9
+ );
10
+ --_--g-date-timeline-color-selection-handle: var(
11
+ --g-date-timeline-color-selection-handle,
12
+ var(--g-color-base-brand)
13
+ );
14
+ --_--g-date-timeline-color-selection-handle-hover: var(
15
+ --g-date-timeline-color-selection-handle-hover,
16
+ var(--g-color-base-brand-hover)
17
+ );
18
+ position: absolute;
19
+ z-index: 1;
20
+ inset-block: 0;
21
+ }
22
+ @supports (selector(:has(*))) and (selector(:focus-visible)) {
23
+ .g-date-range-date-selection-control__start:has(.g-date-range-date-selection-control__slider-input:focus-visible), .g-date-range-date-selection-control__end:has(.g-date-range-date-selection-control__slider-input:focus-visible), .g-date-range-date-selection-control__dragger:has(.g-date-range-date-selection-control__slider-input:focus-visible) {
24
+ border-radius: var(--g-border-radius-xs);
25
+ outline: var(--g-color-line-focus) solid 2px;
26
+ }
27
+ }
28
+ @supports not (selector(:has(*))) {
29
+ .g-date-range-date-selection-control__start:focus-within, .g-date-range-date-selection-control__end:focus-within, .g-date-range-date-selection-control__dragger:focus-within {
30
+ border-radius: var(--g-border-radius-xs);
31
+ outline: var(--g-color-line-focus) solid 2px;
32
+ }
33
+ }
34
+ .g-date-range-date-selection-control__dragger {
35
+ position: absolute;
36
+ inset-block: 0;
37
+ inset-inline: 2px;
38
+ cursor: move;
39
+ background: var(--_--g-date-timeline-color-selection);
40
+ }
41
+ .g-date-range-date-selection-control__dragger:hover, .g-date-range-date-selection-control__dragger_dragging {
42
+ background: var(--_--g-date-timeline-color-selection-hover);
43
+ }
44
+ .g-date-range-date-selection-control__dragger_disabled {
45
+ pointer-events: none;
46
+ }
47
+ .g-date-range-date-selection-control__start {
48
+ position: absolute;
49
+ inset-block: 0;
50
+ inset-inline-start: 0;
51
+ width: 4px;
52
+ background: var(--_--g-date-timeline-color-selection-handle);
53
+ }
54
+ .g-date-range-date-selection-control__end {
55
+ position: absolute;
56
+ inset-inline-end: 0;
57
+ width: 4px;
58
+ height: 100%;
59
+ background: var(--_--g-date-timeline-color-selection-handle);
60
+ }
61
+ .g-date-range-date-selection-control__start, .g-date-range-date-selection-control__end {
62
+ cursor: ew-resize;
63
+ }
64
+ .g-date-range-date-selection-control__start:hover, .g-date-range-date-selection-control__start_dragging, .g-date-range-date-selection-control__end:hover, .g-date-range-date-selection-control__end_dragging {
65
+ background: var(--_--g-date-timeline-color-selection-handle-hover);
66
+ }
67
+ .g-date-range-date-selection-control__slider-input-wrapper {
68
+ position: absolute;
69
+ overflow: hidden;
70
+ clip: rect(0, 0, 0, 0);
71
+ width: 1px;
72
+ height: 1px;
73
+ margin: -1px;
74
+ padding: 0;
75
+ white-space: nowrap;
76
+ border: 0;
77
+ clip-path: inset(50%);
78
+ }
@@ -0,0 +1,8 @@
1
+ import type { AccessibilityProps, DomProps, StyleProps } from '../../../types';
2
+ import type { RangeDateSelectionState } from '../../hooks/useRangeDateSelectionState';
3
+ import './SelectionControl.css';
4
+ interface SelectionControlProps extends DomProps, StyleProps, AccessibilityProps {
5
+ state: RangeDateSelectionState;
6
+ }
7
+ export declare function SelectionControl({ state, style, className, ...props }: SelectionControlProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,132 @@
1
+ import { __rest } from "tslib";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from 'react';
4
+ import { useMove } from '../../../../hooks/useMove';
5
+ import { block } from '../../../../utils/cn';
6
+ import { i18n } from '../../i18n';
7
+ import { lerp } from '../../utils/span';
8
+ import { useViewportDimensions, useViewportInterval } from '../Ruler/Ruler';
9
+ import './SelectionControl.css';
10
+ const b = block('range-date-selection-control');
11
+ const MIN_SELECTION_WIDTH = 12; // px;
12
+ const MIN_DRAG_SCALE = 0.01;
13
+ const MAX_DRAG_SCALE = 100;
14
+ const DRAG_SCALE_STRENGTH = 20;
15
+ export function SelectionControl(_a) {
16
+ var _b;
17
+ var { state, style, className } = _a, props = __rest(_a, ["state", "style", "className"]);
18
+ const { start: startDate, end: endDate } = useViewportInterval();
19
+ const viewport = useViewportDimensions();
20
+ const [isDragging, setIsDragging] = React.useState(null);
21
+ const moveDataRef = React.useRef({
22
+ isZooming: false,
23
+ });
24
+ const visualMinDuration = (MIN_SELECTION_WIDTH * (endDate.valueOf() - startDate.valueOf())) / viewport.width;
25
+ const handleMoveEnd = () => {
26
+ state.endDragging();
27
+ setIsDragging(null);
28
+ };
29
+ const { moveProps } = useMove({
30
+ onMoveStart: (e) => {
31
+ setIsDragging('dragger');
32
+ state.startDragging();
33
+ moveDataRef.current = {
34
+ isZooming: state.canResize && (e.ctrlKey || e.metaKey),
35
+ };
36
+ },
37
+ onMoveEnd: handleMoveEnd,
38
+ onMove: (e) => {
39
+ if (e.deltaX === 0) {
40
+ return;
41
+ }
42
+ if (moveDataRef.current.isZooming) {
43
+ let scale = Math.exp((e.deltaX * DRAG_SCALE_STRENGTH) / viewport.width);
44
+ scale = Math.min(Math.max(scale, MIN_DRAG_SCALE), MAX_DRAG_SCALE);
45
+ state.scale(scale, { visualMinDuration });
46
+ }
47
+ else {
48
+ let delta = (e.deltaX * (endDate.valueOf() - startDate.valueOf())) / viewport.width;
49
+ if (e.pointerType === 'keyboard') {
50
+ delta =
51
+ delta > 0
52
+ ? Math.max(delta, state.align)
53
+ : Math.min(delta, -1 * state.align);
54
+ }
55
+ state.move(delta, { visualMinDuration });
56
+ }
57
+ },
58
+ });
59
+ const { moveProps: moveStartProps } = useMove({
60
+ onMoveStart: () => {
61
+ setIsDragging('start');
62
+ state.startDragging();
63
+ },
64
+ onMoveEnd: handleMoveEnd,
65
+ onMove: (e) => {
66
+ if (e.deltaX === 0) {
67
+ return;
68
+ }
69
+ let delta = (e.deltaX * (endDate.valueOf() - startDate.valueOf())) / viewport.width;
70
+ if (e.pointerType === 'keyboard') {
71
+ delta =
72
+ delta > 0 ? Math.max(delta, state.align) : Math.min(delta, -1 * state.align);
73
+ }
74
+ state.moveStart(delta, { visualMinDuration });
75
+ },
76
+ });
77
+ const { moveProps: moveEndProps } = useMove({
78
+ onMoveStart: () => {
79
+ setIsDragging('end');
80
+ state.startDragging();
81
+ },
82
+ onMoveEnd: handleMoveEnd,
83
+ onMove: (e) => {
84
+ if (e.deltaX === 0) {
85
+ return;
86
+ }
87
+ let delta = (e.deltaX * (endDate.valueOf() - startDate.valueOf())) / viewport.width;
88
+ if (e.pointerType === 'keyboard') {
89
+ delta =
90
+ delta > 0 ? Math.max(delta, state.align) : Math.min(delta, -1 * state.align);
91
+ }
92
+ state.moveEnd(delta, { visualMinDuration });
93
+ },
94
+ });
95
+ const { start: startValue, end: endValue } = state.value;
96
+ const startX = lerp({ start: startDate.valueOf(), end: endDate.valueOf() }, { start: 0, end: viewport.width }, startValue.valueOf());
97
+ const endX = lerp({ start: startDate.valueOf(), end: endDate.valueOf() }, { start: 0, end: viewport.width }, endValue.valueOf());
98
+ const draggerInputRef = React.useRef(null);
99
+ const startInputRef = React.useRef(null);
100
+ const endInputRef = React.useRef(null);
101
+ let id = React.useId();
102
+ id = (_b = props.id) !== null && _b !== void 0 ? _b : id;
103
+ return (_jsxs("div", { className: b(null, className), style: Object.assign(Object.assign({}, style), { left: startX, width: endX - startX }), children: [_jsx("div", Object.assign({ className: b('dragger', {
104
+ dragging: isDragging === 'dragger',
105
+ disabled: Boolean(isDragging && isDragging !== 'dragger'),
106
+ }) }, moveProps, { onClick: () => {
107
+ var _a;
108
+ (_a = draggerInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
109
+ }, children: _jsx("div", { className: b('slider-input-wrapper'), children: _jsx("input", { id: `${id}-range`, ref: draggerInputRef, className: b('slider-input'), type: "range", step: 1, "aria-label": i18n('Range'), "aria-labelledby": [`${id}-range`, props['aria-labelledby']]
110
+ .filter(Boolean)
111
+ .join(' '), "aria-orientation": "horizontal", "aria-valuetext": `${startValue.format()} - ${endValue.format()}`, value: 0, onChange: () => { } }) }) })), state.canResize ? (
112
+ /* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
113
+ _jsx("div", Object.assign({ className: b('start', {
114
+ dragging: isDragging === 'start',
115
+ disabled: Boolean(isDragging && isDragging !== 'start'),
116
+ }) }, moveStartProps, { onClick: () => {
117
+ var _a;
118
+ (_a = startInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
119
+ }, children: _jsx("div", { className: b('slider-input-wrapper'), children: _jsx("input", { id: `${id}-start`, ref: startInputRef, className: b('slider-input'), type: "range", step: 1, "aria-label": i18n('Start of range'), "aria-labelledby": [`${id}-start`, props['aria-labelledby']]
120
+ .filter(Boolean)
121
+ .join(' '), "aria-orientation": "horizontal", "aria-valuetext": `${startValue.format()}`, value: 0, onChange: () => { } }) }) }))) : null, state.canResize ? (
122
+ /* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
123
+ _jsx("div", Object.assign({ className: b('end', {
124
+ dragging: isDragging === 'end',
125
+ disabled: Boolean(isDragging && isDragging !== 'end'),
126
+ }) }, moveEndProps, { onClick: () => {
127
+ var _a;
128
+ (_a = endInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
129
+ }, children: _jsx("div", { className: b('slider-input-wrapper'), children: _jsx("input", { id: `${id}-end`, ref: endInputRef, className: b('slider-input'), type: "range", step: 1, "aria-label": i18n('End of range'), "aria-labelledby": [`${id}-end`, props['aria-labelledby']]
130
+ .filter(Boolean)
131
+ .join(' '), "aria-orientation": "horizontal", "aria-valuetext": `${endValue.format()}`, value: 0, onChange: () => { } }) }) }))) : null] }));
132
+ }
@@ -0,0 +1,3 @@
1
+ export declare function MiddleTicks({ timeZone }: {
2
+ timeZone?: string;
3
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useViewportDimensions } from '../Ruler/Ruler';
3
+ import { Ticks, makeMiddleTicksGeometry } from './Ticks';
4
+ export function MiddleTicks({ timeZone }) {
5
+ const { height } = useViewportDimensions();
6
+ return (_jsx(Ticks, { minTickWidth: 8, maxTickWidth: 20, theme: "dim", geometry: makeMiddleTicksGeometry({ tickHeight: 4, viewportHeight: height }), timeZone: timeZone }));
7
+ }
@@ -0,0 +1,5 @@
1
+ import type { DateTime } from '@gravity-ui/date-utils';
2
+ export declare function SlitTicks({ formatTime, timeZone, }: {
3
+ formatTime?: (time: DateTime) => string;
4
+ timeZone?: string;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useViewportDimensions } from '../Ruler/Ruler';
3
+ import { Ticks, makeSlitTicksGeometry } from './Ticks';
4
+ export function SlitTicks({ formatTime, timeZone, }) {
5
+ const { height } = useViewportDimensions();
6
+ return (_jsx(Ticks, { minTickWidth: 80, maxTickWidth: 200, theme: "normal", geometry: makeSlitTicksGeometry({ tickHeight: 4, viewportHeight: height }), hasLabels: true, formatTime: formatTime, timeZone: timeZone }));
7
+ }
@@ -0,0 +1,16 @@
1
+ .g-date-timeline-ticks {
2
+ stroke: var(--g-color-text-secondary);
3
+ fill: var(--g-color-text-secondary);
4
+ stroke-width: 1px;
5
+ }
6
+ .g-date-timeline-ticks_theme_dim {
7
+ stroke: var(--g-color-line-generic-accent);
8
+ fill: var(--g-color-line-generic-accent);
9
+ }
10
+ .g-date-timeline-ticks__label {
11
+ font-size: 11px;
12
+ line-height: 14px;
13
+ user-select: none;
14
+ fill: var(--g-color-text-secondary);
15
+ text-anchor: middle;
16
+ }
@@ -0,0 +1,18 @@
1
+ import type { DateTime } from '@gravity-ui/date-utils';
2
+ import type { Geometry } from './utils';
3
+ import { makeMiddleTicksGeometry, makeSlitTicksGeometry } from './utils';
4
+ import './Ticks.css';
5
+ interface TicksProps {
6
+ theme: 'normal' | 'dim';
7
+ minTickWidth: number;
8
+ maxTickWidth: number;
9
+ hasLabels?: boolean;
10
+ geometry: Geometry;
11
+ className?: string;
12
+ labelsClassName?: string;
13
+ formatTime?: (time: DateTime) => string;
14
+ timeZone?: string;
15
+ }
16
+ export { makeMiddleTicksGeometry, makeSlitTicksGeometry };
17
+ export type { Geometry };
18
+ export declare function Ticks({ theme, minTickWidth, maxTickWidth, hasLabels, geometry, className, labelsClassName, formatTime, timeZone, }: TicksProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { dateTime } from '@gravity-ui/date-utils';
4
+ import { block } from '../../../../utils/cn';
5
+ import { getLerpCoeff } from '../../utils/span';
6
+ import { useViewportDimensions, useViewportInterval } from '../Ruler/Ruler';
7
+ import { calculateTickTimes, formatTick as defaultFormatTime, makeMiddleTicksGeometry, makeSlitTicksGeometry, } from './utils';
8
+ import './Ticks.css';
9
+ const b = block('timeline-ticks');
10
+ export { makeMiddleTicksGeometry, makeSlitTicksGeometry };
11
+ export function Ticks({ theme, minTickWidth, maxTickWidth, hasLabels, geometry, className, labelsClassName, formatTime, timeZone, }) {
12
+ const viewport = useViewportDimensions();
13
+ const interval = useViewportInterval();
14
+ const tickTimes = React.useMemo(() => calculateTickTimes({
15
+ minTickWidth,
16
+ maxTickWidth,
17
+ viewportStart: interval.start,
18
+ viewportEnd: interval.end,
19
+ viewportWidth: viewport.width,
20
+ timeZone,
21
+ }), [maxTickWidth, minTickWidth, interval, viewport.width, timeZone]);
22
+ const tickCoords = React.useMemo(() => {
23
+ const timeToXCoeff = getLerpCoeff({ start: interval.start.valueOf(), end: interval.end.valueOf() }, { start: 0, end: viewport.width });
24
+ return tickTimes.map((t) => 0 + Math.round((t.valueOf() - interval.start.valueOf()) * timeToXCoeff));
25
+ }, [tickTimes, interval, viewport.width]);
26
+ const ticksGeometry = React.useMemo(() => {
27
+ return tickCoords.map(geometry).join('');
28
+ }, [geometry, tickCoords]);
29
+ const ticksPath = _jsx("path", { className: b({ theme }, className), d: ticksGeometry });
30
+ const tickLabels = React.useMemo(() => {
31
+ if (!hasLabels || tickCoords.length < 2) {
32
+ return null;
33
+ }
34
+ const result = [];
35
+ for (let i = 0; i < tickCoords.length; i++) {
36
+ const x = tickCoords[i];
37
+ const t = tickTimes[i];
38
+ const dt = dateTime({ input: t, timeZone });
39
+ result.push(_jsx("text", { className: b('label', labelsClassName), dominantBaseline: "middle", x: x, y: "50%", filter: "url(#g-date-tt-ticklabelbg)", children: formatTime ? formatTime(dt) : defaultFormatTime(dt) }, i));
40
+ }
41
+ return result;
42
+ }, [hasLabels, tickCoords, tickTimes, timeZone, labelsClassName, formatTime]);
43
+ return (_jsxs(React.Fragment, { children: [ticksPath, tickLabels] }));
44
+ }
@@ -0,0 +1,15 @@
1
+ import type { DateTime } from '@gravity-ui/date-utils';
2
+ import type { Geometry } from './utils';
3
+ import { makeMiddleTicksGeometry, makeSlitTicksGeometry } from './utils';
4
+ import './Ticks.css';
5
+ interface TicksProps {
6
+ theme: 'normal' | 'dim';
7
+ tickWidth: number;
8
+ minValue?: DateTime;
9
+ maxValue?: DateTime;
10
+ geometry: Geometry;
11
+ className?: string;
12
+ }
13
+ export { makeMiddleTicksGeometry, makeSlitTicksGeometry };
14
+ export type { Geometry };
15
+ export declare function UnavailableTicks({ theme, geometry, tickWidth, minValue, maxValue, className, }: TicksProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { block } from '../../../../utils/cn';
4
+ import { useViewportDimensions, useViewportInterval } from '../Ruler/Ruler';
5
+ import { calculatePosition, makeMiddleTicksGeometry, makeSlitTicksGeometry } from './utils';
6
+ import './Ticks.css';
7
+ const b = block('timeline-ticks');
8
+ export { makeMiddleTicksGeometry, makeSlitTicksGeometry };
9
+ export function UnavailableTicks({ theme, geometry, tickWidth, minValue, maxValue, className, }) {
10
+ const viewport = useViewportDimensions();
11
+ const interval = useViewportInterval();
12
+ const endPoint = calculatePosition(minValue, interval, viewport.width);
13
+ const startPoint = calculatePosition(maxValue, interval, viewport.width);
14
+ const tickCoords = [];
15
+ if (!isNaN(endPoint) && endPoint > 0) {
16
+ let x = endPoint - tickWidth;
17
+ if (endPoint > viewport.width) {
18
+ x = endPoint - tickWidth * Math.floor((endPoint - viewport.width) / tickWidth);
19
+ }
20
+ tickCoords.push(x);
21
+ while (x > 0) {
22
+ x -= tickWidth;
23
+ tickCoords.push(x);
24
+ }
25
+ }
26
+ if (!isNaN(startPoint) && startPoint < viewport.width) {
27
+ let x = startPoint;
28
+ if (startPoint < 0) {
29
+ x = startPoint + tickWidth * Math.floor((0 - startPoint) / tickWidth) - tickWidth;
30
+ }
31
+ tickCoords.push(x);
32
+ while (x < viewport.width) {
33
+ x += tickWidth;
34
+ tickCoords.push(x);
35
+ }
36
+ }
37
+ const ticksGeometry = React.useMemo(() => {
38
+ return tickCoords.map(geometry).join('');
39
+ }, [geometry, tickCoords]);
40
+ const ticksPath = _jsx("path", { className: b({ theme }, className), d: ticksGeometry });
41
+ return _jsx(React.Fragment, { children: ticksPath });
42
+ }
@@ -0,0 +1,24 @@
1
+ import type { DateTime } from '@gravity-ui/date-utils/build/typings';
2
+ import type { RangeValue } from '../../../types';
3
+ interface CalculateTicksOptions {
4
+ viewportStart: DateTime;
5
+ viewportEnd: DateTime;
6
+ minTickWidth: number;
7
+ maxTickWidth: number;
8
+ viewportWidth: number;
9
+ timeZone?: string;
10
+ }
11
+ export declare function calculateTickTimes({ viewportStart, viewportEnd, minTickWidth, maxTickWidth, viewportWidth, timeZone, }: CalculateTicksOptions): number[];
12
+ export declare function formatTick(t: DateTime): string;
13
+ export interface Geometry {
14
+ (x: number): string;
15
+ }
16
+ interface MakeTicksOptions {
17
+ tickHeight: number;
18
+ viewportHeight: number;
19
+ }
20
+ export declare function makeMiddleTicksGeometry({ tickHeight, viewportHeight }: MakeTicksOptions): Geometry;
21
+ export declare function makeSlitTicksGeometry({ tickHeight, viewportHeight }: MakeTicksOptions): Geometry;
22
+ export declare function makeUnavailableTicksGeometry({ tickHeight, viewportHeight, }: MakeTicksOptions): Geometry;
23
+ export declare function calculatePosition(value: DateTime | undefined, interval: RangeValue<DateTime>, width: number): number;
24
+ export {};
@@ -0,0 +1,158 @@
1
+ import { dateTimeUtc, settings } from '@gravity-ui/date-utils';
2
+ import { DAY, HOUR, MINUTE, MONTH, SECOND, WEEK, YEAR } from '../../../utils/constants';
3
+ import { getLerpCoeff } from '../../utils/span';
4
+ const MAX_TICKS = 100000;
5
+ function makeTickSizes(periodSize, periodName, ...multipliers) {
6
+ return multipliers.map((m) => ({
7
+ duration: m * periodSize,
8
+ period: periodName,
9
+ multiplier: m,
10
+ }));
11
+ }
12
+ const tickSizes = [
13
+ ...makeTickSizes(SECOND, 'second', 1, 2, 5, 10, 15, 30),
14
+ ...makeTickSizes(MINUTE, 'minute', 1, 2, 5, 10, 20, 30),
15
+ ...makeTickSizes(HOUR, 'hour', 1, 2, 3, 6, 12),
16
+ ...makeTickSizes(DAY, 'day', 1, 2, 3),
17
+ ...makeTickSizes(WEEK, 'week', 1, 2),
18
+ ...makeTickSizes(MONTH, 'month', 1, 2, 3, 6),
19
+ ...makeTickSizes(YEAR, 'year', 1, 2, 5, 10, 15, 20, 30),
20
+ ];
21
+ const TickFormat = {
22
+ SECONDS: 'HH:mm:ss',
23
+ MINUTES: 'HH:mm',
24
+ DATE: 'DD.MM.YY',
25
+ MONTH: 'MM-YYYY',
26
+ YEAR: 'YYYY',
27
+ };
28
+ function findTickSize({ minWidth, maxWidth, timelineWidth, timelineDuration }) {
29
+ const minDuration = (minWidth / timelineWidth) * timelineDuration;
30
+ const maxDuration = (maxWidth / timelineWidth) * timelineDuration;
31
+ const suitableSizes = [];
32
+ for (const size of tickSizes) {
33
+ if (minDuration <= size.duration) {
34
+ if (size.duration <= maxDuration) {
35
+ suitableSizes.push(size);
36
+ }
37
+ else if (!suitableSizes.length) {
38
+ return size;
39
+ }
40
+ }
41
+ }
42
+ if (!suitableSizes.length) {
43
+ return tickSizes[tickSizes.length - 1];
44
+ }
45
+ return suitableSizes[Math.floor(suitableSizes.length / 2)];
46
+ }
47
+ export function calculateTickTimes({ viewportStart, viewportEnd, minTickWidth, maxTickWidth, viewportWidth, timeZone, }) {
48
+ const tickSize = findTickSize({
49
+ minWidth: minTickWidth,
50
+ maxWidth: maxTickWidth,
51
+ timelineDuration: viewportEnd.valueOf() - viewportStart.valueOf(),
52
+ timelineWidth: viewportWidth,
53
+ });
54
+ let cursor;
55
+ const stableDate = dateTimeUtc({ input: '1977-01-01' }).timeZone(timeZone || settings.getDefaultTimeZone(), true);
56
+ const diff = stableDate.diff(viewportStart, tickSize.period);
57
+ const divider = tickSize.multiplier;
58
+ const multiplier = Math.floor(diff / divider);
59
+ cursor = stableDate.subtract(multiplier * divider, tickSize.period);
60
+ if (viewportStart.isBefore(cursor)) {
61
+ cursor = cursor.subtract(divider, tickSize.period);
62
+ }
63
+ const timeDiff = stableDate.utcOffset() - cursor.utcOffset();
64
+ if (timeDiff !== 0 &&
65
+ (tickSize.period === 'hour' || tickSize.period === 'minute' || tickSize.period === 'second')) {
66
+ cursor = cursor.add(timeDiff, 'minutes');
67
+ }
68
+ const ticks = [];
69
+ let i = 0;
70
+ let maxTickValue = cursor.valueOf();
71
+ while (cursor.valueOf() < viewportEnd.valueOf()) {
72
+ if (i >= MAX_TICKS) {
73
+ throw new Error('TimeLine: too much ticks');
74
+ }
75
+ i++;
76
+ ticks.push(cursor.valueOf());
77
+ let next = cursor.add(tickSize.multiplier, tickSize.period);
78
+ // Get time shift when season timeZone changed
79
+ const timeDiff = cursor.utcOffset() - next.utcOffset();
80
+ if (timeDiff !== 0 &&
81
+ (tickSize.period === 'hour' ||
82
+ tickSize.period === 'minute' ||
83
+ tickSize.period === 'second')) {
84
+ // Tick should be on same local time distance. Then labels will have correct format (such as date/week/month)
85
+ // For example, Amsterdam timezone:
86
+ //
87
+ // 2023-10-28T22:00Z -> Summer time is 2023-10-29T00:00, displays as 29.10.2023
88
+ // 2023-10-29T22:00Z -> Winter time is 2023-10-29T23:00, displays as 23:00
89
+ //
90
+ // Add 1h:
91
+ // 2023-10-29T23:00Z -> Winter time is 2023-10-30T00:00, displays as 30.10.2023
92
+ //
93
+ next = next.add(timeDiff, 'minutes');
94
+ let k = 1;
95
+ while (next.valueOf() <= maxTickValue) {
96
+ // Example of case, Amsterdam timezone:
97
+ // 2023-03-26T00:00Z -> Winter time is 2023-03-26T01:00, displays as 01:00
98
+ // 2023-03-26T01:00Z -> Summer time is 2023-03-26T03:00, displays as 03:00
99
+ // We shift time on -1h... And again:
100
+ // 2023-03-26T00:00Z -> Winter time is 2023-03-26T01:00, displays as 01:00
101
+ // Infinite loop
102
+ //
103
+ // Therefore we need to skip period in the past
104
+ next = next
105
+ .add(tickSize.multiplier * k++, tickSize.period)
106
+ .add(timeDiff, 'minutes');
107
+ }
108
+ }
109
+ cursor = next;
110
+ maxTickValue = Math.max(maxTickValue, cursor.valueOf());
111
+ }
112
+ return ticks;
113
+ }
114
+ export function formatTick(t) {
115
+ let format;
116
+ if (t.valueOf() % MINUTE === 0) {
117
+ if (t.hour() === 0 && t.minute() === 0) {
118
+ if (t.date() === 1) {
119
+ if (t.month() === 0) {
120
+ format = TickFormat.YEAR;
121
+ }
122
+ else {
123
+ format = TickFormat.MONTH;
124
+ }
125
+ }
126
+ else {
127
+ format = TickFormat.DATE;
128
+ }
129
+ }
130
+ else {
131
+ format = TickFormat.MINUTES;
132
+ }
133
+ }
134
+ else {
135
+ format = TickFormat.SECONDS;
136
+ }
137
+ return t.format(format);
138
+ }
139
+ export function makeMiddleTicksGeometry({ tickHeight, viewportHeight }) {
140
+ const tickYStart = (viewportHeight - tickHeight) / 2;
141
+ return (x) => `M${x},${tickYStart}l0,${tickHeight}`;
142
+ }
143
+ export function makeSlitTicksGeometry({ tickHeight, viewportHeight }) {
144
+ const slitHeight = viewportHeight - 2 * tickHeight;
145
+ return (x) => `M${x},0l0,${tickHeight}m0,${slitHeight}l0,${tickHeight}`;
146
+ }
147
+ export function makeUnavailableTicksGeometry({ tickHeight, viewportHeight, }) {
148
+ const w = tickHeight;
149
+ const h = viewportHeight;
150
+ return (x) => `M${x},0l${h},${h}l${w},0l${-h},${-h}l${-w},0`;
151
+ }
152
+ export function calculatePosition(value, interval, width) {
153
+ if (!value) {
154
+ return NaN;
155
+ }
156
+ const timeToXCoeff = getLerpCoeff({ start: interval.start.valueOf(), end: interval.end.valueOf() }, { start: 0, end: width });
157
+ return Math.round((value.valueOf() - interval.start.valueOf()) * timeToXCoeff);
158
+ }