aport-tools 4.2.0 → 4.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,84 @@
1
+ import React from 'react';
2
+ interface Option {
3
+ id: number | string;
4
+ label: string;
5
+ value: any;
6
+ }
7
+ interface InputListProps {
8
+ name: string;
9
+ /**
10
+ * Placeholder text displayed in the input box when no selection is made.
11
+ * @type {string}
12
+ * @default "Choose value/s"
13
+ */
14
+ placeholder?: string;
15
+ /**
16
+ * Custom styles for the component.
17
+ * @type {object}
18
+ */
19
+ style?: object;
20
+ /**
21
+ * Array of options to display in the dropdown. Each option should have an id, label, and value.
22
+ * @type {Option[]}
23
+ */
24
+ options: Option[];
25
+ /**
26
+ * Enables multi-selection mode when true. If enabled, users can select multiple values.
27
+ * @type {boolean}
28
+ * @default false
29
+ */
30
+ multi?: boolean;
31
+ /**
32
+ * Disables the dropdown input when true.
33
+ * @type {boolean}
34
+ * @default false
35
+ */
36
+ disabled?: boolean;
37
+ /**
38
+ * Key used to sort options in the dropdown (e.g., by 'id', 'label').
39
+ * @type {keyof Option}
40
+ */
41
+ sortBy?: keyof Option;
42
+ /**
43
+ * If true, displays a separator line between each option.
44
+ * @type {boolean}
45
+ * @default false
46
+ */
47
+ separator?: boolean;
48
+ /**
49
+ * Closes the dropdown if the user scrolls the list.
50
+ * @type {boolean}
51
+ * @default false
52
+ */
53
+ closeOnScroll?: boolean;
54
+ /**
55
+ * Closes the dropdown after selecting an item if true (only relevant when multi is false).
56
+ * @type {boolean}
57
+ * @default true
58
+ */
59
+ closeOnSelect?: boolean;
60
+ /**
61
+ * Limits the maximum number of items that can be selected when multi is true.
62
+ * Once the limit is reached, the dropdown closes, and no further selections can be made until
63
+ * an item is deselected.
64
+ * @type {number}
65
+ */
66
+ maxSelection?: number;
67
+ }
68
+ /**
69
+ * InputList component - A custom dropdown list component for React Native with multi-selection support,
70
+ * customizable styling, sorting, and configurable close behavior on selection or scrolling.
71
+ *
72
+ * @param {string} placeholder - Placeholder text for the input.
73
+ * @param {object} style - Custom styles for the component.
74
+ * @param {Option[]} options - Array of options to display in the dropdown.
75
+ * @param {boolean} multi - Enables multi-selection mode.
76
+ * @param {boolean} disabled - Disables the dropdown input.
77
+ * @param {keyof Option} sortBy - Key to sort options by (e.g., 'id').
78
+ * @param {boolean} separator - If true, adds a separator line between options.
79
+ * @param {boolean} closeOnScroll - Closes the dropdown if the user scrolls the list.
80
+ * @param {boolean} closeOnSelect - Closes the dropdown on selection in single-select mode.
81
+ * @param {number} maxSelection - Maximum number of items selectable in multi-select mode.
82
+ */
83
+ export declare const InputList: React.FC<InputListProps>;
84
+ export {};
@@ -3,3 +3,4 @@ export { Form, useFormContext } from './FormContext';
3
3
  export { default as TextArea } from './TextArea';
4
4
  export { default as Label } from './Label';
5
5
  export { default as ErrorList } from './ErrorList';
6
+ export { InputList } from './InputList';
package/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
- /*! aport-tools v4.2.0 | ISC */
2
- import React, { useContext, useState, createContext, useMemo } from 'react';
3
- import { StyleSheet, Text as Text$1, View, TextInput, TouchableOpacity, ActivityIndicator, Platform } from 'react-native';
1
+ /*! aport-tools v4.2.2 | ISC */
2
+ import React, { useContext, useState, createContext, useCallback, useMemo } from 'react';
3
+ import { StyleSheet, Text as Text$1, View, TextInput, TouchableOpacity, Modal, Pressable, FlatList, Keyboard, ActivityIndicator, Platform } from 'react-native';
4
4
  import { ThemeContext } from 'aport-themes';
5
5
 
