@ledgerhq/lumen-ui-rnative-visualization 0.1.22 → 0.1.24

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 (68) hide show
  1. package/dist/module/lib/Components/Line/Line.js +4 -4
  2. package/dist/module/lib/Components/Line/Line.js.map +1 -1
  3. package/dist/module/lib/Components/LineChart/LineChart.js +4 -4
  4. package/dist/module/lib/Components/LineChart/LineChart.js.map +1 -1
  5. package/dist/module/lib/Components/LineChart/LineChart.performance.test.js +99 -0
  6. package/dist/module/lib/Components/LineChart/LineChart.performance.test.js.map +1 -0
  7. package/dist/module/lib/Components/Point/Point.js +81 -50
  8. package/dist/module/lib/Components/Point/Point.js.map +1 -1
  9. package/dist/module/lib/Components/Point/Point.test.js +41 -0
  10. package/dist/module/lib/Components/Point/Point.test.js.map +1 -1
  11. package/dist/module/lib/Components/Point/constants.js +2 -0
  12. package/dist/module/lib/Components/Point/constants.js.map +1 -1
  13. package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.js +19 -9
  14. package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.js.map +1 -1
  15. package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.test.js +10 -1
  16. package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.test.js.map +1 -1
  17. package/dist/module/lib/Components/Point/pointContext/index.js +1 -1
  18. package/dist/module/lib/Components/Point/pointContext/index.js.map +1 -1
  19. package/dist/module/lib/Components/Point/pointContext/magneticPointsContext.js +22 -4
  20. package/dist/module/lib/Components/Point/pointContext/magneticPointsContext.js.map +1 -1
  21. package/dist/module/lib/Components/Point/usePointGeometry.js +60 -0
  22. package/dist/module/lib/Components/Point/usePointGeometry.js.map +1 -0
  23. package/dist/module/lib/Components/Point/usePointGeometry.test.js +86 -0
  24. package/dist/module/lib/Components/Point/usePointGeometry.test.js.map +1 -0
  25. package/dist/module/lib/Components/Point/utils.js +58 -15
  26. package/dist/module/lib/Components/Point/utils.js.map +1 -1
  27. package/dist/module/lib/Components/Point/utils.test.js +37 -75
  28. package/dist/module/lib/Components/Point/utils.test.js.map +1 -1
  29. package/dist/module/lib/Components/Scrubber/ScrubberProvider.js +2 -2
  30. package/dist/module/lib/Components/Scrubber/ScrubberProvider.js.map +1 -1
  31. package/dist/module/lib/utils/domain/domain.js +4 -4
  32. package/dist/module/lib/utils/domain/domain.js.map +1 -1
  33. package/dist/typescript/src/lib/Components/Line/Line.d.ts +1 -1
  34. package/dist/typescript/src/lib/Components/Line/Line.d.ts.map +1 -1
  35. package/dist/typescript/src/lib/Components/LineChart/LineChart.d.ts.map +1 -1
  36. package/dist/typescript/src/lib/Components/Point/Point.d.ts +1 -1
  37. package/dist/typescript/src/lib/Components/Point/Point.d.ts.map +1 -1
  38. package/dist/typescript/src/lib/Components/Point/constants.d.ts +2 -0
  39. package/dist/typescript/src/lib/Components/Point/constants.d.ts.map +1 -1
  40. package/dist/typescript/src/lib/Components/Point/pointContext/MagneticPointsProvider.d.ts.map +1 -1
  41. package/dist/typescript/src/lib/Components/Point/pointContext/index.d.ts +1 -1
  42. package/dist/typescript/src/lib/Components/Point/pointContext/index.d.ts.map +1 -1
  43. package/dist/typescript/src/lib/Components/Point/pointContext/magneticPointsContext.d.ts +22 -5
  44. package/dist/typescript/src/lib/Components/Point/pointContext/magneticPointsContext.d.ts.map +1 -1
  45. package/dist/typescript/src/lib/Components/Point/types.d.ts +32 -0
  46. package/dist/typescript/src/lib/Components/Point/types.d.ts.map +1 -1
  47. package/dist/typescript/src/lib/Components/Point/usePointGeometry.d.ts +33 -0
  48. package/dist/typescript/src/lib/Components/Point/usePointGeometry.d.ts.map +1 -0
  49. package/dist/typescript/src/lib/Components/Point/utils.d.ts +30 -4
  50. package/dist/typescript/src/lib/Components/Point/utils.d.ts.map +1 -1
  51. package/package.json +4 -4
  52. package/src/lib/Components/Line/Line.tsx +4 -4
  53. package/src/lib/Components/LineChart/LineChart.performance.test.tsx +101 -0
  54. package/src/lib/Components/LineChart/LineChart.tsx +18 -11
  55. package/src/lib/Components/Point/Point.test.tsx +46 -0
  56. package/src/lib/Components/Point/Point.tsx +79 -57
  57. package/src/lib/Components/Point/constants.ts +2 -0
  58. package/src/lib/Components/Point/pointContext/MagneticPointsProvider.test.tsx +13 -1
  59. package/src/lib/Components/Point/pointContext/MagneticPointsProvider.tsx +20 -8
  60. package/src/lib/Components/Point/pointContext/index.ts +4 -2
  61. package/src/lib/Components/Point/pointContext/magneticPointsContext.ts +29 -7
  62. package/src/lib/Components/Point/types.ts +41 -0
  63. package/src/lib/Components/Point/usePointGeometry.test.ts +95 -0
  64. package/src/lib/Components/Point/usePointGeometry.ts +77 -0
  65. package/src/lib/Components/Point/utils.test.ts +34 -80
  66. package/src/lib/Components/Point/utils.ts +77 -19
  67. package/src/lib/Components/Scrubber/ScrubberProvider.tsx +2 -2
  68. package/src/lib/utils/domain/domain.ts +4 -4
