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