@sudobility/components-rn 1.0.37 → 1.0.38

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.
@@ -0,0 +1,46 @@
1
+ import { default as React } from 'react';
2
+ export interface PopupSelectOption {
3
+ label: string;
4
+ value: string;
5
+ disabled?: boolean;
6
+ }
7
+ export interface PopupSelectProps {
8
+ /** Currently selected value */
9
+ value?: string;
10
+ /** Callback when value changes */
11
+ onValueChange?: (value: string) => void;
12
+ /** Options to display */
13
+ options: PopupSelectOption[];
14
+ /** Placeholder text when no value selected */
15
+ placeholder?: string;
16
+ /** Title shown in the modal header */
17
+ title?: string;
18
+ /** Whether the select is disabled */
19
+ disabled?: boolean;
20
+ /** Style overrides for the trigger button */
21
+ triggerStyle?: object;
22
+ /** Style overrides for the trigger text */
23
+ triggerTextStyle?: object;
24
+ }
25
+ /**
26
+ * PopupSelect Component
27
+ *
28
+ * A select component that opens a pageSheet modal with a styled option list.
29
+ * Provides a native-feeling selection experience on iOS and Android.
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * <PopupSelect
34
+ * value={selected}
35
+ * onValueChange={setSelected}
36
+ * options={[
37
+ * { label: 'Option A', value: 'a' },
38
+ * { label: 'Option B', value: 'b' },
39
+ * ]}
40
+ * placeholder="Choose..."
41
+ * title="Select Option"
42
+ * />
43
+ * ```
44
+ */
45
+ export declare const PopupSelect: React.FC<PopupSelectProps>;
46
+ //# sourceMappingURL=PopupSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PopupSelect.d.ts","sourceRoot":"","sources":["../../../src/ui/PopupSelect/PopupSelect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAYrD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,yBAAyB;IACzB,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAID;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAkGlD,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { PopupSelect } from './PopupSelect';
2
+ export type { PopupSelectProps, PopupSelectOption } from './PopupSelect';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/PopupSelect/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudobility/components-rn",
3
- "version": "1.0.37",
3
+ "version": "1.0.38",
4
4
  "description": "React Native UI components and design system - Ported from @sudobility/components",
5
5
  "type": "module",
6
6
  "main": "dist/index.esm.js",
package/src/index.ts CHANGED
@@ -33,6 +33,7 @@ export * from './ui/Checkbox';
33
33
  export * from './ui/Switch';
34
34
  export * from './ui/HelperText';
35
35
  export * from './ui/Select';
36
+ export * from './ui/PopupSelect';
36
37
  export * from './ui/SearchInput';
37
38
  export * from './ui/NumberInput';
38
39
 
