jp-composter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/dist/index.d.mts +997 -0
  2. package/dist/index.d.ts +997 -0
  3. package/dist/index.js +36837 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +36778 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/package.json +66 -0
  8. package/src/SliceUI/IconMoon.tsx +33 -0
  9. package/src/SliceUI/assets/Anatomy diagram copy.svg +19 -0
  10. package/src/SliceUI/assets/Anatomy diagram.svg +19 -0
  11. package/src/SliceUI/assets/Anatomycheck.svg +15 -0
  12. package/src/SliceUI/assets/Anatomyinput.svg +32 -0
  13. package/src/SliceUI/assets/Checkbox.jpg +0 -0
  14. package/src/SliceUI/assets/Diagram copy.svg +15 -0
  15. package/src/SliceUI/assets/Diagram.jpg +0 -0
  16. package/src/SliceUI/assets/Diagram.svg +15 -0
  17. package/src/SliceUI/assets/Frame 5 copy.png +0 -0
  18. package/src/SliceUI/assets/Frame 5.png +0 -0
  19. package/src/SliceUI/assets/Frame 65.png +0 -0
  20. package/src/SliceUI/assets/Frame_65.png +0 -0
  21. package/src/SliceUI/assets/Icon copy.svg +3 -0
  22. package/src/SliceUI/assets/Icon.svg +3 -0
  23. package/src/SliceUI/assets/Icon_Bridging copy.svg +39 -0
  24. package/src/SliceUI/assets/Icon_Bridging.svg +39 -0
  25. package/src/SliceUI/assets/Icon_Consistent copy.svg +39 -0
  26. package/src/SliceUI/assets/Icon_Consistent.svg +39 -0
  27. package/src/SliceUI/assets/Icon_Plug copy.svg +38 -0
  28. package/src/SliceUI/assets/Icon_Plug.svg +38 -0
  29. package/src/SliceUI/assets/Icon_Reusable copy.svg +39 -0
  30. package/src/SliceUI/assets/Icon_Reusable.svg +39 -0
  31. package/src/SliceUI/assets/Layer_1.png +0 -0
  32. package/src/SliceUI/assets/accessibility.png +0 -0
  33. package/src/SliceUI/assets/accessibility.svg +1 -0
  34. package/src/SliceUI/assets/addon-library.png +0 -0
  35. package/src/SliceUI/assets/assets.png +0 -0
  36. package/src/SliceUI/assets/avif-test-image.avif +0 -0
  37. package/src/SliceUI/assets/bridging.svg +13 -0
  38. package/src/SliceUI/assets/consistent.svg +11 -0
  39. package/src/SliceUI/assets/context.png +0 -0
  40. package/src/SliceUI/assets/discord.svg +1 -0
  41. package/src/SliceUI/assets/docs.png +0 -0
  42. package/src/SliceUI/assets/figma-plugin.png +0 -0
  43. package/src/SliceUI/assets/github.svg +1 -0
  44. package/src/SliceUI/assets/resources/Anatomy diagram.svg +19 -0
  45. package/src/SliceUI/assets/resources/Anatomycheck.svg +15 -0
  46. package/src/SliceUI/assets/resources/Anatomyinput.svg +32 -0
  47. package/src/SliceUI/assets/resources/Diagram.svg +15 -0
  48. package/src/SliceUI/assets/resources/Frame 5.png +0 -0
  49. package/src/SliceUI/assets/resources/Frame 65.png +0 -0
  50. package/src/SliceUI/assets/resources/Icon.svg +3 -0
  51. package/src/SliceUI/assets/resources/Icon_Bridging.svg +39 -0
  52. package/src/SliceUI/assets/resources/Icon_Consistent.svg +39 -0
  53. package/src/SliceUI/assets/resources/Icon_Plug.svg +38 -0
  54. package/src/SliceUI/assets/resources/Icon_Reusable.svg +39 -0
  55. package/src/SliceUI/assets/resources/fonts/FontIcon.json +150 -0
  56. package/src/SliceUI/assets/resources/fonts/Lato-Black.ttf +0 -0
  57. package/src/SliceUI/assets/resources/fonts/Lato-Bold.ttf +0 -0
  58. package/src/SliceUI/assets/resources/fonts/Lato-Heavy.ttf +0 -0
  59. package/src/SliceUI/assets/resources/fonts/Lato-Medium.ttf +0 -0
  60. package/src/SliceUI/assets/resources/fonts/Lato-Regular.ttf +0 -0
  61. package/src/SliceUI/assets/resources/fonts/Lato.woff2 +0 -0
  62. package/src/SliceUI/assets/resources/fonts/icomoon.eot +0 -0
  63. package/src/SliceUI/assets/resources/fonts/icomoon.svg +601 -0
  64. package/src/SliceUI/assets/resources/fonts/icomoon.ttf +0 -0
  65. package/src/SliceUI/assets/resources/fonts/icomoon.woff +0 -0
  66. package/src/SliceUI/assets/resources/fonts/selection.json +1 -0
  67. package/src/SliceUI/assets/share.png +0 -0
  68. package/src/SliceUI/assets/styling.png +0 -0
  69. package/src/SliceUI/assets/testing.png +0 -0
  70. package/src/SliceUI/assets/theming.png +0 -0
  71. package/src/SliceUI/assets/tutorials.svg +1 -0
  72. package/src/SliceUI/assets/youtube.svg +1 -0
  73. package/src/SliceUI/automation/helper.ts +29 -0
  74. package/src/SliceUI/avatar/Avatar.tsx +237 -0
  75. package/src/SliceUI/avatar/Token.ts +116 -0
  76. package/src/SliceUI/avatar/Type.ts +36 -0
  77. package/src/SliceUI/avatar/helper.ts +53 -0
  78. package/src/SliceUI/badge/Badge.tsx +308 -0
  79. package/src/SliceUI/badge/Token.ts +202 -0
  80. package/src/SliceUI/badge/Type.ts +46 -0
  81. package/src/SliceUI/badge/helper.ts +39 -0
  82. package/src/SliceUI/button/Button.tsx +243 -0
  83. package/src/SliceUI/button/Token.ts +138 -0
  84. package/src/SliceUI/button/Type.ts +34 -0
  85. package/src/SliceUI/button/helper.ts +125 -0
  86. package/src/SliceUI/checkbox/Checkbox.tsx +176 -0
  87. package/src/SliceUI/checkbox/Token.ts +128 -0
  88. package/src/SliceUI/checkbox/Type.ts +35 -0
  89. package/src/SliceUI/chip/Chip.tsx +290 -0
  90. package/src/SliceUI/chip/Token.ts +151 -0
  91. package/src/SliceUI/chip/Type.ts +43 -0
  92. package/src/SliceUI/chip/helper.ts +40 -0
  93. package/src/SliceUI/colors/Pallete.ts +151 -0
  94. package/src/SliceUI/colors/Token.ts +110 -0
  95. package/src/SliceUI/colors/Type.ts +56 -0
  96. package/src/SliceUI/contextProvider/context.tsx +108 -0
  97. package/src/SliceUI/divider/Divider.tsx +109 -0
  98. package/src/SliceUI/divider/Token.ts +18 -0
  99. package/src/SliceUI/divider/Type.ts +26 -0
  100. package/src/SliceUI/icon/CustomIcon.ts +4 -0
  101. package/src/SliceUI/icon/IcoMoonIcon.tsx +11 -0
  102. package/src/SliceUI/icon/Icon.tsx +38 -0
  103. package/src/SliceUI/icon/Token.ts +14 -0
  104. package/src/SliceUI/icon/Type.ts +13 -0
  105. package/src/SliceUI/icon/selection.json +1 -0
  106. package/src/SliceUI/input/Input.tsx +573 -0
  107. package/src/SliceUI/input/ToDo.md +99 -0
  108. package/src/SliceUI/input/Token.ts +372 -0
  109. package/src/SliceUI/input/Type.ts +109 -0
  110. package/src/SliceUI/input/components/InputPortal.tsx +211 -0
  111. package/src/SliceUI/input/components/NativeBottomSheet.tsx +296 -0
  112. package/src/SliceUI/input/components/SelectChip.tsx +185 -0
  113. package/src/SliceUI/input/components/SelectList.tsx +173 -0
  114. package/src/SliceUI/input/components/SelectListItem.tsx +377 -0
  115. package/src/SliceUI/input/components/SelectScrollbarStyle.ts +44 -0
  116. package/src/SliceUI/input/hooks/useCustomScrollbar.ts +17 -0
  117. package/src/SliceUI/input/hooks/useInputState.ts +41 -0
  118. package/src/SliceUI/input/hooks/useLabelAnimation.ts +132 -0
  119. package/src/SliceUI/input/hooks/useOutsideClick.ts +38 -0
  120. package/src/SliceUI/input/hooks/useSelectLogic.ts +338 -0
  121. package/src/SliceUI/input/utils/inputUtils.ts +120 -0
  122. package/src/SliceUI/input/utils/selectUtils.ts +85 -0
  123. package/src/SliceUI/input/utils/styleUtils.ts +50 -0
  124. package/src/SliceUI/input/variants/CurrencyInput/CurrencyInput.tsx +16 -0
  125. package/src/SliceUI/input/variants/CurrencyInput/NativeCurrencyInput.tsx +181 -0
  126. package/src/SliceUI/input/variants/CurrencyInput/WebCurrencyInput.tsx +163 -0
  127. package/src/SliceUI/input/variants/CurrencyInput/types.ts +17 -0
  128. package/src/SliceUI/input/variants/PhoneInput/NativePhoneInput.tsx +189 -0
  129. package/src/SliceUI/input/variants/PhoneInput/PhoneInput.tsx +16 -0
  130. package/src/SliceUI/input/variants/PhoneInput/WebPhoneInput.tsx +291 -0
  131. package/src/SliceUI/input/variants/PhoneInput/types.ts +22 -0
  132. package/src/SliceUI/input/variants/SelectInput/SelectInput.tsx +407 -0
  133. package/src/SliceUI/input/variants/SelectInput/types.ts +34 -0
  134. package/src/SliceUI/input/variants/TextInput.tsx +68 -0
  135. package/src/SliceUI/layout/Box.tsx +38 -0
  136. package/src/SliceUI/layout/Center.tsx +38 -0
  137. package/src/SliceUI/layout/Divider.tsx +37 -0
  138. package/src/SliceUI/layout/Grid.tsx +75 -0
  139. package/src/SliceUI/layout/PageContainer.tsx +60 -0
  140. package/src/SliceUI/layout/ScrollContainer.tsx +72 -0
  141. package/src/SliceUI/layout/Spacer.tsx +54 -0
  142. package/src/SliceUI/layout/Stack.tsx +97 -0
  143. package/src/SliceUI/layout/StickyHeader.tsx +71 -0
  144. package/src/SliceUI/radio/RadioButton.tsx +130 -0
  145. package/src/SliceUI/radio/Token.ts +197 -0
  146. package/src/SliceUI/radio/Type.ts +35 -0
  147. package/src/SliceUI/react-native.config.js +3 -0
  148. package/src/SliceUI/responsive/Type.ts +7 -0
  149. package/src/SliceUI/responsive/helper.ts +53 -0
  150. package/src/SliceUI/switch/Switch.tsx +119 -0
  151. package/src/SliceUI/switch/Token.ts +205 -0
  152. package/src/SliceUI/switch/Type.ts +26 -0
  153. package/src/SliceUI/tab/TabItem.tsx +204 -0
  154. package/src/SliceUI/tab/Tabs.tsx +110 -0
  155. package/src/SliceUI/tab/Token.ts +282 -0
  156. package/src/SliceUI/tab/Type.ts +66 -0
  157. package/src/SliceUI/tab/helper.ts +53 -0
  158. package/src/SliceUI/table/Table.tsx +388 -0
  159. package/src/SliceUI/table/TableCell.tsx +158 -0
  160. package/src/SliceUI/table/TableFooter.tsx +353 -0
  161. package/src/SliceUI/table/TableHeader.tsx +247 -0
  162. package/src/SliceUI/table/TableRow.tsx +218 -0
  163. package/src/SliceUI/table/Token.ts +252 -0
  164. package/src/SliceUI/table/Type.ts +213 -0
  165. package/src/SliceUI/table/helper.ts +376 -0
  166. package/src/SliceUI/table/index.ts +53 -0
  167. package/src/SliceUI/theme/dummyColors.tsx +7 -0
  168. package/src/SliceUI/theme/theme.ts +107 -0
  169. package/src/SliceUI/typography/BaseTypographyToken.ts +62 -0
  170. package/src/SliceUI/typography/FoundationToken.ts +48 -0
  171. package/src/SliceUI/typography/Token.ts +228 -0
  172. package/src/SliceUI/typography/Type.ts +20 -0
  173. package/src/SliceUI/typography/Typography.tsx +99 -0
  174. package/src/SliceUI/values/BorderRadius.ts +17 -0
  175. package/src/SliceUI/values/BorderWidth.ts +7 -0
  176. package/src/SliceUI/values/Dimension.ts +35 -0
  177. package/src/SliceUI/values/IconSizes.ts +13 -0
  178. package/src/SliceUI/values/Spacing.ts +22 -0
  179. package/src/declarations.d.ts +8 -0
  180. package/src/index.tsx +119 -0
  181. package/src/stories/Colors.mdx +1418 -0
  182. package/src/stories/Dimensions.mdx +60 -0
  183. package/src/stories/GetStarted.mdx +90 -0
  184. package/src/stories/Introduction.mdx +136 -0
  185. package/src/stories/Shape.mdx +126 -0
  186. package/src/stories/Spacing.mdx +104 -0
  187. package/src/stories/Typography.mdx +454 -0
  188. package/src/stories/Utils.mdx +277 -0
  189. package/src/stories/story-components/AddIcon.js +13 -0
  190. package/src/stories/story-components/RectangleWithBox.jsx +51 -0
  191. package/src/stories/story-components/RoundedRectangle.jsx +18 -0
  192. package/src/stories/story-components/RoundedWithWhiteInside.jsx +33 -0
  193. package/src/stories/story-components/WhiteRoundedRectangle.jsx +107 -0
  194. package/src/stories/story-components/svgPaths.js +126 -0
