@motor-hero/ui-kit 0.23.1 → 0.24.0

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.js CHANGED
@@ -615,18 +615,52 @@ PasswordInput.displayName = "PasswordInput";
615
615
  // src/components/combobox.tsx
616
616
  import * as PopoverPrimitive from "@radix-ui/react-popover";
617
617
  import { Command as CommandPrimitive } from "cmdk";
618
- import { Check, ChevronsUpDown, Search } from "lucide-react";
619
- import * as React5 from "react";
618
+ import { Check, ChevronsUpDown, Loader2, Search } from "lucide-react";
619
+ import * as React6 from "react";
620
620
  import { Drawer as DrawerPrimitive } from "vaul";
621
621
 
622
- // src/hooks/use-is-desktop.ts
622
+ // src/hooks/use-infinite-scroll-sentinel.ts
623
623
  import * as React4 from "react";
624
+ function useInfiniteScrollSentinel({
625
+ enabled,
626
+ loading,
627
+ onLoadMore
628
+ }) {
629
+ const listRef = React4.useRef(null);
630
+ const sentinelRef = React4.useRef(null);
631
+ const loadingRef = React4.useRef(loading);
632
+ const onLoadMoreRef = React4.useRef(onLoadMore);
633
+ React4.useEffect(() => {
634
+ loadingRef.current = loading;
635
+ onLoadMoreRef.current = onLoadMore;
636
+ });
637
+ React4.useEffect(() => {
638
+ if (!enabled) return;
639
+ const root = listRef.current;
640
+ const sentinel = sentinelRef.current;
641
+ if (!root || !sentinel) return;
642
+ const observer = new IntersectionObserver(
643
+ (entries) => {
644
+ if (entries[0]?.isIntersecting && !loadingRef.current) {
645
+ onLoadMoreRef.current?.();
646
+ }
647
+ },
648
+ { root, rootMargin: "0px 0px 100px 0px" }
649
+ );
650
+ observer.observe(sentinel);
651
+ return () => observer.disconnect();
652
+ }, [enabled]);
653
+ return { listRef, sentinelRef };
654
+ }
655
+
656
+ // src/hooks/use-is-desktop.ts
657
+ import * as React5 from "react";
624
658
  var DESKTOP_QUERY = "(min-width: 640px)";
