@ledgerhq/lumen-ui-rnative 0.1.2 → 0.1.4
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.d.ts.map +1 -1
- package/dist/src/lib/Components/TabBar/TabBar.js +10 -4
- 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/package.json +1 -1
- 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 +28 -12
- package/src/lib/Components/TabBar/types.ts +0 -1
- package/src/lib/Components/index.ts +1 -0
- 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
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/lumen-ui-rnative",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"./styles": "./src/styles/index.ts"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@ledgerhq/lumen-utils-shared": "0.1.
|
|
30
|
+
"@ledgerhq/lumen-utils-shared": "0.1.1",
|
|
31
31
|
"i18next": "^23.7.0",
|
|
32
32
|
"react-i18next": "^14.0.0"
|
|
33
33
|
},
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@types/react": "^19.0.0",
|
|
39
39
|
"@gorhom/bottom-sheet": "^5.0.0",
|
|
40
|
-
"@ledgerhq/lumen-design-core": "0.1.
|
|
40
|
+
"@ledgerhq/lumen-design-core": "0.1.1",
|
|
41
41
|
"react": "^19.0.0",
|
|
42
42
|
"react-native": "~0.79.7",
|
|
43
43
|
"react-native-reanimated": "^3.0.0",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare const durations: {
|
|
2
|
+
readonly '0': 0;
|
|
3
|
+
readonly '75': 75;
|
|
4
|
+
readonly '100': 100;
|
|
5
|
+
readonly '120': 120;
|
|
6
|
+
readonly '150': 150;
|
|
7
|
+
readonly '200': 200;
|
|
8
|
+
readonly '250': 250;
|
|
9
|
+
readonly '300': 300;
|
|
10
|
+
readonly '500': 500;
|
|
11
|
+
readonly '700': 700;
|
|
12
|
+
readonly '1000': 1000;
|
|
13
|
+
readonly '2000': 2000;
|
|
14
|
+
readonly '3000': 3000;
|
|
15
|
+
};
|
|
16
|
+
export type DurationKey = keyof typeof durations;
|
|
17
|
+
export declare const easingCurves: {
|
|
18
|
+
readonly bezier: {
|
|
19
|
+
readonly default: import("react-native-reanimated").EasingFunctionFactory;
|
|
20
|
+
readonly emphasize: import("react-native-reanimated").EasingFunctionFactory;
|
|
21
|
+
readonly out: import("react-native-reanimated").EasingFunctionFactory;
|
|
22
|
+
readonly in: import("react-native-reanimated").EasingFunctionFactory;
|
|
23
|
+
};
|
|
24
|
+
readonly quad: (t: number) => number;
|
|
25
|
+
readonly ease: (t: number) => number;
|
|
26
|
+
readonly linear: (t: number) => number;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/lib/Animations/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,SAAS;;;;;;;;;;;;;;CAcZ,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,SAAS,CAAC;AAEjD,eAAO,MAAM,YAAY;;;;;;;;;;CAUf,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Easing } from 'react-native-reanimated';
|
|
2
|
+
export const durations = {
|
|
3
|
+
'0': 0,
|
|
4
|
+
'75': 75,
|
|
5
|
+
'100': 100,
|
|
6
|
+
'120': 120,
|
|
7
|
+
'150': 150,
|
|
8
|
+
'200': 200,
|
|
9
|
+
'250': 250,
|
|
10
|
+
'300': 300,
|
|
11
|
+
'500': 500,
|
|
12
|
+
'700': 700,
|
|
13
|
+
'1000': 1000,
|
|
14
|
+
'2000': 2000,
|
|
15
|
+
'3000': 3000,
|
|
16
|
+
};
|
|
17
|
+
export const easingCurves = {
|
|
18
|
+
bezier: {
|
|
19
|
+
default: Easing.bezier(0.4, 0, 0.2, 1),
|
|
20
|
+
emphasize: Easing.bezier(0.05, 0.7, 0.1, 1),
|
|
21
|
+
out: Easing.bezier(0, 0, 0.2, 1),
|
|
22
|
+
in: Easing.bezier(0.4, 0, 1, 1),
|
|
23
|
+
},
|
|
24
|
+
quad: Easing.quad,
|
|
25
|
+
ease: Easing.ease,
|
|
26
|
+
linear: Easing.linear,
|
|
27
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/Animations/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/Animations/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC"}
|
|
@@ -33,7 +33,7 @@ import { AmountDisplayProps } from './types';
|
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
35
|
export declare const AmountDisplay: {
|
|
36
|
-
({ value, formatter, hidden, loading, ...props }: AmountDisplayProps): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
({ value, formatter, hidden, loading, animate, ...props }: AmountDisplayProps): import("react/jsx-runtime").JSX.Element;
|
|
37
37
|
displayName: string;
|
|
38
38
|
};
|
|
39
39
|
//# sourceMappingURL=AmountDisplay.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AmountDisplay.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Components/AmountDisplay/AmountDisplay.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AmountDisplay.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Components/AmountDisplay/AmountDisplay.tsx"],"names":[],"mappings":"AAaA,OAAO,EACL,kBAAkB,EAInB,MAAM,SAAS,CAAC;AA6IjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,aAAa;+DAOvB,kBAAkB;;CA8DpB,CAAC"}
|
|
@@ -1,13 +1,53 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useSplitText, buildAriaLabel } from '@ledgerhq/lumen-utils-shared';
|
|
3
|
+
import { memo, useEffect } from 'react';
|
|
4
|
+
import { Text, View } from 'react-native';
|
|
5
|
+
import Animated, { Easing, useAnimatedStyle, useSharedValue, withTiming, } from 'react-native-reanimated';
|
|
6
|
+
import { useCommonTranslation } from '../../../i18n';
|
|
3
7
|
import { useStyleSheet } from '../../../styles';
|
|
4
8
|
import { Pulse } from '../../Animations/Pulse';
|
|
5
9
|
import { Box } from '../Utility';
|
|
10
|
+
import { DIGITS, } from './types';
|
|
11
|
+
const INTEGER_DIGIT_WIDTHS = {
|
|
12
|
+
0: 24.5,
|
|
13
|
+
1: 15,
|
|
14
|
+
2: 23,
|
|
15
|
+
3: 24,
|
|
16
|
+
4: 25,
|
|
17
|
+
5: 23,
|
|
18
|
+
6: 24.5,
|
|
19
|
+
7: 21.5,
|
|
20
|
+
8: 24,
|
|
21
|
+
9: 24,
|
|
22
|
+
};
|
|
23
|
+
const DECIMAL_DIGIT_WIDTHS = {
|
|
24
|
+
0: 17,
|
|
25
|
+
1: 10.5,
|
|
26
|
+
2: 16,
|
|
27
|
+
3: 16.5,
|
|
28
|
+
4: 17.2,
|
|
29
|
+
5: 15.7,
|
|
30
|
+
6: 17,
|
|
31
|
+
7: 14.7,
|
|
32
|
+
8: 16.5,
|
|
33
|
+
9: 16.5,
|
|
34
|
+
};
|
|
35
|
+
const TIMING_CONFIG = {
|
|
36
|
+
duration: 600,
|
|
37
|
+
easing: Easing.inOut(Easing.ease),
|
|
38
|
+
};
|
|
6
39
|
const useStyles = () => {
|
|
7
40
|
return useStyleSheet((t) => ({
|
|
8
41
|
container: {
|
|
9
42
|
flexDirection: 'row',
|
|
10
|
-
alignItems: '
|
|
43
|
+
alignItems: 'flex-end',
|
|
44
|
+
},
|
|
45
|
+
integerPartContainer: {
|
|
46
|
+
flexDirection: 'row',
|
|
47
|
+
},
|
|
48
|
+
decimalPartContainer: {
|
|
49
|
+
flexDirection: 'row',
|
|
50
|
+
paddingBottom: t.spacings.s2,
|
|
11
51
|
},
|
|
12
52
|
integerText: {
|
|
13
53
|
...t.typographies.heading1SemiBold,
|
|
@@ -33,6 +73,35 @@ const useStyles = () => {
|
|
|
33
73
|
},
|
|
34
74
|
}), []);
|
|
35
75
|
};
|
|
76
|
+
const DigitStrip = memo(({ value, textStyle, animate, type }) => {
|
|
77
|
+
const targetWidth = (type === 'integer' ? INTEGER_DIGIT_WIDTHS : DECIMAL_DIGIT_WIDTHS)[value];
|
|
78
|
+
const lineHeight = textStyle.lineHeight;
|
|
79
|
+
const translateY = useSharedValue(-value * lineHeight);
|
|
80
|
+
const width = useSharedValue(targetWidth);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (animate) {
|
|
83
|
+
translateY.value = withTiming(-value * lineHeight, TIMING_CONFIG);
|
|
84
|
+
width.value = withTiming(targetWidth, TIMING_CONFIG);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
translateY.value = -value * lineHeight;
|
|
88
|
+
width.value = targetWidth;
|
|
89
|
+
}
|
|
90
|
+
}, [value, lineHeight, translateY, animate, width, targetWidth]);
|
|
91
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
92
|
+
transform: [{ translateY: translateY.value }],
|
|
93
|
+
}), [translateY]);
|
|
94
|
+
return (_jsx(Animated.View, { style: { height: lineHeight, overflow: 'hidden', width: width }, accessibilityValue: { text: String(value) }, children: _jsx(Animated.View, { style: [animatedStyle, { alignItems: 'center' }], children: DIGITS.map((d) => (_jsx(Text, { style: textStyle, children: d }, d))) }) }));
|
|
95
|
+
});
|
|
96
|
+
const DigitStripList = memo(({ items, textStyle, animate, type }) => {
|
|
97
|
+
return items.map((item, index) => {
|
|
98
|
+
const key = items.length - index;
|
|
99
|
+
if (item.type === 'separator') {
|
|
100
|
+
return (_jsx(Text, { style: textStyle, children: item.value }, key));
|
|
101
|
+
}
|
|
102
|
+
return (_jsx(DigitStrip, { value: Number(item.value), animate: animate, textStyle: textStyle, type: type }, key));
|
|
103
|
+
});
|
|
104
|
+
});
|
|
36
105
|
/**
|
|
37
106
|
* AmountDisplay - Renders formatted monetary amounts with flexible currency positioning and decimal formatting.
|
|
38
107
|
*
|
|
@@ -66,10 +135,12 @@ const useStyles = () => {
|
|
|
66
135
|
* <AmountDisplay value={1234.56} formatter={usdFormatter} hidden={true} />
|
|
67
136
|
* ```
|
|
68
137
|
*/
|
|
69
|
-
export const AmountDisplay = ({ value, formatter, hidden = false, loading = false, ...props }) => {
|
|
138
|
+
export const AmountDisplay = ({ value, formatter, hidden = false, loading = false, animate = true, ...props }) => {
|
|
70
139
|
const styles = useStyles();
|
|
140
|
+
const { t } = useCommonTranslation();
|
|
71
141
|
const parts = formatter(value);
|
|
72
|
-
|
|
73
|
-
|
|
142
|
+
const splitDigits = useSplitText(parts);
|
|
143
|
+
const ariaLabel = buildAriaLabel(parts, hidden, t('components.amountDisplay.amountHiddenAriaLabel'));
|
|
144
|
+
return (_jsx(Box, { accessibilityLabel: ariaLabel, accessibilityState: { busy: loading }, ...props, children: _jsx(Pulse, { animate: loading, children: _jsxs(View, { style: styles.container, accessibilityElementsHidden: true, importantForAccessibility: 'no-hide-descendants', children: [_jsxs(View, { style: styles.integerPartContainer, children: [parts.currencyPosition === 'start' && (_jsx(Text, { style: [styles.currencyStartText, styles.spacingStart], children: parts.currencyText })), hidden ? (_jsx(Text, { style: styles.integerText, children: "\u2022\u2022\u2022\u2022" })) : (_jsx(DigitStripList, { items: splitDigits.integerPart, textStyle: styles.integerText, animate: animate, type: 'integer' }))] }), _jsxs(View, { style: styles.decimalPartContainer, children: [!hidden && parts.decimalPart && (_jsx(Text, { style: styles.decimalText, children: parts.decimalSeparator })), parts.decimalPart && !hidden && (_jsx(DigitStripList, { items: splitDigits.decimalPart, textStyle: styles.decimalText, animate: animate, type: 'decimal' })), parts.currencyPosition === 'end' && (_jsx(Text, { style: [styles.currencyEndText, styles.spacingEnd], children: parts.currencyText }))] })] }) }) }));
|
|
74
145
|
};
|
|
75
146
|
AmountDisplay.displayName = 'AmountDisplay';
|
|
@@ -5,5 +5,6 @@ export default meta;
|
|
|
5
5
|
type Story = StoryObj<typeof AmountDisplay>;
|
|
6
6
|
export declare const Base: Story;
|
|
7
7
|
export declare const WithHideButton: Story;
|
|
8
|
+
export declare const AnimationShowcase: Story;
|
|
8
9
|
export declare const Loading: Story;
|
|
9
10
|
//# sourceMappingURL=AmountDisplay.stories.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AmountDisplay.stories.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Components/AmountDisplay/AmountDisplay.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAKvE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAuChD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,aAAa,
|
|
1
|
+
{"version":3,"file":"AmountDisplay.stories.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Components/AmountDisplay/AmountDisplay.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAKvE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAuChD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,aAAa,CAiDpC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,aAAa,CAAC,CAAC;AAE5C,eAAO,MAAM,IAAI,EAAE,KAWlB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAqB5B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAoB/B,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAUrB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
3
|
import { View } from 'react-native';
|
|
4
4
|
import { Eye, EyeCross } from '../../Symbols';
|
|
5
5
|
import { IconButton } from '../IconButton';
|
|
@@ -40,6 +40,7 @@ const meta = {
|
|
|
40
40
|
args: {
|
|
41
41
|
formatter: eurFormatter,
|
|
42
42
|
hidden: false,
|
|
43
|
+
animate: true,
|
|
43
44
|
},
|
|
44
45
|
argTypes: {
|
|
45
46
|
formatter: {
|
|
@@ -63,7 +64,11 @@ const meta = {
|
|
|
63
64
|
control: {
|
|
64
65
|
type: 'boolean',
|
|
65
66
|
},
|
|
66
|
-
|
|
67
|
+
},
|
|
68
|
+
animate: {
|
|
69
|
+
control: {
|
|
70
|
+
type: 'boolean',
|
|
71
|
+
},
|
|
67
72
|
},
|
|
68
73
|
},
|
|
69
74
|
parameters: {
|
|
@@ -97,6 +102,24 @@ export const WithHideButton = {
|
|
|
97
102
|
return (_jsxs(View, { style: { flexDirection: 'row', alignItems: 'center', gap: 12 }, children: [_jsx(AmountDisplay, { formatter: props.formatter, value: 1234.56, hidden: hidden }), _jsx(IconButton, { appearance: 'transparent', size: 'sm', icon: hidden ? EyeCross : Eye, accessibilityLabel: hidden ? 'Show amount' : 'Hide amount', onPress: () => setHidden((v) => !v) })] }));
|
|
98
103
|
},
|
|
99
104
|
};
|
|
105
|
+
export const AnimationShowcase = {
|
|
106
|
+
args: {
|
|
107
|
+
value: 1234.56,
|
|
108
|
+
},
|
|
109
|
+
render: (props) => {
|
|
110
|
+
const [currentValue, setCurrentValue] = useState(props.value);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
const interval = setInterval(() => {
|
|
113
|
+
setCurrentValue((prev) => {
|
|
114
|
+
const delta = prev * (Math.random() * 0.02 - 0.01);
|
|
115
|
+
return Math.round((prev + delta) * 100) / 100;
|
|
116
|
+
});
|
|
117
|
+
}, 2000);
|
|
118
|
+
return () => clearInterval(interval);
|
|
119
|
+
}, []);
|
|
120
|
+
return _jsx(AmountDisplay, { ...props, value: currentValue });
|
|
121
|
+
},
|
|
122
|
+
};
|
|
100
123
|
export const Loading = {
|
|
101
124
|
render: (props) => {
|
|
102
125
|
return (_jsx(AmountDisplay, { formatter: props.formatter, value: 1234.56, loading: true }));
|
|
@@ -1,30 +1,20 @@
|
|
|
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
|
-
export type FormattedValue
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* The currency text or symbol (e.g., "$", "USD", "€", "BTC")
|
|
15
|
-
*/
|
|
16
|
-
currencyText: string;
|
|
17
|
-
/**
|
|
18
|
-
* The character which separates integer and fractional parts.
|
|
19
|
-
*/
|
|
20
|
-
decimalSeparator: '.' | ',';
|
|
21
|
-
/**
|
|
22
|
-
* Position of the currency text relative to the amount.
|
|
23
|
-
* @optional
|
|
24
|
-
* @default 'start'
|
|
25
|
-
*/
|
|
26
|
-
currencyPosition?: 'start' | 'end';
|
|
4
|
+
export type { FormattedValue };
|
|
5
|
+
export declare const DIGITS: readonly [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
6
|
+
type IntegerDigit = (typeof DIGITS)[number];
|
|
7
|
+
export type DigitStripProps = {
|
|
8
|
+
value: IntegerDigit;
|
|
9
|
+
animate: boolean;
|
|
10
|
+
textStyle: TextStyle & {
|
|
11
|
+
lineHeight: number;
|
|
12
|
+
};
|
|
13
|
+
type: 'integer' | 'decimal';
|
|
27
14
|
};
|
|
15
|
+
export type DigitStripListProps = {
|
|
16
|
+
items: SplitChar[];
|
|
17
|
+
} & Omit<DigitStripProps, 'value'>;
|
|
28
18
|
/**
|
|
29
19
|
* Props for the AmountDisplay component.
|
|
30
20
|
*/
|
|
@@ -49,5 +39,10 @@ export type AmountDisplayProps = ViewProps & {
|
|
|
49
39
|
* @default false
|
|
50
40
|
*/
|
|
51
41
|
loading?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Whether the odometer animation should play on value change or not
|
|
44
|
+
* @default true
|
|
45
|
+
*/
|
|
46
|
+
animate?: boolean;
|
|
52
47
|
} & Omit<StyledViewProps, 'children'>;
|
|
53
48
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Components/AmountDisplay/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Components/AmountDisplay/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B,eAAO,MAAM,MAAM,yCAA0C,CAAC;AAE9D,KAAK,YAAY,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5C,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,SAAS,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB,GAAG,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;AAEnC;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG;IAC3C;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,cAAc,CAAC;IAC7C;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,IAAI,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const DIGITS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SegmentedControlButtonProps, SegmentedControlProps } from './types';
|
|
2
|
+
export declare function SegmentedControlButton({ value, children, icon: Icon, onPress, ...props }: SegmentedControlButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare namespace SegmentedControlButton {
|
|
4
|
+
var displayName: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function SegmentedControl({ selectedValue, onSelectedChange, accessibilityLabel, children, ...props }: SegmentedControlProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare namespace SegmentedControl {
|
|
8
|
+
var displayName: string;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=SegmentedControl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SegmentedControl.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Components/SegmentedControl/SegmentedControl.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,2BAA2B,EAC3B,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAIjB,wBAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,QAAQ,EACR,IAAI,EAAE,IAAI,EACV,OAAO,EACP,GAAG,KAAK,EACT,EAAE,2BAA2B,2CAkC7B;yBAxCe,sBAAsB;;;AA4EtC,wBAAgB,gBAAgB,CAAC,EAC/B,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,2CAwEvB;yBA9Ee,gBAAgB"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useCallback, useEffect, useRef } from 'react';
|
|
3
|
+
import Animated, { useAnimatedStyle, useSharedValue, withTiming, } from 'react-native-reanimated';
|
|
4
|
+
import { useStyleSheet } from '../../../styles';
|
|
5
|
+
import { durations, easingCurves } from '../../Animations/constants';
|
|
6
|
+
import { Box, Pressable, Text } from '../Utility';
|
|
7
|
+
import { SegmentedControlContextProvider, useSegmentedControlContext, } from './SegmentedControlContext';
|
|
8
|
+
const ICON_SIZE = 16;
|
|
9
|
+
export function SegmentedControlButton({ value, children, icon: Icon, onPress, ...props }) {
|
|
10
|
+
const styles = useButtonStyles();
|
|
11
|
+
const { selectedValue, onSelectedChange } = useSegmentedControlContext();
|
|
12
|
+
const selected = selectedValue === value;
|
|
13
|
+
function handlePress() {
|
|
14
|
+
onSelectedChange(value);
|
|
15
|
+
onPress?.();
|
|
16
|
+
}
|
|
17
|
+
return (_jsx(Pressable, { onPress: handlePress, accessibilityState: { selected }, style: styles.button, ...props, children: _jsxs(Box, { style: styles.content, children: [Icon && (_jsx(Box, { style: styles.iconWrap, children: _jsx(Icon, { size: ICON_SIZE }) })), _jsx(Text, { typography: selected ? 'body2SemiBold' : 'body2', lx: { color: 'base' }, style: styles.label, children: children })] }) }));
|
|
18
|
+
}
|
|
19
|
+
SegmentedControlButton.displayName = 'SegmentedControlButton';
|
|
20
|
+
function useButtonStyles() {
|
|
21
|
+
return useStyleSheet((t) => ({
|
|
22
|
+
button: {
|
|
23
|
+
flex: 1,
|
|
24
|
+
flexDirection: 'row',
|
|
25
|
+
alignItems: 'center',
|
|
26
|
+
justifyContent: 'center',
|
|
27
|
+
paddingHorizontal: t.spacings.s16,
|
|
28
|
+
paddingVertical: t.spacings.s8,
|
|
29
|
+
borderRadius: t.borderRadius.full,
|
|
30
|
+
zIndex: 1,
|
|
31
|
+
},
|
|
32
|
+
content: {
|
|
33
|
+
flexDirection: 'row',
|
|
34
|
+
alignItems: 'center',
|
|
35
|
+
justifyContent: 'center',
|
|
36
|
+
gap: t.spacings.s8,
|
|
37
|
+
},
|
|
38
|
+
label: {
|
|
39
|
+
textAlign: 'center',
|
|
40
|
+
includeFontPadding: false,
|
|
41
|
+
},
|
|
42
|
+
iconWrap: {
|
|
43
|
+
flexDirection: 'row',
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
},
|
|
46
|
+
}), []);
|
|
47
|
+
}
|
|
48
|
+
export function SegmentedControl({ selectedValue, onSelectedChange, accessibilityLabel, children, ...props }) {
|
|
49
|
+
const styles = useRootStyles();
|
|
50
|
+
const pillTranslateX = useSharedValue(0);
|
|
51
|
+
const pillWidth = useSharedValue(0);
|
|
52
|
+
const pillHeight = useSharedValue(0);
|
|
53
|
+
const hasLayoutRef = useRef(false);
|
|
54
|
+
const getSelectedIndex = useCallback(() => {
|
|
55
|
+
return React.Children.toArray(children).findIndex((child) => {
|
|
56
|
+
if (React.isValidElement(child) && child.props != null) {
|
|
57
|
+
return child.props.value === selectedValue;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
});
|
|
61
|
+
}, [selectedValue, children]);
|
|
62
|
+
function onLayout(e) {
|
|
63
|
+
const { width, height } = e.nativeEvent.layout;
|
|
64
|
+
const count = React.Children.count(children);
|
|
65
|
+
const slotWidth = count > 0 ? width / count : 0;
|
|
66
|
+
pillWidth.value = slotWidth;
|
|
67
|
+
pillHeight.value = height;
|
|
68
|
+
if (!hasLayoutRef.current) {
|
|
69
|
+
hasLayoutRef.current = true;
|
|
70
|
+
const index = getSelectedIndex();
|
|
71
|
+
if (index >= 0) {
|
|
72
|
+
pillTranslateX.value = index * slotWidth;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (!hasLayoutRef.current)
|
|
78
|
+
return;
|
|
79
|
+
const index = getSelectedIndex();
|
|
80
|
+
if (index >= 0 && pillWidth.value > 0) {
|
|
81
|
+
pillTranslateX.value = withTiming(index * pillWidth.value, {
|
|
82
|
+
duration: durations['250'],
|
|
83
|
+
easing: easingCurves.bezier.default,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}, [pillWidth, pillTranslateX, getSelectedIndex]);
|
|
87
|
+
const animatedPillStyle = useAnimatedStyle(() => ({
|
|
88
|
+
transform: [{ translateX: pillTranslateX.value }],
|
|
89
|
+
width: pillWidth.value,
|
|
90
|
+
height: pillHeight.value,
|
|
91
|
+
}), [pillTranslateX, pillWidth, pillHeight]);
|
|
92
|
+
return (_jsx(SegmentedControlContextProvider, { value: { selectedValue, onSelectedChange }, children: _jsxs(Box, { accessibilityRole: 'radiogroup', accessibilityLabel: accessibilityLabel, onLayout: onLayout, style: styles.container, ...props, children: [children, _jsx(Animated.View, { style: [styles.pill, animatedPillStyle], pointerEvents: 'none' })] }) }));
|
|
93
|
+
}
|
|
94
|
+
SegmentedControl.displayName = 'SegmentedControl';
|
|
95
|
+
function useRootStyles() {
|
|
96
|
+
return useStyleSheet((t) => ({
|
|
97
|
+
container: {
|
|
98
|
+
flexDirection: 'row',
|
|
99
|
+
alignItems: 'center',
|
|
100
|
+
position: 'relative',
|
|
101
|
+
width: '100%',
|
|
102
|
+
borderRadius: t.borderRadius.full,
|
|
103
|
+
backgroundColor: t.colors.bg.baseTransparent,
|
|
104
|
+
},
|
|
105
|
+
pill: {
|
|
106
|
+
position: 'absolute',
|
|
107
|
+
top: 0,
|
|
108
|
+
left: 0,
|
|
109
|
+
borderRadius: t.borderRadius.sm,
|
|
110
|
+
backgroundColor: t.colors.bg.muted,
|
|
111
|
+
zIndex: 0,
|
|
112
|
+
},
|
|
113
|
+
}), []);
|
|
114
|
+
}
|