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,181 @@
1
+ // CurrencyInput/NativeCurrencyInput.tsx
2
+ import React from 'react';
3
+ import {
4
+ View,
5
+ Text,
6
+ StyleSheet,
7
+ TouchableOpacity,
8
+ FlatList,
9
+ TextInput,
10
+ } from 'react-native';
11
+ import { spacing } from '../../../values/Spacing';
12
+ import {
13
+ currencySymbols,
14
+ availableCurrencies,
15
+ } from '../../utils/inputUtils';
16
+ import NativeBottomSheet from '../../components/NativeBottomSheet';
17
+ import type { CurrencyInputProps } from './types';
18
+
19
+ const NativeCurrencyInput = ({
20
+ value,
21
+ currency,
22
+ disabled,
23
+ labelText,
24
+ placeholder,
25
+ onChangeText,
26
+ onToggleDropdown,
27
+ onSelectCurrency,
28
+ currencyDropdownOpen,
29
+ colorTheme,
30
+ }: CurrencyInputProps) => {
31
+ const currencySymbolColorStyle = {
32
+ color: disabled
33
+ ? colorTheme.colors.colorForegroundTertiary
34
+ : colorTheme.colors.colorForegroundPrimary,
35
+ };
36
+
37
+ const separatorColorStyle = {
38
+ backgroundColor: colorTheme.colors.colorBorderSubtle,
39
+ };
40
+
41
+ const currencyInputTextColorStyle = {
42
+ color: disabled
43
+ ? colorTheme.colors.colorForegroundTertiary
44
+ : colorTheme.colors.colorForegroundPrimary,
45
+ };
46
+
47
+ const renderCurrencyOption = ({ item }: { item: string }) => {
48
+ const selected = item === currency;
49
+ const currencyOptionBackgroundStyle = {
50
+ backgroundColor: selected
51
+ ? colorTheme.colors.colorStatePrimaryHover
52
+ : colorTheme.colors.colorBackgroundPrimary,
53
+ };
54
+ const currencyOptionTextColorStyle = {
55
+ color: colorTheme.colors.colorForegroundPrimary,
56
+ };
57
+
58
+ return (
59
+ <TouchableOpacity
60
+ style={[
61
+ styles.currencyOption,
62
+ currencyOptionBackgroundStyle,
63
+ ]}
64
+ activeOpacity={0.7}
65
+ onPress={() => {
66
+ onSelectCurrency(item);
67
+ onToggleDropdown();
68
+ }}
69
+ >
70
+ <Text
71
+ style={[
72
+ styles.currencyText,
73
+ currencyOptionTextColorStyle,
74
+ ]}
75
+ >
76
+ {currencySymbols[item] || item} ({item})
77
+ </Text>
78
+ </TouchableOpacity>
79
+ );
80
+ };
81
+
82
+ return (
83
+ <>
84
+ {/* Input row */}
85
+ <View style={styles.nativeContainer}>
86
+ <TouchableOpacity
87
+ style={styles.currencyButton}
88
+ onPress={onToggleDropdown}
89
+ disabled={disabled}
90
+ activeOpacity={0.7}
91
+ >
92
+ <Text
93
+ style={[
94
+ styles.currencySymbol,
95
+ currencySymbolColorStyle,
96
+ ]}
97
+ >
98
+ {currencySymbols[currency] || currency}
99
+ </Text>
100
+ </TouchableOpacity>
101
+
102
+ <View
103
+ style={[
104
+ styles.separator,
105
+ separatorColorStyle,
106
+ ]}
107
+ />
108
+
109
+ <TextInput
110
+ style={[
111
+ styles.currencyInput,
112
+ currencyInputTextColorStyle,
113
+ ]}
114
+ value={typeof value === 'string' ? value : ''}
115
+ onChangeText={onChangeText}
116
+ placeholder={!labelText ? placeholder : undefined}
117
+ placeholderTextColor={
118
+ colorTheme.colors.colorForegroundTertiary
119
+ }
120
+ editable={!disabled}
121
+ />
122
+ </View>
123
+
124
+ {/* Currency picker */}
125
+ <NativeBottomSheet
126
+ open={currencyDropdownOpen}
127
+ onClose={onToggleDropdown}
128
+ title="Select Currency"
129
+ heightRatio={0.4}
130
+ colorTheme={colorTheme}
131
+ >
132
+ <FlatList
133
+ data={availableCurrencies}
134
+ keyExtractor={(item) => item}
135
+ renderItem={renderCurrencyOption}
136
+ keyboardShouldPersistTaps="handled"
137
+ />
138
+ </NativeBottomSheet>
139
+ </>
140
+ );
141
+ };
142
+
143
+ const styles = StyleSheet.create({
144
+ nativeContainer: {
145
+ flexDirection: 'row',
146
+ alignItems: 'center',
147
+ width: '100%',
148
+ minHeight: 48,
149
+ },
150
+
151
+ currencyButton: {
152
+ justifyContent: 'center',
153
+ paddingHorizontal: 12,
154
+ },
155
+
156
+ currencySymbol: {
157
+ fontSize: 16,
158
+ fontWeight: '500',
159
+ },
160
+ separator: {
161
+ width: 1,
162
+ height: 24,
163
+ marginHorizontal: 4,
164
+ },
165
+ currencyInput: {
166
+ flex: 1,
167
+ minWidth: 0,
168
+ fontSize: 16,
169
+ paddingLeft: spacing.space200,
170
+ paddingRight: spacing.space200,
171
+ },
172
+ currencyOption: {
173
+ paddingHorizontal: 16,
174
+ paddingVertical: 12,
175
+ },
176
+ currencyText: {
177
+ fontSize: 16,
178
+ },
179
+ });
180
+
181
+ export default NativeCurrencyInput;
@@ -0,0 +1,163 @@
1
+ // CurrencyInput/WebCurrencyInput.tsx
2
+ import React, {useCallback, useMemo, useRef} from 'react';
3
+ import {spacing} from '../../../values/Spacing';
4
+ import {currencySymbols, availableCurrencies} from '../../utils/inputUtils';
5
+ import type {CurrencyInputProps} from './types';
6
+ import InputPortal from '../../components/InputPortal';
7
+
8
+ const webStyles = {
9
+ root: {
10
+ display: 'flex',
11
+ alignItems: 'center',
12
+ width: '100%',
13
+ minHeight: 'var(--slice-input-min-height, 32px)',
14
+ } as const,
15
+ selectorWrapper: {
16
+ display: 'inline-flex',
17
+ alignItems: 'center',
18
+ height: '100%',
19
+ } as const,
20
+ currencyButton: {
21
+ background: 'transparent',
22
+ border: 'none',
23
+ fontSize: 'var(--slice-input-font-size, 16px)',
24
+ padding: '0 12px',
25
+ height: '100%',
26
+ } as const,
27
+ dropdownOption: {
28
+ padding: '6px 12px',
29
+ cursor: 'pointer',
30
+ userSelect: 'none',
31
+ } as const,
32
+ separator: {
33
+ width: 1,
34
+ height: 24,
35
+ margin: '0 4px',
36
+ } as const,
37
+ input: {
38
+ flex: 1,
39
+ minWidth: 0,
40
+ border: 'none',
41
+ outline: 'none',
42
+ fontSize: 'var(--slice-input-font-size, 16px)',
43
+ backgroundColor: 'transparent',
44
+ fontFamily: 'Lato',
45
+ } as const,
46
+ };
47
+
48
+ const WebCurrencyInput = ({
49
+ value,
50
+ currency,
51
+ disabled,
52
+ labelText,
53
+ placeholder,
54
+ onChangeText,
55
+ onToggleDropdown,
56
+ onCloseDropdown,
57
+ onSelectCurrency,
58
+ currencyDropdownOpen,
59
+ colorTheme,
60
+ portalAnchorRef,
61
+ }: CurrencyInputProps) => {
62
+ const containerRef = useRef<HTMLDivElement>(null);
63
+ const resolvedAnchorRef = portalAnchorRef ?? containerRef;
64
+ const handlePortalClose = onCloseDropdown ?? onToggleDropdown;
65
+
66
+ const buttonTextColor = disabled
67
+ ? colorTheme.colors.colorForegroundTertiary
68
+ : colorTheme.colors.colorForegroundPrimary;
69
+
70
+ const inputTextColor = disabled
71
+ ? colorTheme.colors.colorForegroundTertiary
72
+ : colorTheme.colors.colorForegroundPrimary;
73
+
74
+ const currencyButtonStyle = useMemo(
75
+ () => ({
76
+ ...webStyles.currencyButton,
77
+ cursor: disabled ? 'not-allowed' : 'pointer',
78
+ color: buttonTextColor,
79
+ }),
80
+ [disabled, buttonTextColor],
81
+ );
82
+
83
+ const separatorStyle = useMemo(
84
+ () => ({
85
+ ...webStyles.separator,
86
+ backgroundColor: colorTheme.colors.colorBorderSubtle,
87
+ }),
88
+ [colorTheme],
89
+ );
90
+
91
+ const inputStyle = useMemo(
92
+ () => ({
93
+ ...webStyles.input,
94
+ paddingLeft: spacing.space200,
95
+ paddingRight: spacing.space200,
96
+ color: inputTextColor,
97
+ }),
98
+ [inputTextColor],
99
+ );
100
+
101
+ const getCurrencyOptionStyle = useCallback(
102
+ (itemCurrency: string) => ({
103
+ ...webStyles.dropdownOption,
104
+ backgroundColor:
105
+ itemCurrency === currency
106
+ ? colorTheme.colors.colorBorderAccent
107
+ : 'transparent',
108
+ color: colorTheme.colors.colorForegroundPrimary,
109
+ }),
110
+ [currency, colorTheme],
111
+ );
112
+
113
+ const renderCurrencyOptions = useMemo(
114
+ () =>
115
+ availableCurrencies.map(c => (
116
+ <div
117
+ key={c}
118
+ onClick={() => onSelectCurrency(c)}
119
+ style={getCurrencyOptionStyle(c)}>
120
+ {currencySymbols[c] || c}
121
+ </div>
122
+ )),
123
+ [getCurrencyOptionStyle, onSelectCurrency],
124
+ );
125
+
126
+ return (
127
+ <div ref={containerRef} style={webStyles.root}>
128
+ <div style={webStyles.selectorWrapper}>
129
+ <button
130
+ type="button"
131
+ onClick={onToggleDropdown}
132
+ disabled={disabled}
133
+ aria-label="Select currency"
134
+ style={currencyButtonStyle}>
135
+ {currencySymbols[currency] || currency}
136
+ </button>
137
+ </div>
138
+
139
+ <InputPortal
140
+ open={currencyDropdownOpen && !disabled}
141
+ anchorRef={resolvedAnchorRef}
142
+ borderColor={colorTheme.colors.colorBorderSubtle}
143
+ backgroundColor={colorTheme.colors.colorBackgroundPrimary}
144
+ onRequestClose={handlePortalClose}
145
+ maxHeight={180}>
146
+ {renderCurrencyOptions}
147
+ </InputPortal>
148
+
149
+ <div style={separatorStyle} />
150
+
151
+ <input
152
+ type="text"
153
+ value={typeof value === 'string' ? value : ''}
154
+ onChange={e => onChangeText?.(e.target.value)}
155
+ placeholder={!labelText ? placeholder : undefined}
156
+ disabled={disabled}
157
+ style={inputStyle}
158
+ />
159
+ </div>
160
+ );
161
+ };
162
+
163
+ export default React.memo(WebCurrencyInput);
@@ -0,0 +1,17 @@
1
+ // CurrencyInput/types.ts
2
+ import type React from 'react';
3
+
4
+ export interface CurrencyInputProps {
5
+ value?: string;
6
+ currency: string;
7
+ disabled?: boolean;
8
+ labelText?: string;
9
+ placeholder?: string;
10
+ onChangeText?: (value: string) => void;
11
+ onToggleDropdown: () => void;
12
+ onCloseDropdown?: () => void;
13
+ onSelectCurrency: (currency: string) => void;
14
+ currencyDropdownOpen: boolean;
15
+ colorTheme: any;
16
+ portalAnchorRef?: React.RefObject<HTMLElement>;
17
+ }
@@ -0,0 +1,189 @@
1
+ import React from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ FlatList,
8
+ TextInput,
9
+ Animated,
10
+ } from 'react-native';
11
+ import type {CountryCode} from 'libphonenumber-js';
12
+ import {getCountries, getCountryCallingCode} from 'react-phone-number-input';
13
+ import NativeBottomSheet from '../../components/NativeBottomSheet';
14
+ import type {PhoneInputProps} from './types';
15
+
16
+ const flagSizeBySize: Record<string, {width: number; height: number}> = {
17
+ small: {width: 20, height: 15},
18
+ medium: {width: 24, height: 18},
19
+ large: {width: 28, height: 21},
20
+ xlarge: {width: 32, height: 24},
21
+ xxlarge: {width: 36, height: 27},
22
+ };
23
+
24
+ const NativePhoneInput = ({
25
+ mode,
26
+ country,
27
+ number,
28
+ dropdownOpen,
29
+ disabled,
30
+ labelText,
31
+ placeholder,
32
+ onToggleDropdown,
33
+ onSelectCountry,
34
+ onNumberChange,
35
+ onFocus,
36
+ onBlur,
37
+ colorTheme,
38
+ size = 'medium',
39
+ }: PhoneInputProps) => {
40
+ const flagSize = flagSizeBySize[size] || flagSizeBySize.medium;
41
+ const flagSizeStyle = {
42
+ width: flagSize.width,
43
+ height: flagSize.height,
44
+ };
45
+ const countryCodeTextColorStyle = {
46
+ color: colorTheme.colors.colorForegroundPrimary,
47
+ };
48
+ const separatorColorStyle = {
49
+ backgroundColor: colorTheme.colors.colorBorderSubtle,
50
+ };
51
+ const phoneInputTextColorStyle = {
52
+ color: colorTheme.colors.colorForegroundPrimary,
53
+ };
54
+
55
+ const renderCountryOption = ({item}: {item: string}) => {
56
+ const isSelected = item === country;
57
+ const countryOptionBackgroundStyle = {
58
+ backgroundColor: isSelected
59
+ ? colorTheme.colors.colorStatePrimaryHover
60
+ : colorTheme.colors.colorBackgroundPrimary,
61
+ };
62
+ const countryTextColorStyle = {
63
+ color: colorTheme.colors.colorForegroundPrimary,
64
+ };
65
+
66
+ return (
67
+ <TouchableOpacity
68
+ style={[styles.countryOption, countryOptionBackgroundStyle]}
69
+ activeOpacity={0.7}
70
+ onPress={() => {
71
+ onSelectCountry(item as CountryCode);
72
+ onToggleDropdown();
73
+ }}>
74
+ {mode === 'flag' && (
75
+ <Animated.Image
76
+ source={{
77
+ uri: `https://flagcdn.com/w20/${item.toLowerCase()}.png`,
78
+ }}
79
+ style={[styles.flagImage, flagSizeStyle]}
80
+ />
81
+ )}
82
+
83
+ <Text style={[styles.countryText, countryTextColorStyle]}>
84
+ +{getCountryCallingCode(item as CountryCode)} {item}
85
+ </Text>
86
+ </TouchableOpacity>
87
+ );
88
+ };
89
+
90
+ return (
91
+ <>
92
+ {/* Input row */}
93
+ <View style={styles.nativeContainer}>
94
+ <TouchableOpacity
95
+ style={styles.countryCodeButton}
96
+ onPress={onToggleDropdown}
97
+ disabled={disabled}
98
+ activeOpacity={0.7}>
99
+ {mode === 'flag' && (
100
+ <Animated.Image
101
+ source={{
102
+ uri: `https://flagcdn.com/w40/${country.toLowerCase()}.png`,
103
+ }}
104
+ style={[styles.flagImage, flagSizeStyle]}
105
+ />
106
+ )}
107
+
108
+ <Text style={[styles.countryCodeText, countryCodeTextColorStyle]}>
109
+ +{getCountryCallingCode(country)}
110
+ </Text>
111
+ </TouchableOpacity>
112
+
113
+ <View style={[styles.separator, separatorColorStyle]} />
114
+
115
+ <TextInput
116
+ style={[styles.phoneInput, phoneInputTextColorStyle]}
117
+ value={number}
118
+ onChangeText={onNumberChange}
119
+ onFocus={onFocus}
120
+ onBlur={onBlur}
121
+ placeholder={!labelText ? placeholder : undefined}
122
+ placeholderTextColor={colorTheme.colors.colorForegroundTertiary}
123
+ editable={!disabled}
124
+ keyboardType="phone-pad"
125
+ />
126
+ </View>
127
+
128
+ {/* Country picker */}
129
+ <NativeBottomSheet
130
+ open={dropdownOpen}
131
+ onClose={onToggleDropdown}
132
+ title="Select Country"
133
+ heightRatio={0.6}
134
+ colorTheme={colorTheme}>
135
+ <FlatList
136
+ data={getCountries()}
137
+ keyExtractor={item => item}
138
+ renderItem={renderCountryOption}
139
+ keyboardShouldPersistTaps="handled"
140
+ />
141
+ </NativeBottomSheet>
142
+ </>
143
+ );
144
+ };
145
+
146
+ const styles = StyleSheet.create({
147
+ nativeContainer: {
148
+ flexDirection: 'row',
149
+ alignItems: 'center',
150
+ flex: 1,
151
+ },
152
+ countryCodeButton: {
153
+ flexDirection: 'row',
154
+ alignItems: 'center',
155
+ paddingHorizontal: 8,
156
+ },
157
+ flagImage: {
158
+ borderRadius: 2,
159
+ marginRight: 8,
160
+ },
161
+ countryCodeText: {
162
+ fontSize: 16,
163
+ fontWeight: '500',
164
+ },
165
+ separator: {
166
+ width: 1,
167
+ height: 24,
168
+ marginHorizontal: 8,
169
+ },
170
+ phoneInput: {
171
+ flex: 1,
172
+ fontSize: 16,
173
+ paddingHorizontal: 8,
174
+ paddingVertical: 0,
175
+ textAlignVertical: 'center',
176
+ },
177
+ countryOption: {
178
+ flexDirection: 'row',
179
+ alignItems: 'center',
180
+ paddingHorizontal: 16,
181
+ paddingVertical: 12,
182
+ },
183
+ countryText: {
184
+ fontSize: 16,
185
+ marginLeft: 12,
186
+ },
187
+ });
188
+
189
+ export default NativePhoneInput;
@@ -0,0 +1,16 @@
1
+ // PhoneInput.tsx
2
+ import React from 'react';
3
+ import { isWeb } from '../../utils/inputUtils';
4
+ import type { PhoneInputProps } from './types';
5
+ import NativePhoneInput from './NativePhoneInput';
6
+ import WebPhoneInput from './WebPhoneInput';
7
+
8
+ const PhoneInput = (props: PhoneInputProps) => {
9
+ if (isWeb) {
10
+ return <WebPhoneInput {...props} />;
11
+ }
12
+
13
+ return <NativePhoneInput {...props} />;
14
+ };
15
+
16
+ export default PhoneInput;