@kaushverse/pickify 1.1.7 → 1.1.9

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.
package/dist/index.d.mts CHANGED
@@ -151,6 +151,9 @@ type Props = {
151
151
  styles?: FloatingButtonStyles;
152
152
  style?: ViewStyle;
153
153
  mainIconName?: string;
154
+ spacing?: number;
155
+ offsetX?: number;
156
+ offsetY?: number;
154
157
  };
155
158
  declare class FloatingButton extends React$1.Component<Props> {
156
159
  animation: Animated.Value;
package/dist/index.d.ts CHANGED
@@ -151,6 +151,9 @@ type Props = {
151
151
  styles?: FloatingButtonStyles;
152
152
  style?: ViewStyle;
153
153
  mainIconName?: string;
154
+ spacing?: number;
155
+ offsetX?: number;
156
+ offsetY?: number;
154
157
  };
155
158
  declare class FloatingButton extends React$1.Component<Props> {
156
159
  animation: Animated.Value;
package/dist/index.js CHANGED
@@ -291,31 +291,41 @@ function MultiPickerGroup({
291
291
  renderGroupIcon,
292
292
  defaultOpen = false
293
293
  }) {
294
- const [open, setOpen] = (0, import_react2.useState)(defaultOpen);
294
+ const [open, setOpen] = (0, import_react2.useState)(false);
295
295
  const [contentHeight, setContentHeight] = (0, import_react2.useState)(0);
296
- const [isMeasured, setIsMeasured] = (0, import_react2.useState)(false);
297
- const animatedHeight = (0, import_react2.useRef)(
298
- new import_react_native2.Animated.Value(defaultOpen ? 1 : 0)
299
- ).current;
296
+ const [isMounted, setIsMounted] = (0, import_react2.useState)(false);
297
+ const animatedHeight = (0, import_react2.useRef)(new import_react_native2.Animated.Value(0)).current;
298
+ const animationRef = (0, import_react2.useRef)(null);
300
299
  (0, import_react2.useEffect)(() => {
301
- if (defaultOpen && contentHeight > 0 && !isMeasured) {
302
- setIsMeasured(true);
300
+ setIsMounted(true);
301
+ return () => {
302
+ if (animationRef.current) {
303
+ animationRef.current.stop();
304
+ }
305
+ };
306
+ }, []);
307
+ (0, import_react2.useEffect)(() => {
308
+ if (defaultOpen && contentHeight > 0 && !open) {
303
309
  animatedHeight.setValue(contentHeight);
304
310
  setOpen(true);
305
311
  }
306
- }, [contentHeight, defaultOpen, isMeasured]);
312
+ }, [defaultOpen, contentHeight]);
307
313
  const toggle = () => {
308
314
  if (contentHeight === 0) return;
309
- const newOpenState = !open;
310
- const toValue = newOpenState ? contentHeight : 0;
311
- import_react_native2.Animated.timing(animatedHeight, {
315
+ if (animationRef.current) {
316
+ animationRef.current.stop();
317
+ }
318
+ const toValue = open ? 0 : contentHeight;
319
+ animationRef.current = import_react_native2.Animated.timing(animatedHeight, {
312
320
  toValue,
313
- duration: 250,
321
+ duration: 200,
314
322
  useNativeDriver: false
315
- }).start(() => {
316
- setOpen(newOpenState);
317
323
  });
318
- setOpen(newOpenState);
324
+ animationRef.current.start(() => {
325
+ setOpen(!open);
326
+ animationRef.current = null;
327
+ });
328
+ setOpen(!open);
319
329
  };
320
330
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: styles.group, children: [
321
331
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
@@ -340,9 +350,11 @@ function MultiPickerGroup({
340
350
  style: styles.hidden,
341
351
  pointerEvents: "none",
342
352
  onLayout: (e) => {
343
- const height = e.nativeEvent.layout.height;
344
- if (height > 0 && height !== contentHeight) {
345
- setContentHeight(height);
353
+ if (isMounted) {
354
+ const height = e.nativeEvent.layout.height;
355
+ if (height > 0 && height !== contentHeight) {
356
+ setContentHeight(height);
357
+ }
346
358
  }
347
359
  },
348
360
  children
@@ -373,8 +385,8 @@ var styles = import_react_native2.StyleSheet.create({
373
385
  flexDirection: "row",
374
386
  justifyContent: "space-between",
375
387
  alignItems: "center",
376
- paddingVertical: 12,
377
- paddingHorizontal: 8,
388
+ paddingVertical: 14,
389
+ paddingHorizontal: 12,
378
390
  backgroundColor: "#F9FAFB",
379
391
  borderRadius: 8
380
392
  },
@@ -387,8 +399,8 @@ var styles = import_react_native2.StyleSheet.create({
387
399
  overflow: "hidden"
388
400
  },
389
401
  content: {
390
- marginTop: 6,
391
- paddingHorizontal: 8,
402
+ paddingTop: 8,
403
+ paddingHorizontal: 12,
392
404
  paddingBottom: 4
393
405
  },
394
406
  hidden: {
@@ -471,6 +483,7 @@ var defaultStyles2 = import_react_native3.StyleSheet.create({
471
483
 
472
484
  // src/core/MultiPickerModal.tsx
473
485
  var import_jsx_runtime4 = require("react/jsx-runtime");
486
+ var { height: SCREEN_HEIGHT } = import_react_native4.Dimensions.get("window");
474
487
  function MultiPickerModal({
475
488
  visible,
476
489
  setVisible,
@@ -488,36 +501,67 @@ function MultiPickerModal({
488
501
  }) {
489
502
  const [internalVisible, setInternalVisible] = (0, import_react3.useState)(false);
490
503
  const [modalKey, setModalKey] = (0, import_react3.useState)(0);
491
- const slideAnim = (0, import_react3.useRef)(new import_react_native4.Animated.Value(0)).current;
504
+ const slideAnim = (0, import_react3.useRef)(new import_react_native4.Animated.Value(SCREEN_HEIGHT)).current;
505
+ const backdropAnim = (0, import_react3.useRef)(new import_react_native4.Animated.Value(0)).current;
506
+ const isAnimating = (0, import_react3.useRef)(false);
492
507
  const isControlled = visible !== void 0 && setVisible !== void 0;
493
508
  const isVisible = isControlled ? visible : internalVisible;
494
509
  (0, import_react3.useEffect)(() => {
495
510
  if (isVisible) {
496
511
  setModalKey((prev) => prev + 1);
497
- import_react_native4.Animated.spring(slideAnim, {
498
- toValue: 1,
499
- useNativeDriver: true,
500
- damping: 15,
501
- mass: 0.8,
502
- stiffness: 150
503
- }).start();
512
+ openModal();
504
513
  } else {
514
+ closeModal();
515
+ }
516
+ }, [isVisible]);
517
+ const openModal = () => {
518
+ if (isAnimating.current) return;
519
+ isAnimating.current = true;
520
+ import_react_native4.Animated.parallel([
505
521
  import_react_native4.Animated.spring(slideAnim, {
506
522
  toValue: 0,
507
523
  useNativeDriver: true,
508
- damping: 15,
524
+ damping: 20,
509
525
  mass: 0.8,
510
- stiffness: 150
511
- }).start();
512
- }
513
- }, [isVisible]);
526
+ stiffness: 200
527
+ }),
528
+ import_react_native4.Animated.timing(backdropAnim, {
529
+ toValue: 1,
530
+ duration: 200,
531
+ useNativeDriver: true
532
+ })
533
+ ]).start(() => {
534
+ isAnimating.current = false;
535
+ });
536
+ };
537
+ const closeModal = () => {
538
+ if (isAnimating.current) return;
539
+ isAnimating.current = true;
540
+ import_react_native4.Animated.parallel([
541
+ import_react_native4.Animated.timing(slideAnim, {
542
+ toValue: SCREEN_HEIGHT,
543
+ duration: 250,
544
+ useNativeDriver: true
545
+ }),
546
+ import_react_native4.Animated.timing(backdropAnim, {
547
+ toValue: 0,
548
+ duration: 200,
549
+ useNativeDriver: true
550
+ })
551
+ ]).start(() => {
552
+ isAnimating.current = false;
553
+ if (!isControlled) {
554
+ setInternalVisible(false);
555
+ }
556
+ });
557
+ };
514
558
  const open = () => {
515
559
  if (isControlled) setVisible(true);
516
560
  else setInternalVisible(true);
517
561
  };
518
562
  const close = () => {
519
563
  if (isControlled) setVisible(false);
520
- else setInternalVisible(false);
564
+ else closeModal();
521
565
  };
522
566
  const handleSelect = (val) => {
523
567
  const updated = toggleValue(selectedValues, val);
@@ -528,16 +572,19 @@ function MultiPickerModal({
528
572
  const all = groups.length ? groups.flatMap((g) => g.data) : options;
529
573
  const selectedItems = all.filter((o) => selectedValues.includes(o.value)).map((o) => o.label);
530
574
  if (selectedItems.length === 0) return placeholder;
575
+ if (selectedItems.length > 2) {
576
+ return `${selectedItems.slice(0, 2).join(", ")} +${selectedItems.length - 2}`;
577
+ }
531
578
  return selectedItems.join(", ");
532
579
  };
533
580
  const renderList = () => {
534
581
  if (groups.length > 0) {
535
- return groups.map((group, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
582
+ return groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
536
583
  MultiPickerGroup,
537
584
  {
538
585
  label: group.label,
539
586
  renderGroupIcon,
540
- defaultOpen: index === 0,
587
+ defaultOpen: false,
541
588
  children: group.data.map((item) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
542
589
  MultiPickerItem,
543
590
  {
@@ -565,15 +612,14 @@ function MultiPickerModal({
565
612
  item.value
566
613
  ));
567
614
  };
568
- const modalAnimatedStyle = {
569
- transform: [
570
- {
571
- translateY: slideAnim.interpolate({
572
- inputRange: [0, 1],
573
- outputRange: [600, 0]
574
- })
575
- }
576
- ]
615
+ const backdropStyle = {
616
+ backgroundColor: backdropAnim.interpolate({
617
+ inputRange: [0, 1],
618
+ outputRange: ["rgba(0,0,0,0)", "rgba(0,0,0,0.5)"]
619
+ })
620
+ };
621
+ const modalStyle = {
622
+ transform: [{ translateY: slideAnim }]
577
623
  };
578
624
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
579
625
  label && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [defaultStyles3.label, styles2?.label], children: label }),
@@ -598,7 +644,7 @@ function MultiPickerModal({
598
644
  ),
599
645
  renderInputIcon?.({
600
646
  name: "chevron-down",
601
- size: 18,
647
+ size: 20,
602
648
  color: "#6B7280"
603
649
  })
604
650
  ]
@@ -612,35 +658,45 @@ function MultiPickerModal({
612
658
  transparent: true,
613
659
  animationType: "none",
614
660
  onRequestClose: close,
615
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: [defaultStyles3.overlay, styles2?.overlay], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
616
- import_react_native4.Animated.View,
617
- {
618
- style: [
619
- defaultStyles3.container,
620
- styles2?.container,
621
- modalAnimatedStyle
622
- ],
623
- children: [
624
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
625
- import_react_native4.TouchableOpacity,
626
- {
627
- onPress: close,
628
- style: [defaultStyles3.done, styles2?.doneBtn],
629
- activeOpacity: 0.7,
630
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [defaultStyles3.doneText, styles2?.doneText], children: "Done" })
631
- }
632
- ),
633
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
634
- import_react_native4.ScrollView,
635
- {
636
- showsVerticalScrollIndicator: false,
637
- contentContainerStyle: defaultStyles3.scrollContent,
638
- children: renderList()
639
- }
640
- )
641
- ]
642
- }
643
- ) })
661
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.Animated.View, { style: [defaultStyles3.overlay, backdropStyle], children: [
662
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
663
+ import_react_native4.TouchableOpacity,
664
+ {
665
+ style: defaultStyles3.backdropTouchable,
666
+ activeOpacity: 1,
667
+ onPress: close
668
+ }
669
+ ),
670
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
671
+ import_react_native4.Animated.View,
672
+ {
673
+ style: [defaultStyles3.container, styles2?.container, modalStyle],
674
+ children: [
675
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: defaultStyles3.header, children: [
676
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: defaultStyles3.handle }),
677
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: defaultStyles3.headerTitle, children: "Select Options" }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
679
+ import_react_native4.TouchableOpacity,
680
+ {
681
+ onPress: close,
682
+ style: defaultStyles3.doneBtn,
683
+ activeOpacity: 0.7,
684
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: defaultStyles3.doneText, children: "Done" })
685
+ }
686
+ )
687
+ ] }),
688
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
689
+ import_react_native4.ScrollView,
690
+ {
691
+ showsVerticalScrollIndicator: false,
692
+ contentContainerStyle: defaultStyles3.scrollContent,
693
+ children: renderList()
694
+ }
695
+ )
696
+ ]
697
+ }
698
+ )
699
+ ] })
644
700
  }
