@protobase/ui 0.12.12 → 0.12.14

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.
@@ -0,0 +1,88 @@
1
+ import { cn } from "../lib/cn.js";
2
+ import { menuKeyTarget } from "./menu-keys.js";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { Check, ChevronDown } from "lucide-react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/primitives/menu-select.tsx
7
+ /**
8
+ * The choices of a Segmented switch in a menu, for a row without room for every option: a button with the chosen
9
+ * option's icon and label, opening the options under its right edge.
10
+ */
11
+ const MenuSelect = ({ value, options, onChange, label }) => {
12
+ const [open, setOpen] = useState(false);
13
+ const root = useRef(null);
14
+ const trigger = useRef(null);
15
+ const items = useRef([]);
16
+ const chosen = options.findIndex((option) => option.value === value);
17
+ const current = options[chosen];
18
+ useEffect(() => {
19
+ if (!open) return;
20
+ items.current[Math.max(chosen, 0)]?.focus();
21
+ const close = (event) => {
22
+ if (event.target instanceof Node && !root.current?.contains(event.target)) setOpen(false);
23
+ };
24
+ document.addEventListener("pointerdown", close);
25
+ return () => document.removeEventListener("pointerdown", close);
26
+ }, [open]);
27
+ const closeToTrigger = () => {
28
+ setOpen(false);
29
+ trigger.current?.focus();
30
+ };
31
+ const onKeyDown = (event) => {
32
+ if (event.key === "Escape") return closeToTrigger();
33
+ if (event.key === "Tab") return setOpen(false);
34
+ const target = menuKeyTarget(event.key, items.current.indexOf(document.activeElement), options.length);
35
+ if (target === void 0) return;
36
+ event.preventDefault();
37
+ items.current[target]?.focus();
38
+ };
39
+ return /* @__PURE__ */ jsxs("div", {
40
+ ref: root,
41
+ className: "relative shrink-0",
42
+ children: [/* @__PURE__ */ jsxs("button", {
43
+ ref: trigger,
44
+ type: "button",
45
+ "aria-label": current ? `${label}: ${current.label}` : label,
46
+ "aria-haspopup": "menu",
47
+ "aria-expanded": open,
48
+ onClick: () => setOpen(!open),
49
+ className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border-strong bg-background px-2.5 text-[13px] font-medium shadow-sm transition-colors hover:bg-muted pointer-coarse:h-11",
50
+ children: [
51
+ current?.icon && /* @__PURE__ */ jsx(current.icon, { className: "size-3.5" }),
52
+ current?.label ?? label,
53
+ /* @__PURE__ */ jsx(ChevronDown, { className: "size-3.5 text-muted-foreground" })
54
+ ]
55
+ }), open && /* @__PURE__ */ jsx("ul", {
56
+ role: "menu",
57
+ "aria-label": label,
58
+ onKeyDown,
59
+ className: "absolute right-0 top-full z-30 mt-1 min-w-40 rounded-md border bg-background py-1 text-[13px] shadow-pop",
60
+ children: options.map((option, index) => /* @__PURE__ */ jsx("li", {
61
+ role: "none",
62
+ children: /* @__PURE__ */ jsxs("button", {
63
+ ref: (element) => {
64
+ items.current[index] = element;
65
+ },
66
+ type: "button",
67
+ role: "menuitemradio",
68
+ "aria-checked": option.value === value,
69
+ onClick: () => {
70
+ closeToTrigger();
71
+ if (option.value !== value) onChange(option.value);
72
+ },
73
+ className: cn("flex min-h-9 w-full items-center gap-2 whitespace-nowrap px-3 py-1.5 text-left hover:bg-muted focus:bg-muted focus:outline-none pointer-coarse:min-h-11", option.value === value && "font-medium"),
74
+ children: [
75
+ option.icon ? /* @__PURE__ */ jsx(option.icon, { className: "size-3.5" }) : /* @__PURE__ */ jsx("span", { className: "size-3.5" }),
76
+ /* @__PURE__ */ jsx("span", {
77
+ className: "flex-1",
78
+ children: option.label
79
+ }),
80
+ option.value === value && /* @__PURE__ */ jsx(Check, { className: "size-3.5 text-primary-text" })
81
+ ]
82
+ })
83
+ }, option.value))
84
+ })]
85
+ });
86
+ };
87
+ //#endregion
88
+ export { MenuSelect };