@@ -0,0 +1,247 @@
1
+ import React, { useState, useCallback } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ TouchableOpacity,
6
+ Modal,
7
+ FlatList,
8
+ StyleSheet,
9
+ Pressable,
10
+ } from 'react-native';
11
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
12
+
13
+ export interface PopupSelectOption {
14
+ label: string;
15
+ value: string;
16
+ disabled?: boolean;
17
+ }
18
+
19
+ export interface PopupSelectProps {
20
+ /** Currently selected value */
21
+ value?: string;
22
+ /** Callback when value changes */
23
+ onValueChange?: (value: string) => void;
24
+ /** Options to display */
25
+ options: PopupSelectOption[];
26
+ /** Placeholder text when no value selected */
27
+ placeholder?: string;
28
+ /** Title shown in the modal header */
29
+ title?: string;
30
+ /** Whether the select is disabled */
31
+ disabled?: boolean;
32
+ /** Style overrides for the trigger button */
33
+ triggerStyle?: object;
34
+ /** Style overrides for the trigger text */
35
+ triggerTextStyle?: object;
36
+ }
37
+
38
+ const ItemSeparator = () => <View style={styles.separator} />;
39
+
40
+ /**
41
+ * PopupSelect Component
42
+ *
43
+ * A select component that opens a pageSheet modal with a styled option list.
44
+ * Provides a native-feeling selection experience on iOS and Android.
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * <PopupSelect
49
+ * value={selected}
50
+ * onValueChange={setSelected}
51
+ * options={[
52
+ * { label: 'Option A', value: 'a' },
53
+ * { label: 'Option B', value: 'b' },
54
+ * ]}
55
+ * placeholder="Choose..."
56
+ * title="Select Option"
57
+ * />
58
+ * ```
59
+ */
60
+ export const PopupSelect: React.FC<PopupSelectProps> = ({
61
+ value,
62
+ onValueChange,
63
+ options,
64
+ placeholder = 'Select...',
65
+ title = 'Select',
66
+ disabled = false,
67
+ triggerStyle,
68
+ triggerTextStyle,
69
+ }) => {
70
+ const insets = useSafeAreaInsets();
71
+ const [modalVisible, setModalVisible] = useState(false);
72
+
73
+ const selectedOption = options.find(opt => opt.value === value);
74
+
75
+ const handleSelect = useCallback(
76
+ (optionValue: string) => {
77
+ onValueChange?.(optionValue);
78
+ setModalVisible(false);
79
+ },
80
+ [onValueChange]
81
+ );
82
+
83
+ const renderOption = ({ item }: { item: PopupSelectOption }) => {
84
+ const isSelected = item.value === value;
85
+ return (
86
+ <TouchableOpacity
87
+ style={[styles.optionItem, isSelected && styles.optionItemSelected]}
88
+ onPress={() => !item.disabled && handleSelect(item.value)}
89
+ disabled={item.disabled}
90
+ activeOpacity={0.7}
91
+ >
92
+ <Text
93
+ style={[
94
+ styles.optionLabel,
95
+ isSelected && styles.optionLabelSelected,
96
+ item.disabled && styles.optionDisabled,
97
+ ]}
98
+ >
99
+ {item.label}
100
+ </Text>
101
+ {isSelected && <Text style={styles.checkmark}>✓</Text>}
102
+ </TouchableOpacity>
103
+ );
104
+ };
105
+
106
+ return (
107
+ <>
108
+ <TouchableOpacity
109
+ style={[
110
+ styles.trigger,
111
+ disabled && styles.triggerDisabled,
112
+ triggerStyle,
113
+ ]}
114
+ onPress={() => !disabled && setModalVisible(true)}
115
+ activeOpacity={0.7}
116
+ >
117
+ <Text
118
+ style={[
119
+ styles.triggerText,
120
+ !selectedOption && styles.triggerPlaceholder,
121
+ triggerTextStyle,
122
+ ]}
123
+ numberOfLines={1}
124
+ >
125
+ {selectedOption?.label ?? placeholder}
126
+ </Text>
127
+ <Text style={styles.triggerArrow}>▼</Text>
128
+ </TouchableOpacity>
129
+
130
+ <Modal
131
+ visible={modalVisible}
132
+ animationType='slide'
133
+ presentationStyle='pageSheet'
134
+ onRequestClose={() => setModalVisible(false)}
135
+ >
136
+ <View style={[styles.modalContainer, { paddingTop: insets.top }]}>
137
+ <View style={styles.modalHeader}>
138
+ <Text style={styles.modalTitle}>{title}</Text>
139
+ <Pressable
140
+ style={styles.closeButton}
141
+ onPress={() => setModalVisible(false)}
142
+ >
143
+ <Text style={styles.closeButtonText}>Done</Text>
144
+ </Pressable>
145
+ </View>
146
+
147
+ <FlatList
148
+ data={options}
149
+ keyExtractor={(item: PopupSelectOption) => item.value}
150
+ renderItem={renderOption}
151
+ contentContainerStyle={styles.listContent}
152
+ ItemSeparatorComponent={ItemSeparator}
153
+ />
154
+ </View>
155
+ </Modal>
156
+ </>
157
+ );
158
+ };
159
+
160
+ const styles = StyleSheet.create({
161
+ trigger: {
162
+ flexDirection: 'row',
163
+ alignItems: 'center',
164
+ justifyContent: 'space-between',
165
+ borderWidth: 1,
166
+ borderColor: '#d1d5db',
167
+ borderRadius: 8,
168
+ paddingHorizontal: 12,
169
+ paddingVertical: 12,
170
+ backgroundColor: '#ffffff',
171
+ },
172
+ triggerDisabled: {
173
+ opacity: 0.5,
174
+ },
175
+ triggerText: {
176
+ flex: 1,
177
+ fontSize: 16,
178
+ color: '#1a1a1a',
179
+ },
180
+ triggerPlaceholder: {
181
+ color: '#9ca3af',
182
+ },
183
+ triggerArrow: {
184
+ fontSize: 10,
185
+ color: '#9ca3af',
186
+ marginLeft: 8,
187
+ },
188
+ modalContainer: {
189
+ flex: 1,
190
+ backgroundColor: '#ffffff',
191
+ },
192
+ modalHeader: {
193
+ flexDirection: 'row',
194
+ justifyContent: 'space-between',
195
+ alignItems: 'center',
196
+ paddingHorizontal: 16,
197
+ paddingVertical: 12,
198
+ borderBottomWidth: 1,
199
+ borderBottomColor: '#e9ecef',
200
+ },
201
+ modalTitle: {
202
+ fontSize: 18,
203
+ fontWeight: '600',
204
+ color: '#1a1a1a',
205
+ },
206
+ closeButton: {
207
+ padding: 8,
208
+ },
209
+ closeButtonText: {
210
+ fontSize: 16,
211
+ fontWeight: '600',
212
+ color: '#3b82f6',
213
+ },
214
+ listContent: {
215
+ padding: 16,
216
+ },
217
+ optionItem: {
218
+ flexDirection: 'row',
219
+ alignItems: 'center',
220
+ padding: 16,
221
+ backgroundColor: '#f8f9fa',
222
+ borderRadius: 12,
223
+ },
224
+ optionItemSelected: {
225
+ backgroundColor: '#e0f2fe',
226
+ },
227
+ optionLabel: {
228
+ flex: 1,
229
+ fontSize: 16,
230
+ color: '#1a1a1a',
231
+ },
232
+ optionLabelSelected: {
233
+ fontWeight: '600',
234
+ color: '#3b82f6',
235
+ },
236
+ optionDisabled: {
237
+ opacity: 0.5,
238
+ },
239
+ checkmark: {
240
+ fontSize: 18,
241
+ color: '#3b82f6',
242
+ fontWeight: 'bold',
243
+ },
244
+ separator: {
245
+ height: 8,
246
+ },
247
+ });
@@ -0,0 +1,2 @@
1
+ export { PopupSelect } from './PopupSelect';
2
+ export type { PopupSelectProps, PopupSelectOption } from './PopupSelect';