@@ -1,14 +1,12 @@
1
- import { describe, expect, it, jest } from '@jest/globals';
2
- import { renderHook } from '@testing-library/react-native';
1
+ import { describe, expect, it } from '@jest/globals';
3
2
 
4
- import type { BaseAxisProps } from '../Axis';
5
3
  import { ARROW_HEIGHT, ARROW_WIDTH, GAP, LABEL_FONT_SIZE } from './constants';
6
4
  import {
7
5
  buildArrowPoints,
6
+ computeLabelX,
8
7
  computeLabelY,
9
8
  isWithinBounds,
10
9
  resolveLabel,
11
- useMagneticRegistration,
12
10
  } from './utils';
13
11
 
14
12
  describe('isWithinBounds', () => {
@@ -117,92 +115,48 @@ describe('computeLabelY', () => {
117
115
  });
118
116
  });
119
117
 
120
- const makeContext = () => ({
121
- register: jest.fn(),
122
- unregister: jest.fn(),
123
- version: 0,
124
- getMagneticPoints: () => new Set<number>(),
125
- });
126
-
127
- const noAxisConfig = (): BaseAxisProps | undefined => undefined;
118
+ describe('computeLabelX', () => {
119
+ const area = { x: 100, y: 0, width: 200, height: 100 };
120
+ const halfArrow = ARROW_WIDTH / 2;
128
121
 
129
- describe('useMagneticRegistration', () => {
130
- it('registers the data index when magnetic is true', () => {
131
- const ctx = makeContext();
132
- renderHook(() => useMagneticRegistration(true, 3, noAxisConfig, ctx));
133
-
134
- expect(ctx.register).toHaveBeenCalledWith(3);
135
- });
136
-
137
- it('does not register when magnetic is false', () => {
138
- const ctx = makeContext();
139
- renderHook(() => useMagneticRegistration(false, 3, noAxisConfig, ctx));
140
-
141
- expect(ctx.register).not.toHaveBeenCalled();
142
- });
143
-
144
- it('unregisters on unmount', () => {
145
- const ctx = makeContext();
146
- const { unmount } = renderHook(() =>
147
- useMagneticRegistration(true, 3, noAxisConfig, ctx),
148
- );
149
-
150
- unmount();
151
- expect(ctx.unregister).toHaveBeenCalledWith(3);
122
+ it('centres the label on the point when clamping is disabled', () => {
123
+ expect(
124
+ computeLabelX(105, 'A very long label', area, 'center', true),
125
+ ).toEqual({
126
+ x: 105,
127
+ textAnchor: 'middle',
128
+ });
152
129
  });
153
130
 
154
- it('resolves dataX through axis config data when provided', () => {
155
- const ctx = makeContext();
156
- const getXAxisConfig = (): BaseAxisProps => ({
157
- scaleType: 'band',
158
- data: [100, 200, 300],
131
+ it('centres the label on the point when it fits inside the bounds', () => {
132
+ expect(computeLabelX(200, 'Peak', area, 'auto', true)).toEqual({
133
+ x: 200,
134
+ textAnchor: 'middle',
159
135
  });
160
-
161
- renderHook(() => useMagneticRegistration(true, 200, getXAxisConfig, ctx));
162
-
163
- expect(ctx.register).toHaveBeenCalledWith(1);
164
136
  });
165
137
 
166
- it('does not register when axis data exists but does not contain the value', () => {
167
- const ctx = makeContext();
168
- const getXAxisConfig = (): BaseAxisProps => ({
169
- scaleType: 'band',
170
- data: [100, 200, 300],
138
+ it('anchors near the left edge, offset by half the arrow width from the point', () => {
139
+ expect(computeLabelX(105, 'Long label', area, 'auto', true)).toEqual({
140
+ x: 105 - halfArrow,
141
+ textAnchor: 'start',
171
142
  });
172
-
173
- renderHook(() => useMagneticRegistration(true, 999, getXAxisConfig, ctx));
174
-
175
- expect(ctx.register).not.toHaveBeenCalled();
176
143
  });
177
144
 
178
- it('re-registers when dataX changes', () => {
179
- const ctx = makeContext();
180
- const { rerender } = renderHook(
181
- ({ dataX }: { dataX: number }) =>
182
- useMagneticRegistration(true, dataX, noAxisConfig, ctx),
183
- { initialProps: { dataX: 2 } },
184
- );
185
-
186
- expect(ctx.register).toHaveBeenCalledWith(2);
187
-
188
- rerender({ dataX: 4 });
189
-
190
- expect(ctx.unregister).toHaveBeenCalledWith(2);
191
- expect(ctx.register).toHaveBeenCalledWith(4);
145
+ it('anchors near the right edge, offset by half the arrow width from the point', () => {
146
+ expect(computeLabelX(295, 'Long label', area, 'auto', true)).toEqual({
147
+ x: 295 + halfArrow,
148
+ textAnchor: 'end',
149
+ });
192
150
  });
193
151
 
194
- it('unregisters and stops when magnetic switches to false', () => {
195
- const ctx = makeContext();
196
- const { rerender } = renderHook(
197
- ({ magnetic }: { magnetic: boolean }) =>
198
- useMagneticRegistration(magnetic, 3, noAxisConfig, ctx),
199
- { initialProps: { magnetic: true } },
200
- );
201
-
202
- expect(ctx.register).toHaveBeenCalledWith(3);
203
-
204
- rerender({ magnetic: false });
205
-
206
- expect(ctx.unregister).toHaveBeenCalledWith(3);
152
+ it('omits the arrow offset when no arrow is rendered', () => {
153
+ expect(computeLabelX(105, 'Long label', area, 'auto', false)).toEqual({
154
+ x: 105,
155
+ textAnchor: 'start',
156
+ });
157
+ expect(computeLabelX(295, 'Long label', area, 'auto', false)).toEqual({
158
+ x: 295,
159
+ textAnchor: 'end',
160
+ });
207
161
  });
208
162
  });
@@ -1,9 +1,15 @@
1
- import { useEffect } from 'react';
2
-
3
1
  import type { DrawingArea } from '../../utils/types';
4
2
  import type { BaseAxisProps } from '../Axis';
5
- import { LABEL_FONT_SIZE, ARROW_WIDTH, ARROW_HEIGHT, GAP } from './constants';
6
- import type { MagneticPointsContextValue } from './pointContext/magneticPointsContext';
3
+ import {
4
+ ARROW_HEIGHT,
5
+ ARROW_WIDTH,
6
+ GAP,
7
+ LABEL_CHAR_WIDTH_RATIO,
8
+ LABEL_FONT_SIZE,
9
+ } from './constants';
10
+ import type { LabelAlignment } from './types';
11
+
12
+ export type LabelTextAnchor = 'start' | 'middle' | 'end';
7
13
 
8
14
  export const isWithinBounds = (
9
15
  px: number,
@@ -63,22 +69,33 @@ export const resolveDataXToIndex = (
63
69
  };
64
70
 
65
71
  /**
66
- * Registers/unregisters a data index as a magnetic snap target
67
- * when the Point has `magnetic` enabled.
72
+ * Computes the label's horizontal placement, keeping it inside the drawing area
73
+ * near the left/right edges. Centred on the point when it fits; otherwise
74
+ * anchored to the edge and aligned with the arrow's outer vertex (offset by half
75
+ * the arrow width from the point, when an arrow is rendered).
68
76
  */
69
- export const useMagneticRegistration = (
70
- magnetic: boolean,
71
- dataX: number,
72
- getXAxisConfig: () => BaseAxisProps | undefined,
73
- { register, unregister }: MagneticPointsContextValue,
74
- ): void => {
75
- useEffect(() => {
76
- if (!magnetic) return;
77
- const index = resolveDataXToIndex(dataX, getXAxisConfig());
78
- if (index === undefined) return;
79
- register(index);
80
- return () => unregister(index);
81
- }, [magnetic, dataX, getXAxisConfig, register, unregister]);
77
+ export const computeLabelX = (
78
+ pixelX: number,
79
+ label: string,
80
+ area: DrawingArea,
81
+ alignment: LabelAlignment,
82
+ hasArrow: boolean,
83
+ ): { x: number; textAnchor: LabelTextAnchor } => {
84
+ if (alignment === 'center') {
85
+ return { x: pixelX, textAnchor: 'middle' };
86
+ }
87
+
88
+ const halfWidth =
89
+ (label.length * LABEL_FONT_SIZE * LABEL_CHAR_WIDTH_RATIO) / 2;
90
+ const arrowOffset = hasArrow ? ARROW_WIDTH / 2 : 0;
91
+
92
+ if (pixelX - halfWidth < area.x) {
93
+ return { x: pixelX - arrowOffset, textAnchor: 'start' };
94
+ }
95
+ if (pixelX + halfWidth > area.x + area.width) {
96
+ return { x: pixelX + arrowOffset, textAnchor: 'end' };
97
+ }
98
+ return { x: pixelX, textAnchor: 'middle' };
82
99
  };
83
100
 
84
101
  /**
@@ -96,3 +113,44 @@ export const computeLabelY = (
96
113
  ? pixelY - radius - arrowOffset - GAP
97
114
  : pixelY + radius + arrowOffset + GAP + LABEL_FONT_SIZE;
98
115
  };
116
+
117
+ /**
118
+ * Computes where a point's label and its connector arrow are drawn: the
119
+ * (optionally clamped) horizontal placement, the vertical baseline, and whether
120
+ * the arrow is rendered. Label content is resolved separately via
121
+ * {@link resolveLabel}.
122
+ */
123
+ export const computeLabelGeometry = ({
124
+ text,
125
+ pixelX,
126
+ pixelY,
127
+ size,
128
+ labelPosition,
129
+ showLabelArrow,
130
+ area,
131
+ alignment,
132
+ }: {
133
+ text: string;
134
+ pixelX: number;
135
+ pixelY: number;
136
+ size: number;
137
+ labelPosition: 'top' | 'bottom';
138
+ showLabelArrow: boolean;
139
+ area: DrawingArea;
140
+ alignment: LabelAlignment;
141
+ }): {
142
+ x: number;
143
+ y: number;
144
+ textAnchor: LabelTextAnchor;
145
+ } => {
146
+ const { x, textAnchor } = computeLabelX(
147
+ pixelX,
148
+ text,
149
+ area,
150
+ alignment,
151
+ showLabelArrow,
152
+ );
153
+ const y = computeLabelY(pixelY, size / 2, labelPosition, showLabelArrow);
154
+
155
+ return { x, y, textAnchor };
156
+ };
@@ -7,7 +7,7 @@ import { scheduleOnRN } from 'react-native-worklets';
7
7
 
8
8
  import { clamp } from '../../utils/numbers';
9
9
  import { useCartesianChartContext } from '../CartesianChart/context';
10
- import { useMagneticPointsContext } from '../Point/pointContext';
10
+ import { useMagneticSnapshot } from '../Point/pointContext';
11
11
  import { ScrubberContextProvider } from './context';
12
12
  import type { ScrubberProviderProps } from './types';
13
13
  import {
@@ -46,7 +46,7 @@ export function ScrubberProvider({
46
46
  >();
47
47
 
48
48
  const { getXScale, getXAxisConfig, dataLength } = useCartesianChartContext();
49
- const { getMagneticPoints, version } = useMagneticPointsContext();
49
+ const { getMagneticPoints, version } = useMagneticSnapshot();
50
50
 
51
51
  // All values touched by the gesture's JS-thread callback live in a single
52
52
  // ref. Reading via `latest.current` keeps the callbacks reference-stable
@@ -55,13 +55,13 @@ export const computeYDomain = (
55
55
  if (!s.data) continue;
56
56
  for (const v of s.data) {
57
57
  if (v !== null && v !== undefined) {
58
- if (!hasValue) {
58
+ if (hasValue) {
59
+ if (v < min) min = v;
60
+ if (v > max) max = v;
61
+ } else {
59
62
  min = v;
60
63
  max = v;
61
64
  hasValue = true;
62
- } else {
63
- if (v < min) min = v;
64
- if (v > max) max = v;
65
65
  }
66
66
  }
67
67
  }