@ledgerhq/lumen-ui-rnative 0.1.4 → 0.1.6

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 (147) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/lib/Components/AmountDisplay/AmountDisplay.d.ts.map +1 -1
  3. package/dist/src/lib/Components/AmountDisplay/AmountDisplay.js +5 -1
  4. package/dist/src/lib/Components/SegmentedControl/SegmentedControl.d.ts +1 -1
  5. package/dist/src/lib/Components/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  6. package/dist/src/lib/Components/SegmentedControl/SegmentedControl.js +34 -61
  7. package/dist/src/lib/Components/SegmentedControl/SegmentedControlContext.d.ts +1 -0
  8. package/dist/src/lib/Components/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  9. package/dist/src/lib/Components/SegmentedControl/types.d.ts +12 -2
  10. package/dist/src/lib/Components/SegmentedControl/types.d.ts.map +1 -1
  11. package/dist/src/lib/Components/SegmentedControl/usePillLayout.d.ts +19 -0
  12. package/dist/src/lib/Components/SegmentedControl/usePillLayout.d.ts.map +1 -0
  13. package/dist/src/lib/Components/SegmentedControl/usePillLayout.js +46 -0
  14. package/dist/src/lib/Components/TileButton/TileButton.js +2 -2
  15. package/jest.config.ts +0 -1
  16. package/package.json +1 -1
  17. package/src/lib/Components/AmountDisplay/AmountDisplay.tsx +8 -1
  18. package/src/lib/Components/SegmentedControl/SegmentedControl.mdx +1 -38
  19. package/src/lib/Components/SegmentedControl/SegmentedControl.stories.tsx +35 -19
  20. package/src/lib/Components/SegmentedControl/SegmentedControl.tsx +61 -77
  21. package/src/lib/Components/SegmentedControl/SegmentedControlContext.tsx +1 -0
  22. package/src/lib/Components/SegmentedControl/types.ts +12 -2
  23. package/src/lib/Components/SegmentedControl/usePillLayout.ts +76 -0
  24. package/src/lib/Components/TileButton/TileButton.tsx +2 -2
  25. package/src/lib/Components/Utility/Gradient/LinearGradient/LinearGradient.stories.tsx +1 -1
  26. package/tsconfig.json +3 -16
  27. package/tsconfig.lib.json +5 -0
  28. package/dist/src/lib/Animations/Pulse/Pulse.stories.d.ts +0 -9
  29. package/dist/src/lib/Animations/Pulse/Pulse.stories.d.ts.map +0 -1
  30. package/dist/src/lib/Animations/Pulse/Pulse.stories.js +0 -38
  31. package/dist/src/lib/Animations/Spin/Spin.stories.d.ts +0 -9
  32. package/dist/src/lib/Animations/Spin/Spin.stories.d.ts.map +0 -1
  33. package/dist/src/lib/Animations/Spin/Spin.stories.js +0 -27
  34. package/dist/src/lib/Components/AddressInput/AddressInput.stories.d.ts +0 -13
  35. package/dist/src/lib/Components/AddressInput/AddressInput.stories.d.ts.map +0 -1
  36. package/dist/src/lib/Components/AddressInput/AddressInput.stories.js +0 -128
  37. package/dist/src/lib/Components/AmountDisplay/AmountDisplay.stories.d.ts +0 -10
  38. package/dist/src/lib/Components/AmountDisplay/AmountDisplay.stories.d.ts.map +0 -1
  39. package/dist/src/lib/Components/AmountDisplay/AmountDisplay.stories.js +0 -127
  40. package/dist/src/lib/Components/AmountInput/AmountInput.stories.d.ts +0 -16
  41. package/dist/src/lib/Components/AmountInput/AmountInput.stories.d.ts.map +0 -1
  42. package/dist/src/lib/Components/AmountInput/AmountInput.stories.js +0 -186
  43. package/dist/src/lib/Components/Avatar/Avatar.stories.d.ts +0 -22
  44. package/dist/src/lib/Components/Avatar/Avatar.stories.d.ts.map +0 -1
  45. package/dist/src/lib/Components/Avatar/Avatar.stories.js +0 -72
  46. package/dist/src/lib/Components/Banner/Banner.stories.d.ts +0 -16
  47. package/dist/src/lib/Components/Banner/Banner.stories.d.ts.map +0 -1
  48. package/dist/src/lib/Components/Banner/Banner.stories.js +0 -268
  49. package/dist/src/lib/Components/BottomSheet/BottomSheet.stories.d.ts +0 -87
  50. package/dist/src/lib/Components/BottomSheet/BottomSheet.stories.d.ts.map +0 -1
  51. package/dist/src/lib/Components/BottomSheet/BottomSheet.stories.js +0 -266
  52. package/dist/src/lib/Components/Button/Button.stories.d.ts +0 -16
  53. package/dist/src/lib/Components/Button/Button.stories.d.ts.map +0 -1
  54. package/dist/src/lib/Components/Button/Button.stories.js +0 -143
  55. package/dist/src/lib/Components/CardButton/CardButton.stories.d.ts +0 -16
  56. package/dist/src/lib/Components/CardButton/CardButton.stories.d.ts.map +0 -1
  57. package/dist/src/lib/Components/CardButton/CardButton.stories.js +0 -208
  58. package/dist/src/lib/Components/Checkbox/Checkbox.stories.d.ts +0 -14
  59. package/dist/src/lib/Components/Checkbox/Checkbox.stories.d.ts.map +0 -1
  60. package/dist/src/lib/Components/Checkbox/Checkbox.stories.js +0 -72
  61. package/dist/src/lib/Components/ContentBanner/ContentBanner.stories.d.ts +0 -11
  62. package/dist/src/lib/Components/ContentBanner/ContentBanner.stories.d.ts.map +0 -1
  63. package/dist/src/lib/Components/ContentBanner/ContentBanner.stories.js +0 -91
  64. package/dist/src/lib/Components/Divider/Divider.stories.d.ts +0 -9
  65. package/dist/src/lib/Components/Divider/Divider.stories.d.ts.map +0 -1
  66. package/dist/src/lib/Components/Divider/Divider.stories.js +0 -51
  67. package/dist/src/lib/Components/Icon/Icon.stories.d.ts +0 -15
  68. package/dist/src/lib/Components/Icon/Icon.stories.d.ts.map +0 -1
  69. package/dist/src/lib/Components/Icon/Icon.stories.js +0 -137
  70. package/dist/src/lib/Components/IconButton/IconButton.stories.d.ts +0 -10
  71. package/dist/src/lib/Components/IconButton/IconButton.stories.d.ts.map +0 -1
  72. package/dist/src/lib/Components/IconButton/IconButton.stories.js +0 -74
  73. package/dist/src/lib/Components/InteractiveIcon/InteractiveIcon.stories.d.ts +0 -11
  74. package/dist/src/lib/Components/InteractiveIcon/InteractiveIcon.stories.d.ts.map +0 -1
  75. package/dist/src/lib/Components/InteractiveIcon/InteractiveIcon.stories.js +0 -90
  76. package/dist/src/lib/Components/Link/Link.stories.d.ts +0 -17
  77. package/dist/src/lib/Components/Link/Link.stories.d.ts.map +0 -1
  78. package/dist/src/lib/Components/Link/Link.stories.js +0 -275
  79. package/dist/src/lib/Components/ListItem/ListItem.stories.d.ts +0 -10
  80. package/dist/src/lib/Components/ListItem/ListItem.stories.d.ts.map +0 -1
  81. package/dist/src/lib/Components/ListItem/ListItem.stories.js +0 -106
  82. package/dist/src/lib/Components/NavBar/NavBar.stories.d.ts +0 -10
  83. package/dist/src/lib/Components/NavBar/NavBar.stories.d.ts.map +0 -1
  84. package/dist/src/lib/Components/NavBar/NavBar.stories.js +0 -72
  85. package/dist/src/lib/Components/PageIndicator/PageIndicator.stories.d.ts +0 -27
  86. package/dist/src/lib/Components/PageIndicator/PageIndicator.stories.d.ts.map +0 -1
  87. package/dist/src/lib/Components/PageIndicator/PageIndicator.stories.js +0 -42
  88. package/dist/src/lib/Components/SearchInput/SearchInput.stories.d.ts +0 -12
  89. package/dist/src/lib/Components/SearchInput/SearchInput.stories.d.ts.map +0 -1
  90. package/dist/src/lib/Components/SearchInput/SearchInput.stories.js +0 -98
  91. package/dist/src/lib/Components/SegmentedControl/SegmentedControl.stories.d.ts +0 -58
  92. package/dist/src/lib/Components/SegmentedControl/SegmentedControl.stories.d.ts.map +0 -1
  93. package/dist/src/lib/Components/SegmentedControl/SegmentedControl.stories.js +0 -61
  94. package/dist/src/lib/Components/Select/Select.stories.d.ts +0 -11
  95. package/dist/src/lib/Components/Select/Select.stories.d.ts.map +0 -1
  96. package/dist/src/lib/Components/Select/Select.stories.js +0 -83
  97. package/dist/src/lib/Components/Skeleton/Skeleton.stories.d.ts +0 -11
  98. package/dist/src/lib/Components/Skeleton/Skeleton.stories.d.ts.map +0 -1
  99. package/dist/src/lib/Components/Skeleton/Skeleton.stories.js +0 -49
  100. package/dist/src/lib/Components/Spinner/Spinner.stories.d.ts +0 -9
  101. package/dist/src/lib/Components/Spinner/Spinner.stories.d.ts.map +0 -1
  102. package/dist/src/lib/Components/Spinner/Spinner.stories.js +0 -50
  103. package/dist/src/lib/Components/Spot/Spot.stories.d.ts +0 -12
  104. package/dist/src/lib/Components/Spot/Spot.stories.d.ts.map +0 -1
  105. package/dist/src/lib/Components/Spot/Spot.stories.js +0 -156
  106. package/dist/src/lib/Components/Stepper/Stepper.stories.d.ts +0 -9
  107. package/dist/src/lib/Components/Stepper/Stepper.stories.d.ts.map +0 -1
  108. package/dist/src/lib/Components/Stepper/Stepper.stories.js +0 -35
  109. package/dist/src/lib/Components/Subheader/Subheader.stories.d.ts +0 -10
  110. package/dist/src/lib/Components/Subheader/Subheader.stories.d.ts.map +0 -1
  111. package/dist/src/lib/Components/Subheader/Subheader.stories.js +0 -34
  112. package/dist/src/lib/Components/Switch/Switch.stories.d.ts +0 -14
  113. package/dist/src/lib/Components/Switch/Switch.stories.d.ts.map +0 -1
  114. package/dist/src/lib/Components/Switch/Switch.stories.js +0 -65
  115. package/dist/src/lib/Components/TabBar/TabBar.stories.d.ts +0 -50
  116. package/dist/src/lib/Components/TabBar/TabBar.stories.d.ts.map +0 -1
  117. package/dist/src/lib/Components/TabBar/TabBar.stories.js +0 -60
  118. package/dist/src/lib/Components/Tag/Tag.stories.d.ts +0 -10
  119. package/dist/src/lib/Components/Tag/Tag.stories.d.ts.map +0 -1
  120. package/dist/src/lib/Components/Tag/Tag.stories.js +0 -45
  121. package/dist/src/lib/Components/TextInput/TextInput.stories.d.ts +0 -14
  122. package/dist/src/lib/Components/TextInput/TextInput.stories.d.ts.map +0 -1
  123. package/dist/src/lib/Components/TextInput/TextInput.stories.js +0 -128
  124. package/dist/src/lib/Components/Tile/Tile.stories.d.ts +0 -14
  125. package/dist/src/lib/Components/Tile/Tile.stories.d.ts.map +0 -1
  126. package/dist/src/lib/Components/Tile/Tile.stories.js +0 -117
  127. package/dist/src/lib/Components/TileButton/TileButton.stories.d.ts +0 -12
  128. package/dist/src/lib/Components/TileButton/TileButton.stories.d.ts.map +0 -1
  129. package/dist/src/lib/Components/TileButton/TileButton.stories.js +0 -63
  130. package/dist/src/lib/Components/Tooltip/Tooltip.stories.d.ts +0 -10
  131. package/dist/src/lib/Components/Tooltip/Tooltip.stories.d.ts.map +0 -1
  132. package/dist/src/lib/Components/Tooltip/Tooltip.stories.js +0 -50
  133. package/dist/src/lib/Components/Utility/Box/Box.stories.d.ts +0 -7
  134. package/dist/src/lib/Components/Utility/Box/Box.stories.d.ts.map +0 -1
  135. package/dist/src/lib/Components/Utility/Box/Box.stories.js +0 -42
  136. package/dist/src/lib/Components/Utility/Gradient/LinearGradient/LinearGradient.stories.d.ts +0 -11
  137. package/dist/src/lib/Components/Utility/Gradient/LinearGradient/LinearGradient.stories.d.ts.map +0 -1
  138. package/dist/src/lib/Components/Utility/Gradient/LinearGradient/LinearGradient.stories.js +0 -105
  139. package/dist/src/lib/Components/Utility/Gradient/RadialGradient/RadialGradient.stories.d.ts +0 -10
  140. package/dist/src/lib/Components/Utility/Gradient/RadialGradient/RadialGradient.stories.d.ts.map +0 -1
  141. package/dist/src/lib/Components/Utility/Gradient/RadialGradient/RadialGradient.stories.js +0 -101
  142. package/dist/src/lib/Components/Utility/Pressable/Pressable.stories.d.ts +0 -7
  143. package/dist/src/lib/Components/Utility/Pressable/Pressable.stories.d.ts.map +0 -1
  144. package/dist/src/lib/Components/Utility/Pressable/Pressable.stories.js +0 -47
  145. package/dist/src/lib/Components/Utility/Text/Text.stories.d.ts +0 -7
  146. package/dist/src/lib/Components/Utility/Text/Text.stories.d.ts.map +0 -1
  147. package/dist/src/lib/Components/Utility/Text/Text.stories.js +0 -33
