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,377 @@
1
+ import React, { memo, useMemo, useCallback } from 'react';
2
+ import { View, TouchableOpacity, StyleSheet } from 'react-native';
3
+ import Checkbox from '../../checkbox/Checkbox';
4
+ import type { SelectOption } from '../Type';
5
+ import Typography from '../../typography/Typography';
6
+ import { isWeb } from '../utils/inputUtils';
7
+
8
+ interface SelectListItemProps {
9
+ option: SelectOption;
10
+ index: number;
11
+ isSelected: boolean;
12
+ normalizedListType: 'default' | 'checkbox';
13
+ selectedIcon?: React.ReactNode;
14
+ onOptionSelect: (value: string) => void;
15
+ onCheckboxToggle: (value: string, checked: boolean) => void;
16
+ colorTheme: any;
17
+ disabled?: boolean;
18
+ registerOptionRef?: (index: number, el: HTMLElement | null) => void;
19
+ }
20
+
21
+ const webCheckboxItemBaseStyle: React.CSSProperties = {
22
+ padding: '14px 16px',
23
+ borderRadius: 8,
24
+ };
25
+
26
+ const webDefaultItemBaseStyle: React.CSSProperties = {
27
+ padding: '14px 16px',
28
+ borderRadius: 8,
29
+ display: 'flex',
30
+ alignItems: 'center',
31
+ gap: 12,
32
+ marginBottom: 2,
33
+ };
34
+
35
+ const webInlineIconStyle: React.CSSProperties = {
36
+ display: 'flex',
37
+ alignItems: 'center',
38
+ flexShrink: 0,
39
+ };
40
+
41
+ const webTextBaseStyle: React.CSSProperties = {
42
+ flex: 1,
43
+ overflow: 'hidden',
44
+ };
45
+
46
+ const nativeTextBaseStyle = {
47
+ flex: 1,
48
+ overflow: 'hidden',
49
+ } as const;
50
+
51
+ const SelectListItemCheckbox = ({
52
+ option,
53
+ index,
54
+ isSelected,
55
+ onCheckboxToggle,
56
+ colorTheme,
57
+ disabled,
58
+ registerOptionRef,
59
+ }: SelectListItemProps) => {
60
+ const backgroundColor = useMemo(() => {
61
+ if (disabled) {return 'transparent';}
62
+ return undefined;
63
+ }, [disabled]);
64
+
65
+ const handleRef = useCallback((el: HTMLDivElement | null) => {
66
+ registerOptionRef?.(index, el);
67
+ }, [index, registerOptionRef]);
68
+
69
+ const handleCheckboxChange = useCallback((checked: boolean) => {
70
+ if (!disabled) {
71
+ onCheckboxToggle(option.value, checked);
72
+ }
73
+ }, [disabled, onCheckboxToggle, option.value]);
74
+
75
+ const hoverBackgroundColor = colorTheme.colors.colorStatePrimaryHover;
76
+
77
+ const nativeCheckboxItemStyle = useMemo(
78
+ () => [styles.nativeCheckboxItem, { backgroundColor }],
79
+ [backgroundColor],
80
+ );
81
+
82
+ const webCheckboxItemStyle = useMemo(
83
+ () => ({
84
+ ...webCheckboxItemBaseStyle,
85
+ backgroundColor,
86
+ ...(disabled
87
+ ? { ['--hover-bg' as any]: 'transparent' }
88
+ : { ['--hover-bg' as any]: hoverBackgroundColor }),
89
+ }),
90
+ [backgroundColor, disabled, hoverBackgroundColor],
91
+ );
92
+
93
+ if (!isWeb) {
94
+ return (
95
+ <View style={nativeCheckboxItemStyle}>
96
+ <Checkbox
97
+ checked={isSelected}
98
+ onChange={handleCheckboxChange}
99
+ label={option.label}
100
+ size="medium"
101
+ labelColor={disabled ? colorTheme.colors.colorForegroundTertiary : 'black'}
102
+ disabled={disabled}
103
+ />
104
+ </View>
105
+ );
106
+ }
107
+
108
+ return (
109
+ <div
110
+ className="select-list-item"
111
+ tabIndex={-1}
112
+ ref={handleRef}
113
+ style={webCheckboxItemStyle}
114
+ >
115
+ <Checkbox
116
+ checked={isSelected}
117
+ onChange={handleCheckboxChange}
118
+ label={option.label}
119
+ size="medium"
120
+ labelColor={disabled ? colorTheme.colors.colorForegroundTertiary : 'black'}
121
+ disabled={disabled}
122
+ />
123
+ </div>
124
+ );
125
+ };
126
+
127
+ const SelectListItemDefault = ({
128
+ option,
129
+ index,
130
+ isSelected,
131
+ selectedIcon,
132
+ onOptionSelect,
133
+ colorTheme,
134
+ disabled,
135
+ registerOptionRef,
136
+ }: SelectListItemProps) => {
137
+ const selectedBackgroundColor = colorTheme.colors.colorBackgroundLighter;
138
+ const hoverBackgroundColor = colorTheme.colors.colorStatePrimaryHover;
139
+ const primaryTextColor = colorTheme.colors.colorForegroundPrimary;
140
+ const tertiaryTextColor = colorTheme.colors.colorForegroundTertiary;
141
+
142
+ const backgroundColor = useMemo(() => {
143
+ if (disabled) {return 'transparent';}
144
+ if (isSelected && !selectedIcon) {
145
+ return selectedBackgroundColor;
146
+ }
147
+ return undefined;
148
+ }, [disabled, isSelected, selectedIcon, selectedBackgroundColor]);
149
+
150
+ const textColor = useMemo(() => {
151
+ return disabled ? tertiaryTextColor : primaryTextColor;
152
+ }, [disabled, tertiaryTextColor, primaryTextColor]);
153
+
154
+ const handleRef = useCallback((el: HTMLDivElement | null) => {
155
+ registerOptionRef?.(index, el);
156
+ }, [index, registerOptionRef]);
157
+
158
+ const handleClick = useCallback((e: React.MouseEvent) => {
159
+ if (disabled) {return;}
160
+ e.stopPropagation();
161
+ onOptionSelect(option.value);
162
+ }, [disabled, onOptionSelect, option.value]);
163
+
164
+ const nativeDefaultItemStyle = useMemo(
165
+ () => [styles.nativeDefaultItem, { backgroundColor }],
166
+ [backgroundColor],
167
+ );
168
+
169
+ const nativeTextStyle = useMemo(
170
+ () => ({
171
+ ...nativeTextBaseStyle,
172
+ color: textColor,
173
+ }),
174
+ [textColor],
175
+ );
176
+
177
+ const webDefaultItemStyle = useMemo(
178
+ () => ({
179
+ ...webDefaultItemBaseStyle,
180
+ cursor: disabled ? 'not-allowed' : 'pointer',
181
+ backgroundColor,
182
+ ...(disabled
183
+ ? { ['--hover-bg' as any]: 'transparent' }
184
+ : { ['--hover-bg' as any]: hoverBackgroundColor }),
185
+ }),
186
+ [backgroundColor, disabled, hoverBackgroundColor],
187
+ );
188
+
189
+ const webTextStyle = useMemo(
190
+ () => ({
191
+ ...webTextBaseStyle,
192
+ color: textColor,
193
+ }),
194
+ [textColor],
195
+ );
196
+
197
+ if (!isWeb) {
198
+ return (
199
+ <TouchableOpacity
200
+ style={nativeDefaultItemStyle}
201
+ activeOpacity={disabled ? 1 : 0.7}
202
+ onPress={() => !disabled && onOptionSelect(option.value)}
203
+ disabled={disabled}
204
+ >
205
+ {option.icon && (
206
+ <View style={styles.nativeLeadingIcon}>
207
+ {option.icon}
208
+ </View>
209
+ )}
210
+ <Typography
211
+ variant="body2Regular"
212
+ style={nativeTextStyle}
213
+ >
214
+ {option.label}
215
+ </Typography>
216
+ {isSelected && selectedIcon && (
217
+ <View style={styles.nativeTrailingIcon}>
218
+ {selectedIcon}
219
+ </View>
220
+ )}
221
+ </TouchableOpacity>
222
+ );
223
+ }
224
+
225
+ return (
226
+ <div
227
+ className="select-list-item"
228
+ ref={handleRef}
229
+ tabIndex={-1}
230
+ onClick={handleClick}
231
+ style={webDefaultItemStyle}
232
+ >
233
+ {option.icon && (
234
+ <span style={webInlineIconStyle}>
235
+ {option.icon}
236
+ </span>
237
+ )}
238
+
239
+ <Typography
240
+ variant="body2Regular"
241
+ style={webTextStyle}
242
+ >
243
+ {option.label}
244
+ </Typography>
245
+
246
+
247
+ {isSelected && selectedIcon && (
248
+ <span style={webInlineIconStyle}>
249
+ {selectedIcon}
250
+ </span>
251
+ )}
252
+ </div>
253
+ );
254
+ };
255
+
256
+ const SelectListItem = memo(({
257
+ option,
258
+ index,
259
+ isSelected,
260
+ normalizedListType,
261
+ selectedIcon,
262
+ onOptionSelect,
263
+ onCheckboxToggle,
264
+ colorTheme,
265
+ disabled = false,
266
+ registerOptionRef,
267
+ }: SelectListItemProps) => {
268
+ const handleClickWrapper = useCallback(() => {
269
+ if (!disabled) {
270
+ onOptionSelect(option.value);
271
+ }
272
+ }, [disabled, onOptionSelect, option.value]);
273
+
274
+ if (!isWeb) {
275
+ return normalizedListType === 'checkbox' ? (
276
+ <SelectListItemCheckbox
277
+ option={option}
278
+ index={index}
279
+ isSelected={isSelected}
280
+ normalizedListType={normalizedListType}
281
+ onOptionSelect={onOptionSelect}
282
+ onCheckboxToggle={onCheckboxToggle}
283
+ colorTheme={colorTheme}
284
+ disabled={disabled}
285
+ />
286
+ ) : (
287
+ <SelectListItemDefault
288
+ option={option}
289
+ index={index}
290
+ isSelected={isSelected}
291
+ normalizedListType={normalizedListType}
292
+ selectedIcon={selectedIcon}
293
+ onOptionSelect={onOptionSelect}
294
+ onCheckboxToggle={onCheckboxToggle}
295
+ colorTheme={colorTheme}
296
+ disabled={disabled}
297
+ />
298
+ );
299
+ }
300
+
301
+ return (
302
+ <div data-select-option data-index={index} onClick={handleClickWrapper}>
303
+ {normalizedListType === 'checkbox' ? (
304
+ <SelectListItemCheckbox
305
+ option={option}
306
+ index={index}
307
+ isSelected={isSelected}
308
+ normalizedListType={normalizedListType}
309
+ onOptionSelect={onOptionSelect}
310
+ onCheckboxToggle={onCheckboxToggle}
311
+ colorTheme={colorTheme}
312
+ disabled={disabled}
313
+ registerOptionRef={registerOptionRef}
314
+ />
315
+ ) : (
316
+ <SelectListItemDefault
317
+ option={option}
318
+ index={index}
319
+ isSelected={isSelected}
320
+ normalizedListType={normalizedListType}
321
+ selectedIcon={selectedIcon}
322
+ onOptionSelect={onOptionSelect}
323
+ onCheckboxToggle={onCheckboxToggle}
324
+ colorTheme={colorTheme}
325
+ disabled={disabled}
326
+ registerOptionRef={registerOptionRef}
327
+ />
328
+ )}
329
+ </div>
330
+ );
331
+ }, (prev, next) => {
332
+ if (prev.disabled !== next.disabled) {return false;}
333
+ if (prev.isSelected !== next.isSelected) {return false;}
334
+ if (prev.normalizedListType !== next.normalizedListType) {return false;}
335
+
336
+ if (prev.isSelected || next.isSelected) {
337
+ if (prev.selectedIcon !== next.selectedIcon) {return false;}
338
+ }
339
+
340
+ if (prev.index !== next.index) {return false;}
341
+ if (prev.option.value !== next.option.value) {return false;}
342
+ if (prev.option.label !== next.option.label) {return false;}
343
+ if (prev.colorTheme !== next.colorTheme) {return false;}
344
+
345
+ return true;
346
+ });
347
+
348
+ SelectListItem.displayName = 'SelectListItem';
349
+
350
+ const styles = StyleSheet.create({
351
+ nativeCheckboxItem: {
352
+ paddingHorizontal: 16,
353
+ paddingVertical: 14,
354
+ borderRadius: 8,
355
+ marginBottom: 2,
356
+ },
357
+ nativeDefaultItem: {
358
+ paddingHorizontal: 16,
359
+ paddingVertical: 14,
360
+ borderRadius: 8,
361
+ flexDirection: 'row',
362
+ alignItems: 'center',
363
+ marginBottom: 2,
364
+ },
365
+ nativeLeadingIcon: {
366
+ alignItems: 'center',
367
+ flexShrink: 0,
368
+ marginRight: 12,
369
+ },
370
+ nativeTrailingIcon: {
371
+ alignItems: 'center',
372
+ flexShrink: 0,
373
+ marginLeft: 12,
374
+ },
375
+ });
376
+
377
+ export default SelectListItem;
@@ -0,0 +1,44 @@
1
+ export const selectScrollbarCSS = `
2
+ .select-scroll-container {
3
+ max-height: 280px;
4
+ overflow-y: auto;
5
+ padding: 0;
6
+ margin: 0;
7
+ overscroll-behavior: contain;
8
+ scrollbar-width: thin;
9
+ scrollbar-color: rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.25);
10
+ }
11
+
12
+ .select-scroll-container::-webkit-scrollbar {
13
+ width: 6px;
14
+ }
15
+
16
+ .select-scroll-container::-webkit-scrollbar-track {
17
+ background: rgba(255, 255, 255, 0.25);
18
+ }
19
+
20
+ .select-scroll-container::-webkit-scrollbar-thumb {
21
+ background-color: rgba(0, 0, 0, 0.25);
22
+ border-radius: 8px;
23
+ }
24
+
25
+ .select-scroll-container::-webkit-scrollbar-thumb:hover {
26
+ background-color: rgba(0, 0, 0, 0.4);
27
+ }
28
+
29
+ /* Hover styling for select list items using CSS variable for theme color */
30
+ .select-list-item:hover {
31
+ background-color: var(--hover-bg);
32
+ }
33
+
34
+ /* Keyboard focus styling to match hover */
35
+ .select-list-item:focus {
36
+ background-color: var(--hover-bg);
37
+ outline: none;
38
+ }
39
+
40
+ /* When the dropdown is hovered, suppress focus highlight so mouse takes precedence */
41
+ .select-dropdown:hover .select-list-item:focus {
42
+ background-color: transparent;
43
+ }
44
+ `;
@@ -0,0 +1,17 @@
1
+ import { useEffect } from 'react';
2
+
3
+ export const useCustomScrollbar = (
4
+ id: string,
5
+ css: string,
6
+ ) => {
7
+ useEffect(() => {
8
+ if (typeof document === 'undefined') return;
9
+
10
+ if (document.getElementById(id)) return;
11
+
12
+ const style = document.createElement('style');
13
+ style.id = id;
14
+ style.innerHTML = css;
15
+ document.head.appendChild(style);
16
+ }, [id, css]);
17
+ };
@@ -0,0 +1,41 @@
1
+ import { useState, useMemo } from 'react';
2
+
3
+ interface UseInputStateParams {
4
+ value?: string | string[];
5
+ hover?: boolean;
6
+ normalizedMultiple?: boolean;
7
+ variant?: string;
8
+ }
9
+
10
+ export const useInputState = ({
11
+ value,
12
+ hover = false,
13
+ normalizedMultiple,
14
+ variant,
15
+ }: UseInputStateParams) => {
16
+ const [isFocused, setIsFocused] = useState(false);
17
+ const [internalHovered, setInternalHovered] = useState(false);
18
+
19
+ const isHovered = hover || internalHovered;
20
+
21
+ const selectedValues: string[] = useMemo(() => {
22
+ if (!normalizedMultiple) return [];
23
+
24
+ return Array.isArray(value) ? value : [];
25
+ }, [normalizedMultiple, value]);
26
+
27
+ const hasValue = useMemo(() => {
28
+ return normalizedMultiple
29
+ ? selectedValues.length > 0
30
+ : typeof value === 'string' && value.length > 0;
31
+ }, [normalizedMultiple, selectedValues, value, variant]);
32
+
33
+ return {
34
+ isFocused,
35
+ setIsFocused,
36
+ isHovered,
37
+ setInternalHovered,
38
+ hasValue,
39
+ selectedValues,
40
+ };
41
+ };
@@ -0,0 +1,132 @@
1
+ import {useEffect, useRef, useMemo} from 'react';
2
+ import {Animated, Platform} from 'react-native';
3
+ import type {InputSizeType} from '../Type';
4
+ import type {ExtendedInputVariant} from '../Type';
5
+ import {labelLeftOffsetBySize} from '../utils/inputUtils';
6
+
7
+ const isWeb = Platform.OS === 'web';
8
+
9
+ interface UseLabelAnimationParams {
10
+ value?: string | string[];
11
+ hasValue: boolean;
12
+ isFocused: boolean;
13
+ variant?: ExtendedInputVariant;
14
+ size?: InputSizeType;
15
+ prefixIcon?: boolean;
16
+ selectDropdownOpen?: boolean;
17
+ }
18
+ const LABEL_ANIMATION_BY_SIZE: Record<
19
+ InputSizeType,
20
+ {
21
+ translateY: number;
22
+ scale: number;
23
+ }
24
+ > = {
25
+ small: {
26
+ translateY: -20,
27
+ scale: 0.88,
28
+ },
29
+ medium: {
30
+ translateY: -24,
31
+ scale: 0.85,
32
+ },
33
+ large: {
34
+ translateY: -24,
35
+ scale: 0.9,
36
+ },
37
+ xlarge: {
38
+ translateY: -30,
39
+ scale: 0.95,
40
+ },
41
+ xxlarge: {
42
+ translateY: -32,
43
+ scale: 1,
44
+ },
45
+ };
46
+
47
+ export const useLabelAnimation = ({
48
+ value,
49
+ hasValue,
50
+ isFocused,
51
+ variant,
52
+ size = 'medium',
53
+ prefixIcon = false,
54
+ selectDropdownOpen = false,
55
+ }: UseLabelAnimationParams) => {
56
+ const forceFloatLabelByDefault =
57
+ variant === 'phoneWithFlag' ||
58
+ variant === 'phoneWithCodeOnly' ||
59
+ variant === 'currency';
60
+
61
+ const animationConfig =
62
+ LABEL_ANIMATION_BY_SIZE[size] ?? LABEL_ANIMATION_BY_SIZE.medium;
63
+
64
+ const labelAnimation = useRef(
65
+ new Animated.Value(forceFloatLabelByDefault || value ? 1 : 0),
66
+ ).current;
67
+
68
+ useEffect(() => {
69
+ if (variant === 'select') {
70
+ Animated.timing(labelAnimation, {
71
+ toValue: selectDropdownOpen || hasValue ? 1 : 0,
72
+ duration: 200,
73
+ useNativeDriver: false,
74
+ }).start();
75
+ return;
76
+ }
77
+
78
+ if (forceFloatLabelByDefault) {
79
+ Animated.timing(labelAnimation, {
80
+ toValue: 1,
81
+ duration: 200,
82
+ useNativeDriver: false,
83
+ }).start();
84
+ } else if (isFocused || hasValue) {
85
+ Animated.timing(labelAnimation, {
86
+ toValue: 1,
87
+ duration: 200,
88
+ useNativeDriver: false,
89
+ }).start();
90
+ } else {
91
+ Animated.timing(labelAnimation, {
92
+ toValue: 0,
93
+ duration: 200,
94
+ useNativeDriver: false,
95
+ }).start();
96
+ }
97
+ }, [
98
+ isFocused,
99
+ hasValue,
100
+ variant,
101
+ selectDropdownOpen,
102
+ labelAnimation,
103
+ forceFloatLabelByDefault,
104
+ ]);
105
+
106
+ const translateY = labelAnimation.interpolate({
107
+ inputRange: [0, 1],
108
+ outputRange: [0, animationConfig.translateY],
109
+ });
110
+
111
+ const scale = labelAnimation.interpolate({
112
+ inputRange: [0, 1],
113
+ outputRange: [1, animationConfig.scale],
114
+ });
115
+
116
+ const initialLeft = prefixIcon
117
+ ? (labelLeftOffsetBySize[size] || 44) + 10
118
+ : 12;
119
+
120
+ const left = labelAnimation.interpolate({
121
+ inputRange: [0, 1],
122
+ outputRange: [initialLeft, 12],
123
+ });
124
+
125
+ return {
126
+ labelAnimation,
127
+ animatedStyle: {
128
+ transform: [{translateY}, {scale}],
129
+ left,
130
+ },
131
+ };
132
+ };
@@ -0,0 +1,38 @@
1
+ import { useEffect } from 'react';
2
+ import { Platform } from 'react-native';
3
+
4
+ const isWeb = Platform.OS === 'web';
5
+
6
+ interface UseOutsideClickParams {
7
+ enabled: boolean;
8
+ onOutsideClick: () => void;
9
+ ignoredClassNames: string[];
10
+ }
11
+
12
+ export const useOutsideClick = ({
13
+ enabled,
14
+ onOutsideClick,
15
+ ignoredClassNames,
16
+ }: UseOutsideClickParams) => {
17
+ useEffect(() => {
18
+ if (!isWeb || !enabled) return;
19
+
20
+ const onClickOutside = (event: MouseEvent) => {
21
+ const target = event.target as HTMLElement | null;
22
+ if (!target) return;
23
+
24
+ const shouldIgnore = ignoredClassNames.some(className =>
25
+ target.closest(`.${className}`),
26
+ );
27
+
28
+ if (!shouldIgnore) {
29
+ onOutsideClick();
30
+ }
31
+ };
32
+
33
+ window.addEventListener('mousedown', onClickOutside);
34
+ return () => {
35
+ window.removeEventListener('mousedown', onClickOutside);
36
+ };
37
+ }, [enabled, onOutsideClick, ignoredClassNames]);
38
+ };