625
659
  function useIsDesktop() {
626
- const [isDesktop, setIsDesktop] = React4.useState(
660
+ const [isDesktop, setIsDesktop] = React5.useState(
627
661
  () => typeof window !== "undefined" ? window.matchMedia(DESKTOP_QUERY).matches : true
628
662
  );
629
- React4.useEffect(() => {
663
+ React5.useEffect(() => {
630
664
  const mql = window.matchMedia(DESKTOP_QUERY);
631
665
  const onChange = () => setIsDesktop(mql.matches);
632
666
  onChange();
@@ -649,11 +683,22 @@ function Combobox({
649
683
  id,
650
684
  className,
651
685
  "aria-invalid": ariaInvalid,
652
- "aria-describedby": ariaDescribedby
686
+ "aria-describedby": ariaDescribedby,
687
+ onSearchChange,
688
+ onLoadMore,
689
+ loading,
690
+ hasMore,
691
+ selectedOption
653
692
  }) {
654
- const [open, setOpen] = React5.useState(false);
693
+ const [open, setOpen] = React6.useState(false);
655
694
  const isDesktop = useIsDesktop();
656
- const selected = options.find((option) => option.value === value);
695
+ const isServer = onSearchChange !== void 0;
696
+ const selected = selectedOption?.value === value ? selectedOption : options.find((option) => option.value === value);
697
+ const { listRef, sentinelRef } = useInfiniteScrollSentinel({
698
+ enabled: isServer && !!hasMore,
699
+ loading,
700
+ onLoadMore
701
+ });
657
702
  const trigger = /* @__PURE__ */ jsxs14(
658
703
  "button",
659
704
  {
@@ -674,55 +719,73 @@ function Combobox({
674
719
  ]
675
720
  }
676
721
  );
677
- const command = /* @__PURE__ */ jsxs14(CommandPrimitive, { className: "flex h-full w-full flex-col overflow-hidden", children: [
678
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center border-b px-3", children: [
679
- /* @__PURE__ */ jsx16(Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
680
- /* @__PURE__ */ jsx16(
681
- CommandPrimitive.Input,
682
- {
683
- placeholder: searchPlaceholder,
684
- className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
685
- }
686
- )
687
- ] }),
688
- /* @__PURE__ */ jsxs14(CommandPrimitive.List, { className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60", children: [
689
- /* @__PURE__ */ jsx16(CommandPrimitive.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
690
- options.map((option) => /* @__PURE__ */ jsxs14(
691
- CommandPrimitive.Item,
692
- {
693
- value: option.label,
694
- disabled: option.disabled,
695
- onSelect: () => {
696
- onChange(option.value);
697
- setOpen(false);
698
- },
699
- className: "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 sm:py-1.5",
700
- children: [
701
- /* @__PURE__ */ jsx16(
702
- Check,
703
- {
704
- className: cn(
705
- "h-4 w-4 shrink-0",
706
- value === option.value ? "opacity-100" : "opacity-0"
707
- )
708
- }
709
- ),
710
- /* @__PURE__ */ jsx16("span", { className: "truncate", children: option.label })
711
- ]
712
- },
713
- option.value
714
- ))
715
- ] })
716
- ] });
722
+ const command = /* @__PURE__ */ jsxs14(
723
+ CommandPrimitive,
724
+ {
725
+ shouldFilter: !isServer,
726
+ className: "flex h-full w-full flex-col overflow-hidden",
727
+ children: [
728
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center border-b px-3", children: [
729
+ /* @__PURE__ */ jsx16(Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
730
+ /* @__PURE__ */ jsx16(
731
+ CommandPrimitive.Input,
732
+ {
733
+ placeholder: searchPlaceholder,
734
+ onValueChange: isServer ? onSearchChange : void 0,
735
+ className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
736
+ }
737
+ )
738
+ ] }),
739
+ /* @__PURE__ */ jsxs14(
740
+ CommandPrimitive.List,
741
+ {
742
+ ref: listRef,
743
+ className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60",
744
+ children: [
745
+ !isServer && /* @__PURE__ */ jsx16(CommandPrimitive.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
746
+ options.map((option) => /* @__PURE__ */ jsxs14(
747
+ CommandPrimitive.Item,
748
+ {
749
+ value: isServer ? option.value : option.label,
750
+ disabled: option.disabled,
751
+ onSelect: () => {
752
+ onChange(option.value);
753
+ setOpen(false);
754
+ },
755
+ className: "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 sm:py-1.5",
756
+ children: [
757
+ /* @__PURE__ */ jsx16(
758
+ Check,
759
+ {
760
+ className: cn(
761
+ "h-4 w-4 shrink-0",
762
+ value === option.value ? "opacity-100" : "opacity-0"
763
+ )
764
+ }
765
+ ),
766
+ /* @__PURE__ */ jsx16("span", { className: "truncate", children: option.label })
767
+ ]
768
+ },
769
+ option.value
770
+ )),
771
+ isServer && !loading && options.length === 0 && /* @__PURE__ */ jsx16("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
772
+ isServer && loading && /* @__PURE__ */ jsx16("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx16(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }),
773
+ isServer && hasMore && /* @__PURE__ */ jsx16("div", { ref: sentinelRef, "aria-hidden": true, className: "h-1" })
774
+ ]
775
+ }
776
+ )
777
+ ]
778
+ }
779
+ );
717
780
  if (isDesktop) {
718
- return /* @__PURE__ */ jsxs14(PopoverPrimitive.Root, { open, onOpenChange: setOpen, modal: true, children: [
781
+ return /* @__PURE__ */ jsxs14(PopoverPrimitive.Root, { open, onOpenChange: setOpen, children: [
719
782
  /* @__PURE__ */ jsx16(PopoverPrimitive.Trigger, { asChild: true, children: trigger }),
720
783
  /* @__PURE__ */ jsx16(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx16(
721
784
  PopoverPrimitive.Content,
722
785
  {
723
786
  align: "start",
724
787
  sideOffset: 4,
725
- className: "z-50 overflow-hidden rounded-md border bg-popover p-0 text-popover-foreground shadow-md",
788
+ className: "pointer-events-auto z-50 overflow-hidden rounded-md border bg-popover p-0 text-popover-foreground shadow-md",
726
789
  style: { width: "var(--radix-popover-trigger-width)" },
727
790
  children: command
728
791
  }
@@ -745,8 +808,8 @@ function Combobox({
745
808
  // src/components/multi-combobox.tsx
746
809
  import * as PopoverPrimitive2 from "@radix-ui/react-popover";
747
810
  import { Command as CommandPrimitive2 } from "cmdk";
748
- import { Check as Check2, ChevronsUpDown as ChevronsUpDown2, Search as Search2, X as X2 } from "lucide-react";
749
- import * as React6 from "react";
811
+ import { Check as Check2, ChevronsUpDown as ChevronsUpDown2, Loader2 as Loader22, Search as Search2, X as X2 } from "lucide-react";
812
+ import * as React7 from "react";
750
813
  import { Drawer as DrawerPrimitive2 } from "vaul";
751
814
  import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
752
815
  function MultiCombobox({
@@ -760,11 +823,27 @@ function MultiCombobox({
760
823
  id,
761
824
  className,
762
825
  "aria-invalid": ariaInvalid,
763
- "aria-describedby": ariaDescribedby
826
+ "aria-describedby": ariaDescribedby,
827
+ onSearchChange,
828
+ onLoadMore,
829
+ loading,
830
+ hasMore,
831
+ selectedOptions
764
832
  }) {
765
- const [open, setOpen] = React6.useState(false);
833
+ const [open, setOpen] = React7.useState(false);
766
834
  const isDesktop = useIsDesktop();
767
- const selected = options.filter((option) => value.includes(option.value));
835
+ const isServer = onSearchChange !== void 0;
836
+ const seen = /* @__PURE__ */ new Set();
837
+ const selected = [...selectedOptions ?? [], ...options].filter((option) => {
838
+ if (!value.includes(option.value) || seen.has(option.value)) return false;
839
+ seen.add(option.value);
840
+ return true;
841
+ });
842
+ const { listRef, sentinelRef } = useInfiniteScrollSentinel({
843
+ enabled: isServer && !!hasMore,
844
+ loading,
845
+ onLoadMore
846
+ });
768
847
  const toggleValue = (optionValue) => {
769
848
  onChange(
770
849
  value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
@@ -824,43 +903,61 @@ function MultiCombobox({
824
903
  ]
825
904
  }
826
905
  );
827
- const command = /* @__PURE__ */ jsxs15(CommandPrimitive2, { className: "flex h-full w-full flex-col overflow-hidden", children: [
828
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center border-b px-3", children: [
829
- /* @__PURE__ */ jsx17(Search2, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
830
- /* @__PURE__ */ jsx17(
831
- CommandPrimitive2.Input,
832
- {
833
- placeholder: searchPlaceholder,
834
- className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
835
- }
836
- )
837
- ] }),
838
- /* @__PURE__ */ jsxs15(CommandPrimitive2.List, { className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60", children: [
839
- /* @__PURE__ */ jsx17(CommandPrimitive2.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
840
- options.map((option) => /* @__PURE__ */ jsxs15(
841
- CommandPrimitive2.Item,
842
- {
843
- value: option.label,
844
- disabled: option.disabled,
845
- onSelect: () => toggleValue(option.value),
846
- className: "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 sm:py-1.5",
847
- children: [
848
- /* @__PURE__ */ jsx17(
849
- Check2,
850
- {
851
- className: cn(
852
- "h-4 w-4 shrink-0",
853
- value.includes(option.value) ? "opacity-100" : "opacity-0"
854
- )
855
- }
856
- ),
857
- /* @__PURE__ */ jsx17("span", { className: "truncate", children: option.label })
858
- ]
859
- },
860
- option.value
861
- ))
862
- ] })
863
- ] });
906
+ const command = /* @__PURE__ */ jsxs15(
907
+ CommandPrimitive2,
908
+ {
909
+ shouldFilter: !isServer,
910
+ className: "flex h-full w-full flex-col overflow-hidden",
911
+ children: [
912
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center border-b px-3", children: [
913
+ /* @__PURE__ */ jsx17(Search2, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
914
+ /* @__PURE__ */ jsx17(
915
+ CommandPrimitive2.Input,
916
+ {
917
+ placeholder: searchPlaceholder,
918
+ onValueChange: isServer ? onSearchChange : void 0,
919
+ className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
920
+ }
921
+ )
922
+ ] }),
923
+ /* @__PURE__ */ jsxs15(
924
+ CommandPrimitive2.List,
925
+ {
926
+ ref: listRef,
927
+ className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60",
928
+ children: [
929
+ !isServer && /* @__PURE__ */ jsx17(CommandPrimitive2.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
930
+ options.map((option) => /* @__PURE__ */ jsxs15(
931
+ CommandPrimitive2.Item,
932
+ {
933
+ value: isServer ? option.value : option.label,
934
+ disabled: option.disabled,
935
+ onSelect: () => toggleValue(option.value),
936
+ className: "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 sm:py-1.5",
937
+ children: [
938
+ /* @__PURE__ */ jsx17(
939
+ Check2,
940
+ {
941
+ className: cn(
942
+ "h-4 w-4 shrink-0",
943
+ value.includes(option.value) ? "opacity-100" : "opacity-0"
944
+ )
945
+ }
946
+ ),
947
+ /* @__PURE__ */ jsx17("span", { className: "truncate", children: option.label })
948
+ ]
949
+ },
950
+ option.value
951
+ )),
952
+ isServer && !loading && options.length === 0 && /* @__PURE__ */ jsx17("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
953
+ isServer && loading && /* @__PURE__ */ jsx17("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx17(Loader22, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }),
954
+ isServer && hasMore && /* @__PURE__ */ jsx17("div", { ref: sentinelRef, "aria-hidden": true, className: "h-1" })
955
+ ]
956
+ }
957
+ )
958
+ ]
959
+ }
960
+ );
864
961
  if (isDesktop) {
865
962
  return /* @__PURE__ */ jsxs15(PopoverPrimitive2.Root, { open, onOpenChange: setOpen, children: [
866
963
  /* @__PURE__ */ jsx17(PopoverPrimitive2.Trigger, { asChild: true, disabled, children: trigger }),
@@ -892,7 +989,7 @@ function MultiCombobox({
892
989
  // src/components/color-picker.tsx
893
990
  import * as PopoverPrimitive3 from "@radix-ui/react-popover";
894
991
  import { ChevronsUpDown as ChevronsUpDown3 } from "lucide-react";
895
- import * as React7 from "react";
992
+ import * as React8 from "react";
896
993
  import { HexColorInput, HexColorPicker } from "react-colorful";
897
994
  import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
898
995
  function ColorPicker({
@@ -905,7 +1002,7 @@ function ColorPicker({
905
1002
  "aria-invalid": ariaInvalid,
906
1003
  "aria-describedby": ariaDescribedby
907
1004
  }) {
908
- const [open, setOpen] = React7.useState(false);
1005
+ const [open, setOpen] = React8.useState(false);
909
1006
  const color = value || "";
910
1007
  return /* @__PURE__ */ jsxs16(PopoverPrimitive3.Root, { open, onOpenChange: setOpen, modal: true, children: [
911
1008
  /* @__PURE__ */ jsx18(PopoverPrimitive3.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs16(
@@ -1041,7 +1138,7 @@ function DataTableWrapper({
1041
1138
  }
1042
1139
 
1043
1140
  // src/components/mobile-card-list.tsx
1044
- import { useRef } from "react";
1141
+ import { useRef as useRef2 } from "react";
1045
1142
  import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
1046
1143
  var SCROLL_THRESHOLD = 8;
1047
1144
  function MobileCardList({
@@ -1052,8 +1149,8 @@ function MobileCardList({
1052
1149
  loadingCount = 5,
1053
1150
  className
1054
1151
  }) {
1055
- const start = useRef(null);
1056
- const scrolled = useRef(false);
1152
+ const start = useRef2(null);
1153
+ const scrolled = useRef2(false);
1057
1154
  const onTouchStart = (e) => {
1058
1155
  const t = e.touches[0];
1059
1156
  start.current = { x: t.clientX, y: t.clientY };