@shaquillehinds/react-native-dropdown-selector 0.0.1

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 (61) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +526 -0
  3. package/lib/commonjs/DropDownSelector/DropDownSelector.controller.js +129 -0
  4. package/lib/commonjs/DropDownSelector/DropDownSelector.controller.js.map +1 -0
  5. package/lib/commonjs/DropDownSelector/DropDownSelector.js +136 -0
  6. package/lib/commonjs/DropDownSelector/DropDownSelector.js.map +1 -0
  7. package/lib/commonjs/DropDownSelector/DropDownSelector.types.js +6 -0
  8. package/lib/commonjs/DropDownSelector/DropDownSelector.types.js.map +1 -0
  9. package/lib/commonjs/DropDownSelector/index.js +28 -0
  10. package/lib/commonjs/DropDownSelector/index.js.map +1 -0
  11. package/lib/commonjs/DropDownSelector/svgs/ChevronUp.js +26 -0
  12. package/lib/commonjs/DropDownSelector/svgs/ChevronUp.js.map +1 -0
  13. package/lib/commonjs/index.js +21 -0
  14. package/lib/commonjs/index.js.map +1 -0
  15. package/lib/commonjs/package.json +1 -0
  16. package/lib/module/DropDownSelector/DropDownSelector.controller.js +125 -0
  17. package/lib/module/DropDownSelector/DropDownSelector.controller.js.map +1 -0
  18. package/lib/module/DropDownSelector/DropDownSelector.js +132 -0
  19. package/lib/module/DropDownSelector/DropDownSelector.js.map +1 -0
  20. package/lib/module/DropDownSelector/DropDownSelector.types.js +4 -0
  21. package/lib/module/DropDownSelector/DropDownSelector.types.js.map +1 -0
  22. package/lib/module/DropDownSelector/index.js +5 -0
  23. package/lib/module/DropDownSelector/index.js.map +1 -0
  24. package/lib/module/DropDownSelector/svgs/ChevronUp.js +21 -0
  25. package/lib/module/DropDownSelector/svgs/ChevronUp.js.map +1 -0
  26. package/lib/module/index.js +6 -0
  27. package/lib/module/index.js.map +1 -0
  28. package/lib/module/package.json +1 -0
  29. package/lib/typescript/commonjs/package.json +1 -0
  30. package/lib/typescript/commonjs/src/DropDownSelector/DropDownSelector.controller.d.ts +28 -0
  31. package/lib/typescript/commonjs/src/DropDownSelector/DropDownSelector.controller.d.ts.map +1 -0
  32. package/lib/typescript/commonjs/src/DropDownSelector/DropDownSelector.d.ts +3 -0
  33. package/lib/typescript/commonjs/src/DropDownSelector/DropDownSelector.d.ts.map +1 -0
  34. package/lib/typescript/commonjs/src/DropDownSelector/DropDownSelector.types.d.ts +45 -0
  35. package/lib/typescript/commonjs/src/DropDownSelector/DropDownSelector.types.d.ts.map +1 -0
  36. package/lib/typescript/commonjs/src/DropDownSelector/index.d.ts +3 -0
  37. package/lib/typescript/commonjs/src/DropDownSelector/index.d.ts.map +1 -0
  38. package/lib/typescript/commonjs/src/DropDownSelector/svgs/ChevronUp.d.ts +5 -0
  39. package/lib/typescript/commonjs/src/DropDownSelector/svgs/ChevronUp.d.ts.map +1 -0
  40. package/lib/typescript/commonjs/src/index.d.ts +4 -0
  41. package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
  42. package/lib/typescript/module/package.json +1 -0
  43. package/lib/typescript/module/src/DropDownSelector/DropDownSelector.controller.d.ts +28 -0
  44. package/lib/typescript/module/src/DropDownSelector/DropDownSelector.controller.d.ts.map +1 -0
  45. package/lib/typescript/module/src/DropDownSelector/DropDownSelector.d.ts +3 -0
  46. package/lib/typescript/module/src/DropDownSelector/DropDownSelector.d.ts.map +1 -0
  47. package/lib/typescript/module/src/DropDownSelector/DropDownSelector.types.d.ts +45 -0
  48. package/lib/typescript/module/src/DropDownSelector/DropDownSelector.types.d.ts.map +1 -0
  49. package/lib/typescript/module/src/DropDownSelector/index.d.ts +3 -0
  50. package/lib/typescript/module/src/DropDownSelector/index.d.ts.map +1 -0
  51. package/lib/typescript/module/src/DropDownSelector/svgs/ChevronUp.d.ts +5 -0
  52. package/lib/typescript/module/src/DropDownSelector/svgs/ChevronUp.d.ts.map +1 -0
  53. package/lib/typescript/module/src/index.d.ts +4 -0
  54. package/lib/typescript/module/src/index.d.ts.map +1 -0
  55. package/package.json +203 -0
  56. package/src/DropDownSelector/DropDownSelector.controller.tsx +174 -0
  57. package/src/DropDownSelector/DropDownSelector.tsx +183 -0
  58. package/src/DropDownSelector/DropDownSelector.types.ts +44 -0
  59. package/src/DropDownSelector/index.ts +2 -0
  60. package/src/DropDownSelector/svgs/ChevronUp.tsx +18 -0
  61. package/src/index.tsx +4 -0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Shaquille Hinds
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,526 @@
1
+ # @shaquillehinds/react-native-dropdown-selector
2
+
3
+ A beautifully animated, fully customizable dropdown selector for React Native that intelligently adapts to screen position and just works out of the box.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@shaquillehinds/react-native-dropdown-selector.svg)](https://www.npmjs.com/package/@shaquillehinds/react-native-dropdown-selector)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## ✨ Features
9
+
10
+ - 🎯 **Smart Positioning** - Automatically detects available screen space and renders upward or downward
11
+ - 🎨 **Fully Customizable** - Style every element from the button to individual items
12
+ - 🔄 **Smooth Animations** - Fluid spring and timing animations with configurable parameters
13
+ - 📱 **Cross-Platform** - Works seamlessly on iOS and Android with platform-specific optimizations
14
+ - 🎭 **Custom Components** - Replace default components with your own React components
15
+ - 🔍 **TypeScript Support** - Full type safety with generic type support
16
+ - 🪶 **Lightweight** - Minimal dependencies, built on react-native-essentials
17
+ - ♿ **Accessible** - Includes proper touch targets and visual feedback
18
+
19
+ ## 📦 Installation
20
+
21
+ ```bash
22
+ npm install @shaquillehinds/react-native-dropdown-selector
23
+ ```
24
+
25
+ or
26
+
27
+ ```bash
28
+ yarn add @shaquillehinds/react-native-dropdown-selector
29
+ ```
30
+
31
+ ### Peer Dependencies
32
+
33
+ This package requires the following peer dependencies:
34
+
35
+ ```bash
36
+ npm install react-native-gesture-handler react-native-reanimated react-native-svg
37
+ ```
38
+
39
+ Make sure to complete the installation setup for these libraries as per their documentation.
40
+
41
+ ## 🚀 Basic Usage
42
+
43
+ ```tsx
44
+ import { DropDownSelector } from '@shaquillehinds/react-native-dropdown-selector';
45
+ import { useState } from 'react';
46
+
47
+ function MyComponent() {
48
+ const [selectedValue, setSelectedValue] = useState<string>('apple');
49
+
50
+ const fruits = [
51
+ { label: 'Apple', value: 'apple' },
52
+ { label: 'Banana', value: 'banana' },
53
+ { label: 'Orange', value: 'orange' },
54
+ { label: 'Mango', value: 'mango' },
55
+ ];
56
+
57
+ return (
58
+ <DropDownSelector
59
+ items={fruits}
60
+ selectedItem={selectedValue}
61
+ onSelect={setSelectedValue}
62
+ placeholder="Select a fruit"
63
+ />
64
+ );
65
+ }
66
+ ```
67
+
68
+ ## 🎨 Advanced Customization
69
+
70
+ ### Styling the Dropdown Button
71
+
72
+ ```tsx
73
+ <DropDownSelector
74
+ items={items}
75
+ selectedItem={selectedValue}
76
+ onSelect={setSelectedValue}
77
+ placeholder="Select option"
78
+ dropdownButtonProps={{
79
+ backgroundColor: '#007AFF',
80
+ borderRadius: 'large',
81
+ padding: [2, 6],
82
+ }}
83
+ dropdownButtonTextProps={{
84
+ color: 'white',
85
+ fontSize: 16,
86
+ fontWeight: '600',
87
+ }}
88
+ />
89
+ ```
90
+
91
+ ### Custom Icon Component
92
+
93
+ ```tsx
94
+ import { ChevronDown } from './icons';
95
+
96
+ <DropDownSelector
97
+ items={items}
98
+ selectedItem={selectedValue}
99
+ onSelect={setSelectedValue}
100
+ placeholder="Select option"
101
+ DropdownButtonIcon={({ isOpen, expandDirection }) => (
102
+ <ChevronDown
103
+ color="#007AFF"
104
+ style={{
105
+ transform: [{ rotate: isOpen ? '180deg' : '0deg' }],
106
+ }}
107
+ />
108
+ )}
109
+ />;
110
+ ```
111
+
112
+ ### Custom Dropdown Item Component
113
+
114
+ ```tsx
115
+ <DropDownSelector
116
+ items={items}
117
+ selectedItem={selectedValue}
118
+ onSelect={setSelectedValue}
119
+ placeholder="Select option"
120
+ DropdownItemComponent={({ item, isSelected }) => (
121
+ <View style={styles.customItem}>
122
+ <Image source={{ uri: item.icon }} style={styles.icon} />
123
+ <Text style={[styles.itemText, isSelected && styles.selected]}>
124
+ {item.label}
125
+ </Text>
126
+ {isSelected && <CheckIcon />}
127
+ </View>
128
+ )}
129
+ />
130
+ ```
131
+
132
+ ### Configuring Animations
133
+
134
+ ```tsx
135
+ <DropDownSelector
136
+ items={items}
137
+ selectedItem={selectedValue}
138
+ onSelect={setSelectedValue}
139
+ placeholder="Select option"
140
+ expandAnimationConfig={{
141
+ type: 'spring',
142
+ speed: 12,
143
+ bounciness: 8,
144
+ }}
145
+ unMountDelayInMilliSeconds={250}
146
+ />
147
+ ```
148
+
149
+ ### Controlling Expand Direction
150
+
151
+ ```tsx
152
+ // Force expand upward
153
+ <DropDownSelector
154
+ items={items}
155
+ selectedItem={selectedValue}
156
+ onSelect={setSelectedValue}
157
+ placeholder="Select option"
158
+ expandDirection="up"
159
+ />
160
+
161
+ // Force expand downward
162
+ <DropDownSelector
163
+ items={items}
164
+ selectedItem={selectedValue}
165
+ onSelect={setSelectedValue}
166
+ placeholder="Select option"
167
+ expandDirection="down"
168
+ />
169
+
170
+ // Auto-detect (default behavior)
171
+ <DropDownSelector
172
+ items={items}
173
+ selectedItem={selectedValue}
174
+ onSelect={setSelectedValue}
175
+ placeholder="Select option"
176
+ // expandDirection not specified - automatically detects best direction
177
+ />
178
+ ```
179
+
180
+ ### Custom Expand Distance
181
+
182
+ ```tsx
183
+ <DropDownSelector
184
+ items={items}
185
+ selectedItem={selectedValue}
186
+ onSelect={setSelectedValue}
187
+ placeholder="Select option"
188
+ expandDistance={250} // Maximum height in pixels
189
+ />
190
+ ```
191
+
192
+ ## 📚 API Reference
193
+
194
+ ### Props
195
+
196
+ | Prop | Type | Required | Description |
197
+ | ------------------------------- | --------------------------------- | -------- | ------------------------------------------------- |
198
+ | `items` | `DropDownItem<T>[]` | ✅ | Array of items to display in dropdown |
199
+ | `selectedItem` | `T` | ✅ | Currently selected item value |
200
+ | `onSelect` | `(item: T) => void` | ✅ | Callback when an item is selected |
201
+ | `placeholder` | `string` | ✅ | Placeholder text when no item is selected |
202
+ | `onOpen` | `() => void` | ❌ | Callback when dropdown opens |
203
+ | `onClose` | `() => void` | ❌ | Callback when dropdown closes |
204
+ | `unMountDelayInMilliSeconds` | `number` | ❌ | Delay before unmounting dropdown (default: 300ms) |
205
+ | `isDisabled` | `boolean` | ❌ | Disables the dropdown selector |
206
+ | `disableShadow` | `boolean` | ❌ | Removes shadow from dropdown |
207
+ | `expandDirection` | `'up' \| 'down'` | ❌ | Forces dropdown to expand in specific direction |
208
+ | `expandDistance` | `number` | ❌ | Maximum height for dropdown content |
209
+ | `expandAnimationConfig` | `AnimationConfig` | ❌ | Custom animation configuration |
210
+ | `containerProps` | `LayoutProps` | ❌ | Props for the root container |
211
+ | `dropdownButtonProps` | `RNPressableLayoutProps` | ❌ | Props for the dropdown button |
212
+ | `dropdownButtonTextProps` | `BaseTextProps` | ❌ | Props for the button text |
213
+ | `DropdownButtonIcon` | `Component` | ❌ | Custom button icon component |
214
+ | `dropdownScrollViewProps` | `ScrollViewProps` | ❌ | Props for the dropdown ScrollView |
215
+ | `dropdownContentContainerProps` | `LayoutProps` | ❌ | Props for the dropdown content container |
216
+ | `DropdownItemComponent` | `Component` | ❌ | Custom dropdown item component |
217
+ | `onDropdownItemPress` | `(item: DropDownItem<T>) => void` | ❌ | Callback when dropdown item is pressed |
218
+ | `dropdownItemProps` | `TouchableLayoutProps` | ❌ | Props for individual dropdown items |
219
+ | `dropdownItemTextProps` | `BaseTextProps` | ❌ | Props for dropdown item text |
220
+ | `DropdownItemSelectedIcon` | `Component` | ❌ | Custom selected item icon component |
221
+
222
+ ### Types
223
+
224
+ #### DropDownItem<T>
225
+
226
+ ```typescript
227
+ type DropDownItem<T> = {
228
+ label: string; // Display text
229
+ value: T; // Value (can be any type)
230
+ };
231
+ ```
232
+
233
+ #### AnimationConfig
234
+
235
+ ```typescript
236
+ type AnimationConfig =
237
+ | (Omit<Animated.TimingAnimationConfig, 'toValue'> & { type: 'timing' })
238
+ | (Omit<Animated.SpringAnimationConfig, 'toValue'> & { type: 'spring' });
239
+ ```
240
+
241
+ ### Custom Component Props
242
+
243
+ #### DropdownButtonIcon
244
+
245
+ ```typescript
246
+ type DropdownButtonIconProps = {
247
+ isOpen: boolean;
248
+ expandDirection: 'up' | 'down';
249
+ };
250
+ ```
251
+
252
+ #### DropdownItemComponent
253
+
254
+ ```typescript
255
+ type DropdownItemComponentProps<T> = {
256
+ item: DropDownItem<T>;
257
+ isSelected: boolean;
258
+ };
259
+ ```
260
+
261
+ #### DropdownItemSelectedIcon
262
+
263
+ ```typescript
264
+ type DropdownItemSelectedIconProps<T> = {
265
+ item: DropDownItem<T>;
266
+ };
267
+ ```
268
+
269
+ ## 🎯 Advanced Examples
270
+
271
+ ### With TypeScript Generics
272
+
273
+ ```tsx
274
+ interface User {
275
+ id: number;
276
+ name: string;
277
+ email: string;
278
+ }
279
+
280
+ function UserSelector() {
281
+ const [selectedUserId, setSelectedUserId] = useState<number>(1);
282
+
283
+ const users: DropDownItem<number>[] = [
284
+ { label: 'John Doe', value: 1 },
285
+ { label: 'Jane Smith', value: 2 },
286
+ { label: 'Bob Johnson', value: 3 },
287
+ ];
288
+
289
+ return (
290
+ <DropDownSelector<number>
291
+ items={users}
292
+ selectedItem={selectedUserId}
293
+ onSelect={setSelectedUserId}
294
+ placeholder="Select a user"
295
+ />
296
+ );
297
+ }
298
+ ```
299
+
300
+ ### With Complex Objects
301
+
302
+ ```tsx
303
+ interface Country {
304
+ code: string;
305
+ name: string;
306
+ flag: string;
307
+ }
308
+
309
+ function CountrySelector() {
310
+ const [selectedCountry, setSelectedCountry] = useState<string>('US');
311
+
312
+ const countries: DropDownItem<string>[] = [
313
+ { label: '🇺🇸 United States', value: 'US' },
314
+ { label: '🇨🇦 Canada', value: 'CA' },
315
+ { label: '🇬🇧 United Kingdom', value: 'GB' },
316
+ { label: '🇦🇺 Australia', value: 'AU' },
317
+ ];
318
+
319
+ return (
320
+ <DropDownSelector<string>
321
+ items={countries}
322
+ selectedItem={selectedCountry}
323
+ onSelect={setSelectedCountry}
324
+ placeholder="Select a country"
325
+ onDropdownItemPress={(item) => {
326
+ console.log('Selected country:', item.label);
327
+ }}
328
+ />
329
+ );
330
+ }
331
+ ```
332
+
333
+ ### With Lifecycle Callbacks
334
+
335
+ ```tsx
336
+ <DropDownSelector
337
+ items={items}
338
+ selectedItem={selectedValue}
339
+ onSelect={setSelectedValue}
340
+ placeholder="Select option"
341
+ onOpen={() => {
342
+ console.log('Dropdown opened');
343
+ // Track analytics, pause animations, etc.
344
+ }}
345
+ onClose={() => {
346
+ console.log('Dropdown closed');
347
+ // Resume animations, save state, etc.
348
+ }}
349
+ onDropdownItemPress={(item) => {
350
+ console.log('Item pressed:', item);
351
+ // Custom handling before selection
352
+ }}
353
+ />
354
+ ```
355
+
356
+ ### Fully Customized Theme
357
+
358
+ ```tsx
359
+ <DropDownSelector
360
+ items={items}
361
+ selectedItem={selectedValue}
362
+ onSelect={setSelectedValue}
363
+ placeholder="Select option"
364
+ containerProps={{
365
+ margin: [2, 0],
366
+ }}
367
+ dropdownButtonProps={{
368
+ backgroundColor: '#1E1E1E',
369
+ borderRadius: 'large',
370
+ padding: [3, 5],
371
+ borderWidth: 1,
372
+ borderColor: '#333',
373
+ }}
374
+ dropdownButtonTextProps={{
375
+ color: '#FFFFFF',
376
+ fontSize: 16,
377
+ fontWeight: '500',
378
+ }}
379
+ dropdownContentContainerProps={{
380
+ backgroundColor: '#2A2A2A',
381
+ borderRadius: 'soft',
382
+ }}
383
+ dropdownItemProps={{
384
+ padding: [3, 5],
385
+ backgroundColor: '#2A2A2A',
386
+ }}
387
+ dropdownItemTextProps={{
388
+ color: '#FFFFFF',
389
+ fontSize: 15,
390
+ }}
391
+ disableShadow={false}
392
+ />
393
+ ```
394
+
395
+ ## 🔧 How It Works
396
+
397
+ ### Smart Positioning
398
+
399
+ The dropdown automatically measures its position on the screen and determines whether there's enough space to expand downward. If not, it expands upward. This ensures the dropdown is always visible and accessible.
400
+
401
+ ```tsx
402
+ // The component automatically:
403
+ // 1. Measures its position using onLayout
404
+ // 2. Calculates distance to bottom of screen
405
+ // 3. Compares with expandDistance (or default maxHeight)
406
+ // 4. Renders upward if insufficient space below
407
+ // 5. Animates chevron icon to match direction
408
+ ```
409
+
410
+ ### Animation System
411
+
412
+ The component uses `react-native-reanimated` for smooth, performant animations:
413
+
414
+ - **Dropdown expansion**: Configurable spring or timing animation
415
+ - **Chevron rotation**: Spring animation that follows expand direction
416
+ - **Android shadow**: Timing animation for platform-specific shadow rendering
417
+
418
+ ### Modal Integration
419
+
420
+ Uses `@shaquillehinds/react-native-essentials` modal system for proper overlay handling, ensuring the dropdown appears above other content with correct z-index management.
421
+
422
+ ## 🎨 Styling with react-native-essentials
423
+
424
+ This package leverages `@shaquillehinds/react-native-essentials` for its layout and styling system, which provides:
425
+
426
+ - Predefined border radius sizes (`soft`, `medium`, `large`)
427
+ - Shadow utilities with platform-specific handling
428
+ - Responsive sizing based on device orientation
429
+ - Flexible layout components with intuitive props
430
+
431
+ Example of essentials-powered styling:
432
+
433
+ ```tsx
434
+ <DropDownSelector
435
+ dropdownButtonProps={{
436
+ borderRadius: 'medium', // Uses predefined radius
437
+ padding: [2, 5], // [vertical, horizontal] shorthand
438
+ backgroundColor: 'white',
439
+ }}
440
+ />
441
+ ```
442
+
443
+ ## 🐛 Troubleshooting
444
+
445
+ ### Dropdown not appearing
446
+
447
+ Ensure you've installed and configured peer dependencies:
448
+
449
+ - `react-native-gesture-handler`
450
+ - `react-native-reanimated`
451
+ - `react-native-svg`
452
+
453
+ ### Items not scrolling
454
+
455
+ Check that `react-native-gesture-handler` is properly set up in your app's entry file:
456
+
457
+ ```tsx
458
+ import 'react-native-gesture-handler';
459
+ // ... rest of your imports
460
+ ```
461
+
462
+ ### Shadow not showing on Android
463
+
464
+ The component handles Android shadows differently. If you're still not seeing shadows, try:
465
+
466
+ - Setting `disableShadow={false}` explicitly
467
+ - Checking that your Android API level supports the shadow properties
468
+
469
+ ### Dropdown position incorrect
470
+
471
+ The component measures position on mount and when opened. If your layout shifts after initial render:
472
+
473
+ ```tsx
474
+ // Force remeasure by adding key prop when layout changes
475
+ <DropDownSelector
476
+ key={layoutKey}
477
+ items={items}
478
+ selectedItem={selectedValue}
479
+ onSelect={setSelectedValue}
480
+ placeholder="Select option"
481
+ />
482
+ ```
483
+
484
+ ## 📝 Examples Repository
485
+
486
+ For more examples and complete implementation patterns, check out the [examples folder](https://github.com/shaquillehinds/react-native-dropdown-selector/tree/main/example) in the repository.
487
+
488
+ ## 🤝 Contributing
489
+
490
+ Contributions are welcome! Please read our [Contributing Guide](./CONTRIBUTING.md) and [Code of Conduct](./CODE_OF_CONDUCT.md) before submitting pull requests.
491
+
492
+ ### Development Setup
493
+
494
+ ```bash
495
+ # Clone the repository
496
+ git clone https://github.com/shaquillehinds/react-native-dropdown-selector.git
497
+
498
+ # Install dependencies
499
+ yarn install
500
+
501
+ # Run the example app
502
+ yarn example start
503
+ ```
504
+
505
+ ## 📄 License
506
+
507
+ MIT © [Shaquille Hinds](https://github.com/shaquillehinds)
508
+
509
+ ## 🙏 Acknowledgments
510
+
511
+ Built with:
512
+
513
+ - [@shaquillehinds/react-native-essentials](https://www.npmjs.com/package/@shaquillehinds/react-native-essentials) - Layout and utility components
514
+ - [react-native-reanimated](https://docs.swmansion.com/react-native-reanimated/) - Smooth animations
515
+ - [react-native-gesture-handler](https://docs.swmansion.com/react-native-gesture-handler/) - Touch handling
516
+ - [react-native-svg](https://github.com/software-mansion/react-native-svg) - Vector graphics
517
+
518
+ ## 📮 Support
519
+
520
+ - 📧 Email: shaqdulove@gmail.com
521
+ - 🐛 Issues: [GitHub Issues](https://github.com/shaquillehinds/react-native-dropdown-selector/issues)
522
+ - 💬 Discussions: [GitHub Discussions](https://github.com/shaquillehinds/react-native-dropdown-selector/discussions)
523
+
524
+ ---
525
+
526
+ Made with ❤️ by [Shaquille Hinds](https://github.com/shaquillehinds)
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.DropDownSelectorController = DropDownSelectorController;
7
+ var _reactNativeEssentials = require("@shaquillehinds/react-native-essentials");
8
+ var _react = require("react");
9
+ function DropDownSelectorController(props) {
10
+ const [showItems, setShowItems] = (0, _react.useState)(false);
11
+ const [hasPageY, setHasPageY] = (0, _react.useState)(false);
12
+ const {
13
+ screenHeight,
14
+ relativeY
15
+ } = (0, _reactNativeEssentials.useDeviceOrientation)();
16
+ const pageYRef = (0, _react.useRef)(null);
17
+ const scrollViewRef = (0, _react.useRef)(null);
18
+ const canRenderDownRef = (0, _react.useRef)(null);
19
+ const animateChevronRef = (0, _react.useRef)(null);
20
+ const animateComponentRef = (0, _react.useRef)(null);
21
+ const animateAndroidShadowRef = (0, _react.useRef)(null);
22
+ const unMountDelayInMilliSeconds = (0, _react.useMemo)(() => {
23
+ if (props.expandAnimationConfig?.type === 'timing') return props.expandAnimationConfig?.duration || 300;
24
+ return props.unMountDelayInMilliSeconds || 300;
25
+ }, [props.expandAnimationConfig]);
26
+ const maxHeight = (0, _react.useMemo)(() => props.expandDistance || relativeY(30), [relativeY, props.expandDistance]);
27
+ const label = (0, _react.useMemo)(() => props.items.find(item => item.value === props.selectedItem)?.label, [props.items, props.selectedItem]);
28
+ const canRenderDown = (0, _react.useMemo)(() => {
29
+ if (props.expandDirection === 'up') return false;
30
+ if (props.expandDirection === 'down') return true;
31
+ if (!pageYRef.current) return null;
32
+ if (!showItems && canRenderDownRef.current !== null) return canRenderDownRef.current;
33
+ const distanceToBottom = screenHeight - pageYRef.current;
34
+ if (distanceToBottom < maxHeight) canRenderDownRef.current = false;else canRenderDownRef.current = true;
35
+ return canRenderDownRef.current;
36
+ }, [showItems, screenHeight, maxHeight, hasPageY]);
37
+ const androidShadowAnimationConfig = (0, _react.useMemo)(() => ({
38
+ toValue: 1,
39
+ type: 'timing',
40
+ useNativeDriver: true,
41
+ duration: canRenderDown ? showItems ? 400 : 200 : showItems ? 800 : 100
42
+ }), [canRenderDown, showItems]);
43
+ const scrollViewStyle = (0, _react.useMemo)(() => ({
44
+ overflow: 'hidden',
45
+ maxHeight: maxHeight,
46
+ minHeight: relativeY(5),
47
+ borderRadius: _reactNativeEssentials.radiusSizes.soft,
48
+ transform: [{
49
+ translateY: canRenderDown ? 0 : -maxHeight + relativeY(4)
50
+ }]
51
+ }), [canRenderDown, maxHeight, relativeY]);
52
+ const chevronAnimationConfig = (0, _react.useMemo)(() => ({
53
+ toValue: canRenderDown ? 1 : -1,
54
+ type: 'spring',
55
+ speed: 1,
56
+ bounciness: 1,
57
+ useNativeDriver: true
58
+ }), [canRenderDown]);
59
+ const selectionItemsListAnimationConfig = (0, _react.useMemo)(() => ({
60
+ toValue: 0,
61
+ type: 'spring',
62
+ speed: 1,
63
+ bounciness: 1,
64
+ useNativeDriver: true
65
+ }), []);
66
+ const chevronAnimatedStyle = (0, _react.useCallback)(scaleY => ({
67
+ transform: [{
68
+ scaleY
69
+ }]
70
+ }), []);
71
+ const handleLayout = (0, _react.useCallback)(e => {
72
+ e.currentTarget.measure((_x, _y, _width, _height, _pageX, pageY) => {
73
+ pageYRef.current = pageY || screenHeight / 2;
74
+ setHasPageY(true);
75
+ });
76
+ props.containerProps?.onLayout?.(e);
77
+ }, [props.containerProps?.onLayout]);
78
+ const androidShadowAnimatedStyle = (0, _react.useCallback)(opacity => ({
79
+ opacity,
80
+ right: 0,
81
+ left: 0,
82
+ top: 0,
83
+ bottom: 0,
84
+ position: 'absolute',
85
+ boxShadow: canRenderDown ? '5px 18px 25px 0px rgba(0,0,0,0.15)' : '5px -18px 25px 0px rgba(0,0,0,0.15)',
86
+ borderRadius: _reactNativeEssentials.radiusSizes.soft,
87
+ transform: [{
88
+ translateY: canRenderDown ? 0 : -maxHeight + relativeY(4)
89
+ }]
90
+ }), [canRenderDown, maxHeight, relativeY]);
91
+ const selectionItemsListAnimatedStyle = (0, _react.useCallback)(translateY => ({
92
+ transform: [{
93
+ translateY
94
+ }]
95
+ }), []);
96
+ (0, _react.useEffect)(() => {
97
+ if (animateComponentRef.current && !showItems) animateComponentRef.current.reverse();
98
+ if (animateChevronRef.current) {
99
+ if (showItems) animateChevronRef.current.start();else {
100
+ animateChevronRef.current.reverse();
101
+ }
102
+ }
103
+ if (_reactNativeEssentials.isAndroid && !showItems) animateAndroidShadowRef.current?.reverse();
104
+ }, [showItems]);
105
+ return {
106
+ showItems,
107
+ setShowItems,
108
+ label,
109
+ canRenderDown,
110
+ relativeY,
111
+ scrollViewRef,
112
+ animateComponentRef,
113
+ animateChevronRef,
114
+ animateAndroidShadowRef,
115
+ pageYRef,
116
+ canRenderDownRef,
117
+ maxHeight,
118
+ handleLayout,
119
+ androidShadowAnimatedStyle,
120
+ androidShadowAnimationConfig,
121
+ scrollViewStyle,
122
+ selectionItemsListAnimatedStyle,
123
+ selectionItemsListAnimationConfig,
124
+ chevronAnimationConfig,
125
+ chevronAnimatedStyle,
126
+ unMountDelayInMilliSeconds
127
+ };
128
+ }
129
+ //# sourceMappingURL=DropDownSelector.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_reactNativeEssentials","require","_react","DropDownSelectorController","props","showItems","setShowItems","useState","hasPageY","setHasPageY","screenHeight","relativeY","useDeviceOrientation","pageYRef","useRef","scrollViewRef","canRenderDownRef","animateChevronRef","animateComponentRef","animateAndroidShadowRef","unMountDelayInMilliSeconds","useMemo","expandAnimationConfig","type","duration","maxHeight","expandDistance","label","items","find","item","value","selectedItem","canRenderDown","expandDirection","current","distanceToBottom","androidShadowAnimationConfig","toValue","useNativeDriver","scrollViewStyle","overflow","minHeight","borderRadius","radiusSizes","soft","transform","translateY","chevronAnimationConfig","speed","bounciness","selectionItemsListAnimationConfig","chevronAnimatedStyle","useCallback","scaleY","handleLayout","e","currentTarget","measure","_x","_y","_width","_height","_pageX","pageY","containerProps","onLayout","androidShadowAnimatedStyle","opacity","right","left","top","bottom","position","boxShadow","selectionItemsListAnimatedStyle","useEffect","reverse","start","isAndroid"],"sourceRoot":"../../../src","sources":["DropDownSelector/DropDownSelector.controller.tsx"],"mappings":"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","ignoreList":[]}