645
701
  )
646
702
  ] });
@@ -677,34 +733,71 @@ var defaultStyles3 = import_react_native4.StyleSheet.create({
677
733
  },
678
734
  overlay: {
679
735
  flex: 1,
680
- justifyContent: "flex-end",
681
- backgroundColor: "rgba(0,0,0,0.4)"
736
+ justifyContent: "flex-end"
737
+ },
738
+ backdropTouchable: {
739
+ position: "absolute",
740
+ top: 0,
741
+ left: 0,
742
+ right: 0,
743
+ bottom: 0
682
744
  },
683
745
  container: {
684
- backgroundColor: "#fff",
685
- padding: 20,
746
+ backgroundColor: "#FFFFFF",
686
747
  borderTopLeftRadius: 20,
687
748
  borderTopRightRadius: 20,
688
749
  maxHeight: "70%",
689
- minHeight: "40%"
750
+ minHeight: "40%",
751
+ shadowColor: "#000",
752
+ shadowOffset: {
753
+ width: 0,
754
+ height: -2
755
+ },
756
+ shadowOpacity: 0.1,
757
+ shadowRadius: 4,
758
+ elevation: 5
690
759
  },
691
- scrollContent: {
692
- paddingBottom: 20
760
+ header: {
761
+ flexDirection: "row",
762
+ justifyContent: "space-between",
763
+ alignItems: "center",
764
+ paddingHorizontal: 20,
765
+ paddingTop: 12,
766
+ paddingBottom: 16,
767
+ borderBottomWidth: 1,
768
+ borderBottomColor: "#F0F0F0"
693
769
  },
694
- done: {
770
+ handle: {
771
+ width: 40,
772
+ height: 4,
773
+ backgroundColor: "#E5E7EB",
774
+ borderRadius: 2,
775
+ position: "absolute",
776
+ top: 8,
777
+ alignSelf: "center"
778
+ },
779
+ headerTitle: {
780
+ fontSize: 16,
781
+ fontWeight: "600",
782
+ color: "#1F2937",
783
+ flex: 1,
784
+ textAlign: "center"
785
+ },
786
+ doneBtn: {
787
+ paddingVertical: 6,
788
+ paddingHorizontal: 12,
695
789
  backgroundColor: "#6366f1",
696
- paddingVertical: 10,
697
- paddingHorizontal: 14,
698
- borderRadius: 10,
699
- alignSelf: "flex-end",
700
- marginBottom: 16,
701
- minWidth: 80,
702
- alignItems: "center"
790
+ borderRadius: 8
703
791
  },
704
792
  doneText: {
705
- color: "#fff",
793
+ color: "#FFFFFF",
706
794
  fontWeight: "600",
707
795
  fontSize: 14
796
+ },
797
+ scrollContent: {
798
+ paddingHorizontal: 20,
799
+ paddingTop: 12,
800
+ paddingBottom: 24
708
801
  }
709
802
  });
710
803
 
@@ -712,6 +805,7 @@ var defaultStyles3 = import_react_native4.StyleSheet.create({
712
805
  var import_react4 = __toESM(require("react"));
713
806
  var import_react_native5 = require("react-native");
714
807
  var import_jsx_runtime5 = require("react/jsx-runtime");
808
+ var { width: SCREEN_WIDTH, height: SCREEN_HEIGHT2 } = import_react_native5.Dimensions.get("window");
715
809
  var FloatingButton = class extends import_react4.default.Component {
716
810
  animation = new import_react_native5.Animated.Value(0);
717
811
  open = false;
@@ -719,8 +813,8 @@ var FloatingButton = class extends import_react4.default.Component {
719
813
  import_react_native5.Animated.spring(this.animation, {
720
814
  toValue: this.open ? 0 : 1,
721
815
  useNativeDriver: true,
722
- damping: 12,
723
- mass: 0.6,
816
+ damping: 15,
817
+ mass: 0.8,
724
818
  stiffness: 180
725
819
  }).start();
726
820
  this.open = !this.open;
@@ -731,26 +825,28 @@ var FloatingButton = class extends import_react4.default.Component {
731
825
  radius = 100,
732
826
  actions,
733
827
  renderItemIcon,
734
- styles: styles2
828
+ styles: styles2,
829
+ spacing = 75
830
+ // Default spacing between buttons
735
831
  } = this.props;
736
832
  let translateX = 0;
737
833
  let translateY = 0;
738
834
  if (mode === "vertical") {
739
- translateY = -70 * (index + 1);
835
+ translateY = -spacing * (index + 1);
740
836
  translateX = 0;
741
837
  }
742
838
  if (mode === "horizontal") {
743
- translateX = -70 * (index + 1);
839
+ translateX = -spacing * (index + 1);
744
840
  translateY = 0;
745
841
  }
746
842
  if (mode === "circle") {
747
843
  const totalActions = actions.length;
748
- const startAngle = -Math.PI / 3;
749
- const endAngle = -Math.PI * 0.8;
750
- const angle = startAngle + index / (totalActions - 1 || 1) * (endAngle - startAngle);
751
- const adjustedRadius = radius * 0.65;
752
- translateX = adjustedRadius * Math.cos(angle);
753
- translateY = adjustedRadius * Math.sin(angle);
844
+ const startAngle = Math.PI * 0.85;
845
+ const endAngle = Math.PI * 0.15;
846
+ const angle = startAngle - index / (totalActions - 1 || 1) * (startAngle - endAngle);
847
+ const adjustedRadius = radius;
848
+ translateX = -adjustedRadius * Math.cos(angle);
849
+ translateY = -adjustedRadius * Math.sin(angle);
754
850
  }
755
851
  const animStyle = {
756
852
  transform: [
@@ -804,7 +900,15 @@ var FloatingButton = class extends import_react4.default.Component {
804
900
  );
805
901
  };
806
902
  render() {
807
- const { actions, renderMainIcon, styles: styles2, style, mainIconName } = this.props;
903
+ const {
904
+ renderMainIcon,
905
+ styles: styles2,
906
+ style,
907
+ mainIconName,
908
+ mode = "vertical",
909
+ offsetX = 24,
910
+ offsetY = 100
911
+ } = this.props;
808
912
  const rotation = {
809
913
  transform: [
810
914
  {
@@ -826,32 +930,26 @@ var FloatingButton = class extends import_react4.default.Component {
826
930
  ]
827
931
  };
828
932
  const mainIcon = mainIconName || "add";
829
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: [defaultStyles4.container, styles2?.container, style], children: [
830
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
933
+ const containerStyle = [
934
+ defaultStyles4.container,
935
+ styles2?.container,
936
+ style,
937
+ {
938
+ bottom: offsetY,
939
+ right: offsetX
940
+ }
941
+ ];
942
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: containerStyle, children: [
943
+ this.props.actions.map(this.renderAction),
944
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.TouchableWithoutFeedback, { onPress: this.toggleMenu, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
831
945
  import_react_native5.Animated.View,
832
946
  {
833
947
  style: [
834
- defaultStyles4.circleContainer,
835
- styles2?.circleContainer,
836
- {
837
- transform: [
838
- {
839
- scale: this.animation.interpolate({
840
- inputRange: [0, 1],
841
- outputRange: [0, 1]
842
- })
843
- }
844
- ],
845
- opacity: this.animation
846
- }
948
+ defaultStyles4.mainButtonWrapper,
949
+ styles2?.mainButton,
950
+ rotation,
951
+ scale
847
952
  ],
848
- children: actions.map(this.renderAction)
849
- }
850
- ),
851
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.TouchableWithoutFeedback, { onPress: this.toggleMenu, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
852
- import_react_native5.Animated.View,
853
- {
854
- style: [defaultStyles4.mainButtonWrapper, rotation, scale],
855
953
  children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: [defaultStyles4.button, styles2?.mainButton], children: renderMainIcon?.({
856
954
  name: mainIcon,
857
955
  size: 26,
@@ -865,44 +963,34 @@ var FloatingButton = class extends import_react4.default.Component {
865
963
  var defaultStyles4 = import_react_native5.StyleSheet.create({
866
964
  container: {
867
965
  position: "absolute",
868
- bottom: 100,
869
- right: 20,
870
966
  alignItems: "center",
871
- justifyContent: "center"
967
+ justifyContent: "center",
968
+ zIndex: 1e3
872
969
  },
873
970
  mainButtonWrapper: {
874
- width: 60,
875
- height: 60,
876
- borderRadius: 30,
971
+ width: 56,
972
+ height: 56,
973
+ borderRadius: 28,
877
974
  backgroundColor: "#F02A4B",
878
975
  alignItems: "center",
879
976
  justifyContent: "center",
880
977
  shadowColor: "#000",
881
978
  shadowOffset: {
882
979
  width: 0,
883
- height: 2
980
+ height: 4
884
981
  },
885
- shadowOpacity: 0.25,
886
- shadowRadius: 3.84,
887
- elevation: 5
982
+ shadowOpacity: 0.3,
983
+ shadowRadius: 4.65,
984
+ elevation: 8
888
985
  },
889
986
  button: {
890
- width: 60,
891
- height: 60,
892
- borderRadius: 30,
987
+ width: 56,
988
+ height: 56,
989
+ borderRadius: 28,
893
990
  backgroundColor: "#F02A4B",
894
991
  alignItems: "center",
895
992
  justifyContent: "center"
896
993
  },
897
- circleContainer: {
898
- position: "absolute",
899
- width: 200,
900
- height: 200,
901
- right: -70,
902
- bottom: -70,
903
- alignItems: "center",
904
- justifyContent: "center"
905
- },
906
994
  secondary: {
907
995
  position: "absolute",
908
996
  width: 48,
package/dist/index.mjs CHANGED
@@ -246,7 +246,8 @@ import {
246
246
  TouchableOpacity as TouchableOpacity4,
247
247
  StyleSheet as StyleSheet4,
248
248
  ScrollView as ScrollView2,
249
- Animated as Animated2
249
+ Animated as Animated2,
250
+ Dimensions
250
251
  } from "react-native";
251
252
 
252
253
  // src/utils/toggleValue.ts
@@ -270,31 +271,41 @@ function MultiPickerGroup({
270
271
  renderGroupIcon,
271
272
  defaultOpen = false
272
273
  }) {
273
- const [open, setOpen] = useState2(defaultOpen);
274
+ const [open, setOpen] = useState2(false);
274
275
  const [contentHeight, setContentHeight] = useState2(0);
275
- const [isMeasured, setIsMeasured] = useState2(false);
276
- const animatedHeight = useRef(
277
- new Animated.Value(defaultOpen ? 1 : 0)
278
- ).current;
276
+ const [isMounted, setIsMounted] = useState2(false);
277
+ const animatedHeight = useRef(new Animated.Value(0)).current;
278
+ const animationRef = useRef(null);
279
279
  useEffect2(() => {
280
- if (defaultOpen && contentHeight > 0 && !isMeasured) {
281
- setIsMeasured(true);
280
+ setIsMounted(true);
281
+ return () => {
282
+ if (animationRef.current) {
283
+ animationRef.current.stop();
284
+ }
285
+ };
286
+ }, []);
287
+ useEffect2(() => {
288
+ if (defaultOpen && contentHeight > 0 && !open) {
282
289
  animatedHeight.setValue(contentHeight);
283
290
  setOpen(true);
284
291
  }
285
- }, [contentHeight, defaultOpen, isMeasured]);
292
+ }, [defaultOpen, contentHeight]);
286
293
  const toggle = () => {
287
294
  if (contentHeight === 0) return;
288
- const newOpenState = !open;
289
- const toValue = newOpenState ? contentHeight : 0;
290
- Animated.timing(animatedHeight, {
295
+ if (animationRef.current) {
296
+ animationRef.current.stop();
297
+ }
298
+ const toValue = open ? 0 : contentHeight;
299
+ animationRef.current = Animated.timing(animatedHeight, {
291
300
  toValue,
292
- duration: 250,
301
+ duration: 200,
293
302
  useNativeDriver: false
294
- }).start(() => {
295
- setOpen(newOpenState);
296
303
  });
297
- setOpen(newOpenState);
304
+ animationRef.current.start(() => {
305
+ setOpen(!open);
306
+ animationRef.current = null;
307
+ });
308
+ setOpen(!open);
298
309
  };
299
310
  return /* @__PURE__ */ jsxs2(View2, { style: styles.group, children: [
300
311
  /* @__PURE__ */ jsxs2(
@@ -319,9 +330,11 @@ function MultiPickerGroup({
319
330
  style: styles.hidden,
320
331
  pointerEvents: "none",
321
332
  onLayout: (e) => {
322
- const height = e.nativeEvent.layout.height;
323
- if (height > 0 && height !== contentHeight) {
324
- setContentHeight(height);
333
+ if (isMounted) {
334
+ const height = e.nativeEvent.layout.height;
335
+ if (height > 0 && height !== contentHeight) {
336
+ setContentHeight(height);
337
+ }
325
338
  }
326
339
  },
327
340
  children
@@ -352,8 +365,8 @@ var styles = StyleSheet2.create({
352
365
  flexDirection: "row",
353
366
  justifyContent: "space-between",
354
367
  alignItems: "center",
355
- paddingVertical: 12,
356
- paddingHorizontal: 8,
368
+ paddingVertical: 14,
369
+ paddingHorizontal: 12,
357
370
  backgroundColor: "#F9FAFB",
358
371
  borderRadius: 8
359
372
  },
@@ -366,8 +379,8 @@ var styles = StyleSheet2.create({
366
379
  overflow: "hidden"
367
380
  },
368
381
  content: {
369
- marginTop: 6,
370
- paddingHorizontal: 8,
382
+ paddingTop: 8,
383
+ paddingHorizontal: 12,
371
384
  paddingBottom: 4
372
385
  },
373
386
  hidden: {
@@ -450,6 +463,7 @@ var defaultStyles2 = StyleSheet3.create({
450
463
 
451
464
  // src/core/MultiPickerModal.tsx
452
465
  import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
466
+ var { height: SCREEN_HEIGHT } = Dimensions.get("window");
453
467
  function MultiPickerModal({
454
468
  visible,
455
469
  setVisible,
@@ -467,36 +481,67 @@ function MultiPickerModal({
467
481
  }) {
468
482
  const [internalVisible, setInternalVisible] = useState3(false);
469
483
  const [modalKey, setModalKey] = useState3(0);
470
- const slideAnim = useRef2(new Animated2.Value(0)).current;
484
+ const slideAnim = useRef2(new Animated2.Value(SCREEN_HEIGHT)).current;
485
+ const backdropAnim = useRef2(new Animated2.Value(0)).current;
486
+ const isAnimating = useRef2(false);
471
487
  const isControlled = visible !== void 0 && setVisible !== void 0;
472
488
  const isVisible = isControlled ? visible : internalVisible;
473
489
  useEffect3(() => {
474
490
  if (isVisible) {
475
491
  setModalKey((prev) => prev + 1);
476
- Animated2.spring(slideAnim, {
477
- toValue: 1,
478
- useNativeDriver: true,
479
- damping: 15,
480
- mass: 0.8,
481
- stiffness: 150
482
- }).start();
492
+ openModal();
483
493
  } else {
494
+ closeModal();
495
+ }
496
+ }, [isVisible]);
497
+ const openModal = () => {
498
+ if (isAnimating.current) return;
499
+ isAnimating.current = true;
500
+ Animated2.parallel([
484
501
  Animated2.spring(slideAnim, {
485
502
  toValue: 0,
486
503
  useNativeDriver: true,
487
- damping: 15,
504
+ damping: 20,
488
505
  mass: 0.8,
489
- stiffness: 150
490
- }).start();
491
- }
492
- }, [isVisible]);
506
+ stiffness: 200
507
+ }),
508
+ Animated2.timing(backdropAnim, {
509
+ toValue: 1,
510
+ duration: 200,
511
+ useNativeDriver: true
512
+ })
513
+ ]).start(() => {
514
+ isAnimating.current = false;
515
+ });
516
+ };
517
+ const closeModal = () => {
518
+ if (isAnimating.current) return;
519
+ isAnimating.current = true;
520
+ Animated2.parallel([
521
+ Animated2.timing(slideAnim, {
522
+ toValue: SCREEN_HEIGHT,
523
+ duration: 250,
524
+ useNativeDriver: true
525
+ }),
526
+ Animated2.timing(backdropAnim, {
527
+ toValue: 0,
528
+ duration: 200,
529
+ useNativeDriver: true
530
+ })
531
+ ]).start(() => {
532
+ isAnimating.current = false;
533
+ if (!isControlled) {
534
+ setInternalVisible(false);
535
+ }
536
+ });
537
+ };
493
538
  const open = () => {
494
539
  if (isControlled) setVisible(true);
495
540
  else setInternalVisible(true);
496
541
  };
497
542
  const close = () => {
498
543
  if (isControlled) setVisible(false);
499
- else setInternalVisible(false);
544
+ else closeModal();
500
545
  };
501
546
  const handleSelect = (val) => {
502
547
  const updated = toggleValue(selectedValues, val);
@@ -507,16 +552,19 @@ function MultiPickerModal({
507
552
  const all = groups.length ? groups.flatMap((g) => g.data) : options;
508
553
  const selectedItems = all.filter((o) => selectedValues.includes(o.value)).map((o) => o.label);
509
554
  if (selectedItems.length === 0) return placeholder;
555
+ if (selectedItems.length > 2) {
556
+ return `${selectedItems.slice(0, 2).join(", ")} +${selectedItems.length - 2}`;
557
+ }
510
558
  return selectedItems.join(", ");
511
559
  };
512
560
  const renderList = () => {
513
561
  if (groups.length > 0) {
514
- return groups.map((group, index) => /* @__PURE__ */ jsx4(
562
+ return groups.map((group) => /* @__PURE__ */ jsx4(
515
563
  MultiPickerGroup,
516
564
  {
517
565
  label: group.label,
518
566
  renderGroupIcon,
519
- defaultOpen: index === 0,
567
+ defaultOpen: false,
520
568
  children: group.data.map((item) => /* @__PURE__ */ jsx4(
521
569
  MultiPickerItem,
522
570
  {
@@ -544,15 +592,14 @@ function MultiPickerModal({
544
592
  item.value
545
593
  ));
546
594
  };
547
- const modalAnimatedStyle = {
548
- transform: [
549
- {
550
- translateY: slideAnim.interpolate({
551
- inputRange: [0, 1],
552
- outputRange: [600, 0]
553
- })
554
- }
555
- ]
595
+ const backdropStyle = {
596
+ backgroundColor: backdropAnim.interpolate({
597
+ inputRange: [0, 1],
598
+ outputRange: ["rgba(0,0,0,0)", "rgba(0,0,0,0.5)"]
599
+ })
600
+ };
601
+ const modalStyle = {
602
+ transform: [{ translateY: slideAnim }]
556
603
  };
557
604
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
558
605
  label && /* @__PURE__ */ jsx4(Text4, { style: [defaultStyles3.label, styles2?.label], children: label }),
@@ -577,7 +624,7 @@ function MultiPickerModal({
577
624
  ),
578
625
  renderInputIcon?.({
579
626
  name: "chevron-down",
580
- size: 18,
627
+ size: 20,
581
628
  color: "#6B7280"
582
629
  })
583
630
  ]
@@ -591,35 +638,45 @@ function MultiPickerModal({
591
638
  transparent: true,
592
639
  animationType: "none",
593
640
  onRequestClose: close,
594
- children: /* @__PURE__ */ jsx4(View4, { style: [defaultStyles3.overlay, styles2?.overlay], children: /* @__PURE__ */ jsxs4(
595
- Animated2.View,
596
- {
597
- style: [
598
- defaultStyles3.container,
599
- styles2?.container,
600
- modalAnimatedStyle
601
- ],
602
- children: [
603
- /* @__PURE__ */ jsx4(
604
- TouchableOpacity4,
605
- {
606
- onPress: close,
607
- style: [defaultStyles3.done, styles2?.doneBtn],
608
- activeOpacity: 0.7,
609
- children: /* @__PURE__ */ jsx4(Text4, { style: [defaultStyles3.doneText, styles2?.doneText], children: "Done" })
610
- }
611
- ),
612
- /* @__PURE__ */ jsx4(
613
- ScrollView2,
614
- {
615
- showsVerticalScrollIndicator: false,
616
- contentContainerStyle: defaultStyles3.scrollContent,
617
- children: renderList()
618
- }
619
- )
620
- ]
621
- }
622
- ) })
641
+ children: /* @__PURE__ */ jsxs4(Animated2.View, { style: [defaultStyles3.overlay, backdropStyle], children: [
642
+ /* @__PURE__ */ jsx4(
643
+ TouchableOpacity4,
644
+ {
645
+ style: defaultStyles3.backdropTouchable,
646
+ activeOpacity: 1,
647
+ onPress: close
648
+ }
649
+ ),
650
+ /* @__PURE__ */ jsxs4(
651
+ Animated2.View,
652
+ {
653
+ style: [defaultStyles3.container, styles2?.container, modalStyle],
654
+ children: [
655
+ /* @__PURE__ */ jsxs4(View4, { style: defaultStyles3.header, children: [
656
+ /* @__PURE__ */ jsx4(View4, { style: defaultStyles3.handle }),
657
+ /* @__PURE__ */ jsx4(Text4, { style: defaultStyles3.headerTitle, children: "Select Options" }),
658
+ /* @__PURE__ */ jsx4(
659
+ TouchableOpacity4,
660
+ {
661
+ onPress: close,
662
+ style: defaultStyles3.doneBtn,
663
+ activeOpacity: 0.7,
664
+ children: /* @__PURE__ */ jsx4(Text4, { style: defaultStyles3.doneText, children: "Done" })
665
+ }
666
+ )
667
+ ] }),
668
+ /* @__PURE__ */ jsx4(
669
+ ScrollView2,
670
+ {
671
+ showsVerticalScrollIndicator: false,
672
+ contentContainerStyle: defaultStyles3.scrollContent,
673
+ children: renderList()
674
+ }
675
+ )
676
+ ]
677
+ }
678
+ )
679
+ ] })
623
680
  }
624
681
  )
625
682
  ] });
@@ -656,34 +713,71 @@ var defaultStyles3 = StyleSheet4.create({
656
713
  },
657
714
  overlay: {
658
715
  flex: 1,
659
- justifyContent: "flex-end",
660
- backgroundColor: "rgba(0,0,0,0.4)"
716
+ justifyContent: "flex-end"
717
+ },
718
+ backdropTouchable: {
719
+ position: "absolute",
720
+ top: 0,
721
+ left: 0,
722
+ right: 0,
723
+ bottom: 0
661
724
  },
662
725
  container: {
663
- backgroundColor: "#fff",
664
- padding: 20,
726
+ backgroundColor: "#FFFFFF",
665
727
  borderTopLeftRadius: 20,
666
728
  borderTopRightRadius: 20,
667
729
  maxHeight: "70%",
668
- minHeight: "40%"
730
+ minHeight: "40%",
731
+ shadowColor: "#000",
732
+ shadowOffset: {
733
+ width: 0,
734
+ height: -2
735
+ },
736
+ shadowOpacity: 0.1,
737
+ shadowRadius: 4,
738
+ elevation: 5
669
739
  },
670
- scrollContent: {
671
- paddingBottom: 20
740
+ header: {
741
+ flexDirection: "row",
742
+ justifyContent: "space-between",
743
+ alignItems: "center",
744
+ paddingHorizontal: 20,
745
+ paddingTop: 12,
746
+ paddingBottom: 16,
747
+ borderBottomWidth: 1,
748
+ borderBottomColor: "#F0F0F0"
672
749
  },
673
- done: {
750
+ handle: {
751
+ width: 40,
752
+ height: 4,
753
+ backgroundColor: "#E5E7EB",
754
+ borderRadius: 2,
755
+ position: "absolute",
756
+ top: 8,
757
+ alignSelf: "center"
758
+ },
759
+ headerTitle: {
760
+ fontSize: 16,
761
+ fontWeight: "600",
762
+ color: "#1F2937",
763
+ flex: 1,
764
+ textAlign: "center"
765
+ },
766
+ doneBtn: {
767
+ paddingVertical: 6,
768
+ paddingHorizontal: 12,
674
769
  backgroundColor: "#6366f1",
675
- paddingVertical: 10,
676
- paddingHorizontal: 14,
677
- borderRadius: 10,
678
- alignSelf: "flex-end",
679
- marginBottom: 16,
680
- minWidth: 80,
681
- alignItems: "center"
770
+ borderRadius: 8
682
771
  },
683
772
  doneText: {
684
- color: "#fff",
773
+ color: "#FFFFFF",
685
774
  fontWeight: "600",
686
775
  fontSize: 14
776
+ },
777
+ scrollContent: {
778
+ paddingHorizontal: 20,
779
+ paddingTop: 12,
780
+ paddingBottom: 24
687
781
  }
688
782
  });
689
783
 
@@ -693,9 +787,11 @@ import {
693
787
  Animated as Animated3,
694
788
  StyleSheet as StyleSheet5,
695
789
  TouchableWithoutFeedback,
696
- View as View5
790
+ View as View5,
791
+ Dimensions as Dimensions2
697
792
  } from "react-native";
698
793
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
794
+ var { width: SCREEN_WIDTH, height: SCREEN_HEIGHT2 } = Dimensions2.get("window");
699
795
  var FloatingButton = class extends React4.Component {
700
796
  animation = new Animated3.Value(0);
701
797
  open = false;
@@ -703,8 +799,8 @@ var FloatingButton = class extends React4.Component {
703
799
  Animated3.spring(this.animation, {
704
800
  toValue: this.open ? 0 : 1,
705
801
  useNativeDriver: true,
706
- damping: 12,
707
- mass: 0.6,
802
+ damping: 15,
803
+ mass: 0.8,
708
804
  stiffness: 180
709
805
  }).start();
710
806
  this.open = !this.open;
@@ -715,26 +811,28 @@ var FloatingButton = class extends React4.Component {
715
811
  radius = 100,
716
812
  actions,
717
813
  renderItemIcon,
718
- styles: styles2
814
+ styles: styles2,
815
+ spacing = 75
816
+ // Default spacing between buttons
719
817
  } = this.props;
720
818
  let translateX = 0;
721
819
  let translateY = 0;
722
820
  if (mode === "vertical") {
723
- translateY = -70 * (index + 1);
821
+ translateY = -spacing * (index + 1);
724
822
  translateX = 0;
725
823
  }
726
824
  if (mode === "horizontal") {
727
- translateX = -70 * (index + 1);
825
+ translateX = -spacing * (index + 1);
728
826
  translateY = 0;
729
827
  }
730
828
  if (mode === "circle") {
731
829
  const totalActions = actions.length;
732
- const startAngle = -Math.PI / 3;
733
- const endAngle = -Math.PI * 0.8;
734
- const angle = startAngle + index / (totalActions - 1 || 1) * (endAngle - startAngle);
735
- const adjustedRadius = radius * 0.65;
736
- translateX = adjustedRadius * Math.cos(angle);
737
- translateY = adjustedRadius * Math.sin(angle);
830
+ const startAngle = Math.PI * 0.85;
831
+ const endAngle = Math.PI * 0.15;
832
+ const angle = startAngle - index / (totalActions - 1 || 1) * (startAngle - endAngle);
833
+ const adjustedRadius = radius;
834
+ translateX = -adjustedRadius * Math.cos(angle);
835
+ translateY = -adjustedRadius * Math.sin(angle);
738
836
  }
739
837
  const animStyle = {
740
838
  transform: [
@@ -788,7 +886,15 @@ var FloatingButton = class extends React4.Component {
788
886
  );
789
887
  };
790
888
  render() {
791
- const { actions, renderMainIcon, styles: styles2, style, mainIconName } = this.props;
889
+ const {
890
+ renderMainIcon,
891
+ styles: styles2,
892
+ style,
893
+ mainIconName,
894
+ mode = "vertical",
895
+ offsetX = 24,
896
+ offsetY = 100
897
+ } = this.props;
792
898
  const rotation = {
793
899
  transform: [
794
900
  {
@@ -810,32 +916,26 @@ var FloatingButton = class extends React4.Component {
810
916
  ]
811
917
  };
812
918
  const mainIcon = mainIconName || "add";
813
- return /* @__PURE__ */ jsxs5(View5, { style: [defaultStyles4.container, styles2?.container, style], children: [
814
- /* @__PURE__ */ jsx5(
919
+ const containerStyle = [
920
+ defaultStyles4.container,
921
+ styles2?.container,
922
+ style,
923
+ {
924
+ bottom: offsetY,
925
+ right: offsetX
926
+ }
927
+ ];
928
+ return /* @__PURE__ */ jsxs5(View5, { style: containerStyle, children: [
929
+ this.props.actions.map(this.renderAction),
930
+ /* @__PURE__ */ jsx5(TouchableWithoutFeedback, { onPress: this.toggleMenu, children: /* @__PURE__ */ jsx5(
815
931
  Animated3.View,
816
932
  {
817
933
  style: [
818
- defaultStyles4.circleContainer,
819
- styles2?.circleContainer,
820
- {
821
- transform: [
822
- {
823
- scale: this.animation.interpolate({
824
- inputRange: [0, 1],
825
- outputRange: [0, 1]
826
- })
827
- }
828
- ],
829
- opacity: this.animation
830
- }
934
+ defaultStyles4.mainButtonWrapper,
935
+ styles2?.mainButton,
936
+ rotation,
937
+ scale
831
938
  ],
832
- children: actions.map(this.renderAction)
833
- }
834
- ),
835
- /* @__PURE__ */ jsx5(TouchableWithoutFeedback, { onPress: this.toggleMenu, children: /* @__PURE__ */ jsx5(
836
- Animated3.View,
837
- {
838
- style: [defaultStyles4.mainButtonWrapper, rotation, scale],
839
939
  children: /* @__PURE__ */ jsx5(View5, { style: [defaultStyles4.button, styles2?.mainButton], children: renderMainIcon?.({
840
940
  name: mainIcon,
841
941
  size: 26,
@@ -849,44 +949,34 @@ var FloatingButton = class extends React4.Component {
849
949
  var defaultStyles4 = StyleSheet5.create({
850
950
  container: {
851
951
  position: "absolute",
852
- bottom: 100,
853
- right: 20,
854
952
  alignItems: "center",
855
- justifyContent: "center"
953
+ justifyContent: "center",
954
+ zIndex: 1e3
856
955
  },
857
956
  mainButtonWrapper: {
858
- width: 60,
859
- height: 60,
860
- borderRadius: 30,
957
+ width: 56,
958
+ height: 56,
959
+ borderRadius: 28,
861
960
  backgroundColor: "#F02A4B",
862
961
  alignItems: "center",
863
962
  justifyContent: "center",
864
963
  shadowColor: "#000",
865
964
  shadowOffset: {
866
965
  width: 0,
867
- height: 2
966
+ height: 4
868
967
  },
869
- shadowOpacity: 0.25,
870
- shadowRadius: 3.84,
871
- elevation: 5
968
+ shadowOpacity: 0.3,
969
+ shadowRadius: 4.65,
970
+ elevation: 8
872
971
  },
873
972
  button: {
874
- width: 60,
875
- height: 60,
876
- borderRadius: 30,
973
+ width: 56,
974
+ height: 56,
975
+ borderRadius: 28,
877
976
  backgroundColor: "#F02A4B",
878
977
  alignItems: "center",
879
978
  justifyContent: "center"
880
979
  },
881
- circleContainer: {
882
- position: "absolute",
883
- width: 200,
884
- height: 200,
885
- right: -70,
886
- bottom: -70,
887
- alignItems: "center",
888
- justifyContent: "center"
889
- },
890
980
  secondary: {
891
981
  position: "absolute",
892
982
  width: 48,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaushverse/pickify",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "description": "A fully customizable React Native picker with search, multi-select, grouping, and async support.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",