@motor-hero/ui-kit 0.23.2 → 0.25.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 +19 -1
- 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 +219 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +224 -97
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/combobox.tsx +112 -14
- package/src/components/multi-combobox.tsx +61 -8
- package/src/hooks/use-infinite-scroll-sentinel.ts +47 -0
|
@@ -19,6 +19,24 @@ 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;
|
|
34
|
+
/** Permite confirmar o texto digitado como valor, mesmo fora da lista
|
|
35
|
+
* (ex.: unidade de medida com texto livre). Mostra uma linha "criar" no
|
|
36
|
+
* topo e exibe o valor personalizado no gatilho. */
|
|
37
|
+
creatable?: boolean;
|
|
38
|
+
/** Rótulo da linha de criação. Padrão: `Usar "<texto>"`. */
|
|
39
|
+
formatCreateLabel?: (search: string) => string;
|
|
22
40
|
}
|
|
23
41
|
/**
|
|
24
42
|
* Select com busca (combobox) acessível e responsivo.
|
|
@@ -34,5 +52,5 @@ export interface ComboboxProps {
|
|
|
34
52
|
* Aceita `aria-invalid`/`aria-describedby` (encaminhados pelo FormField em
|
|
35
53
|
* estado de erro) e fica com a borda vermelha quando inválido.
|
|
36
54
|
*/
|
|
37
|
-
export declare function Combobox({ options, value, onChange, placeholder, searchPlaceholder, emptyMessage, disabled, id, className, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, }: ComboboxProps): React.JSX.Element;
|
|
55
|
+
export declare function Combobox({ options, value, onChange, placeholder, searchPlaceholder, emptyMessage, disabled, id, className, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, onSearchChange, onLoadMore, loading, hasMore, selectedOption, creatable, formatCreateLabel, }: ComboboxProps): React.JSX.Element;
|
|
38
56
|
//# 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;IAC/B;;yDAEqD;IACrD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAA;CAC/C;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,EACd,SAAS,EACT,iBAAiB,GAClB,EAAE,aAAa,qBAwKf"}
|
|
@@ -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,41 @@ 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,
|
|
766
|
+
creatable,
|
|
767
|
+
formatCreateLabel
|
|
727
768
|
}) {
|
|
728
|
-
const [open, setOpen] =
|
|
769
|
+
const [open, setOpen] = React6.useState(false);
|
|
770
|
+
const [search, setSearch] = React6.useState("");
|
|
729
771
|
const isDesktop = useIsDesktop();
|
|
730
|
-
const
|
|
772
|
+
const isServer = onSearchChange !== void 0;
|
|
773
|
+
const selected = selectedOption?.value === value ? selectedOption : options.find((option) => option.value === value) ?? (creatable && value ? { value, label: value } : void 0);
|
|
774
|
+
const handleOpenChange = (next) => {
|
|
775
|
+
setOpen(next);
|
|
776
|
+
if (!next) setSearch("");
|
|
777
|
+
};
|
|
778
|
+
const handleSelect = (next) => {
|
|
779
|
+
onChange(next);
|
|
780
|
+
handleOpenChange(false);
|
|
781
|
+
};
|
|
782
|
+
const handleSearchChange = (next) => {
|
|
783
|
+
setSearch(next);
|
|
784
|
+
onSearchChange?.(next);
|
|
785
|
+
};
|
|
786
|
+
const trimmedSearch = search.trim();
|
|
787
|
+
const showCreate = !!creatable && trimmedSearch.length > 0 && !options.some(
|
|
788
|
+
(option) => option.value.toLowerCase() === trimmedSearch.toLowerCase()
|
|
789
|
+
);
|
|
790
|
+
const { listRef, sentinelRef } = useInfiniteScrollSentinel({
|
|
791
|
+
enabled: isServer && !!hasMore,
|
|
792
|
+
loading,
|
|
793
|
+
onLoadMore
|
|
794
|
+
});
|
|
731
795
|
const trigger = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
732
796
|
"button",
|
|
733
797
|
{
|
|
@@ -748,48 +812,77 @@ function Combobox({
|
|
|
748
812
|
]
|
|
749
813
|
}
|
|
750
814
|
);
|
|
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
|
-
|
|
815
|
+
const command = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
816
|
+
import_cmdk.Command,
|
|
817
|
+
{
|
|
818
|
+
shouldFilter: !isServer,
|
|
819
|
+
className: "flex h-full w-full flex-col overflow-hidden",
|
|
820
|
+
children: [
|
|
821
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center border-b px-3", children: [
|
|
822
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react4.Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
|
|
823
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
824
|
+
import_cmdk.Command.Input,
|
|
825
|
+
{
|
|
826
|
+
placeholder: searchPlaceholder,
|
|
827
|
+
onValueChange: isServer || creatable ? handleSearchChange : void 0,
|
|
828
|
+
className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
|
|
829
|
+
}
|
|
830
|
+
)
|
|
831
|
+
] }),
|
|
832
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
833
|
+
import_cmdk.Command.List,
|
|
834
|
+
{
|
|
835
|
+
ref: listRef,
|
|
836
|
+
className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60",
|
|
837
|
+
children: [
|
|
838
|
+
!isServer && !showCreate && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_cmdk.Command.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
|
|
839
|
+
showCreate && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
840
|
+
import_cmdk.Command.Item,
|
|
841
|
+
{
|
|
842
|
+
value: trimmedSearch,
|
|
843
|
+
onSelect: () => handleSelect(trimmedSearch),
|
|
844
|
+
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 sm:py-1.5",
|
|
845
|
+
children: [
|
|
846
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react4.Plus, { className: "h-4 w-4 shrink-0" }),
|
|
847
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate", children: (formatCreateLabel ?? ((text) => `Usar "${text}"`))(
|
|
848
|
+
trimmedSearch
|
|
849
|
+
) })
|
|
850
|
+
]
|
|
851
|
+
}
|
|
852
|
+
),
|
|
853
|
+
options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
854
|
+
import_cmdk.Command.Item,
|
|
855
|
+
{
|
|
856
|
+
value: isServer ? option.value : option.label,
|
|
857
|
+
disabled: option.disabled,
|
|
858
|
+
onSelect: () => handleSelect(option.value),
|
|
859
|
+
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",
|
|
860
|
+
children: [
|
|
861
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
862
|
+
import_lucide_react4.Check,
|
|
863
|
+
{
|
|
864
|
+
className: cn(
|
|
865
|
+
"h-4 w-4 shrink-0",
|
|
866
|
+
value === option.value ? "opacity-100" : "opacity-0"
|
|
867
|
+
)
|
|
868
|
+
}
|
|
869
|
+
),
|
|
870
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate", children: option.label })
|
|
871
|
+
]
|
|
872
|
+
},
|
|
873
|
+
option.value
|
|
874
|
+
)),
|
|
875
|
+
isServer && !loading && options.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
|
|
876
|
+
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" }) }),
|
|
877
|
+
isServer && hasMore && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: sentinelRef, "aria-hidden": true, className: "h-1" })
|
|
878
|
+
]
|
|
879
|
+
}
|
|
880
|
+
)
|
|
881
|
+
]
|
|
882
|
+
}
|
|
883
|
+
);
|
|
791
884
|
if (isDesktop) {
|
|
792
|
-
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(PopoverPrimitive.Root, { open, onOpenChange:
|
|
885
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(PopoverPrimitive.Root, { open, onOpenChange: handleOpenChange, children: [
|
|
793
886
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PopoverPrimitive.Trigger, { asChild: true, children: trigger }),
|
|
794
887
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PopoverPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
795
888
|
PopoverPrimitive.Content,
|
|
@@ -803,7 +896,7 @@ function Combobox({
|
|
|
803
896
|
) })
|
|
804
897
|
] });
|
|
805
898
|
}
|
|
806
|
-
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_vaul.Drawer.Root, { open, onOpenChange:
|
|
899
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_vaul.Drawer.Root, { open, onOpenChange: handleOpenChange, children: [
|
|
807
900
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_vaul.Drawer.Trigger, { asChild: true, children: trigger }),
|
|
808
901
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_vaul.Drawer.Portal, { children: [
|
|
809
902
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_vaul.Drawer.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
|
|
@@ -820,7 +913,7 @@ function Combobox({
|
|
|
820
913
|
var PopoverPrimitive2 = __toESM(require("@radix-ui/react-popover"), 1);
|
|
821
914
|
var import_cmdk2 = require("cmdk");
|
|
822
915
|
var import_lucide_react5 = require("lucide-react");
|
|
823
|
-
var
|
|
916
|
+
var React7 = __toESM(require("react"), 1);
|
|
824
917
|
var import_vaul2 = require("vaul");
|
|
825
918
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
826
919
|
function MultiCombobox({
|
|
@@ -834,11 +927,27 @@ function MultiCombobox({
|
|
|
834
927
|
id,
|
|
835
928
|
className,
|
|
836
929
|
"aria-invalid": ariaInvalid,
|
|
837
|
-
"aria-describedby": ariaDescribedby
|
|
930
|
+
"aria-describedby": ariaDescribedby,
|
|
931
|
+
onSearchChange,
|
|
932
|
+
onLoadMore,
|
|
933
|
+
loading,
|
|
934
|
+
hasMore,
|
|
935
|
+
selectedOptions
|
|
838
936
|
}) {
|
|
839
|
-
const [open, setOpen] =
|
|
937
|
+
const [open, setOpen] = React7.useState(false);
|
|
840
938
|
const isDesktop = useIsDesktop();
|
|
841
|
-
const
|
|
939
|
+
const isServer = onSearchChange !== void 0;
|
|
940
|
+
const seen = /* @__PURE__ */ new Set();
|
|
941
|
+
const selected = [...selectedOptions ?? [], ...options].filter((option) => {
|
|
942
|
+
if (!value.includes(option.value) || seen.has(option.value)) return false;
|
|
943
|
+
seen.add(option.value);
|
|
944
|
+
return true;
|
|
945
|
+
});
|
|
946
|
+
const { listRef, sentinelRef } = useInfiniteScrollSentinel({
|
|
947
|
+
enabled: isServer && !!hasMore,
|
|
948
|
+
loading,
|
|
949
|
+
onLoadMore
|
|
950
|
+
});
|
|
842
951
|
const toggleValue = (optionValue) => {
|
|
843
952
|
onChange(
|
|
844
953
|
value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
|
|
@@ -898,43 +1007,61 @@ function MultiCombobox({
|
|
|
898
1007
|
]
|
|
899
1008
|
}
|
|
900
1009
|
);
|
|
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
|
-
|
|
1010
|
+
const command = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
1011
|
+
import_cmdk2.Command,
|
|
1012
|
+
{
|
|
1013
|
+
shouldFilter: !isServer,
|
|
1014
|
+
className: "flex h-full w-full flex-col overflow-hidden",
|
|
1015
|
+
children: [
|
|
1016
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center border-b px-3", children: [
|
|
1017
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
|
|
1018
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1019
|
+
import_cmdk2.Command.Input,
|
|
1020
|
+
{
|
|
1021
|
+
placeholder: searchPlaceholder,
|
|
1022
|
+
onValueChange: isServer ? onSearchChange : void 0,
|
|
1023
|
+
className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
|
|
1024
|
+
}
|
|
1025
|
+
)
|
|
1026
|
+
] }),
|
|
1027
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
1028
|
+
import_cmdk2.Command.List,
|
|
1029
|
+
{
|
|
1030
|
+
ref: listRef,
|
|
1031
|
+
className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60",
|
|
1032
|
+
children: [
|
|
1033
|
+
!isServer && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_cmdk2.Command.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
|
|
1034
|
+
options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
1035
|
+
import_cmdk2.Command.Item,
|
|
1036
|
+
{
|
|
1037
|
+
value: isServer ? option.value : option.label,
|
|
1038
|
+
disabled: option.disabled,
|
|
1039
|
+
onSelect: () => toggleValue(option.value),
|
|
1040
|
+
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",
|
|
1041
|
+
children: [
|
|
1042
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1043
|
+
import_lucide_react5.Check,
|
|
1044
|
+
{
|
|
1045
|
+
className: cn(
|
|
1046
|
+
"h-4 w-4 shrink-0",
|
|
1047
|
+
value.includes(option.value) ? "opacity-100" : "opacity-0"
|
|
1048
|
+
)
|
|
1049
|
+
}
|
|
1050
|
+
),
|
|
1051
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate", children: option.label })
|
|
1052
|
+
]
|
|
1053
|
+
},
|
|
1054
|
+
option.value
|
|
1055
|
+
)),
|
|
1056
|
+
isServer && !loading && options.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
|
|
1057
|
+
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" }) }),
|
|
1058
|
+
isServer && hasMore && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref: sentinelRef, "aria-hidden": true, className: "h-1" })
|
|
1059
|
+
]
|
|
1060
|
+
}
|
|
1061
|
+
)
|
|
1062
|
+
]
|
|
1063
|
+
}
|
|
1064
|
+
);
|
|
938
1065
|
if (isDesktop) {
|
|
939
1066
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(PopoverPrimitive2.Root, { open, onOpenChange: setOpen, children: [
|
|
940
1067
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(PopoverPrimitive2.Trigger, { asChild: true, disabled, children: trigger }),
|
|
@@ -966,7 +1093,7 @@ function MultiCombobox({
|
|
|
966
1093
|
// src/components/color-picker.tsx
|
|
967
1094
|
var PopoverPrimitive3 = __toESM(require("@radix-ui/react-popover"), 1);
|
|
968
1095
|
var import_lucide_react6 = require("lucide-react");
|
|
969
|
-
var
|
|
1096
|
+
var React8 = __toESM(require("react"), 1);
|
|
970
1097
|
var import_react_colorful = require("react-colorful");
|
|
971
1098
|
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
972
1099
|
function ColorPicker({
|
|
@@ -979,7 +1106,7 @@ function ColorPicker({
|
|
|
979
1106
|
"aria-invalid": ariaInvalid,
|
|
980
1107
|
"aria-describedby": ariaDescribedby
|
|
981
1108
|
}) {
|
|
982
|
-
const [open, setOpen] =
|
|
1109
|
+
const [open, setOpen] = React8.useState(false);
|
|
983
1110
|
const color = value || "";
|
|
984
1111
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(PopoverPrimitive3.Root, { open, onOpenChange: setOpen, modal: true, children: [
|
|
985
1112
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PopoverPrimitive3.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|