@ledgerhq/lumen-ui-rnative 0.1.1 → 0.1.3
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.
- package/dist/package.json +3 -3
- package/dist/src/i18n/locales/de.json +3 -0
- package/dist/src/i18n/locales/en.json +3 -0
- package/dist/src/i18n/locales/es.json +3 -0
- package/dist/src/i18n/locales/fr.json +3 -0
- package/dist/src/i18n/locales/ja.json +3 -0
- package/dist/src/i18n/locales/ko.json +3 -0
- package/dist/src/i18n/locales/pt.json +3 -0
- package/dist/src/i18n/locales/ru.json +3 -0
- package/dist/src/i18n/locales/th.json +3 -0
- package/dist/src/i18n/locales/tr.json +3 -0
- package/dist/src/i18n/locales/zh.json +3 -0
- package/dist/src/lib/Animations/constants.d.ts +28 -0
- package/dist/src/lib/Animations/constants.d.ts.map +1 -0
- package/dist/src/lib/Animations/constants.js +27 -0
- package/dist/src/lib/Animations/index.d.ts +1 -0
- package/dist/src/lib/Animations/index.d.ts.map +1 -1
- package/dist/src/lib/Animations/index.js +1 -0
- package/dist/src/lib/Components/AmountDisplay/AmountDisplay.d.ts +1 -1
- package/dist/src/lib/Components/AmountDisplay/AmountDisplay.d.ts.map +1 -1
- package/dist/src/lib/Components/AmountDisplay/AmountDisplay.js +76 -5
- package/dist/src/lib/Components/AmountDisplay/AmountDisplay.stories.d.ts +1 -0
- package/dist/src/lib/Components/AmountDisplay/AmountDisplay.stories.d.ts.map +1 -1
- package/dist/src/lib/Components/AmountDisplay/AmountDisplay.stories.js +25 -2
- package/dist/src/lib/Components/AmountDisplay/types.d.ts +20 -25
- package/dist/src/lib/Components/AmountDisplay/types.d.ts.map +1 -1
- package/dist/src/lib/Components/AmountDisplay/types.js +1 -1
- package/dist/src/lib/Components/SegmentedControl/SegmentedControl.d.ts +10 -0
- package/dist/src/lib/Components/SegmentedControl/SegmentedControl.d.ts.map +1 -0
- package/dist/src/lib/Components/SegmentedControl/SegmentedControl.js +114 -0
- package/dist/src/lib/Components/SegmentedControl/SegmentedControl.stories.d.ts +58 -0
- package/dist/src/lib/Components/SegmentedControl/SegmentedControl.stories.d.ts.map +1 -0
- package/dist/src/lib/Components/SegmentedControl/SegmentedControl.stories.js +61 -0
- package/dist/src/lib/Components/SegmentedControl/SegmentedControlContext.d.ts +11 -0
- package/dist/src/lib/Components/SegmentedControl/SegmentedControlContext.d.ts.map +1 -0
- package/dist/src/lib/Components/SegmentedControl/SegmentedControlContext.js +7 -0
- package/dist/src/lib/Components/SegmentedControl/index.d.ts +3 -0
- package/dist/src/lib/Components/SegmentedControl/index.d.ts.map +1 -0
- package/dist/src/lib/Components/SegmentedControl/index.js +1 -0
- package/dist/src/lib/Components/SegmentedControl/types.d.ts +45 -0
- package/dist/src/lib/Components/SegmentedControl/types.d.ts.map +1 -0
- package/dist/src/lib/Components/SegmentedControl/types.js +1 -0
- package/dist/src/lib/Components/TabBar/TabBar.js +1 -0
- package/dist/src/lib/Components/TabBar/types.d.ts +0 -1
- package/dist/src/lib/Components/TabBar/types.d.ts.map +1 -1
- package/dist/src/lib/Components/index.d.ts +1 -0
- package/dist/src/lib/Components/index.d.ts.map +1 -1
- package/dist/src/lib/Components/index.js +1 -0
- package/dist/src/styles/theme/resolvers/resolveFontWeights.js +1 -1
- package/package.json +3 -3
- package/src/i18n/locales/de.json +3 -0
- package/src/i18n/locales/en.json +3 -0
- package/src/i18n/locales/es.json +3 -0
- package/src/i18n/locales/fr.json +3 -0
- package/src/i18n/locales/ja.json +3 -0
- package/src/i18n/locales/ko.json +3 -0
- package/src/i18n/locales/pt.json +3 -0
- package/src/i18n/locales/ru.json +3 -0
- package/src/i18n/locales/th.json +3 -0
- package/src/i18n/locales/tr.json +3 -0
- package/src/i18n/locales/zh.json +3 -0
- package/src/lib/Animations/constants.ts +31 -0
- package/src/lib/Animations/index.ts +1 -0
- package/src/lib/Components/AmountDisplay/AmountDisplay.mdx +7 -1
- package/src/lib/Components/AmountDisplay/AmountDisplay.stories.tsx +29 -2
- package/src/lib/Components/AmountDisplay/AmountDisplay.test.tsx +101 -51
- package/src/lib/Components/AmountDisplay/AmountDisplay.tsx +175 -24
- package/src/lib/Components/AmountDisplay/types.ts +22 -25
- package/src/lib/Components/SegmentedControl/SegmentedControl.mdx +159 -0
- package/src/lib/Components/SegmentedControl/SegmentedControl.stories.tsx +102 -0
- package/src/lib/Components/SegmentedControl/SegmentedControl.test.tsx +57 -0
- package/src/lib/Components/SegmentedControl/SegmentedControl.tsx +202 -0
- package/src/lib/Components/SegmentedControl/SegmentedControlContext.tsx +17 -0
- package/src/lib/Components/SegmentedControl/index.ts +2 -0
- package/src/lib/Components/SegmentedControl/types.ts +46 -0
- package/src/lib/Components/TabBar/TabBar.tsx +1 -0
- package/src/lib/Components/TabBar/types.ts +0 -1
- package/src/lib/Components/index.ts +1 -0
- package/src/styles/theme/createStylesheetTheme.test.ts +1 -1
- package/src/styles/theme/resolvers/resolveFontWeights.test.ts +9 -6
- package/src/styles/theme/resolvers/resolveFontWeights.ts +1 -1
- package/dist/src/lib/Components/Banner/Banner.figma.d.ts +0 -2
- package/dist/src/lib/Components/Banner/Banner.figma.d.ts.map +0 -1
- package/dist/src/lib/Components/Banner/Banner.figma.js +0 -45
- package/dist/src/lib/Components/Checkbox/Checkbox.figma.d.ts +0 -2
- package/dist/src/lib/Components/Checkbox/Checkbox.figma.d.ts.map +0 -1
- package/dist/src/lib/Components/Checkbox/Checkbox.figma.js +0 -32
- package/dist/src/lib/Components/InteractiveIcon/InteractiveIcon.figma.d.ts +0 -2
- package/dist/src/lib/Components/InteractiveIcon/InteractiveIcon.figma.d.ts.map +0 -1
- package/dist/src/lib/Components/InteractiveIcon/InteractiveIcon.figma.js +0 -26
- package/dist/src/lib/Components/Switch/Switch.figma.d.ts +0 -2
- package/dist/src/lib/Components/Switch/Switch.figma.d.ts.map +0 -1
- package/dist/src/lib/Components/Switch/Switch.figma.js +0 -32
- package/dist/src/lib/Components/Tile/Tile.figma.d.ts +0 -2
- package/dist/src/lib/Components/Tile/Tile.figma.d.ts.map +0 -1
- package/dist/src/lib/Components/Tile/Tile.figma.js +0 -28
- package/src/lib/Components/Banner/Banner.figma.tsx +0 -59
- package/src/lib/Components/Checkbox/Checkbox.figma.tsx +0 -49
- package/src/lib/Components/InteractiveIcon/InteractiveIcon.figma.tsx +0 -42
- package/src/lib/Components/Switch/Switch.figma.tsx +0 -47
- package/src/lib/Components/Tile/Tile.figma.tsx +0 -53
|
@@ -12,6 +12,8 @@ const TestWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
|
|
12
12
|
</ThemeProvider>
|
|
13
13
|
);
|
|
14
14
|
|
|
15
|
+
const hidden = { includeHiddenElements: true } as const;
|
|
16
|
+
|
|
15
17
|
describe('AmountDisplay', () => {
|
|
16
18
|
const createFormatter =
|
|
17
19
|
(overrides: Partial<FormattedValue> = {}) =>
|
|
@@ -24,17 +26,65 @@ describe('AmountDisplay', () => {
|
|
|
24
26
|
...overrides,
|
|
25
27
|
});
|
|
26
28
|
|
|
29
|
+
type TestNode = {
|
|
30
|
+
type: unknown;
|
|
31
|
+
props: {
|
|
32
|
+
accessibilityValue?: { text?: string };
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const getDigitStripValues = (): number[] => {
|
|
38
|
+
return screen.root
|
|
39
|
+
.findAll(
|
|
40
|
+
(node: TestNode) =>
|
|
41
|
+
typeof node.type !== 'function' &&
|
|
42
|
+
node.props.accessibilityValue?.text !== undefined,
|
|
43
|
+
)
|
|
44
|
+
.map((node: TestNode) => Number(node.props.accessibilityValue?.text));
|
|
45
|
+
};
|
|
46
|
+
|
|
27
47
|
it('renders with basic formatter', () => {
|
|
28
48
|
const formatter = createFormatter();
|
|
29
49
|
render(
|
|
30
50
|
<TestWrapper>
|
|
31
|
-
<AmountDisplay value={1234.56} formatter={formatter} />
|
|
51
|
+
<AmountDisplay value={1234.56} formatter={formatter} hidden={false} />
|
|
52
|
+
</TestWrapper>,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
expect(screen.getByText('USD', hidden)).toBeTruthy();
|
|
56
|
+
expect(screen.getByText('.', hidden)).toBeTruthy();
|
|
57
|
+
expect(getDigitStripValues()).toEqual([1, 2, 3, 4, 5, 6]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('handles zero value', () => {
|
|
61
|
+
const formatter = createFormatter({
|
|
62
|
+
integerPart: '0',
|
|
63
|
+
decimalPart: '00',
|
|
64
|
+
});
|
|
65
|
+
render(
|
|
66
|
+
<TestWrapper>
|
|
67
|
+
<AmountDisplay value={0} formatter={formatter} />
|
|
68
|
+
</TestWrapper>,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
expect(getDigitStripValues()).toEqual([0, 0, 0]);
|
|
72
|
+
expect(screen.getByText('.', hidden)).toBeTruthy();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('handles large numbers', () => {
|
|
76
|
+
const formatter = createFormatter({
|
|
77
|
+
integerPart: '1234567',
|
|
78
|
+
decimalPart: '89',
|
|
79
|
+
});
|
|
80
|
+
render(
|
|
81
|
+
<TestWrapper>
|
|
82
|
+
<AmountDisplay value={1234567.89} formatter={formatter} />
|
|
32
83
|
</TestWrapper>,
|
|
33
84
|
);
|
|
34
85
|
|
|
35
|
-
expect(
|
|
36
|
-
expect(screen.getByText('
|
|
37
|
-
expect(screen.getByText('.56')).toBeTruthy();
|
|
86
|
+
expect(getDigitStripValues()).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
87
|
+
expect(screen.getByText('.', hidden)).toBeTruthy();
|
|
38
88
|
});
|
|
39
89
|
|
|
40
90
|
it('renders currency at start position', () => {
|
|
@@ -45,8 +95,7 @@ describe('AmountDisplay', () => {
|
|
|
45
95
|
</TestWrapper>,
|
|
46
96
|
);
|
|
47
97
|
|
|
48
|
-
expect(screen.getByText('USD')).toBeTruthy();
|
|
49
|
-
expect(screen.getByText('1234')).toBeTruthy();
|
|
98
|
+
expect(screen.getByText('USD', hidden)).toBeTruthy();
|
|
50
99
|
});
|
|
51
100
|
|
|
52
101
|
it('renders currency at end position', () => {
|
|
@@ -57,8 +106,7 @@ describe('AmountDisplay', () => {
|
|
|
57
106
|
</TestWrapper>,
|
|
58
107
|
);
|
|
59
108
|
|
|
60
|
-
expect(screen.getByText('USD')).toBeTruthy();
|
|
61
|
-
expect(screen.getByText('.56')).toBeTruthy();
|
|
109
|
+
expect(screen.getByText('USD', hidden)).toBeTruthy();
|
|
62
110
|
});
|
|
63
111
|
|
|
64
112
|
it('renders without decimal part', () => {
|
|
@@ -69,8 +117,7 @@ describe('AmountDisplay', () => {
|
|
|
69
117
|
</TestWrapper>,
|
|
70
118
|
);
|
|
71
119
|
|
|
72
|
-
expect(screen.
|
|
73
|
-
expect(screen.queryByText(/\./)).toBeNull();
|
|
120
|
+
expect(screen.queryByText('.', hidden)).toBeNull();
|
|
74
121
|
});
|
|
75
122
|
|
|
76
123
|
it('uses comma as decimal separator', () => {
|
|
@@ -81,18 +128,18 @@ describe('AmountDisplay', () => {
|
|
|
81
128
|
</TestWrapper>,
|
|
82
129
|
);
|
|
83
130
|
|
|
84
|
-
expect(screen.getByText(',
|
|
131
|
+
expect(screen.getByText(',', hidden)).toBeTruthy();
|
|
85
132
|
});
|
|
86
133
|
|
|
87
|
-
it('uses default dot separator
|
|
88
|
-
const formatter = createFormatter(
|
|
134
|
+
it('uses default dot separator', () => {
|
|
135
|
+
const formatter = createFormatter();
|
|
89
136
|
render(
|
|
90
137
|
<TestWrapper>
|
|
91
138
|
<AmountDisplay value={1234.56} formatter={formatter} />
|
|
92
139
|
</TestWrapper>,
|
|
93
140
|
);
|
|
94
141
|
|
|
95
|
-
expect(screen.getByText('.
|
|
142
|
+
expect(screen.getByText('.', hidden)).toBeTruthy();
|
|
96
143
|
});
|
|
97
144
|
|
|
98
145
|
it('forwards additional props', () => {
|
|
@@ -110,85 +157,88 @@ describe('AmountDisplay', () => {
|
|
|
110
157
|
expect(screen.getByTestId('amount-display')).toBeTruthy();
|
|
111
158
|
});
|
|
112
159
|
|
|
113
|
-
it('
|
|
114
|
-
const formatter = createFormatter(
|
|
115
|
-
integerPart: '0',
|
|
116
|
-
decimalPart: '00',
|
|
117
|
-
});
|
|
160
|
+
it('displays bullet points when hidden is true', () => {
|
|
161
|
+
const formatter = createFormatter();
|
|
118
162
|
render(
|
|
119
163
|
<TestWrapper>
|
|
120
|
-
<AmountDisplay value={
|
|
164
|
+
<AmountDisplay value={1234.56} formatter={formatter} hidden={true} />
|
|
121
165
|
</TestWrapper>,
|
|
122
166
|
);
|
|
123
167
|
|
|
124
|
-
expect(screen.getByText('
|
|
125
|
-
expect(screen.getByText('
|
|
168
|
+
expect(screen.getByText('••••', hidden)).toBeTruthy();
|
|
169
|
+
expect(screen.getByText('USD', hidden)).toBeTruthy();
|
|
170
|
+
expect(screen.queryByText('.', hidden)).toBeNull();
|
|
126
171
|
});
|
|
127
172
|
|
|
128
|
-
it('
|
|
129
|
-
const formatter = createFormatter(
|
|
130
|
-
integerPart: '1,234,567',
|
|
131
|
-
decimalPart: '89',
|
|
132
|
-
});
|
|
173
|
+
it('renders correctly when loading is true', () => {
|
|
174
|
+
const formatter = createFormatter();
|
|
133
175
|
render(
|
|
134
176
|
<TestWrapper>
|
|
135
|
-
<AmountDisplay value={
|
|
177
|
+
<AmountDisplay value={1234.56} formatter={formatter} loading={true} />
|
|
136
178
|
</TestWrapper>,
|
|
137
179
|
);
|
|
138
180
|
|
|
139
|
-
expect(screen.getByText('
|
|
140
|
-
expect(screen.getByText('.
|
|
181
|
+
expect(screen.getByText('USD', hidden)).toBeTruthy();
|
|
182
|
+
expect(screen.getByText('.', hidden)).toBeTruthy();
|
|
141
183
|
});
|
|
142
184
|
|
|
143
|
-
it('
|
|
185
|
+
it('renders correctly when loading is false', () => {
|
|
144
186
|
const formatter = createFormatter();
|
|
145
187
|
render(
|
|
146
188
|
<TestWrapper>
|
|
147
|
-
<AmountDisplay value={1234.56} formatter={formatter}
|
|
189
|
+
<AmountDisplay value={1234.56} formatter={formatter} loading={false} />
|
|
148
190
|
</TestWrapper>,
|
|
149
191
|
);
|
|
150
192
|
|
|
151
|
-
expect(screen.getByText('
|
|
152
|
-
expect(screen.getByText('
|
|
153
|
-
expect(screen.queryByText('1234')).toBeNull();
|
|
154
|
-
expect(screen.queryByText('.56')).toBeNull();
|
|
193
|
+
expect(screen.getByText('USD', hidden)).toBeTruthy();
|
|
194
|
+
expect(screen.getByText('.', hidden)).toBeTruthy();
|
|
155
195
|
});
|
|
156
196
|
|
|
157
|
-
it('
|
|
158
|
-
const formatter = createFormatter();
|
|
197
|
+
it('sets accessibilityLabel with currency at start', () => {
|
|
198
|
+
const formatter = createFormatter({ currencyPosition: 'start' });
|
|
159
199
|
render(
|
|
160
200
|
<TestWrapper>
|
|
161
|
-
<AmountDisplay value={1234.56} formatter={formatter}
|
|
201
|
+
<AmountDisplay value={1234.56} formatter={formatter} />
|
|
162
202
|
</TestWrapper>,
|
|
163
203
|
);
|
|
164
204
|
|
|
165
|
-
expect(screen.
|
|
166
|
-
expect(screen.getByText('.56')).toBeTruthy();
|
|
167
|
-
expect(screen.queryByText('••••')).toBeNull();
|
|
205
|
+
expect(screen.getByLabelText('USD 1234.56')).toBeTruthy();
|
|
168
206
|
});
|
|
169
207
|
|
|
170
|
-
it('
|
|
208
|
+
it('sets accessibilityLabel with currency at end', () => {
|
|
171
209
|
const formatter = createFormatter({ currencyPosition: 'end' });
|
|
172
210
|
render(
|
|
173
211
|
<TestWrapper>
|
|
174
|
-
<AmountDisplay value={1234.56} formatter={formatter}
|
|
212
|
+
<AmountDisplay value={1234.56} formatter={formatter} />
|
|
175
213
|
</TestWrapper>,
|
|
176
214
|
);
|
|
177
215
|
|
|
178
|
-
expect(screen.
|
|
179
|
-
expect(screen.queryByText('.56')).toBeNull();
|
|
216
|
+
expect(screen.getByLabelText('1234.56 USD')).toBeTruthy();
|
|
180
217
|
});
|
|
181
218
|
|
|
182
|
-
it('
|
|
219
|
+
it('sets accessibilityLabel to "Amount hidden" when hidden', () => {
|
|
183
220
|
const formatter = createFormatter();
|
|
184
221
|
render(
|
|
185
222
|
<TestWrapper>
|
|
186
|
-
<AmountDisplay value={1234.56} formatter={formatter}
|
|
223
|
+
<AmountDisplay value={1234.56} formatter={formatter} hidden={true} />
|
|
224
|
+
</TestWrapper>,
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
expect(screen.getByLabelText('Amount hidden')).toBeTruthy();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('renders group separators', () => {
|
|
231
|
+
const formatter = createFormatter({
|
|
232
|
+
integerPart: '1,234,567',
|
|
233
|
+
decimalPart: '89',
|
|
234
|
+
decimalSeparator: '.',
|
|
235
|
+
});
|
|
236
|
+
render(
|
|
237
|
+
<TestWrapper>
|
|
238
|
+
<AmountDisplay value={1234567.89} formatter={formatter} />
|
|
187
239
|
</TestWrapper>,
|
|
188
240
|
);
|
|
189
241
|
|
|
190
|
-
expect(screen.
|
|
191
|
-
expect(screen.getByText('1234')).toBeTruthy();
|
|
192
|
-
expect(screen.getByText('.56')).toBeTruthy();
|
|
242
|
+
expect(screen.getAllByText(',', hidden)).toHaveLength(2);
|
|
193
243
|
});
|
|
194
244
|
});
|
|
@@ -1,15 +1,67 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useSplitText, buildAriaLabel } from '@ledgerhq/lumen-utils-shared';
|
|
2
|
+
import { memo, useEffect } from 'react';
|
|
3
|
+
import { Text, View } from 'react-native';
|
|
4
|
+
import Animated, {
|
|
5
|
+
Easing,
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
useSharedValue,
|
|
8
|
+
withTiming,
|
|
9
|
+
} from 'react-native-reanimated';
|
|
10
|
+
import { useCommonTranslation } from '../../../i18n';
|
|
2
11
|
import { useStyleSheet } from '../../../styles';
|
|
3
12
|
import { Pulse } from '../../Animations/Pulse';
|
|
4
13
|
import { Box } from '../Utility';
|
|
5
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
AmountDisplayProps,
|
|
16
|
+
DigitStripListProps,
|
|
17
|
+
DigitStripProps,
|
|
18
|
+
DIGITS,
|
|
19
|
+
} from './types';
|
|
20
|
+
|
|
21
|
+
const INTEGER_DIGIT_WIDTHS = {
|
|
22
|
+
0: 24.5,
|
|
23
|
+
1: 15,
|
|
24
|
+
2: 23,
|
|
25
|
+
3: 24,
|
|
26
|
+
4: 25,
|
|
27
|
+
5: 23,
|
|
28
|
+
6: 24.5,
|
|
29
|
+
7: 21.5,
|
|
30
|
+
8: 24,
|
|
31
|
+
9: 24,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const DECIMAL_DIGIT_WIDTHS = {
|
|
35
|
+
0: 17,
|
|
36
|
+
1: 10.5,
|
|
37
|
+
2: 16,
|
|
38
|
+
3: 16.5,
|
|
39
|
+
4: 17.2,
|
|
40
|
+
5: 15.7,
|
|
41
|
+
6: 17,
|
|
42
|
+
7: 14.7,
|
|
43
|
+
8: 16.5,
|
|
44
|
+
9: 16.5,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const TIMING_CONFIG = {
|
|
48
|
+
duration: 600,
|
|
49
|
+
easing: Easing.inOut(Easing.ease),
|
|
50
|
+
};
|
|
6
51
|
|
|
7
52
|
const useStyles = () => {
|
|
8
53
|
return useStyleSheet(
|
|
9
54
|
(t) => ({
|
|
10
55
|
container: {
|
|
11
56
|
flexDirection: 'row',
|
|
12
|
-
alignItems: '
|
|
57
|
+
alignItems: 'flex-end',
|
|
58
|
+
},
|
|
59
|
+
integerPartContainer: {
|
|
60
|
+
flexDirection: 'row',
|
|
61
|
+
},
|
|
62
|
+
decimalPartContainer: {
|
|
63
|
+
flexDirection: 'row',
|
|
64
|
+
paddingBottom: t.spacings.s2,
|
|
13
65
|
},
|
|
14
66
|
integerText: {
|
|
15
67
|
...t.typographies.heading1SemiBold,
|
|
@@ -38,6 +90,73 @@ const useStyles = () => {
|
|
|
38
90
|
);
|
|
39
91
|
};
|
|
40
92
|
|
|
93
|
+
const DigitStrip = memo(
|
|
94
|
+
({ value, textStyle, animate, type }: DigitStripProps) => {
|
|
95
|
+
const targetWidth = (
|
|
96
|
+
type === 'integer' ? INTEGER_DIGIT_WIDTHS : DECIMAL_DIGIT_WIDTHS
|
|
97
|
+
)[value];
|
|
98
|
+
const lineHeight = textStyle.lineHeight;
|
|
99
|
+
const translateY = useSharedValue(-value * lineHeight);
|
|
100
|
+
const width = useSharedValue<number>(targetWidth);
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (animate) {
|
|
104
|
+
translateY.value = withTiming(-value * lineHeight, TIMING_CONFIG);
|
|
105
|
+
width.value = withTiming(targetWidth, TIMING_CONFIG);
|
|
106
|
+
} else {
|
|
107
|
+
translateY.value = -value * lineHeight;
|
|
108
|
+
width.value = targetWidth;
|
|
109
|
+
}
|
|
110
|
+
}, [value, lineHeight, translateY, animate, width, targetWidth]);
|
|
111
|
+
|
|
112
|
+
const animatedStyle = useAnimatedStyle(
|
|
113
|
+
() => ({
|
|
114
|
+
transform: [{ translateY: translateY.value }],
|
|
115
|
+
}),
|
|
116
|
+
[translateY],
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<Animated.View
|
|
121
|
+
style={{ height: lineHeight, overflow: 'hidden', width: width }}
|
|
122
|
+
accessibilityValue={{ text: String(value) }}
|
|
123
|
+
>
|
|
124
|
+
<Animated.View style={[animatedStyle, { alignItems: 'center' }]}>
|
|
125
|
+
{DIGITS.map((d) => (
|
|
126
|
+
<Text key={d} style={textStyle}>
|
|
127
|
+
{d}
|
|
128
|
+
</Text>
|
|
129
|
+
))}
|
|
130
|
+
</Animated.View>
|
|
131
|
+
</Animated.View>
|
|
132
|
+
);
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const DigitStripList = memo(
|
|
137
|
+
({ items, textStyle, animate, type }: DigitStripListProps) => {
|
|
138
|
+
return items.map((item, index) => {
|
|
139
|
+
const key = items.length - index;
|
|
140
|
+
if (item.type === 'separator') {
|
|
141
|
+
return (
|
|
142
|
+
<Text key={key} style={textStyle}>
|
|
143
|
+
{item.value}
|
|
144
|
+
</Text>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
return (
|
|
148
|
+
<DigitStrip
|
|
149
|
+
key={key}
|
|
150
|
+
value={Number(item.value) as DigitStripProps['value']}
|
|
151
|
+
animate={animate}
|
|
152
|
+
textStyle={textStyle}
|
|
153
|
+
type={type}
|
|
154
|
+
/>
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
|
|
41
160
|
/**
|
|
42
161
|
* AmountDisplay - Renders formatted monetary amounts with flexible currency positioning and decimal formatting.
|
|
43
162
|
*
|
|
@@ -76,34 +195,66 @@ export const AmountDisplay = ({
|
|
|
76
195
|
formatter,
|
|
77
196
|
hidden = false,
|
|
78
197
|
loading = false,
|
|
198
|
+
animate = true,
|
|
79
199
|
...props
|
|
80
200
|
}: AmountDisplayProps) => {
|
|
81
201
|
const styles = useStyles();
|
|
202
|
+
const { t } = useCommonTranslation();
|
|
82
203
|
const parts = formatter(value);
|
|
204
|
+
const splitDigits = useSplitText(parts);
|
|
205
|
+
const ariaLabel = buildAriaLabel(
|
|
206
|
+
parts,
|
|
207
|
+
hidden,
|
|
208
|
+
t('components.amountDisplay.amountHiddenAriaLabel'),
|
|
209
|
+
);
|
|
83
210
|
|
|
84
211
|
return (
|
|
85
|
-
<Box
|
|
212
|
+
<Box
|
|
213
|
+
accessibilityLabel={ariaLabel}
|
|
214
|
+
accessibilityState={{ busy: loading }}
|
|
215
|
+
{...props}
|
|
216
|
+
>
|
|
86
217
|
<Pulse animate={loading}>
|
|
87
|
-
<View
|
|
88
|
-
{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
{
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
218
|
+
<View
|
|
219
|
+
style={styles.container}
|
|
220
|
+
accessibilityElementsHidden
|
|
221
|
+
importantForAccessibility='no-hide-descendants'
|
|
222
|
+
>
|
|
223
|
+
<View style={styles.integerPartContainer}>
|
|
224
|
+
{parts.currencyPosition === 'start' && (
|
|
225
|
+
<Text style={[styles.currencyStartText, styles.spacingStart]}>
|
|
226
|
+
{parts.currencyText}
|
|
227
|
+
</Text>
|
|
228
|
+
)}
|
|
229
|
+
{hidden ? (
|
|
230
|
+
<Text style={styles.integerText}>••••</Text>
|
|
231
|
+
) : (
|
|
232
|
+
<DigitStripList
|
|
233
|
+
items={splitDigits.integerPart}
|
|
234
|
+
textStyle={styles.integerText}
|
|
235
|
+
animate={animate}
|
|
236
|
+
type='integer'
|
|
237
|
+
/>
|
|
238
|
+
)}
|
|
239
|
+
</View>
|
|
240
|
+
<View style={styles.decimalPartContainer}>
|
|
241
|
+
{!hidden && parts.decimalPart && (
|
|
242
|
+
<Text style={styles.decimalText}>{parts.decimalSeparator}</Text>
|
|
243
|
+
)}
|
|
244
|
+
{parts.decimalPart && !hidden && (
|
|
245
|
+
<DigitStripList
|
|
246
|
+
items={splitDigits.decimalPart}
|
|
247
|
+
textStyle={styles.decimalText}
|
|
248
|
+
animate={animate}
|
|
249
|
+
type='decimal'
|
|
250
|
+
/>
|
|
251
|
+
)}
|
|
252
|
+
{parts.currencyPosition === 'end' && (
|
|
253
|
+
<Text style={[styles.currencyEndText, styles.spacingEnd]}>
|
|
254
|
+
{parts.currencyText}
|
|
255
|
+
</Text>
|
|
256
|
+
)}
|
|
257
|
+
</View>
|
|
107
258
|
</View>
|
|
108
259
|
</Pulse>
|
|
109
260
|
</Box>
|
|
@@ -1,32 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FormattedValue, SplitChar } from '@ledgerhq/lumen-utils-shared';
|
|
2
|
+
import { ViewProps, TextStyle } from 'react-native';
|
|
2
3
|
import { StyledViewProps } from '../../../styles';
|
|
3
4
|
|
|
4
|
-
export type FormattedValue
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* The currency text or symbol (e.g., "$", "USD", "€", "BTC")
|
|
16
|
-
*/
|
|
17
|
-
currencyText: string;
|
|
18
|
-
/**
|
|
19
|
-
* The character which separates integer and fractional parts.
|
|
20
|
-
*/
|
|
21
|
-
decimalSeparator: '.' | ',';
|
|
22
|
-
/**
|
|
23
|
-
* Position of the currency text relative to the amount.
|
|
24
|
-
* @optional
|
|
25
|
-
* @default 'start'
|
|
26
|
-
*/
|
|
27
|
-
currencyPosition?: 'start' | 'end';
|
|
5
|
+
export type { FormattedValue };
|
|
6
|
+
|
|
7
|
+
export const DIGITS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] as const;
|
|
8
|
+
|
|
9
|
+
type IntegerDigit = (typeof DIGITS)[number];
|
|
10
|
+
|
|
11
|
+
export type DigitStripProps = {
|
|
12
|
+
value: IntegerDigit;
|
|
13
|
+
animate: boolean;
|
|
14
|
+
textStyle: TextStyle & { lineHeight: number };
|
|
15
|
+
type: 'integer' | 'decimal';
|
|
28
16
|
};
|
|
29
17
|
|
|
18
|
+
export type DigitStripListProps = {
|
|
19
|
+
items: SplitChar[];
|
|
20
|
+
} & Omit<DigitStripProps, 'value'>;
|
|
21
|
+
|
|
30
22
|
/**
|
|
31
23
|
* Props for the AmountDisplay component.
|
|
32
24
|
*/
|
|
@@ -51,4 +43,9 @@ export type AmountDisplayProps = ViewProps & {
|
|
|
51
43
|
* @default false
|
|
52
44
|
*/
|
|
53
45
|
loading?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Whether the odometer animation should play on value change or not
|
|
48
|
+
* @default true
|
|
49
|
+
*/
|
|
50
|
+
animate?: boolean;
|
|
54
51
|
} & Omit<StyledViewProps, 'children'>;
|