@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/components/combobox.d.ts +18 -2
- package/dist/components/combobox.d.ts.map +1 -1
- package/dist/components/multi-combobox.d.ts +13 -1
- package/dist/components/multi-combobox.d.ts.map +1 -1
- package/dist/hooks/use-infinite-scroll-sentinel.d.ts +16 -0
- package/dist/hooks/use-infinite-scroll-sentinel.d.ts.map +1 -0
- package/dist/index.cjs +189 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +194 -97
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/combobox.tsx +66 -11
- package/src/components/multi-combobox.tsx +61 -8
- package/src/hooks/use-infinite-scroll-sentinel.ts +47 -0
|
@@ -19,16 +19,32 @@ export interface ComboboxProps {
|
|
|
19
19
|
className?: string;
|
|
20
20
|
"aria-invalid"?: boolean | "true" | "false";
|
|
21
21
|
"aria-describedby"?: string;
|
|
22
|
+
/** Modo servidor: chamado a cada tecla com o texto bruto da busca. Sua
|
|
23
|
+
* presença ativa o modo servidor (filtro do cmdk desligado). */
|
|
24
|
+
onSearchChange?: (search: string) => void;
|
|
25
|
+
/** Modo servidor: chamado ao rolar até o fim da lista. */
|
|
26
|
+
onLoadMore?: () => void;
|
|
27
|
+
/** Modo servidor: mostra uma linha de carregamento no fim da lista. */
|
|
28
|
+
loading?: boolean;
|
|
29
|
+
/** Modo servidor: habilita o disparo de `onLoadMore`. */
|
|
30
|
+
hasMore?: boolean;
|
|
31
|
+
/** Modo servidor: rótulo do valor já selecionado que pode não estar na
|
|
32
|
+
* página carregada (ex.: formulário de edição). */
|
|
33
|
+
selectedOption?: ComboboxOption;
|
|
22
34
|
}
|
|
23
35
|
/**
|
|
24
36
|
* Select com busca (combobox) acessível e responsivo.
|
|
25
37
|
*
|
|
26
|
-
* - Desktop: Popover
|
|
38
|
+
* - Desktop: Popover não-modal + cmdk — o dropdown acompanha a largura do
|
|
39
|
+
* gatilho. Não-modal (com `pointer-events-auto` no conteúdo) de propósito:
|
|
40
|
+
* dentro de um Dialog, um Popover modal trava `body { pointer-events: none }`
|
|
41
|
+
* e prende o foco, então a busca não recebe clique/digitação (mesmo motivo do
|
|
42
|
+
* MultiCombobox).
|
|
27
43
|
* - Mobile: Drawer (bottom sheet) + cmdk — evita os problemas de foco/clique de
|
|
28
44
|
* Popover dentro de modal no touch, então a busca funciona no celular.
|
|
29
45
|
*
|
|
30
46
|
* Aceita `aria-invalid`/`aria-describedby` (encaminhados pelo FormField em
|
|
31
47
|
* estado de erro) e fica com a borda vermelha quando inválido.
|
|
32
48
|
*/
|
|
33
|
-
export declare function Combobox({ options, value, onChange, placeholder, searchPlaceholder, emptyMessage, disabled, id, className, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, }: ComboboxProps): React.JSX.Element;
|
|
49
|
+
export declare function Combobox({ options, value, onChange, placeholder, searchPlaceholder, emptyMessage, disabled, id, className, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, onSearchChange, onLoadMore, loading, hasMore, selectedOption, }: ComboboxProps): React.JSX.Element;
|
|
34
50
|
//# sourceMappingURL=combobox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../src/components/combobox.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../src/components/combobox.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;IAC3C,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;qEACiE;IACjE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;wDACoD;IACpD,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EACP,KAAK,EACL,QAAQ,EACR,WAA4B,EAC5B,iBAA+B,EAC/B,YAA6C,EAC7C,QAAQ,EACR,EAAE,EACF,SAAS,EACT,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,cAAc,EACd,UAAU,EACV,OAAO,EACP,OAAO,EACP,cAAc,GACf,EAAE,aAAa,qBAiIf"}
|
|
@@ -15,6 +15,18 @@ export interface MultiComboboxProps {
|
|
|
15
15
|
className?: string;
|
|
16
16
|
"aria-invalid"?: boolean | "true" | "false";
|
|
17
17
|
"aria-describedby"?: string;
|
|
18
|
+
/** Modo servidor: chamado a cada tecla com o texto bruto da busca. Sua
|
|
19
|
+
* presença ativa o modo servidor (filtro do cmdk desligado). */
|
|
20
|
+
onSearchChange?: (search: string) => void;
|
|
21
|
+
/** Modo servidor: chamado ao rolar até o fim da lista. */
|
|
22
|
+
onLoadMore?: () => void;
|
|
23
|
+
/** Modo servidor: mostra uma linha de carregamento no fim da lista. */
|
|
24
|
+
loading?: boolean;
|
|
25
|
+
/** Modo servidor: habilita o disparo de `onLoadMore`. */
|
|
26
|
+
hasMore?: boolean;
|
|
27
|
+
/** Modo servidor: rótulos dos valores já selecionados que podem não estar
|
|
28
|
+
* na página carregada (chips do formulário de edição). */
|
|
29
|
+
selectedOptions?: ComboboxOption[];
|
|
18
30
|
}
|
|
19
31
|
/**
|
|
20
32
|
* Combobox de múltipla seleção com chips, acessível e responsivo.
|
|
@@ -31,5 +43,5 @@ export interface MultiComboboxProps {
|
|
|
31
43
|
* chip remove a opção. Aceita `aria-invalid`/`aria-describedby` (encaminhados
|
|
32
44
|
* pelo FormField em estado de erro) e fica com a borda vermelha quando inválido.
|
|
33
45
|
*/
|
|
34
|
-
export declare function MultiCombobox({ options, value, onChange, placeholder, searchPlaceholder, emptyMessage, disabled, id, className, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, }: MultiComboboxProps): React.JSX.Element;
|
|
46
|
+
export declare function MultiCombobox({ options, value, onChange, placeholder, searchPlaceholder, emptyMessage, disabled, id, className, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, onSearchChange, onLoadMore, loading, hasMore, selectedOptions, }: MultiComboboxProps): React.JSX.Element;
|
|
35
47
|
//# sourceMappingURL=multi-combobox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi-combobox.d.ts","sourceRoot":"","sources":["../../src/components/multi-combobox.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"multi-combobox.d.ts","sourceRoot":"","sources":["../../src/components/multi-combobox.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IACnC,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;IAC3C,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;qEACiE;IACjE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;+DAC2D;IAC3D,eAAe,CAAC,EAAE,cAAc,EAAE,CAAA;CACnC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,KAAK,EACL,QAAQ,EACR,WAA4B,EAC5B,iBAA+B,EAC/B,YAA6C,EAC7C,QAAQ,EACR,EAAE,EACF,SAAS,EACT,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,cAAc,EACd,UAAU,EACV,OAAO,EACP,OAAO,EACP,eAAe,GAChB,EAAE,kBAAkB,qBA6KpB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Dispara `onLoadMore` quando o `sentinelRef` entra na viewport do `listRef`
|
|
4
|
+
* (raiz = a lista rolável, não a janela — a lista rola por dentro do Popover/
|
|
5
|
+
* Drawer). Usa refs "latest" para `loading`/`onLoadMore` e evita reassinar o
|
|
6
|
+
* observer a cada render.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useInfiniteScrollSentinel({ enabled, loading, onLoadMore, }: {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
loading?: boolean;
|
|
11
|
+
onLoadMore?: () => void;
|
|
12
|
+
}): {
|
|
13
|
+
listRef: React.RefObject<HTMLDivElement | null>;
|
|
14
|
+
sentinelRef: React.RefObject<HTMLDivElement | null>;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=use-infinite-scroll-sentinel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-infinite-scroll-sentinel.d.ts","sourceRoot":"","sources":["../../src/hooks/use-infinite-scroll-sentinel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,OAAO,EACP,UAAU,GACX,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;;;EA8BA"}
|
package/dist/index.cjs
CHANGED
|
@@ -690,17 +690,51 @@ PasswordInput.displayName = "PasswordInput";
|
|
|
690
690
|
var PopoverPrimitive = __toESM(require("@radix-ui/react-popover"), 1);
|
|
691
691
|
var import_cmdk = require("cmdk");
|
|
692
692
|
var import_lucide_react4 = require("lucide-react");
|
|
693
|
-
var
|
|
693
|
+
var React6 = __toESM(require("react"), 1);
|
|
694
694
|
var import_vaul = require("vaul");
|
|
695
695
|
|
|
696
|
-
// src/hooks/use-
|
|
696
|
+
// src/hooks/use-infinite-scroll-sentinel.ts
|
|
697
697
|
var React4 = __toESM(require("react"), 1);
|
|
698
|
+
function useInfiniteScrollSentinel({
|
|
699
|
+
enabled,
|
|
700
|
+
loading,
|
|
701
|
+
onLoadMore
|
|
702
|
+
}) {
|
|
703
|
+
const listRef = React4.useRef(null);
|
|
704
|
+
const sentinelRef = React4.useRef(null);
|
|
705
|
+
const loadingRef = React4.useRef(loading);
|
|
706
|
+
const onLoadMoreRef = React4.useRef(onLoadMore);
|
|
707
|
+
React4.useEffect(() => {
|
|
708
|
+
loadingRef.current = loading;
|
|
709
|
+
onLoadMoreRef.current = onLoadMore;
|
|
710
|
+
});
|
|
711
|
+
React4.useEffect(() => {
|
|
712
|
+
if (!enabled) return;
|
|
713
|
+
const root = listRef.current;
|
|
714
|
+
const sentinel = sentinelRef.current;
|
|
715
|
+
if (!root || !sentinel) return;
|
|
716
|
+
const observer = new IntersectionObserver(
|
|
717
|
+
(entries) => {
|
|
718
|
+
if (entries[0]?.isIntersecting && !loadingRef.current) {
|
|
719
|
+
onLoadMoreRef.current?.();
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
{ root, rootMargin: "0px 0px 100px 0px" }
|
|
723
|
+
);
|
|
724
|
+
observer.observe(sentinel);
|
|
725
|
+
return () => observer.disconnect();
|
|
726
|
+
}, [enabled]);
|
|
727
|
+
return { listRef, sentinelRef };
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// src/hooks/use-is-desktop.ts
|
|
731
|
+
var React5 = __toESM(require("react"), 1);
|
|
698
732
|
var DESKTOP_QUERY = "(min-width: 640px)";
|
|
699
733
|
function useIsDesktop() {
|
|
700
|
-
const [isDesktop, setIsDesktop] =
|
|
734
|
+
const [isDesktop, setIsDesktop] = React5.useState(
|
|
701
735
|
() => typeof window !== "undefined" ? window.matchMedia(DESKTOP_QUERY).matches : true
|
|
702
736
|
);
|
|
703
|
-
|
|
737
|
+
React5.useEffect(() => {
|
|
704
738
|
const mql = window.matchMedia(DESKTOP_QUERY);
|
|
705
739
|
const onChange = () => setIsDesktop(mql.matches);
|
|
706
740
|
onChange();
|
|
@@ -723,11 +757,22 @@ function Combobox({
|
|
|
723
757
|
id,
|
|
724
758
|
className,
|
|
725
759
|
"aria-invalid": ariaInvalid,
|
|
726
|
-
"aria-describedby": ariaDescribedby
|
|
760
|
+
"aria-describedby": ariaDescribedby,
|
|
761
|
+
onSearchChange,
|
|
762
|
+
onLoadMore,
|
|
763
|
+
loading,
|
|
764
|
+
hasMore,
|
|
765
|
+
selectedOption
|
|
727
766
|
}) {
|
|
728
|
-
const [open, setOpen] =
|
|
767
|
+
const [open, setOpen] = React6.useState(false);
|
|
729
768
|
const isDesktop = useIsDesktop();
|
|
730
|
-
const
|
|
769
|
+
const isServer = onSearchChange !== void 0;
|
|
770
|
+
const selected = selectedOption?.value === value ? selectedOption : options.find((option) => option.value === value);
|
|
771
|
+
const { listRef, sentinelRef } = useInfiniteScrollSentinel({
|
|
772
|
+
enabled: isServer && !!hasMore,
|
|
773
|
+
loading,
|
|
774
|
+
onLoadMore
|
|
775
|
+
});
|
|
731
776
|
const trigger = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
732
777
|
"button",
|
|
733
778
|
{
|
|
@@ -748,55 +793,73 @@ function Combobox({
|
|
|
748
793
|
]
|
|
749
794
|
}
|
|
750
795
|
);
|
|
751
|
-
const command = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
796
|
+
const command = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
797
|
+
import_cmdk.Command,
|
|
798
|
+
{
|
|
799
|
+
shouldFilter: !isServer,
|
|
800
|
+
className: "flex h-full w-full flex-col overflow-hidden",
|
|
801
|
+
children: [
|
|
802
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center border-b px-3", children: [
|
|
803
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react4.Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
|
|
804
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
805
|
+
import_cmdk.Command.Input,
|
|
806
|
+
{
|
|
807
|
+
placeholder: searchPlaceholder,
|
|
808
|
+
onValueChange: isServer ? onSearchChange : void 0,
|
|
809
|
+
className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
|
|
810
|
+
}
|
|
811
|
+
)
|
|
812
|
+
] }),
|
|
813
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
814
|
+
import_cmdk.Command.List,
|
|
815
|
+
{
|
|
816
|
+
ref: listRef,
|
|
817
|
+
className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60",
|
|
818
|
+
children: [
|
|
819
|
+
!isServer && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_cmdk.Command.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
|
|
820
|
+
options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
821
|
+
import_cmdk.Command.Item,
|
|
822
|
+
{
|
|
823
|
+
value: isServer ? option.value : option.label,
|
|
824
|
+
disabled: option.disabled,
|
|
825
|
+
onSelect: () => {
|
|
826
|
+
onChange(option.value);
|
|
827
|
+
setOpen(false);
|
|
828
|
+
},
|
|
829
|
+
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",
|
|
830
|
+
children: [
|
|
831
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
832
|
+
import_lucide_react4.Check,
|
|
833
|
+
{
|
|
834
|
+
className: cn(
|
|
835
|
+
"h-4 w-4 shrink-0",
|
|
836
|
+
value === option.value ? "opacity-100" : "opacity-0"
|
|
837
|
+
)
|
|
838
|
+
}
|
|
839
|
+
),
|
|
840
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate", children: option.label })
|
|
841
|
+
]
|
|
842
|
+
},
|
|
843
|
+
option.value
|
|
844
|
+
)),
|
|
845
|
+
isServer && !loading && options.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
|
|
846
|
+
isServer && loading && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react4.Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }),
|
|
847
|
+
isServer && hasMore && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: sentinelRef, "aria-hidden": true, className: "h-1" })
|
|
848
|
+
]
|
|
849
|
+
}
|
|
850
|
+
)
|
|
851
|
+
]
|
|
852
|
+
}
|
|
853
|
+
);
|
|
791
854
|
if (isDesktop) {
|
|
792
|
-
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(PopoverPrimitive.Root, { open, onOpenChange: setOpen,
|
|
855
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(PopoverPrimitive.Root, { open, onOpenChange: setOpen, children: [
|
|
793
856
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PopoverPrimitive.Trigger, { asChild: true, children: trigger }),
|
|
794
857
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PopoverPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
795
858
|
PopoverPrimitive.Content,
|
|
796
859
|
{
|
|
797
860
|
align: "start",
|
|
798
861
|
sideOffset: 4,
|
|
799
|
-
className: "z-50 overflow-hidden rounded-md border bg-popover p-0 text-popover-foreground shadow-md",
|
|
862
|
+
className: "pointer-events-auto z-50 overflow-hidden rounded-md border bg-popover p-0 text-popover-foreground shadow-md",
|
|
800
863
|
style: { width: "var(--radix-popover-trigger-width)" },
|
|
801
864
|
children: command
|
|
802
865
|
}
|
|
@@ -820,7 +883,7 @@ function Combobox({
|
|
|
820
883
|
var PopoverPrimitive2 = __toESM(require("@radix-ui/react-popover"), 1);
|
|
821
884
|
var import_cmdk2 = require("cmdk");
|
|
822
885
|
var import_lucide_react5 = require("lucide-react");
|
|
823
|
-
var
|
|
886
|
+
var React7 = __toESM(require("react"), 1);
|
|
824
887
|
var import_vaul2 = require("vaul");
|
|
825
888
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
826
889
|
function MultiCombobox({
|
|
@@ -834,11 +897,27 @@ function MultiCombobox({
|
|
|
834
897
|
id,
|
|
835
898
|
className,
|
|
836
899
|
"aria-invalid": ariaInvalid,
|
|
837
|
-
"aria-describedby": ariaDescribedby
|
|
900
|
+
"aria-describedby": ariaDescribedby,
|
|
901
|
+
onSearchChange,
|
|
902
|
+
onLoadMore,
|
|
903
|
+
loading,
|
|
904
|
+
hasMore,
|
|
905
|
+
selectedOptions
|
|
838
906
|
}) {
|
|
839
|
-
const [open, setOpen] =
|
|
907
|
+
const [open, setOpen] = React7.useState(false);
|
|
840
908
|
const isDesktop = useIsDesktop();
|
|
841
|
-
const
|
|
909
|
+
const isServer = onSearchChange !== void 0;
|
|
910
|
+
const seen = /* @__PURE__ */ new Set();
|
|
911
|
+
const selected = [...selectedOptions ?? [], ...options].filter((option) => {
|
|
912
|
+
if (!value.includes(option.value) || seen.has(option.value)) return false;
|
|
913
|
+
seen.add(option.value);
|
|
914
|
+
return true;
|
|
915
|
+
});
|
|
916
|
+
const { listRef, sentinelRef } = useInfiniteScrollSentinel({
|
|
917
|
+
enabled: isServer && !!hasMore,
|
|
918
|
+
loading,
|
|
919
|
+
onLoadMore
|
|
920
|
+
});
|
|
842
921
|
const toggleValue = (optionValue) => {
|
|
843
922
|
onChange(
|
|
844
923
|
value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
|
|
@@ -898,43 +977,61 @@ function MultiCombobox({
|
|
|
898
977
|
]
|
|
899
978
|
}
|
|
900
979
|
);
|
|
901
|
-
const command = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
{
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
980
|
+
const command = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
981
|
+
import_cmdk2.Command,
|
|
982
|
+
{
|
|
983
|
+
shouldFilter: !isServer,
|
|
984
|
+
className: "flex h-full w-full flex-col overflow-hidden",
|
|
985
|
+
children: [
|
|
986
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center border-b px-3", children: [
|
|
987
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
|
|
988
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
989
|
+
import_cmdk2.Command.Input,
|
|
990
|
+
{
|
|
991
|
+
placeholder: searchPlaceholder,
|
|
992
|
+
onValueChange: isServer ? onSearchChange : void 0,
|
|
993
|
+
className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
|
|
994
|
+
}
|
|
995
|
+
)
|
|
996
|
+
] }),
|
|
997
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
998
|
+
import_cmdk2.Command.List,
|
|
999
|
+
{
|
|
1000
|
+
ref: listRef,
|
|
1001
|
+
className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60",
|
|
1002
|
+
children: [
|
|
1003
|
+
!isServer && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_cmdk2.Command.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
|
|
1004
|
+
options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
1005
|
+
import_cmdk2.Command.Item,
|
|
1006
|
+
{
|
|
1007
|
+
value: isServer ? option.value : option.label,
|
|
1008
|
+
disabled: option.disabled,
|
|
1009
|
+
onSelect: () => toggleValue(option.value),
|
|
1010
|
+
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",
|
|
1011
|
+
children: [
|
|
1012
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1013
|
+
import_lucide_react5.Check,
|
|
1014
|
+
{
|
|
1015
|
+
className: cn(
|
|
1016
|
+
"h-4 w-4 shrink-0",
|
|
1017
|
+
value.includes(option.value) ? "opacity-100" : "opacity-0"
|
|
1018
|
+
)
|
|
1019
|
+
}
|
|
1020
|
+
),
|
|
1021
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate", children: option.label })
|
|
1022
|
+
]
|
|
1023
|
+
},
|
|
1024
|
+
option.value
|
|
1025
|
+
)),
|
|
1026
|
+
isServer && !loading && options.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
|
|
1027
|
+
isServer && loading && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }),
|
|
1028
|
+
isServer && hasMore && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref: sentinelRef, "aria-hidden": true, className: "h-1" })
|
|
1029
|
+
]
|
|
1030
|
+
}
|
|
1031
|
+
)
|
|
1032
|
+
]
|
|
1033
|
+
}
|
|
1034
|
+
);
|
|
938
1035
|
if (isDesktop) {
|
|
939
1036
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(PopoverPrimitive2.Root, { open, onOpenChange: setOpen, children: [
|
|
940
1037
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(PopoverPrimitive2.Trigger, { asChild: true, disabled, children: trigger }),
|
|
@@ -966,7 +1063,7 @@ function MultiCombobox({
|
|
|
966
1063
|
// src/components/color-picker.tsx
|
|
967
1064
|
var PopoverPrimitive3 = __toESM(require("@radix-ui/react-popover"), 1);
|
|
968
1065
|
var import_lucide_react6 = require("lucide-react");
|
|
969
|
-
var
|
|
1066
|
+
var React8 = __toESM(require("react"), 1);
|
|
970
1067
|
var import_react_colorful = require("react-colorful");
|
|
971
1068
|
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
972
1069
|
function ColorPicker({
|
|
@@ -979,7 +1076,7 @@ function ColorPicker({
|
|
|
979
1076
|
"aria-invalid": ariaInvalid,
|
|
980
1077
|
"aria-describedby": ariaDescribedby
|
|
981
1078
|
}) {
|
|
982
|
-
const [open, setOpen] =
|
|
1079
|
+
const [open, setOpen] = React8.useState(false);
|
|
983
1080
|
const color = value || "";
|
|
984
1081
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(PopoverPrimitive3.Root, { open, onOpenChange: setOpen, modal: true, children: [
|
|
985
1082
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PopoverPrimitive3.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|