@@ -0,0 +1,296 @@
1
+ import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ Modal,
7
+ Animated,
8
+ TouchableOpacity,
9
+ TouchableWithoutFeedback,
10
+ Dimensions,
11
+ Keyboard,
12
+ Platform,
13
+ KeyboardAvoidingView,
14
+ } from 'react-native';
15
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
16
+ import Icon from '../../icon/Icon';
17
+ import IconMoon from '../../IconMoon';
18
+ import FONT_ICON from '../../assets/resources/fonts/FontIcon.json';
19
+
20
+ interface NativeBottomSheetProps {
21
+ open: boolean;
22
+ onClose: () => void;
23
+ title?: string;
24
+ heightRatio?: number;
25
+ fitContent?: boolean;
26
+ children: React.ReactNode;
27
+ headerContent?: React.ReactNode;
28
+ avoidKeyboardForHeader?: boolean;
29
+ colorTheme: any;
30
+ }
31
+
32
+ const { height: SCREEN_HEIGHT } = Dimensions.get('window');
33
+
34
+ const NativeBottomSheet = ({
35
+ open,
36
+ onClose,
37
+ title,
38
+ heightRatio = 0.5,
39
+ fitContent = false,
40
+ children,
41
+ headerContent,
42
+ avoidKeyboardForHeader = false,
43
+ colorTheme,
44
+ }: NativeBottomSheetProps) => {
45
+ const { bottom } = useSafeAreaInsets();
46
+ const sheetMaxHeight = SCREEN_HEIGHT * heightRatio;
47
+
48
+ const [mounted, setMounted] = useState(false);
49
+ const keyboardHeight = useRef(0);
50
+
51
+ const translateY = useRef(
52
+ new Animated.Value(sheetMaxHeight)
53
+ ).current;
54
+
55
+ const opacity = useRef(new Animated.Value(0)).current;
56
+
57
+ const animateTo = useCallback((toValue: number, fade: number, cb?: () => void) => {
58
+ Animated.parallel([
59
+ Animated.timing(translateY, {
60
+ toValue,
61
+ duration: 260,
62
+ useNativeDriver: true,
63
+ }),
64
+ Animated.timing(opacity, {
65
+ toValue: fade,
66
+ duration: 260,
67
+ useNativeDriver: true,
68
+ }),
69
+ ]).start(cb);
70
+ }, [opacity, translateY]);
71
+
72
+ // Open / close
73
+ useEffect(() => {
74
+ if (open) {
75
+ setMounted(true);
76
+ animateTo(0, 1);
77
+ } else if (mounted) {
78
+ animateTo(sheetMaxHeight, 0, () => setMounted(false));
79
+ }
80
+ }, [open, mounted, sheetMaxHeight, animateTo]);
81
+
82
+ // Keyboard awareness
83
+ useEffect(() => {
84
+ if (avoidKeyboardForHeader) {
85
+ return;
86
+ }
87
+
88
+ const showEvent =
89
+ Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
90
+ const hideEvent =
91
+ Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
92
+
93
+ const onShow = (e: any) => {
94
+ keyboardHeight.current = e.endCoordinates?.height || 0;
95
+ animateTo(-keyboardHeight.current / 2, 1);
96
+ };
97
+
98
+ const onHide = () => {
99
+ keyboardHeight.current = 0;
100
+ animateTo(0, 1);
101
+ };
102
+
103
+ const showSub = Keyboard.addListener(showEvent, onShow);
104
+ const hideSub = Keyboard.addListener(hideEvent, onHide);
105
+
106
+ return () => {
107
+ showSub.remove();
108
+ hideSub.remove();
109
+ };
110
+ }, [animateTo, avoidKeyboardForHeader]);
111
+
112
+ const sheetHeightStyle = useMemo(
113
+ () =>
114
+ fitContent
115
+ ? { maxHeight: sheetMaxHeight }
116
+ : { height: sheetMaxHeight },
117
+ [fitContent, sheetMaxHeight]
118
+ );
119
+
120
+ const sheetThemeStyle = useMemo(
121
+ () => ({
122
+ backgroundColor: colorTheme.colors.colorBackgroundPrimary,
123
+ paddingBottom: bottom,
124
+ }),
125
+ [colorTheme, bottom]
126
+ );
127
+
128
+ const sheetAnimationStyle = useMemo(
129
+ () => ({
130
+ transform: [{ translateY }],
131
+ opacity,
132
+ }),
133
+ [translateY, opacity]
134
+ );
135
+
136
+ const headerBorderStyle = useMemo(
137
+ () => ({
138
+ borderBottomColor: colorTheme.colors.colorBorderSubtle,
139
+ }),
140
+ [colorTheme]
141
+ );
142
+
143
+ const titleColorStyle = useMemo(
144
+ () => ({
145
+ color: colorTheme.colors.colorForegroundPrimary,
146
+ }),
147
+ [colorTheme]
148
+ );
149
+
150
+ if (!mounted) {
151
+ return null;
152
+ }
153
+
154
+ return (
155
+ <Modal transparent visible animationType="none" onRequestClose={onClose}>
156
+ <View
157
+ style={styles.overlay}
158
+ pointerEvents={open ? 'auto' : 'none'}
159
+ >
160
+ {open && (
161
+ <TouchableOpacity
162
+ style={StyleSheet.absoluteFill}
163
+ activeOpacity={1}
164
+ onPress={onClose}
165
+ />
166
+ )}
167
+
168
+
169
+ <View style={styles.sheetWrapper}>
170
+ <TouchableWithoutFeedback>
171
+ <Animated.View
172
+ style={[
173
+ styles.sheet,
174
+ sheetHeightStyle,
175
+ sheetThemeStyle,
176
+ sheetAnimationStyle,
177
+ ]}
178
+ >
179
+ <View style={styles.handleBar} />
180
+
181
+ {title && (
182
+ <View
183
+ style={[
184
+ styles.header,
185
+ headerBorderStyle,
186
+ ]}
187
+ >
188
+ <Text
189
+ style={[
190
+ styles.title,
191
+ titleColorStyle,
192
+ ]}
193
+ >
194
+ {title}
195
+ </Text>
196
+
197
+ <TouchableOpacity onPress={onClose}>
198
+ <Icon
199
+ component={<IconMoon icon={FONT_ICON.CLOSE} />}
200
+ variant="medium"
201
+ color={
202
+ colorTheme.colors
203
+ .colorForegroundPrimary
204
+ }
205
+ />
206
+ </TouchableOpacity>
207
+ </View>
208
+ )}
209
+
210
+ {headerContent && (
211
+ avoidKeyboardForHeader ? (
212
+ <KeyboardAvoidingView
213
+ enabled
214
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
215
+ keyboardVerticalOffset={0}
216
+ >
217
+ <View
218
+ style={[
219
+ styles.headerContent,
220
+ headerBorderStyle,
221
+ ]}
222
+ >
223
+ {headerContent}
224
+ </View>
225
+ </KeyboardAvoidingView>
226
+ ) : (
227
+ <View
228
+ style={[
229
+ styles.headerContent,
230
+ headerBorderStyle,
231
+ ]}
232
+ >
233
+ {headerContent}
234
+ </View>
235
+ )
236
+ )}
237
+
238
+ <View style={fitContent ? styles.contentFit : styles.content}>{children}</View>
239
+ </Animated.View>
240
+ </TouchableWithoutFeedback>
241
+ </View>
242
+ </View>
243
+ </Modal>
244
+ );
245
+
246
+ };
247
+
248
+ const styles = StyleSheet.create({
249
+ overlay: {
250
+ flex: 1,
251
+ backgroundColor: 'rgba(0,0,0,0.5)',
252
+ },
253
+ sheetWrapper: {
254
+ flex: 1,
255
+ justifyContent: 'flex-end',
256
+ },
257
+ sheet: {
258
+ borderTopLeftRadius: 20,
259
+ borderTopRightRadius: 20,
260
+ overflow: 'hidden',
261
+ },
262
+ handleBar: {
263
+ width: 40,
264
+ height: 4,
265
+ borderRadius: 2,
266
+ backgroundColor: '#C0C0C0',
267
+ alignSelf: 'center',
268
+ marginVertical: 10,
269
+ },
270
+ header: {
271
+ flexDirection: 'row',
272
+ alignItems: 'center',
273
+ justifyContent: 'space-between',
274
+ paddingHorizontal: 16,
275
+ paddingVertical: 14,
276
+ borderBottomWidth: 1,
277
+ },
278
+ title: {
279
+ fontSize: 18,
280
+ fontWeight: '600',
281
+ },
282
+ content: {
283
+ flex: 1,
284
+ },
285
+ contentFit: {
286
+ flexShrink: 1,
287
+ },
288
+ headerContent: {
289
+ paddingHorizontal: 16,
290
+ paddingBottom: 12,
291
+ borderBottomWidth: 1,
292
+ },
293
+ });
294
+
295
+
296
+ export default NativeBottomSheet;
@@ -0,0 +1,185 @@
1
+ import React, { memo, useMemo } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ Platform,
8
+ } from 'react-native';
9
+ import { spacing } from '../../values/Spacing';
10
+ import { borderRadius } from '../../values/BorderRadius';
11
+ import { borderWidth } from '../../values/BorderWidth';
12
+ import IconMoon from '../../IconMoon';
13
+ import FONT_ICON from '../../assets/resources/fonts/FontIcon.json';
14
+ import Typography from '../../typography/Typography';
15
+
16
+ type ChipSize = 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge';
17
+
18
+ interface SelectChipsProps {
19
+ labels: string[];
20
+ colorTheme: any;
21
+ disabled?: boolean;
22
+ values?: string[];
23
+ onRemove?: (value: string) => void;
24
+ size?: ChipSize;
25
+ wrap?: boolean;
26
+ }
27
+
28
+ const CHIP_SIZE_STYLES: Record<ChipSize, {
29
+ minHeight: number;
30
+ paddingHorizontal: number;
31
+ paddingVertical: number;
32
+ fontSize: number;
33
+ iconSize: number;
34
+ }> = {
35
+ small: { minHeight: 24, paddingHorizontal: 10, paddingVertical: 4, fontSize: 12, iconSize: 10 },
36
+ medium: { minHeight: 28, paddingHorizontal: 12, paddingVertical: 5, fontSize: 13, iconSize: 11 },
37
+ large: { minHeight: 32, paddingHorizontal: 12, paddingVertical: 6, fontSize: 14, iconSize: 12 },
38
+ xlarge: { minHeight: 34, paddingHorizontal: 14, paddingVertical: 7, fontSize: 14, iconSize: 12 },
39
+ xxlarge: { minHeight: 36, paddingHorizontal: 14, paddingVertical: 8, fontSize: 15, iconSize: 13 },
40
+ };
41
+
42
+ const SelectChips = memo(
43
+ ({
44
+ labels,
45
+ colorTheme,
46
+ disabled = false,
47
+ values,
48
+ onRemove,
49
+ size = 'medium',
50
+ wrap = false,
51
+ }: SelectChipsProps) => {
52
+ const sizeStyle = CHIP_SIZE_STYLES[size];
53
+ const chipTextColor = useMemo(
54
+ () => (
55
+ disabled
56
+ ? colorTheme.colors.colorForegroundTertiary
57
+ : colorTheme.colors.colorForegroundPrimary
58
+ ),
59
+ [disabled, colorTheme],
60
+ );
61
+
62
+ const typographyTextStyle = useMemo(
63
+ () => ({
64
+ color: chipTextColor,
65
+ }),
66
+ [chipTextColor],
67
+ );
68
+
69
+ const nativeTextDynamicStyle = useMemo(
70
+ () => ({
71
+ fontSize: sizeStyle.fontSize,
72
+ flexShrink: onRemove ? 1 : 0,
73
+ color: chipTextColor,
74
+ }),
75
+ [sizeStyle.fontSize, onRemove, chipTextColor],
76
+ );
77
+
78
+ const chipDynamicStyle = useMemo(
79
+ () => ({
80
+ borderColor: colorTheme.colors.colorBorderSubtle,
81
+ opacity: disabled ? 0.6 : 1,
82
+ minHeight: sizeStyle.minHeight,
83
+ paddingHorizontal: sizeStyle.paddingHorizontal,
84
+ paddingVertical: sizeStyle.paddingVertical,
85
+ marginBottom: wrap ? spacing.space100 : 0,
86
+ flexShrink: onRemove ? 1 : 0,
87
+ }),
88
+ [
89
+ colorTheme,
90
+ disabled,
91
+ sizeStyle.minHeight,
92
+ sizeStyle.paddingHorizontal,
93
+ sizeStyle.paddingVertical,
94
+ wrap,
95
+ onRemove,
96
+ ],
97
+ );
98
+
99
+ return (
100
+ <>
101
+ {labels.map((label, index) => {
102
+ const value = values?.[index] ?? label;
103
+
104
+ const content = Platform.OS === 'web' ? (
105
+ <Typography
106
+ variant="body2Semibold"
107
+ style={typographyTextStyle}
108
+ >
109
+ {label}
110
+ </Typography>
111
+ ) : (
112
+ <Text
113
+ style={[
114
+ styles.nativeText,
115
+ nativeTextDynamicStyle,
116
+ ]}
117
+ numberOfLines={onRemove ? 1 : undefined}
118
+ >
119
+ {label}
120
+ </Text>
121
+ );
122
+
123
+ return (
124
+ <View
125
+ key={`${label}-${index}`}
126
+ style={[
127
+ styles.chip,
128
+ sizeStyle,
129
+ chipDynamicStyle,
130
+ ]}
131
+ >
132
+ {content}
133
+
134
+ {!!onRemove && !disabled && (
135
+ <TouchableOpacity
136
+ onPress={(e) => {
137
+ e.stopPropagation?.();
138
+ onRemove(value);
139
+ }}
140
+ hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
141
+ style={styles.removeButton}
142
+ >
143
+ <IconMoon
144
+ icon={FONT_ICON.CLOSE}
145
+ size={sizeStyle.iconSize}
146
+ color={colorTheme.colors.colorForegroundPrimary}
147
+ />
148
+ </TouchableOpacity>
149
+ )}
150
+ </View>
151
+ );
152
+ })}
153
+ </>
154
+ );
155
+ }
156
+ );
157
+
158
+ SelectChips.displayName = 'SelectChips';
159
+
160
+ const styles = StyleSheet.create({
161
+ chip: {
162
+ flexDirection: 'row',
163
+ alignItems: 'center',
164
+ borderRadius: borderRadius.borderRadius40,
165
+ borderWidth: borderWidth.borderWidth15,
166
+ alignSelf: 'flex-start',
167
+ marginRight: spacing.space100,
168
+ maxWidth: '100%',
169
+ flexShrink: 1,
170
+ },
171
+ nativeText: {
172
+ fontSize: 14,
173
+ fontWeight: '600',
174
+ flexShrink: 1,
175
+ },
176
+ removeButton: {
177
+ marginLeft: spacing.space100,
178
+ alignItems: 'center',
179
+ justifyContent: 'center',
180
+ width: 16,
181
+ height: 16,
182
+ },
183
+ });
184
+
185
+ export default SelectChips;
@@ -0,0 +1,173 @@
1
+ import React, { useRef, memo, useMemo, useEffect, useCallback } from 'react';
2
+ import type { SelectOption, InputSizeType } from '../Type';
3
+ import SelectListItem from './SelectListItem';
4
+ import { useCustomScrollbar } from '../hooks/useCustomScrollbar';
5
+ import { selectScrollbarCSS } from './SelectScrollbarStyle';
6
+ import Typography from '../../typography/Typography';
7
+ import { isWeb } from '../utils/inputUtils';
8
+
9
+ const marginTopBySize: Record<InputSizeType, number> = {
10
+ small: 8,
11
+ medium: 16,
12
+ large: 24,
13
+ xlarge: 32,
14
+ xxlarge: 40,
15
+ };
16
+
17
+ const emptyStateContainerStyle = {
18
+ padding: '14px 16px',
19
+ textAlign: 'center',
20
+ userSelect: 'none',
21
+ } as const;
22
+
23
+ interface SelectListProps {
24
+ options: SelectOption[];
25
+ value?: string | string[];
26
+ normalizedMultiple?: boolean;
27
+ normalizedListType: 'default' | 'checkbox';
28
+ onOptionSelect: (value: string) => void;
29
+ onCheckboxToggle: (value: string, checked: boolean) => void;
30
+ onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => void;
31
+ colorTheme: any;
32
+ noItemText: string;
33
+ maxVisibleItems?: number;
34
+ selectedIcon?: React.ReactNode;
35
+ disabled?: boolean;
36
+ registerOptionRef: (index: number, el: HTMLElement | null) => void;
37
+ size?: InputSizeType;
38
+ }
39
+
40
+ const SelectList = memo(({
41
+ options,
42
+ value,
43
+ normalizedMultiple,
44
+ normalizedListType,
45
+ onOptionSelect,
46
+ onCheckboxToggle,
47
+ onKeyDown,
48
+ colorTheme,
49
+ noItemText,
50
+ maxVisibleItems,
51
+ selectedIcon,
52
+ disabled = false,
53
+ registerOptionRef,
54
+ size = 'medium',
55
+ }: SelectListProps) => {
56
+ const listRef = useRef<HTMLDivElement | null>(null);
57
+ const optionSelectRef = useRef(onOptionSelect);
58
+ const checkboxToggleRef = useRef(onCheckboxToggle);
59
+
60
+ useEffect(() => {
61
+ optionSelectRef.current = onOptionSelect;
62
+ }, [onOptionSelect]);
63
+
64
+ useEffect(() => {
65
+ checkboxToggleRef.current = onCheckboxToggle;
66
+ }, [onCheckboxToggle]);
67
+
68
+ const stableOnOptionSelect = useCallback((v: string) => {
69
+ optionSelectRef.current(v);
70
+ }, []);
71
+
72
+ const stableOnCheckboxToggle = useCallback((v: string, c: boolean) => {
73
+ checkboxToggleRef.current(v, c);
74
+ }, []);
75
+
76
+ useCustomScrollbar(
77
+ 'select-list-scrollbar-styles',
78
+ selectScrollbarCSS,
79
+ );
80
+
81
+ const ITEM_HEIGHT = 52;
82
+ const maxHeight = useMemo(() => {
83
+ return maxVisibleItems && maxVisibleItems > 0
84
+ ? maxVisibleItems * ITEM_HEIGHT
85
+ : 280;
86
+ }, [maxVisibleItems]);
87
+
88
+ const selectedValuesSet = useMemo(() => {
89
+ if (normalizedMultiple && Array.isArray(value)) {
90
+ return new Set(value);
91
+ }
92
+ return null;
93
+ }, [normalizedMultiple, value]);
94
+
95
+ const dropdownStyle = {
96
+ position: 'absolute',
97
+ top: 'calc(100% - 1px)',
98
+ left: 0,
99
+ width: '100%',
100
+ marginTop: marginTopBySize[size],
101
+ border: `1px solid ${colorTheme.colors.colorBorderSubtle}`,
102
+ backgroundColor: colorTheme.colors.colorBackgroundPrimary,
103
+ borderRadius: 12,
104
+ boxShadow: '0 4px 16px rgba(0,0,0,0.1)',
105
+ zIndex: 9999,
106
+ padding: 0,
107
+ };
108
+
109
+ const scrollContainerStyle = { maxHeight };
110
+
111
+ const noItemsTextStyle = {
112
+ color: colorTheme.colors.colorForegroundSecondary,
113
+ };
114
+
115
+ const renderedItems = useMemo(() => {
116
+ return options.map((option, index) => {
117
+ const isSelected = selectedValuesSet
118
+ ? selectedValuesSet.has(option.value)
119
+ : value === option.value;
120
+
121
+ return (
122
+ <SelectListItem
123
+ key={option.value}
124
+ option={option}
125
+ index={index}
126
+ isSelected={isSelected}
127
+ normalizedListType={normalizedListType}
128
+ onOptionSelect={stableOnOptionSelect}
129
+ onCheckboxToggle={stableOnCheckboxToggle}
130
+ selectedIcon={selectedIcon}
131
+ colorTheme={colorTheme}
132
+ disabled={disabled}
133
+ registerOptionRef={registerOptionRef}
134
+ />
135
+ );
136
+ });
137
+ }, [options, value, selectedValuesSet, normalizedListType, stableOnOptionSelect, stableOnCheckboxToggle, selectedIcon, colorTheme, disabled, registerOptionRef]);
138
+
139
+ // Only render on web platform since this component uses HTML elements
140
+ if (!isWeb) {
141
+ return null;
142
+ }
143
+
144
+ return (
145
+ <div
146
+ ref={listRef}
147
+ className="select-dropdown"
148
+ onKeyDown={onKeyDown}
149
+ style={dropdownStyle}
150
+ >
151
+ <div className="select-scroll-container" style={scrollContainerStyle}>
152
+ {options.length === 0 ? (
153
+ <div style={emptyStateContainerStyle}>
154
+ <Typography
155
+ variant="body2Regular"
156
+ style={noItemsTextStyle}
157
+ >
158
+ {noItemText}
159
+ </Typography>
160
+ </div>
161
+
162
+
163
+ ) : (
164
+ renderedItems
165
+ )}
166
+ </div>
167
+ </div>
168
+ );
169
+ });
170
+
171
+ SelectList.displayName = 'SelectList';
172
+
173
+ export default SelectList;