6
6
  /******************************************************************************
@@ -81,6 +81,16 @@ function __generator(thisArg, body) {
81
81
  }
82
82
  }
83
83
 
84
+ function __spreadArray(to, from, pack) {
85
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
86
+ if (ar || !(i in from)) {
87
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
88
+ ar[i] = from[i];
89
+ }
90
+ }
91
+ return to.concat(ar || Array.prototype.slice.call(from));
92
+ }
93
+
84
94
  typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
85
95
  var e = new Error(message);
86
96
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
@@ -220,23 +230,23 @@ var ErrorList = function ErrorList(_a) {
220
230
  var theme = useContext(ThemeContext).theme;
221
231
  var colors = theme.colors;
222
232
  return /*#__PURE__*/React.createElement(View, {
223
- style: styles$5.container
233
+ style: styles$6.container
224
234
  }, errors.map(function (error, index) {
225
235
  return /*#__PURE__*/React.createElement(View, {
226
236
  key: index,
227
- style: styles$5.errorItem
237
+ style: styles$6.errorItem
228
238
  }, /*#__PURE__*/React.createElement(Text, {
229
- style: [styles$5.bullet, {
239
+ style: [styles$6.bullet, {
230
240
  color: colors.error.hex
231
241
  }]
232
242
  }, "\u2022"), /*#__PURE__*/React.createElement(Text, {
233
- style: [styles$5.errorText, {
243
+ style: [styles$6.errorText, {
234
244
  color: colors.error.hex
235
245
  }]
236
246
  }, error));
237
247
  }));
238
248
  };
239
- var styles$5 = StyleSheet.create({
249
+ var styles$6 = StyleSheet.create({
240
250
  container: {
241
251
  marginTop: 4
242
252
  },
@@ -314,13 +324,13 @@ var Input = function Input(_a) {
314
324
  setFormValue(name, formattedText);
315
325
  };
316
326
  return /*#__PURE__*/React.createElement(View, {
317
- style: styles$4.container
327
+ style: styles$5.container
318
328
  }, /*#__PURE__*/React.createElement(Text, {
319
- style: [styles$4.label, {
329
+ style: [styles$5.label, {
320
330
  color: colors.text.hex
321
331
  }]
322
332
  }, label), /*#__PURE__*/React.createElement(TextInput, __assign({
323
- style: [styles$4.input, {
333
+ style: [styles$5.input, {
324
334
  backgroundColor: colors.body.hex,
325
335
  borderColor: formErrors[name] ? colors.error.hex : "#CCC",
326
336
  color: colors.text.hex
@@ -333,7 +343,7 @@ var Input = function Input(_a) {
333
343
  errors: formErrors[name]
334
344
  })));
335
345
  };
336
- var styles$4 = StyleSheet.create({
346
+ var styles$5 = StyleSheet.create({
337
347
  container: {
338
348
  marginBottom: 16
339
349
  },
@@ -364,11 +374,11 @@ var TextArea = function TextArea(_a) {
364
374
  setFormValue(name, text);
365
375
  };
366
376
  return /*#__PURE__*/React.createElement(View, {
367
- style: styles$3.container
377
+ style: styles$4.container
368
378
  }, /*#__PURE__*/React.createElement(Text, {
369
- style: styles$3.label
379
+ style: styles$4.label
370
380
  }, label), /*#__PURE__*/React.createElement(TextInput, __assign({
371
- style: [styles$3.textArea, style],
381
+ style: [styles$4.textArea, style],
372
382
  value: formValues[name] || '',
373
383
  onChangeText: handleChange,
374
384
  placeholder: label,
@@ -381,7 +391,7 @@ var TextArea = function TextArea(_a) {
381
391
  errors: formErrors[name]
382
392
  })));
383
393
  };
384
- var styles$3 = StyleSheet.create({
394
+ var styles$4 = StyleSheet.create({
385
395
  container: {
386
396
  marginBottom: 16
387
397
  },
@@ -410,18 +420,214 @@ var Label = function Label(_a) {
410
420
  var theme = useContext(ThemeContext).theme;
411
421
  var colors = theme.colors;
412
422
  return /*#__PURE__*/React.createElement(Text, {
413
- style: [styles$2.label, style, {
423
+ style: [styles$3.label, style, {
414
424
  color: colors.text.hex
415
425
  }]
416
426
  }, text);
417
427
  };
418
- var styles$2 = StyleSheet.create({
428
+ var styles$3 = StyleSheet.create({
419
429
  label: {
420
430
  marginBottom: 4,
421
431
  fontWeight: '500'
422
432
  }
423
433
  });
424
434
 
435
+ /**
436
+ * InputList component - A custom dropdown list component for React Native with multi-selection support,
437
+ * customizable styling, sorting, and configurable close behavior on selection or scrolling.
438
+ *
439
+ * @param {string} placeholder - Placeholder text for the input.
440
+ * @param {object} style - Custom styles for the component.
441
+ * @param {Option[]} options - Array of options to display in the dropdown.
442
+ * @param {boolean} multi - Enables multi-selection mode.
443
+ * @param {boolean} disabled - Disables the dropdown input.
444
+ * @param {keyof Option} sortBy - Key to sort options by (e.g., 'id').
445
+ * @param {boolean} separator - If true, adds a separator line between options.
446
+ * @param {boolean} closeOnScroll - Closes the dropdown if the user scrolls the list.
447
+ * @param {boolean} closeOnSelect - Closes the dropdown on selection in single-select mode.
448
+ * @param {number} maxSelection - Maximum number of items selectable in multi-select mode.
449
+ */
450
+ var InputList = function InputList(_a) {
451
+ var name = _a.name,
452
+ _b = _a.placeholder,
453
+ placeholder = _b === void 0 ? "Choose value/s" : _b,
454
+ style = _a.style,
455
+ options = _a.options,
456
+ _c = _a.multi,
457
+ multi = _c === void 0 ? false : _c,
458
+ _d = _a.disabled,
459
+ disabled = _d === void 0 ? false : _d,
460
+ sortBy = _a.sortBy,
461
+ _e = _a.separator,
462
+ separator = _e === void 0 ? false : _e,
463
+ _f = _a.closeOnScroll,
464
+ closeOnScroll = _f === void 0 ? false : _f,
465
+ _g = _a.closeOnSelect,
466
+ closeOnSelect = _g === void 0 ? true : _g,
467
+ maxSelection = _a.maxSelection;
468
+ var _h = useFormContext(),
469
+ formValues = _h.formValues,
470
+ setFormValue = _h.setFormValue;
471
+ var _j = useState(false),
472
+ isDropdownVisible = _j[0],
473
+ setIsDropdownVisible = _j[1];
474
+ var selectedOptions = formValues[name] || (multi ? [] : null);
475
+ var sortedOptions = sortBy ? __spreadArray([], options, true).sort(function (a, b) {
476
+ return a[sortBy] > b[sortBy] ? 1 : -1;
477
+ }) : options;
478
+ var theme = useContext(ThemeContext).theme;
479
+ var colors = theme.colors;
480
+ /**
481
+ * Handles selection of an option. Adds or removes the option from selectedOptions based on
482
+ * multi-selection and maxSelection criteria.
483
+ * @param {Option} option - The selected option object.
484
+ */
485
+ var handleSelectOption = function handleSelectOption(option) {
486
+ if (multi) {
487
+ var alreadySelected = selectedOptions.some(function (opt) {
488
+ return opt.id === option.id;
489
+ });
490
+ // Add or remove item from selected options
491
+ var updatedSelections = alreadySelected ? selectedOptions.filter(function (opt) {
492
+ return opt.id !== option.id;
493
+ }) : __spreadArray(__spreadArray([], selectedOptions, true), [option], false);
494
+ // Close dropdown if max selection is reached after selecting
495
+ if (!alreadySelected && maxSelection && updatedSelections.length >= maxSelection) {
496
+ setIsDropdownVisible(false);
497
+ }
498
+ // Update form value
499
+ setFormValue(name, updatedSelections);
500
+ } else {
501
+ setFormValue(name, option);
502
+ if (closeOnSelect) setIsDropdownVisible(false);
503
+ }
504
+ };
505
+ /**
506
+ * Renders selected options as a comma-separated string or the placeholder if none selected.
507
+ * @returns {string} - The display text for selected options or placeholder.
508
+ */
509
+ var renderSelectedText = function renderSelectedText() {
510
+ if (multi) return selectedOptions.map(function (opt) {
511
+ return opt.label;
512
+ }).join(', ') || placeholder;
513
+ return (selectedOptions === null || selectedOptions === void 0 ? void 0 : selectedOptions.label) || placeholder;
514
+ };
515
+ /**
516
+ * Toggles dropdown visibility. Disables toggle if the component is disabled.
517
+ */
518
+ var toggleDropdown = function toggleDropdown() {
519
+ if (!disabled) {
520
+ setIsDropdownVisible(!isDropdownVisible);
521
+ if (!isDropdownVisible) Keyboard.dismiss();
522
+ }
523
+ };
524
+ /**
525
+ * Closes the dropdown when pressing outside.
526
+ */
527
+ var handleCloseDropdown = useCallback(function () {
528
+ if (isDropdownVisible) setIsDropdownVisible(false);
529
+ }, [isDropdownVisible]);
530
+ // Conditionally render item as disabled if max selection reached and item is unselected
531
+ var isItemDisabled = function isItemDisabled(option) {
532
+ return multi && maxSelection && selectedOptions.length >= maxSelection && !selectedOptions.some(function (opt) {
533
+ return opt.id === option.id;
534
+ });
535
+ };
536
+ return /*#__PURE__*/React.createElement(View, {
537
+ style: [styles$2.container, style]
538
+ }, /*#__PURE__*/React.createElement(TouchableOpacity, {
539
+ style: styles$2.inputContainer,
540
+ onPress: toggleDropdown,
541
+ disabled: disabled
542
+ }, /*#__PURE__*/React.createElement(Text$1, {
543
+ style: {
544
+ color: colors.text.hex
545
+ }
546
+ }, renderSelectedText())), /*#__PURE__*/React.createElement(Modal, {
547
+ visible: isDropdownVisible,
548
+ transparent: true,
549
+ animationType: "fade"
550
+ }, /*#__PURE__*/React.createElement(Pressable, {
551
+ style: styles$2.overlay,
552
+ onPress: handleCloseDropdown
553
+ }), /*#__PURE__*/React.createElement(View, {
554
+ style: [styles$2.dropdownContainer, {
555
+ backgroundColor: colors.body.hex
556
+ }]
557
+ }, /*#__PURE__*/React.createElement(FlatList, {
558
+ data: sortedOptions,
559
+ keyExtractor: function keyExtractor(item) {
560
+ return item.id.toString();
561
+ },
562
+ renderItem: function renderItem(_a) {
563
+ var item = _a.item;
564
+ var isSelected = selectedOptions.some(function (opt) {
565
+ return opt.id === item.id;
566
+ });
567
+ var isDisabled = isItemDisabled(item);
568
+ return /*#__PURE__*/React.createElement(TouchableOpacity, {
569
+ onPress: function onPress() {
570
+ return handleSelectOption(item);
571
+ },
572
+ style: [styles$2.optionItem, isSelected ? {
573
+ backgroundColor: colors.primary.hex
574
+ } : {}, isDisabled ? styles$2.disabledItem : {}],
575
+ disabled: !!isDisabled
576
+ }, /*#__PURE__*/React.createElement(Text$1, {
577
+ style: [{
578
+ color: colors.primary.hex
579
+ }, isDisabled ? styles$2.disabledText : {}]
580
+ }, item.label));
581
+ },
582
+ ItemSeparatorComponent: function ItemSeparatorComponent() {
583
+ return separator ? /*#__PURE__*/React.createElement(View, {
584
+ style: styles$2.separator
585
+ }) : null;
586
+ },
587
+ scrollEnabled: !closeOnScroll
588
+ }))));
589
+ };
590
+ var styles$2 = StyleSheet.create({
591
+ container: {
592
+ padding: 8
593
+ },
594
+ inputContainer: {
595
+ padding: 12,
596
+ borderWidth: 1,
597
+ borderColor: '#ccc',
598
+ borderRadius: 5
599
+ },
600
+ dropdownContainer: {
601
+ position: 'absolute',
602
+ top: '30%',
603
+ // Center the dropdown vertically
604
+ alignSelf: 'center',
605
+ width: '90%',
606
+ backgroundColor: '#fff',
607
+ borderRadius: 8,
608
+ elevation: 5,
609
+ paddingVertical: 10
610
+ },
611
+ optionItem: {
612
+ padding: 12
613
+ },
614
+ disabledItem: {
615
+ backgroundColor: '#f0f0f0'
616
+ },
617
+ disabledText: {
618
+ color: '#999'
619
+ },
620
+ separator: {
621
+ height: 1,
622
+ backgroundColor: '#ddd',
623
+ marginHorizontal: 8
624
+ },
625
+ overlay: {
626
+ flex: 1,
627
+ backgroundColor: 'rgba(0,0,0,0.3)'
628
+ }
629
+ });
630
+
425
631
  // src/components/Button.tsx
426
632
  /**
427
633
  * Determines the styles based on the button type and whether it is disabled.
@@ -600,5 +806,5 @@ var styles = StyleSheet.create({
600
806
  }
601
807
  });
602
808
 
603
- export { Button, Card, ErrorList, Form, Input, Label, Text, TextArea, useFormContext };
809
+ export { Button, Card, ErrorList, Form, Input, InputList, Label, Text, TextArea, useFormContext };
604
810
  //# sourceMappingURL=index.esm.js.map