@motor-hero/ui-kit 0.16.1 → 0.17.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.
@@ -8,8 +8,11 @@ export interface ComboboxProps {
8
8
  options: ComboboxOption[];
9
9
  value?: string;
10
10
  onChange: (value: string) => void;
11
+ /** Texto do gatilho quando nada está selecionado */
11
12
  placeholder?: string;
13
+ /** Placeholder do campo de busca */
12
14
  searchPlaceholder?: string;
15
+ /** Mensagem quando a busca não retorna nada */
13
16
  emptyMessage?: string;
14
17
  disabled?: boolean;
15
18
  id?: string;
@@ -17,5 +20,15 @@ export interface ComboboxProps {
17
20
  "aria-invalid"?: boolean | "true" | "false";
18
21
  "aria-describedby"?: string;
19
22
  }
23
+ /**
24
+ * Select com busca (combobox) acessível e responsivo.
25
+ *
26
+ * - Desktop: Popover (modal) + cmdk — o dropdown acompanha a largura do gatilho.
27
+ * - Mobile: Drawer (bottom sheet) + cmdk — evita os problemas de foco/clique de
28
+ * Popover dentro de modal no touch, então a busca funciona no celular.
29
+ *
30
+ * Aceita `aria-invalid`/`aria-describedby` (encaminhados pelo FormField em
31
+ * estado de erro) e fica com a borda vermelha quando inválido.
32
+ */
20
33
  export declare function Combobox({ options, value, onChange, placeholder, searchPlaceholder, emptyMessage, disabled, id, className, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, }: ComboboxProps): React.JSX.Element;
21
34
  //# 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;AAG9B,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,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,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;CAC5B;AAED,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,GACpC,EAAE,aAAa,qBAyEf"}
1
+ {"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../src/components/combobox.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,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;CAC5B;AAsBD;;;;;;;;;GASG;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,GACpC,EAAE,aAAa,qBAgGf"}
package/dist/index.cjs CHANGED
@@ -615,7 +615,22 @@ var PopoverPrimitive = __toESM(require("@radix-ui/react-popover"), 1);
615
615
  var import_cmdk = require("cmdk");
616
616
  var import_lucide_react4 = require("lucide-react");
617
617
  var React4 = __toESM(require("react"), 1);
618
+ var import_vaul = require("vaul");
618
619
  var import_jsx_runtime16 = require("react/jsx-runtime");
620
+ var DESKTOP_QUERY = "(min-width: 640px)";
621
+ function useIsDesktop() {
622
+ const [isDesktop, setIsDesktop] = React4.useState(
623
+ () => typeof window !== "undefined" ? window.matchMedia(DESKTOP_QUERY).matches : true
624
+ );
625
+ React4.useEffect(() => {
626
+ const mql = window.matchMedia(DESKTOP_QUERY);
627
+ const onChange = () => setIsDesktop(mql.matches);
628
+ onChange();
629
+ mql.addEventListener("change", onChange);
630
+ return () => mql.removeEventListener("change", onChange);
631
+ }, []);
632
+ return isDesktop;
633
+ }
619
634
  function Combobox({
620
635
  options,
621
636
  value,
@@ -630,83 +645,93 @@ function Combobox({
630
645
  "aria-describedby": ariaDescribedby
631
646
  }) {
632
647
  const [open, setOpen] = React4.useState(false);
648
+ const isDesktop = useIsDesktop();
633
649
  const selected = options.find((option) => option.value === value);
634
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(PopoverPrimitive.Root, { open, onOpenChange: setOpen, modal: true, children: [
635
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PopoverPrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
636
- "button",
637
- {
638
- type: "button",
639
- id,
640
- role: "combobox",
641
- "aria-expanded": open,
642
- "aria-invalid": ariaInvalid,
643
- "aria-describedby": ariaDescribedby,
644
- disabled,
645
- className: cn(
646
- "flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive",
647
- className
648
- ),
649
- children: [
650
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
651
- "span",
652
- {
653
- className: cn("truncate", !selected && "text-muted-foreground"),
654
- children: selected ? selected.label : placeholder
655
- }
656
- ),
657
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react4.ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50" })
658
- ]
659
- }
660
- ) }),
661
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PopoverPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
662
- PopoverPrimitive.Content,
663
- {
664
- align: "start",
665
- sideOffset: 4,
666
- className: "z-50 overflow-hidden rounded-md border bg-popover p-0 text-popover-foreground shadow-md",
667
- style: { width: "var(--radix-popover-trigger-width)" },
668
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_cmdk.Command, { className: "flex h-full w-full flex-col overflow-hidden", children: [
669
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center border-b px-3", children: [
670
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react4.Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
650
+ const trigger = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
651
+ "button",
652
+ {
653
+ type: "button",
654
+ id,
655
+ role: "combobox",
656
+ "aria-expanded": open,
657
+ "aria-invalid": ariaInvalid,
658
+ "aria-describedby": ariaDescribedby,
659
+ disabled,
660
+ className: cn(
661
+ "flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive",
662
+ className
663
+ ),
664
+ children: [
665
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: cn("truncate", !selected && "text-muted-foreground"), children: selected ? selected.label : placeholder }),
666
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react4.ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50" })
667
+ ]
668
+ }
669
+ );
670
+ const command = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_cmdk.Command, { className: "flex h-full w-full flex-col overflow-hidden", children: [
671
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center border-b px-3", children: [
672
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react4.Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
673
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
674
+ import_cmdk.Command.Input,
675
+ {
676
+ placeholder: searchPlaceholder,
677
+ className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
678
+ }
679
+ )
680
+ ] }),
681
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_cmdk.Command.List, { className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60", children: [
682
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_cmdk.Command.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
683
+ options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
684
+ import_cmdk.Command.Item,
685
+ {
686
+ value: option.label,
687
+ disabled: option.disabled,
688
+ onSelect: () => {
689
+ onChange(option.value);
690
+ setOpen(false);
691
+ },
692
+ 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",
693
+ children: [
671
694
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
672
- import_cmdk.Command.Input,
695
+ import_lucide_react4.Check,
673
696
  {
674
- placeholder: searchPlaceholder,
675
- className: "flex h-9 w-full bg-transparent py-2 text-sm outline-none placeholder:text-muted-foreground"
697
+ className: cn(
698
+ "h-4 w-4 shrink-0",
699
+ value === option.value ? "opacity-100" : "opacity-0"
700
+ )
676
701
  }
677
- )
678
- ] }),
679
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_cmdk.Command.List, { className: "max-h-60 overflow-y-auto overflow-x-hidden p-1", children: [
680
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_cmdk.Command.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
681
- options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
682
- import_cmdk.Command.Item,
683
- {
684
- value: option.label,
685
- disabled: option.disabled,
686
- onSelect: () => {
687
- onChange(option.value);
688
- setOpen(false);
689
- },
690
- className: "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.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",
691
- children: [
692
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
693
- import_lucide_react4.Check,
694
- {
695
- className: cn(
696
- "h-4 w-4 shrink-0",
697
- value === option.value ? "opacity-100" : "opacity-0"
698
- )
699
- }
700
- ),
701
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate", children: option.label })
702
- ]
703
- },
704
- option.value
705
- ))
706
- ] })
707
- ] })
708
- }
709
- ) })
702
+ ),
703
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate", children: option.label })
704
+ ]
705
+ },
706
+ option.value
707
+ ))
708
+ ] })
709
+ ] });
710
+ if (isDesktop) {
711
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(PopoverPrimitive.Root, { open, onOpenChange: setOpen, modal: true, children: [
712
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PopoverPrimitive.Trigger, { asChild: true, children: trigger }),
713
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PopoverPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
714
+ PopoverPrimitive.Content,
715
+ {
716
+ align: "start",
717
+ sideOffset: 4,
718
+ className: "z-50 overflow-hidden rounded-md border bg-popover p-0 text-popover-foreground shadow-md",
719
+ style: { width: "var(--radix-popover-trigger-width)" },
720
+ children: command
721
+ }
722
+ ) })
723
+ ] });
724
+ }
725
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_vaul.Drawer.Root, { open, onOpenChange: setOpen, children: [
726
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_vaul.Drawer.Trigger, { asChild: true, children: trigger }),
727
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_vaul.Drawer.Portal, { children: [
728
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_vaul.Drawer.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
729
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_vaul.Drawer.Content, { className: "fixed inset-x-0 bottom-0 z-50 mt-24 flex max-h-[85vh] flex-col rounded-t-2xl border bg-popover text-popover-foreground outline-none", children: [
730
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_vaul.Drawer.Title, { className: "sr-only", children: placeholder }),
731
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
732
+ command
733
+ ] })
734
+ ] })
710
735
  ] });
711
736
  }
712
737