@@ -1,12 +1,6 @@
1
- import React, { useCallback, useEffect, useRef } from 'react';
2
- import { LayoutChangeEvent } from 'react-native';
3
- import Animated, {
4
- useAnimatedStyle,
5
- useSharedValue,
6
- withTiming,
7
- } from 'react-native-reanimated';
1
+ import Animated from 'react-native-reanimated';
8
2
  import { useStyleSheet } from '../../../styles';
9
- import { durations, easingCurves } from '../../Animations/constants';
3
+ import type { LumenTextStyle, LumenTypographyTokenName } from '../../../styles';
10
4
  import { Box, Pressable, Text } from '../Utility';
11
5
  import {
12
6
  SegmentedControlContextProvider,
@@ -16,8 +10,10 @@ import type {
16
10
  SegmentedControlButtonProps,
17
11
  SegmentedControlProps,
18
12
  } from './types';
19
-
20
- const ICON_SIZE = 16;
13
+ import {
14
+ usePillLayout,
15
+ useSegmentedControlSelectedIndex,
16
+ } from './usePillLayout';
21
17
 
22
18
  export function SegmentedControlButton({
23
19
  value,
@@ -26,32 +22,35 @@ export function SegmentedControlButton({
26
22
  onPress,
27
23
  ...props
28
24
  }: SegmentedControlButtonProps) {
29
- const styles = useButtonStyles();
30
- const { selectedValue, onSelectedChange } = useSegmentedControlContext();
31
-
25
+ const { selectedValue, onSelectedChange, disabled } =
26
+ useSegmentedControlContext();
32
27
  const selected = selectedValue === value;
28
+ const styles = useButtonStyles({ selected, disabled });
33
29
 
34
30
  function handlePress() {
35
- onSelectedChange(value);
36
- onPress?.();
31
+ if (!disabled) {
32
+ onSelectedChange(value);
33
+ onPress?.();
34
+ }
37
35
  }
38
36
 
39
37
  return (
40
38
  <Pressable
41
39
  onPress={handlePress}
42
- accessibilityState={{ selected }}
40
+ disabled={disabled}
41
+ accessibilityState={{ selected, disabled }}
43
42
  style={styles.button}
44
43
  {...props}
45
44
  >
46
45
  <Box style={styles.content}>
47
46
  {Icon && (
48
47
  <Box style={styles.iconWrap}>
49
- <Icon size={ICON_SIZE} />
48
+ <Icon size={16} color={styles.textColor} />
50
49
  </Box>
51
50
  )}
52
51
  <Text
53
- typography={selected ? 'body2SemiBold' : 'body2'}
54
- lx={{ color: 'base' }}
52
+ typography={styles.typography}
53
+ lx={{ color: styles.textColor }}
55
54
  style={styles.label}
56
55
  >
57
56
  {children}
@@ -63,8 +62,14 @@ export function SegmentedControlButton({
63
62
 
64
63
  SegmentedControlButton.displayName = 'SegmentedControlButton';
65
64
 
66
- function useButtonStyles() {
67
- return useStyleSheet(
65
+ function useButtonStyles({
66
+ selected,
67
+ disabled,
68
+ }: {
69
+ selected: boolean;
70
+ disabled?: boolean;
71
+ }) {
72
+ const styles = useStyleSheet(
68
73
  (t) => ({
69
74
  button: {
70
75
  flex: 1,
@@ -93,6 +98,12 @@ function useButtonStyles() {
93
98
  }),
94
99
  [],
95
100
  );
101
+ const typography: LumenTypographyTokenName = selected
102
+ ? 'body2SemiBold'
103
+ : 'body2';
104
+ const textColor: LumenTextStyle['color'] =
105
+ selected && !disabled ? 'base' : 'muted';
106
+ return { ...styles, typography, textColor };
96
107
  }
97
108
 
98
109
  export function SegmentedControl({
@@ -100,67 +111,31 @@ export function SegmentedControl({
100
111
  onSelectedChange,
101
112
  accessibilityLabel,
102
113
  children,
114
+ disabled,
115
+ appearance = 'background',
103
116
  ...props
104
117
  }: SegmentedControlProps) {
105
- const styles = useRootStyles();
106
- const pillTranslateX = useSharedValue(0);
107
- const pillWidth = useSharedValue(0);
108
- const pillHeight = useSharedValue(0);
109
- const hasLayoutRef = useRef(false);
110
-
111
- const getSelectedIndex = useCallback((): number => {
112
- return React.Children.toArray(children).findIndex((child) => {
113
- if (React.isValidElement(child) && child.props != null) {
114
- return (child.props as { value?: string }).value === selectedValue;
115
- }
116
- return false;
117
- });
118
- }, [selectedValue, children]);
119
-
120
- function onLayout(e: LayoutChangeEvent) {
121
- const { width, height } = e.nativeEvent.layout;
122
- const count = React.Children.count(children);
123
- const slotWidth = count > 0 ? width / count : 0;
124
-
125
- pillWidth.value = slotWidth;
126
- pillHeight.value = height;
127
-
128
- if (!hasLayoutRef.current) {
129
- hasLayoutRef.current = true;
130
- const index = getSelectedIndex();
131
- if (index >= 0) {
132
- pillTranslateX.value = index * slotWidth;
133
- }
134
- }
135
- }
136
-
137
- useEffect(() => {
138
- if (!hasLayoutRef.current) return;
139
- const index = getSelectedIndex();
140
- if (index >= 0 && pillWidth.value > 0) {
141
- pillTranslateX.value = withTiming(index * pillWidth.value, {
142
- duration: durations['250'],
143
- easing: easingCurves.bezier.default,
144
- });
145
- }
146
- }, [pillWidth, pillTranslateX, getSelectedIndex]);
147
-
148
- const animatedPillStyle = useAnimatedStyle(
149
- () => ({
150
- transform: [{ translateX: pillTranslateX.value }],
151
- width: pillWidth.value,
152
- height: pillHeight.value,
153
- }),
154
- [pillTranslateX, pillWidth, pillHeight],
118
+ const styles = useRootStyles({
119
+ disabled: Boolean(disabled),
120
+ appearance,
121
+ });
122
+ const selectedIndex = useSegmentedControlSelectedIndex(
123
+ selectedValue,
124
+ children,
155
125
  );
126
+ const { onLayout, animatedPillStyle } = usePillLayout({
127
+ selectedIndex,
128
+ children,
129
+ });
156
130
 
157
131
  return (
158
132
  <SegmentedControlContextProvider
159
- value={{ selectedValue, onSelectedChange }}
133
+ value={{ selectedValue, onSelectedChange, disabled }}
160
134
  >
161
135
  <Box
162
136
  accessibilityRole='radiogroup'
163
137
  accessibilityLabel={accessibilityLabel}
138
+ accessibilityState={{ disabled }}
164
139
  onLayout={onLayout}
165
140
  style={styles.container}
166
141
  {...props}
@@ -177,7 +152,13 @@ export function SegmentedControl({
177
152
 
178
153
  SegmentedControl.displayName = 'SegmentedControl';
179
154
 
180
- function useRootStyles() {
155
+ function useRootStyles({
156
+ disabled,
157
+ appearance,
158
+ }: {
159
+ disabled: boolean;
160
+ appearance: 'background' | 'no-background';
161
+ }) {
181
162
  return useStyleSheet(
182
163
  (t) => ({
183
164
  container: {
@@ -185,18 +166,21 @@ function useRootStyles() {
185
166
  alignItems: 'center',
186
167
  position: 'relative',
187
168
  width: '100%',
188
- borderRadius: t.borderRadius.full,
189
- backgroundColor: t.colors.bg.baseTransparent,
169
+ borderRadius: t.borderRadius.md,
170
+ backgroundColor:
171
+ appearance === 'background' ? t.colors.bg.surface : 'transparent',
190
172
  },
191
173
  pill: {
192
174
  position: 'absolute',
193
175
  top: 0,
194
176
  left: 0,
195
177
  borderRadius: t.borderRadius.sm,
196
- backgroundColor: t.colors.bg.muted,
178
+ backgroundColor: disabled
179
+ ? t.colors.bg.baseTransparentPressed
180
+ : t.colors.bg.mutedTransparent,
197
181
  zIndex: 0,
198
182
  },
199
183
  }),
200
- [],
184
+ [disabled, appearance],
201
185
  );
202
186
  }
@@ -3,6 +3,7 @@ import { createSafeContext } from '@ledgerhq/lumen-utils-shared';
3
3
  export type SegmentedControlContextValue = {
4
4
  selectedValue: string;
5
5
  onSelectedChange: (value: string) => void;
6
+ disabled?: boolean;
6
7
  };
7
8
 
8
9
  const [SegmentedControlContextProvider, _useSegmentedControlSafeContext] =
@@ -1,5 +1,5 @@
1
1
  import { ComponentType, ReactNode } from 'react';
2
- import { StyledPressableProps } from '../../../styles';
2
+ import { LumenTextStyle, StyledPressableProps } from '../../../styles';
3
3
  import { IconSize } from '../Icon';
4
4
  import { BoxProps } from '../Utility';
5
5
 
@@ -12,18 +12,28 @@ export type SegmentedControlProps = {
12
12
  * Callback when the selected segment value changes.
13
13
  */
14
14
  onSelectedChange: (value: string) => void;
15
+ /**
16
+ * When true, the entire control is disabled (no interaction, selected uses muted styling).
17
+ */
18
+ disabled?: boolean;
19
+ /**
20
+ * Visual style of the control container: "background" shows the surface bg, "no-background" is transparent.
21
+ * @default 'background'
22
+ */
23
+ appearance?: 'background' | 'no-background';
15
24
  /**
16
25
  * Accessible label for the control (e.g. "File view").
17
26
  */
18
27
  accessibilityLabel?: string;
19
28
  /**
20
- * Segment buttons (SegmentedControlButton). Can be wrapped (e.g. in Tooltip).
29
+ * Segment buttons (SegmentedControlButton).
21
30
  */
22
31
  children: ReactNode;
23
32
  } & Omit<BoxProps, 'children'>;
24
33
 
25
34
  type IconComponent = ComponentType<{
26
35
  size?: IconSize;
36
+ color?: LumenTextStyle['color'];
27
37
  }>;
28
38
 
29
39
  export type SegmentedControlButtonProps = {
@@ -0,0 +1,76 @@
1
+ import React, { useEffect, useMemo, useRef } from 'react';
2
+ import { LayoutChangeEvent } from 'react-native';
3
+ import {
4
+ useAnimatedStyle,
5
+ useSharedValue,
6
+ withTiming,
7
+ } from 'react-native-reanimated';
8
+ import { durations, easingCurves } from '../../Animations/constants';
9
+
10
+ export function useSegmentedControlSelectedIndex(
11
+ selectedValue: string,
12
+ children: React.ReactNode,
13
+ ): number {
14
+ return useMemo(
15
+ () =>
16
+ React.Children.toArray(children).findIndex((child) => {
17
+ if (React.isValidElement(child) && child.props != null) {
18
+ return (child.props as { value?: string }).value === selectedValue;
19
+ }
20
+ return false;
21
+ }),
22
+ [selectedValue, children],
23
+ );
24
+ }
25
+
26
+ type UsePillLayoutParams = {
27
+ selectedIndex: number;
28
+ children: React.ReactNode;
29
+ };
30
+
31
+ export function usePillLayout({
32
+ selectedIndex,
33
+ children,
34
+ }: UsePillLayoutParams) {
35
+ const pillTranslateX = useSharedValue(0);
36
+ const pillWidth = useSharedValue(0);
37
+ const pillHeight = useSharedValue(0);
38
+ const hasLayoutRef = useRef(false);
39
+
40
+ const onLayout = (e: LayoutChangeEvent): void => {
41
+ const { width, height } = e.nativeEvent.layout;
42
+ const count = React.Children.count(children);
43
+ const slotWidth = count > 0 ? width / count : 0;
44
+
45
+ pillWidth.value = slotWidth;
46
+ pillHeight.value = height;
47
+
48
+ if (!hasLayoutRef.current) {
49
+ hasLayoutRef.current = true;
50
+ if (selectedIndex >= 0) {
51
+ pillTranslateX.value = selectedIndex * slotWidth;
52
+ }
53
+ }
54
+ };
55
+
56
+ useEffect(() => {
57
+ if (!hasLayoutRef.current) return;
58
+ if (selectedIndex >= 0 && pillWidth.value > 0) {
59
+ pillTranslateX.value = withTiming(selectedIndex * pillWidth.value, {
60
+ duration: durations['250'],
61
+ easing: easingCurves.bezier.default,
62
+ });
63
+ }
64
+ }, [selectedIndex, pillWidth, pillTranslateX]);
65
+
66
+ const animatedPillStyle = useAnimatedStyle(
67
+ () => ({
68
+ transform: [{ translateX: pillTranslateX.value }],
69
+ width: pillWidth.value,
70
+ height: pillHeight.value,
71
+ }),
72
+ [pillTranslateX, pillWidth, pillHeight],
73
+ );
74
+
75
+ return { onLayout, animatedPillStyle };
76
+ }
@@ -38,10 +38,10 @@ const useStyles = ({
38
38
  gap: t.spacings.s8,
39
39
  padding: t.spacings.s12,
40
40
  borderRadius: t.borderRadius.md,
41
- backgroundColor: t.colors.bg.muted,
41
+ backgroundColor: t.colors.bg.surface,
42
42
  },
43
43
  isFull && { width: t.sizes.full },
44
- pressed && !disabled && { backgroundColor: t.colors.bg.mutedPressed },
44
+ pressed && !disabled && { backgroundColor: t.colors.bg.surfacePressed },
45
45
  disabled && { backgroundColor: t.colors.bg.disabled },
46
46
  ]),
47
47
  label: StyleSheet.flatten([
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-native-web-vite';
2
- import { useTheme } from 'src/styles';
2
+ import { useTheme } from '../../../../../styles';
3
3
  import { Box } from '../../Box';
4
4
  import { Text } from '../../Text';
5
5
  import { LinearGradient } from './LinearGradient';
package/tsconfig.json CHANGED
@@ -6,20 +6,7 @@
6
6
  "files": [],
7
7
  "include": [],
8
8
  "references": [
9
- {
10
- "path": "../design-core"
11
- },
12
- {
13
- "path": "../utils-shared"
14
- },
15
- {
16
- "path": "./tsconfig.lib.json"
17
- },
18
- {
19
- "path": "./tsconfig.spec.json"
20
- },
21
- {
22
- "path": "./tsconfig.storybook.json"
23
- }
24
- ],
9
+ { "path": "./tsconfig.lib.json" },
10
+ { "path": "./tsconfig.spec.json" }
11
+ ]
25
12
  }
package/tsconfig.lib.json CHANGED
@@ -15,6 +15,11 @@
15
15
  "src/**/*.test.tsx",
16
16
  "src/**/*.test.js",
17
17
  "src/**/*.test.jsx",
18
+ "src/**/*.stories.ts",
19
+ "src/**/*.stories.tsx",
20
+ "src/**/*.stories.js",
21
+ "src/**/*.stories.jsx",
22
+ "src/**/*.mdx",
18
23
  "eslint.config.js",
19
24
  "eslint.config.cjs",
20
25
  "eslint.config.mjs",
@@ -1,9 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react-native-web-vite';
2
- import { Pulse } from './Pulse';
3
- declare const meta: Meta<typeof Pulse>;
4
- export default meta;
5
- type Story = StoryObj<typeof meta>;
6
- export declare const Base: Story;
7
- export declare const DurationShowcase: Story;
8
- export declare const WithAmountDisplay: Story;
9
- //# sourceMappingURL=Pulse.stories.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Pulse.stories.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Animations/Pulse/Pulse.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAKvE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAchC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,KAAK,CAMC,CAAC;AAE/B,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,IAAI,EAAE,KAQlB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAmC9B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAY/B,CAAC"}
@@ -1,38 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { View } from 'react-native';
3
- import { AmountDisplay } from '../../Components/AmountDisplay';
4
- import { Box, Text } from '../../Components/Utility';
5
- import { Pulse } from './Pulse';
6
- const usdFormatter = (value) => {
7
- const [integerPart, decimalPart] = value.toFixed(2).split(/\.|,/);
8
- return {
9
- integerPart,
10
- decimalPart,
11
- currencyText: '$',
12
- decimalSeparator: '.',
13
- currencyPosition: 'start',
14
- };
15
- };
16
- const meta = {
17
- title: 'Animations/Pulse',
18
- component: Pulse,
19
- parameters: {
20
- layout: 'centered',
21
- },
22
- };
23
- export default meta;
24
- export const Base = {
25
- args: {
26
- duration: 2000,
27
- animate: true,
28
- children: (_jsx(Box, { lx: { width: 's48', height: 's48', backgroundColor: 'accent' } })),
29
- },
30
- };
31
- export const DurationShowcase = {
32
- render: () => (_jsxs(View, { style: { flexDirection: 'row', gap: 24, alignItems: 'center' }, children: [_jsxs(View, { style: { alignItems: 'center', gap: 8 }, children: [_jsx(Pulse, { duration: 1000, animate: true, children: _jsx(Box, { lx: { width: 's48', height: 's48', backgroundColor: 'accent' } }) }), _jsx(Text, { typography: 'body4', lx: { color: 'muted' }, children: "1000ms" })] }), _jsxs(View, { style: { alignItems: 'center', gap: 8 }, children: [_jsx(Pulse, { duration: 2000, animate: true, children: _jsx(Box, { lx: { width: 's48', height: 's48', backgroundColor: 'accent' } }) }), _jsx(Text, { typography: 'body4', lx: { color: 'muted' }, children: "2000ms" })] }), _jsxs(View, { style: { alignItems: 'center', gap: 8 }, children: [_jsx(Pulse, { duration: 3000, animate: true, children: _jsx(Box, { lx: { width: 's48', height: 's48', backgroundColor: 'accent' } }) }), _jsx(Text, { typography: 'body4', lx: { color: 'muted' }, children: "3000ms" })] })] })),
33
- };
34
- export const WithAmountDisplay = {
35
- render: () => {
36
- return (_jsx(View, { style: { flexDirection: 'column', alignItems: 'center', gap: 16 }, children: _jsx(AmountDisplay, { formatter: usdFormatter, value: 1234.56, loading: true }) }));
37
- },
38
- };
@@ -1,9 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react-native-web-vite';
2
- import { Spin } from './Spin';
3
- declare const meta: Meta<typeof Spin>;
4
- export default meta;
5
- type Story = StoryObj<typeof meta>;
6
- export declare const Base: Story;
7
- export declare const DurationShowcase: Story;
8
- export declare const WithSpinner: Story;
9
- //# sourceMappingURL=Spin.stories.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Spin.stories.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Animations/Spin/Spin.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAIvE,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,IAAI,CAMC,CAAC;AAE9B,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,IAAI,EAAE,KAOlB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAmC9B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAQzB,CAAC"}
@@ -1,27 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { View } from 'react-native';
3
- import { Spinner } from '../../Components/Spinner';
4
- import { Box, Text } from '../../Components/Utility';
5
- import { Spin } from './Spin';
6
- const meta = {
7
- title: 'Animations/Spin',
8
- component: Spin,
9
- parameters: {
10
- layout: 'centered',
11
- },
12
- };
13
- export default meta;
14
- export const Base = {
15
- args: {
16
- duration: 1000,
17
- children: (_jsx(Box, { lx: { width: 's48', height: 's48', backgroundColor: 'accent' } })),
18
- },
19
- };
20
- export const DurationShowcase = {
21
- render: () => (_jsxs(View, { style: { flexDirection: 'row', gap: 24, alignItems: 'center' }, children: [_jsxs(View, { style: { alignItems: 'center', gap: 8 }, children: [_jsx(Spin, { duration: 500, children: _jsx(Box, { lx: { width: 's48', height: 's48', backgroundColor: 'accent' } }) }), _jsx(Text, { typography: 'body4', lx: { color: 'muted' }, children: "500ms" })] }), _jsxs(View, { style: { alignItems: 'center', gap: 8 }, children: [_jsx(Spin, { duration: 1000, children: _jsx(Box, { lx: { width: 's48', height: 's48', backgroundColor: 'accent' } }) }), _jsx(Text, { typography: 'body4', lx: { color: 'muted' }, children: "1000ms" })] }), _jsxs(View, { style: { alignItems: 'center', gap: 8 }, children: [_jsx(Spin, { duration: 2000, children: _jsx(Box, { lx: { width: 's48', height: 's48', backgroundColor: 'accent' } }) }), _jsx(Text, { typography: 'body4', lx: { color: 'muted' }, children: "2000ms" })] })] })),
22
- };
23
- export const WithSpinner = {
24
- render: () => {
25
- return (_jsx(View, { style: { flexDirection: 'column', alignItems: 'center', gap: 16 }, children: _jsx(Spinner, { size: 40 }) }));
26
- },
27
- };
@@ -1,13 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react-native-web-vite';
2
- import { AddressInput } from './AddressInput';
3
- declare const meta: Meta<typeof AddressInput>;
4
- export default meta;
5
- type Story = StoryObj<typeof AddressInput>;
6
- export declare const Base: Story;
7
- export declare const WithContent: Story;
8
- export declare const WithCustomPrefix: Story;
9
- export declare const WithoutQrCode: Story;
10
- export declare const WithError: Story;
11
- export declare const DisabledAddressInput: Story;
12
- export declare const WithHiddenClearButton: Story;
13
- //# sourceMappingURL=AddressInput.stories.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AddressInput.stories.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Components/AddressInput/AddressInput.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAGvE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAkCnC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,YAAY,CAAC,CAAC;AA6B3C,eAAO,MAAM,IAAI,EAAE,KAQlB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAQ9B,CAAC;AA2BF,eAAO,MAAM,aAAa,EAAE,KAQ3B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAUvB,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,KASlC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,KASnC,CAAC"}
@@ -1,128 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { View } from 'react-native';
4
- import { AddressInput } from './AddressInput';
5
- const meta = {
6
- component: AddressInput,
7
- title: 'Input/AddressInput',
8
- parameters: {
9
- docs: {
10
- source: {
11
- language: 'tsx',
12
- format: true,
13
- type: 'code',
14
- },
15
- },
16
- },
17
- argTypes: {
18
- placeholder: {
19
- control: 'text',
20
- description: 'Placeholder text when input is empty',
21
- },
22
- prefix: {
23
- control: 'text',
24
- description: 'Custom prefix text (default: "To:")',
25
- },
26
- errorMessage: {
27
- control: 'text',
28
- description: 'Error message to display below input',
29
- },
30
- editable: {
31
- control: 'boolean',
32
- description: 'Whether the input is editable',
33
- },
34
- hideClearButton: {
35
- control: 'boolean',
36
- description: 'Hide the clear button',
37
- },
38
- },
39
- };
40
- export default meta;
41
- const AddressInputStory = (args) => {
42
- const [address, setAddress] = useState(args.value?.toString() ?? '');
43
- return (_jsx(View, { style: {
44
- flex: 1,
45
- minHeight: 96,
46
- alignItems: 'center',
47
- justifyContent: 'center',
48
- padding: 24,
49
- }, children: _jsx(View, { style: { width: '100%', maxWidth: 400 }, children: _jsx(AddressInput, { ...args, value: address, onChangeText: setAddress, onQrCodeClick: args.onQrCodeClick ?? (() => alert('QR code scanner opened')) }) }) }));
50
- };
51
- export const Base = {
52
- render: (args) => _jsx(AddressInputStory, { ...args }),
53
- args: {
54
- placeholder: 'Enter address or ENS',
55
- prefix: 'To:',
56
- editable: true,
57
- hideClearButton: false,
58
- },
59
- };
60
- export const WithContent = {
61
- render: (args) => _jsx(AddressInputStory, { ...args }),
62
- args: {
63
- placeholder: 'Enter address or ENS',
64
- value: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb27',
65
- prefix: 'To:',
66
- editable: true,
67
- hideClearButton: false,
68
- },
69
- };
70
- export const WithCustomPrefix = {
71
- render: (args) => _jsx(AddressInputStory, { ...args }),
72
- args: {
73
- placeholder: 'Enter sender address',
74
- prefix: 'From:',
75
- editable: true,
76
- hideClearButton: false,
77
- },
78
- };
79
- const AddressInputWithoutQrStory = (args) => {
80
- const [address, setAddress] = useState(args.value?.toString() ?? '');
81
- return (_jsx(View, { style: {
82
- flex: 1,
83
- minHeight: 96,
84
- alignItems: 'center',
85
- justifyContent: 'center',
86
- padding: 24,
87
- }, children: _jsx(View, { style: { width: '100%', maxWidth: 400 }, children: _jsx(AddressInput, { ...args, value: address, onChangeText: setAddress, onQrCodeClick: undefined }) }) }));
88
- };
89
- export const WithoutQrCode = {
90
- render: (args) => _jsx(AddressInputWithoutQrStory, { ...args }),
91
- args: {
92
- placeholder: 'Enter address or ENS',
93
- prefix: 'To:',
94
- editable: true,
95
- hideClearButton: false,
96
- },
97
- };
98
- export const WithError = {
99
- render: (args) => _jsx(AddressInputStory, { ...args }),
100
- args: {
101
- placeholder: 'Enter address or ENS',
102
- value: 'invalid-address',
103
- errorMessage: 'Invalid address format',
104
- prefix: 'To:',
105
- editable: true,
106
- hideClearButton: false,
107
- },
108
- };
109
- export const DisabledAddressInput = {
110
- render: (args) => _jsx(AddressInputStory, { ...args }),
111
- args: {
112
- placeholder: 'Enter address or ENS',
113
- value: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb27',
114
- prefix: 'To:',
115
- editable: false,
116
- hideClearButton: false,
117
- },
118
- };
119
- export const WithHiddenClearButton = {
120
- render: (args) => _jsx(AddressInputStory, { ...args }),
121
- args: {
122
- placeholder: 'Enter address or ENS',
123
- value: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb27',
124
- prefix: 'To:',
125
- editable: true,
126
- hideClearButton: true,
127
- },
128
- };
@@ -1,10 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react-native-web-vite';
2
- import { AmountDisplay } from './AmountDisplay';
3
- declare const meta: Meta<typeof AmountDisplay>;
4
- export default meta;
5
- type Story = StoryObj<typeof AmountDisplay>;
6
- export declare const Base: Story;
7
- export declare const WithHideButton: Story;
8
- export declare const AnimationShowcase: Story;
9
- export declare const Loading: Story;
10
- //# sourceMappingURL=AmountDisplay.stories.d.ts.map
@@ -1 +0,0 @@
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"}