tempest-react-sdk 0.66.0 → 0.67.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/BottomSheet/BottomSheet.cjs +1 -1
- package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
- package/dist/components/BottomSheet/BottomSheet.js +31 -40
- package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
- package/dist/components/Combobox/Combobox.cjs +1 -1
- package/dist/components/Combobox/Combobox.cjs.map +1 -1
- package/dist/components/Combobox/Combobox.js +94 -73
- package/dist/components/Combobox/Combobox.js.map +1 -1
- package/dist/components/Combobox/Combobox.module.cjs +1 -1
- package/dist/components/Combobox/Combobox.module.cjs.map +1 -1
- package/dist/components/Combobox/Combobox.module.js +4 -3
- package/dist/components/Combobox/Combobox.module.js.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.cjs +1 -1
- package/dist/components/ContextMenu/ContextMenu.cjs.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.js +80 -83
- package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
- package/dist/components/Drawer/Drawer.cjs +1 -1
- package/dist/components/Drawer/Drawer.cjs.map +1 -1
- package/dist/components/Drawer/Drawer.js +43 -46
- package/dist/components/Drawer/Drawer.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.cjs +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.js +129 -105
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.module.cjs +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.module.cjs.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.module.js +12 -11
- package/dist/components/DropdownMenu/DropdownMenu.module.js.map +1 -1
- package/dist/components/HoverCard/HoverCard.cjs +1 -1
- package/dist/components/HoverCard/HoverCard.cjs.map +1 -1
- package/dist/components/HoverCard/HoverCard.js +54 -29
- package/dist/components/HoverCard/HoverCard.js.map +1 -1
- package/dist/components/HoverCard/HoverCard.module.cjs +1 -1
- package/dist/components/HoverCard/HoverCard.module.cjs.map +1 -1
- package/dist/components/HoverCard/HoverCard.module.js +4 -3
- package/dist/components/HoverCard/HoverCard.module.js.map +1 -1
- package/dist/components/Lightbox/Lightbox.cjs +1 -1
- package/dist/components/Lightbox/Lightbox.cjs.map +1 -1
- package/dist/components/Lightbox/Lightbox.js +86 -89
- package/dist/components/Lightbox/Lightbox.js.map +1 -1
- package/dist/components/Menubar/Menubar.cjs +1 -1
- package/dist/components/Menubar/Menubar.cjs.map +1 -1
- package/dist/components/Menubar/Menubar.js +86 -65
- package/dist/components/Menubar/Menubar.js.map +1 -1
- package/dist/components/Menubar/Menubar.module.cjs +1 -1
- package/dist/components/Menubar/Menubar.module.cjs.map +1 -1
- package/dist/components/Menubar/Menubar.module.js +4 -3
- package/dist/components/Menubar/Menubar.module.js.map +1 -1
- package/dist/components/Modal/Modal.cjs +1 -1
- package/dist/components/Modal/Modal.cjs.map +1 -1
- package/dist/components/Modal/Modal.js +44 -51
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.cjs +1 -1
- package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.js +113 -96
- package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.module.cjs +1 -1
- package/dist/components/MultiSelect/MultiSelect.module.cjs.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.module.js +4 -3
- package/dist/components/MultiSelect/MultiSelect.module.js.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.cjs +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.js +83 -61
- package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.module.cjs +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.module.cjs.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.module.js +4 -3
- package/dist/components/NavigationMenu/NavigationMenu.module.js.map +1 -1
- package/dist/components/Popover/Popover.cjs +1 -1
- package/dist/components/Popover/Popover.cjs.map +1 -1
- package/dist/components/Popover/Popover.js +57 -36
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/Popover/Popover.module.cjs +1 -1
- package/dist/components/Popover/Popover.module.cjs.map +1 -1
- package/dist/components/Popover/Popover.module.js +7 -6
- package/dist/components/Popover/Popover.module.js.map +1 -1
- package/dist/components/Portal/anchor-position.cjs +2 -0
- package/dist/components/Portal/anchor-position.cjs.map +1 -0
- package/dist/components/Portal/anchor-position.js +99 -0
- package/dist/components/Portal/anchor-position.js.map +1 -0
- package/dist/components/Portal/escape-layer.cjs +2 -0
- package/dist/components/Portal/escape-layer.cjs.map +1 -0
- package/dist/components/Portal/escape-layer.js +30 -0
- package/dist/components/Portal/escape-layer.js.map +1 -0
- package/dist/components/Portal/tab-bridge.cjs +2 -0
- package/dist/components/Portal/tab-bridge.cjs.map +1 -0
- package/dist/components/Portal/tab-bridge.js +39 -0
- package/dist/components/Portal/tab-bridge.js.map +1 -0
- package/dist/components/Tooltip/Tooltip.cjs +1 -1
- package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
- package/dist/components/Tooltip/Tooltip.js +43 -29
- package/dist/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/components/Tooltip/Tooltip.module.cjs +1 -1
- package/dist/components/Tooltip/Tooltip.module.cjs.map +1 -1
- package/dist/components/Tooltip/Tooltip.module.js +4 -3
- package/dist/components/Tooltip/Tooltip.module.js.map +1 -1
- package/dist/hooks/use-focus-trap.cjs +1 -1
- package/dist/hooks/use-focus-trap.cjs.map +1 -1
- package/dist/hooks/use-focus-trap.js +2 -2
- package/dist/hooks/use-focus-trap.js.map +1 -1
- package/dist/hooks/use-sortable.cjs +1 -1
- package/dist/hooks/use-sortable.cjs.map +1 -1
- package/dist/hooks/use-sortable.js +3 -3
- package/dist/hooks/use-sortable.js.map +1 -1
- package/dist/http/describe-api-error.cjs +1 -1
- package/dist/http/describe-api-error.cjs.map +1 -1
- package/dist/http/describe-api-error.js +4 -2
- package/dist/http/describe-api-error.js.map +1 -1
- package/dist/styles/actions.css +3 -0
- package/dist/styles/advanced.css +1 -0
- package/dist/styles/component/Combobox.css +1 -0
- package/dist/styles/component/DropdownMenu.css +1 -0
- package/dist/styles/component/HoverCard.css +1 -0
- package/dist/styles/component/Menubar.css +1 -0
- package/dist/styles/component/MultiSelect.css +1 -0
- package/dist/styles/component/NavigationMenu.css +1 -0
- package/dist/styles/component/Popover.css +1 -0
- package/dist/styles/component/Tooltip.css +1 -0
- package/dist/styles/forms.css +2 -0
- package/dist/styles/navigation.css +2 -0
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +147 -13
- package/dist/tempest-react-sdk.js +134 -134
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("./BottomSheet.module.cjs"),
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../Portal/escape-layer.cjs"),n=require("./BottomSheet.module.cjs"),r=require("../Portal/portal-host.cjs");let i=require("react"),a=require("react/jsx-runtime"),o=require("react-dom");function s({open:s,onClose:c,title:l,showHandle:u=!0,dismissOnBackdrop:d=!0,dismissOnEsc:f=!0,className:p,children:m,...h}){t.useEscapeLayer(s,f?c:null),(0,i.useEffect)(()=>{if(!s)return;let e=document.body.style.overflow;return document.body.style.overflow=`hidden`,()=>{document.body.style.overflow=e}},[s]);let g=r.usePortalHost();return!s||!g?null:(0,o.createPortal)((0,a.jsxs)(`div`,{className:n.default.root,role:`dialog`,"aria-modal":`true`,children:[(0,a.jsx)(`div`,{className:n.default.backdrop,onClick:()=>d&&c(),"aria-hidden":`true`}),(0,a.jsxs)(`div`,{className:e.cn(n.default.sheet,p),...h,children:[u&&(0,a.jsx)(`button`,{type:`button`,className:n.default.handle,"aria-label":`Arrastar para fechar`,onClick:c,children:(0,a.jsx)(`span`,{className:n.default.handleBar})}),l&&(0,a.jsx)(`div`,{className:n.default.title,children:l}),(0,a.jsx)(`div`,{className:n.default.body,children:m})]})]}),g)}exports.BottomSheet=s;
|
|
2
2
|
//# sourceMappingURL=BottomSheet.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BottomSheet.cjs","names":[],"sources":["../../../src/components/BottomSheet/BottomSheet.tsx"],"sourcesContent":["import { useEffect } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./BottomSheet.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport interface BottomSheetProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Controlled open state. */\n open: boolean;\n /** Fires when user dismisses (backdrop click, Esc, swipe). */\n onClose: () => void;\n /** Optional title rendered at the top of the sheet. */\n title?: ReactNode;\n /** Show a drag handle indicator at the top. Default `true`. */\n showHandle?: boolean;\n /** When `true`, clicking the backdrop closes the sheet. Default `true`. */\n dismissOnBackdrop?: boolean;\n /** When `true`, pressing Esc closes the sheet. Default `true`. */\n dismissOnEsc?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Slide-up modal panel — mobile-style sheet anchored to the bottom edge.\n * Uses portal + safe-area padding + scroll lock.\n *\n * @example\n * <BottomSheet open={open} onClose={() => setOpen(false)} title=\"Filters\">\n * <FilterForm />\n * </BottomSheet>\n */\nexport function BottomSheet({\n open,\n onClose,\n title,\n showHandle = true,\n dismissOnBackdrop = true,\n dismissOnEsc = true,\n className,\n children,\n ...props\n}: BottomSheetProps) {\n
|
|
1
|
+
{"version":3,"file":"BottomSheet.cjs","names":[],"sources":["../../../src/components/BottomSheet/BottomSheet.tsx"],"sourcesContent":["import { useEffect } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useEscapeLayer } from \"@/components/Portal/escape-layer\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./BottomSheet.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport interface BottomSheetProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Controlled open state. */\n open: boolean;\n /** Fires when user dismisses (backdrop click, Esc, swipe). */\n onClose: () => void;\n /** Optional title rendered at the top of the sheet. */\n title?: ReactNode;\n /** Show a drag handle indicator at the top. Default `true`. */\n showHandle?: boolean;\n /** When `true`, clicking the backdrop closes the sheet. Default `true`. */\n dismissOnBackdrop?: boolean;\n /** When `true`, pressing Esc closes the sheet. Default `true`. */\n dismissOnEsc?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Slide-up modal panel — mobile-style sheet anchored to the bottom edge.\n * Uses portal + safe-area padding + scroll lock.\n *\n * @example\n * <BottomSheet open={open} onClose={() => setOpen(false)} title=\"Filters\">\n * <FilterForm />\n * </BottomSheet>\n */\nexport function BottomSheet({\n open,\n onClose,\n title,\n showHandle = true,\n dismissOnBackdrop = true,\n dismissOnEsc = true,\n className,\n children,\n ...props\n}: BottomSheetProps) {\n useEscapeLayer(open, dismissOnEsc ? onClose : null);\n\n useEffect(() => {\n if (!open) return;\n const previous = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = previous;\n };\n }, [open]);\n\n const portalHost = usePortalHost();\n\n if (!open || !portalHost) return null;\n\n return createPortal(\n <div className={styles.root} role=\"dialog\" aria-modal=\"true\">\n <div\n className={styles.backdrop}\n onClick={() => dismissOnBackdrop && onClose()}\n aria-hidden=\"true\"\n />\n <div className={cn(styles.sheet, className)} {...props}>\n {showHandle && (\n <button\n type=\"button\"\n className={styles.handle}\n aria-label=\"Arrastar para fechar\"\n onClick={onClose}\n >\n <span className={styles.handleBar} />\n </button>\n )}\n {title && <div className={styles.title}>{title}</div>}\n <div className={styles.body}>{children}</div>\n </div>\n </div>,\n portalHost,\n );\n}\n"],"mappings":"wOAiCA,SAAgB,EAAY,CACxB,OACA,UACA,QACA,aAAa,GACb,oBAAoB,GACpB,eAAe,GACf,YACA,WACA,GAAG,GACc,CACjB,EAAA,eAAe,EAAM,EAAe,EAAU,IAAI,GAElD,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAW,SAAS,KAAK,MAAM,SAErC,MADA,UAAS,KAAK,MAAM,SAAW,aAClB,CACT,SAAS,KAAK,MAAM,SAAW,CACnC,CACJ,EAAG,CAAC,CAAI,CAAC,EAET,IAAM,EAAa,EAAA,cAAc,EAIjC,MAFI,CAAC,GAAQ,CAAC,EAAmB,MAEjC,EAAO,EAAA,aAAA,EACH,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAM,KAAK,SAAS,aAAW,OAAtD,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,SAClB,YAAe,GAAqB,EAAQ,EAC5C,cAAY,MACf,CAAA,GACD,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,MAAO,CAAS,EAAG,GAAI,EAAjD,SAAA,CACK,IACG,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,OAClB,aAAW,uBACX,QAAS,EAET,UAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,SAAY,CAAA,CAChC,CAAA,EAEX,IAAS,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,MAAQ,SAAA,CAAW,CAAA,GACpD,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAO,UAAc,CAAA,CAC3C,CACJ,CAAA,CAAA,CACL,CAAA,EAAA,CACJ,CACJ"}
|
|
@@ -1,61 +1,52 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
|
-
import t from "
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { useEscapeLayer as t } from "../Portal/escape-layer.js";
|
|
3
|
+
import n from "./BottomSheet.module.js";
|
|
4
|
+
import { usePortalHost as r } from "../Portal/portal-host.js";
|
|
5
|
+
import { useEffect as i } from "react";
|
|
6
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
7
|
+
import { createPortal as s } from "react-dom";
|
|
7
8
|
//#region src/components/BottomSheet/BottomSheet.tsx
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
if (!
|
|
11
|
-
let e = (e) => {
|
|
12
|
-
e.key === "Escape" && c();
|
|
13
|
-
};
|
|
14
|
-
return window.addEventListener("keydown", e), () => window.removeEventListener("keydown", e);
|
|
15
|
-
}, [
|
|
16
|
-
s,
|
|
17
|
-
f,
|
|
18
|
-
c
|
|
19
|
-
]), r(() => {
|
|
20
|
-
if (!s) return;
|
|
9
|
+
function c({ open: c, onClose: l, title: u, showHandle: d = !0, dismissOnBackdrop: f = !0, dismissOnEsc: p = !0, className: m, children: h, ...g }) {
|
|
10
|
+
t(c, p ? l : null), i(() => {
|
|
11
|
+
if (!c) return;
|
|
21
12
|
let e = document.body.style.overflow;
|
|
22
13
|
return document.body.style.overflow = "hidden", () => {
|
|
23
14
|
document.body.style.overflow = e;
|
|
24
15
|
};
|
|
25
|
-
}, [
|
|
26
|
-
let
|
|
27
|
-
return !
|
|
28
|
-
className:
|
|
16
|
+
}, [c]);
|
|
17
|
+
let _ = r();
|
|
18
|
+
return !c || !_ ? null : s(/* @__PURE__ */ o("div", {
|
|
19
|
+
className: n.root,
|
|
29
20
|
role: "dialog",
|
|
30
21
|
"aria-modal": "true",
|
|
31
|
-
children: [/* @__PURE__ */
|
|
32
|
-
className:
|
|
33
|
-
onClick: () =>
|
|
22
|
+
children: [/* @__PURE__ */ a("div", {
|
|
23
|
+
className: n.backdrop,
|
|
24
|
+
onClick: () => f && l(),
|
|
34
25
|
"aria-hidden": "true"
|
|
35
|
-
}), /* @__PURE__ */
|
|
36
|
-
className: e(
|
|
37
|
-
...
|
|
26
|
+
}), /* @__PURE__ */ o("div", {
|
|
27
|
+
className: e(n.sheet, m),
|
|
28
|
+
...g,
|
|
38
29
|
children: [
|
|
39
|
-
|
|
30
|
+
d && /* @__PURE__ */ a("button", {
|
|
40
31
|
type: "button",
|
|
41
|
-
className:
|
|
32
|
+
className: n.handle,
|
|
42
33
|
"aria-label": "Arrastar para fechar",
|
|
43
|
-
onClick:
|
|
44
|
-
children: /* @__PURE__ */
|
|
34
|
+
onClick: l,
|
|
35
|
+
children: /* @__PURE__ */ a("span", { className: n.handleBar })
|
|
45
36
|
}),
|
|
46
|
-
|
|
47
|
-
className:
|
|
48
|
-
children:
|
|
37
|
+
u && /* @__PURE__ */ a("div", {
|
|
38
|
+
className: n.title,
|
|
39
|
+
children: u
|
|
49
40
|
}),
|
|
50
|
-
/* @__PURE__ */
|
|
51
|
-
className:
|
|
52
|
-
children:
|
|
41
|
+
/* @__PURE__ */ a("div", {
|
|
42
|
+
className: n.body,
|
|
43
|
+
children: h
|
|
53
44
|
})
|
|
54
45
|
]
|
|
55
46
|
})]
|
|
56
|
-
}),
|
|
47
|
+
}), _);
|
|
57
48
|
}
|
|
58
49
|
//#endregion
|
|
59
|
-
export {
|
|
50
|
+
export { c as BottomSheet };
|
|
60
51
|
|
|
61
52
|
//# sourceMappingURL=BottomSheet.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BottomSheet.js","names":[],"sources":["../../../src/components/BottomSheet/BottomSheet.tsx"],"sourcesContent":["import { useEffect } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./BottomSheet.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport interface BottomSheetProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Controlled open state. */\n open: boolean;\n /** Fires when user dismisses (backdrop click, Esc, swipe). */\n onClose: () => void;\n /** Optional title rendered at the top of the sheet. */\n title?: ReactNode;\n /** Show a drag handle indicator at the top. Default `true`. */\n showHandle?: boolean;\n /** When `true`, clicking the backdrop closes the sheet. Default `true`. */\n dismissOnBackdrop?: boolean;\n /** When `true`, pressing Esc closes the sheet. Default `true`. */\n dismissOnEsc?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Slide-up modal panel — mobile-style sheet anchored to the bottom edge.\n * Uses portal + safe-area padding + scroll lock.\n *\n * @example\n * <BottomSheet open={open} onClose={() => setOpen(false)} title=\"Filters\">\n * <FilterForm />\n * </BottomSheet>\n */\nexport function BottomSheet({\n open,\n onClose,\n title,\n showHandle = true,\n dismissOnBackdrop = true,\n dismissOnEsc = true,\n className,\n children,\n ...props\n}: BottomSheetProps) {\n
|
|
1
|
+
{"version":3,"file":"BottomSheet.js","names":[],"sources":["../../../src/components/BottomSheet/BottomSheet.tsx"],"sourcesContent":["import { useEffect } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useEscapeLayer } from \"@/components/Portal/escape-layer\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./BottomSheet.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport interface BottomSheetProps extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Controlled open state. */\n open: boolean;\n /** Fires when user dismisses (backdrop click, Esc, swipe). */\n onClose: () => void;\n /** Optional title rendered at the top of the sheet. */\n title?: ReactNode;\n /** Show a drag handle indicator at the top. Default `true`. */\n showHandle?: boolean;\n /** When `true`, clicking the backdrop closes the sheet. Default `true`. */\n dismissOnBackdrop?: boolean;\n /** When `true`, pressing Esc closes the sheet. Default `true`. */\n dismissOnEsc?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Slide-up modal panel — mobile-style sheet anchored to the bottom edge.\n * Uses portal + safe-area padding + scroll lock.\n *\n * @example\n * <BottomSheet open={open} onClose={() => setOpen(false)} title=\"Filters\">\n * <FilterForm />\n * </BottomSheet>\n */\nexport function BottomSheet({\n open,\n onClose,\n title,\n showHandle = true,\n dismissOnBackdrop = true,\n dismissOnEsc = true,\n className,\n children,\n ...props\n}: BottomSheetProps) {\n useEscapeLayer(open, dismissOnEsc ? onClose : null);\n\n useEffect(() => {\n if (!open) return;\n const previous = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = previous;\n };\n }, [open]);\n\n const portalHost = usePortalHost();\n\n if (!open || !portalHost) return null;\n\n return createPortal(\n <div className={styles.root} role=\"dialog\" aria-modal=\"true\">\n <div\n className={styles.backdrop}\n onClick={() => dismissOnBackdrop && onClose()}\n aria-hidden=\"true\"\n />\n <div className={cn(styles.sheet, className)} {...props}>\n {showHandle && (\n <button\n type=\"button\"\n className={styles.handle}\n aria-label=\"Arrastar para fechar\"\n onClick={onClose}\n >\n <span className={styles.handleBar} />\n </button>\n )}\n {title && <div className={styles.title}>{title}</div>}\n <div className={styles.body}>{children}</div>\n </div>\n </div>,\n portalHost,\n );\n}\n"],"mappings":";;;;;;;;AAiCA,SAAgB,EAAY,EACxB,SACA,YACA,UACA,gBAAa,IACb,uBAAoB,IACpB,kBAAe,IACf,cACA,aACA,GAAG,KACc;CAGjB,AAFA,EAAe,GAAM,IAAe,IAAU,IAAI,GAElD,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,IAAW,SAAS,KAAK,MAAM;EAErC,OADA,SAAS,KAAK,MAAM,WAAW,gBAClB;GACT,SAAS,KAAK,MAAM,WAAW;EACnC;CACJ,GAAG,CAAC,CAAI,CAAC;CAET,IAAM,IAAa,EAAc;CAIjC,OAFI,CAAC,KAAQ,CAAC,IAAmB,OAE1B,EACH,kBAAC,OAAD;EAAK,WAAW,EAAO;EAAM,MAAK;EAAS,cAAW;EAAtD,UAAA,CACI,kBAAC,OAAD;GACI,WAAW,EAAO;GAClB,eAAe,KAAqB,EAAQ;GAC5C,eAAY;EACf,CAAA,GACD,kBAAC,OAAD;GAAK,WAAW,EAAG,EAAO,OAAO,CAAS;GAAG,GAAI;GAAjD,UAAA;IACK,KACG,kBAAC,UAAD;KACI,MAAK;KACL,WAAW,EAAO;KAClB,cAAW;KACX,SAAS;KAET,UAAA,kBAAC,QAAD,EAAM,WAAW,EAAO,UAAY,CAAA;IAChC,CAAA;IAEX,KAAS,kBAAC,OAAD;KAAK,WAAW,EAAO;KAAQ,UAAA;IAAW,CAAA;IACpD,kBAAC,OAAD;KAAK,WAAW,EAAO;KAAO;IAAc,CAAA;GAC3C;EACJ,CAAA,CAAA;CACL,CAAA,GAAA,CACJ;AACJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("./Combobox.module.cjs");let
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../Portal/Portal.cjs"),n=require("../Portal/anchor-position.cjs"),r=require("./Combobox.module.cjs");let i=require("react"),a=require("react/jsx-runtime");var o=4;function s(e,t){return!t||e.label.toLowerCase().includes(t.toLowerCase())}function c({options:c,value:l,onChange:u,label:d,placeholder:f=`Selecione`,helperText:p,error:m,disabled:h,filter:g=s,emptyMessage:_=`Nenhuma opção encontrada`,portal:v=!0,className:y}){let b=(0,i.useId)(),x=(0,i.useRef)(null),S=(0,i.useRef)(null),[C,w]=(0,i.useState)(!1),[T,E]=(0,i.useState)(``),[D,O]=(0,i.useState)(0),[k,A]=(0,i.useState)(null),[j,M]=(0,i.useState)(null),N=n.useAnchorPosition({anchor:k,floating:j,side:`bottom`,align:`start`,offset:o,enabled:v&&C,matchWidth:!0}),P=(0,i.useMemo)(()=>c.find(e=>e.value===l)?.label??``,[c,l]),F=(0,i.useMemo)(()=>c.filter(e=>g(e,T)),[c,g,T]),I=(0,i.useCallback)(()=>{w(!1),E(``),O(0)},[]),L=(0,i.useCallback)(e=>{e.disabled||(u(e.value),I())},[u,I]);(0,i.useEffect)(()=>{if(!C)return;let e=e=>{let t=e.target;x.current?.contains(t)||j?.contains(t)||I()};return window.addEventListener(`mousedown`,e),()=>window.removeEventListener(`mousedown`,e)},[C,I,j]);let R=e=>{if(e.key===`ArrowDown`)e.preventDefault(),w(!0),O(e=>Math.min(F.length-1,e+1));else if(e.key===`ArrowUp`)e.preventDefault(),O(e=>Math.max(0,e-1));else if(e.key===`Enter`){e.preventDefault();let t=F[D];t&&L(t)}else e.key===`Escape`&&(C&&e.preventDefault(),I())},z=(0,a.jsx)(`ul`,{ref:M,id:`${b}-listbox`,role:`listbox`,className:e.cn(r.default.menu,v&&r.default.portalled),style:N,children:F.length===0?(0,a.jsx)(`li`,{className:r.default.empty,children:_}):F.map((t,n)=>(0,a.jsx)(`li`,{role:`option`,"aria-selected":t.value===l,className:e.cn(r.default.option,n===D&&r.default.active,t.value===l&&r.default.selected),onMouseEnter:()=>O(n),onMouseDown:e=>{e.preventDefault(),L(t)},children:t.label},t.value))});return(0,a.jsxs)(`div`,{ref:x,className:e.cn(r.default.wrapper,m&&r.default.error,y),children:[d&&(0,a.jsx)(`label`,{htmlFor:b,className:r.default.label,children:d}),(0,a.jsxs)(`div`,{ref:A,className:r.default.field,children:[(0,a.jsx)(`input`,{id:b,ref:S,type:`text`,role:`combobox`,"aria-expanded":C,"aria-controls":`${b}-listbox`,"aria-autocomplete":`list`,className:r.default.input,placeholder:f,disabled:h,value:C?T:P,onFocus:()=>w(!0),onChange:e=>{E(e.target.value),w(!0),O(0)},onKeyDown:R}),(0,a.jsx)(`span`,{className:r.default.caret,"aria-hidden":!0,children:`▾`})]}),C&&(v?(0,a.jsx)(t.Portal,{children:z}):z),m?(0,a.jsx)(`span`,{className:r.default.errorText,children:m}):p?(0,a.jsx)(`span`,{className:r.default.helper,children:p}):null]})}exports.Combobox=c;
|
|
2
2
|
//# sourceMappingURL=Combobox.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.cjs","names":[],"sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract (label, placeholder, helperText, error, disabled,\n * className) and the three combobox-specific knobs: filter, emptyMessage. The body\n * is the listbox — open state, active descendant, type-ahead and the ARIA wiring —\n * and those cannot be separated without passing the same active index back and\n * forth.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Combobox.module.css\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Custom filter — return true to keep the option. Default is case-insensitive substring match on label. */\n filter?: (option: ComboboxOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n className?: string;\n}\n\nfunction defaultFilter(option: ComboboxOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * Combobox — text input with a filterable dropdown of options.\n *\n * Selecting an option fires `onChange(value)`. Typing filters the list.\n * Keyboard: ArrowUp/ArrowDown to navigate, Enter to select, Esc to close.\n */\nexport function Combobox({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n className,\n}: ComboboxProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n\n const selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? \"\",\n [options, value],\n );\n\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const closeAndReset = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n setActiveIndex(0);\n }, []);\n\n const handleSelect = useCallback(\n (option: ComboboxOption): void => {\n if (option.disabled) return;\n onChange(option.value);\n closeAndReset();\n },\n [onChange, closeAndReset],\n );\n\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) {\n closeAndReset();\n }\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, closeAndReset]);\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) handleSelect(option);\n } else if (event.key === \"Escape\") {\n closeAndReset();\n }\n };\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div className={styles.field}>\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n className={styles.input}\n placeholder={placeholder}\n disabled={disabled}\n value={open ? query : selectedLabel}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n <span className={styles.caret} aria-hidden>\n ▾\n </span>\n </div>\n {open && (\n <ul id={`${id}-listbox`} role=\"listbox\" className={styles.menu}>\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={option.value === value}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n option.value === value && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n handleSelect(option);\n }}\n >\n {option.label}\n </li>\n ))\n )}\n </ul>\n )}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"+HAkCA,SAAS,EAAc,EAAwB,EAAwB,CAEnE,MADA,CAAK,GACE,EAAO,MAAM,YAAY,CAAC,CAAC,SAAS,EAAM,YAAY,CAAC,CAClE,CAQA,SAAgB,EAAS,CACrB,UACA,QACA,WACA,QACA,cAAc,YACd,aACA,QACA,WACA,SAAS,EACT,eAAe,2BACf,aACc,CACd,IAAM,GAAA,EAAK,EAAA,MAAA,CAAM,EACX,GAAA,EAAU,EAAA,OAAA,CAAuB,IAAI,EACrC,GAAA,EAAW,EAAA,OAAA,CAAyB,IAAI,EACxC,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAS,EAAK,EAChC,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAS,EAAE,EAC/B,CAAC,EAAa,IAAA,EAAkB,EAAA,SAAA,CAAS,CAAC,EAE1C,GAAA,EAAgB,EAAA,QAAA,KACZ,EAAQ,KAAM,GAAM,EAAE,QAAU,CAAK,CAAC,EAAE,OAAS,GACvD,CAAC,EAAS,CAAK,CACnB,EAEM,GAAA,EAAW,EAAA,QAAA,KACP,EAAQ,OAAQ,GAAM,EAAO,EAAG,CAAK,CAAC,EAC5C,CAAC,EAAS,EAAQ,CAAK,CAC3B,EAEM,GAAA,EAAgB,EAAA,YAAA,KAAwB,CAC1C,EAAQ,EAAK,EACb,EAAS,EAAE,EACX,EAAe,CAAC,CACpB,EAAG,CAAC,CAAC,EAEC,GAAA,EAAe,EAAA,YAAA,CAChB,GAAiC,CAC1B,EAAO,WACX,EAAS,EAAO,KAAK,EACrB,EAAc,EAClB,EACA,CAAC,EAAU,CAAa,CAC5B,EA8BA,OA5BA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAU,GAA4B,CACpC,EAAQ,SAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,MAAc,GACjE,EAAc,CAEtB,EAEA,OADA,OAAO,iBAAiB,YAAa,CAAM,MAC9B,OAAO,oBAAoB,YAAa,CAAM,CAC/D,EAAG,CAAC,EAAM,CAAa,CAAC,GAoBpB,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,IAAK,EAAS,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,GAAS,EAAA,QAAO,MAAO,CAAS,EAAjF,SAAA,CACK,IACG,EAAA,EAAA,IAAA,CAAC,QAAD,CAAO,QAAS,EAAI,UAAW,EAAA,QAAO,MACjC,SAAA,CACE,CAAA,GAEX,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,MAAvB,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,QAAD,CACQ,KACJ,IAAK,EACL,KAAK,OACL,KAAK,WACL,gBAAe,EACf,gBAAe,GAAG,EAAG,UACrB,oBAAkB,OAClB,UAAW,EAAA,QAAO,MACL,cACH,WACV,MAAO,EAAO,EAAQ,EACtB,YAAe,EAAQ,EAAI,EAC3B,SAAW,GAAU,CACjB,EAAS,EAAM,OAAO,KAAK,EAC3B,EAAQ,EAAI,EACZ,EAAe,CAAC,CACpB,EACA,UA3CO,GAAuD,CAC1E,GAAI,EAAM,MAAQ,YACd,EAAM,eAAe,EACrB,EAAQ,EAAI,EACZ,EAAgB,GAAY,KAAK,IAAI,EAAS,OAAS,EAAG,EAAU,CAAC,CAAC,OACnE,GAAI,EAAM,MAAQ,UACrB,EAAM,eAAe,EACrB,EAAgB,GAAY,KAAK,IAAI,EAAG,EAAU,CAAC,CAAC,OACjD,GAAI,EAAM,MAAQ,QAAS,CAC9B,EAAM,eAAe,EACrB,IAAM,EAAS,EAAS,GACpB,GAAQ,EAAa,CAAM,CACnC,MAAW,EAAM,MAAQ,UACrB,EAAc,CAEtB,CA6Ba,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,MAAO,cAAA,GAAY,SAAA,GAErC,CAAA,CACL,IACJ,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,GAAG,EAAG,UAAW,KAAK,UAAU,UAAW,EAAA,QAAO,KACrD,SAAA,EAAS,SAAW,GACjB,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,MAAQ,SAAA,CAAiB,CAAA,EAE/C,EAAS,KAAK,EAAQ,KAClB,EAAA,EAAA,IAAA,CAAC,KAAD,CAEI,KAAK,SACL,gBAAe,EAAO,QAAU,EAChC,UAAW,EAAA,GACP,EAAA,QAAO,OACP,IAAU,GAAe,EAAA,QAAO,OAChC,EAAO,QAAU,GAAS,EAAA,QAAO,QACrC,EACA,iBAAoB,EAAe,CAAK,EACxC,YAAc,GAAU,CACpB,EAAM,eAAe,EACrB,EAAa,CAAM,CACvB,EAEC,SAAA,EAAO,KACR,EAfK,EAAO,KAeZ,CACP,CAEL,CAAA,EAEP,GACG,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,UAAY,SAAA,CAAY,CAAA,EAChD,GACA,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,OAAS,SAAA,CAAiB,CAAA,EAClD,IACH,GAEb"}
|
|
1
|
+
{"version":3,"file":"Combobox.cjs","names":[],"sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract (label, placeholder, helperText, error, disabled,\n * className) and the combobox-specific knobs: filter, emptyMessage, portal. The body\n * is the listbox — open state, active descendant, type-ahead and the ARIA wiring —\n * and those cannot be separated without passing the same active index back and\n * forth.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Combobox.module.css\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Custom filter — return true to keep the option. Default is case-insensitive substring match on label. */\n filter?: (option: ComboboxOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n /**\n * Render the list in a portal, positioned under the field. Default `true`.\n *\n * In flow, an ancestor with `overflow` other than `visible` clips the list —\n * in the last row of a `DataTable` none of it was visible (measured in Chrome\n * at 1440 px). In a portal it escapes the clip, takes the field's width,\n * flips above the field when there is no room below, and follows it on\n * scroll, resize and when the field or the list changes size.\n */\n portal?: boolean;\n className?: string;\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\nfunction defaultFilter(option: ComboboxOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * Combobox — text input with a filterable dropdown of options.\n *\n * Selecting an option fires `onChange(value)`. Typing filters the list.\n * Keyboard: ArrowUp/ArrowDown to navigate, Enter to select, Esc to close.\n */\nexport function Combobox({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: ComboboxProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n const [fieldNode, setFieldNode] = useState<HTMLDivElement | null>(null);\n const [listNode, setListNode] = useState<HTMLUListElement | null>(null);\n const floatingStyle = useAnchorPosition({\n anchor: fieldNode,\n floating: listNode,\n side: \"bottom\",\n align: \"start\",\n offset: LIST_OFFSET,\n enabled: portal && open,\n matchWidth: true,\n });\n\n const selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? \"\",\n [options, value],\n );\n\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const closeAndReset = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n setActiveIndex(0);\n }, []);\n\n const handleSelect = useCallback(\n (option: ComboboxOption): void => {\n if (option.disabled) return;\n onChange(option.value);\n closeAndReset();\n },\n [onChange, closeAndReset],\n );\n\n /**\n * Close on a press outside both the field and the list.\n *\n * The list is checked on its own because in a portal it is not inside the\n * field's wrapper: a press on its scrollbar, or on the empty message, would\n * otherwise count as outside and close it.\n */\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n const target = event.target as Node;\n if (rootRef.current?.contains(target) || listNode?.contains(target)) return;\n closeAndReset();\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, closeAndReset, listNode]);\n\n /**\n * Keyboard model of the input.\n *\n * `Escape` calls `preventDefault()` only while the list is open: that marks the\n * key as consumed, so an enclosing `Modal` or `Drawer` stays open and the next\n * `Escape` reaches it. With the list closed the key is left alone and dismisses\n * the enclosing layer, as the user expects.\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) handleSelect(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n closeAndReset();\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\n className={cn(styles.menu, portal && styles.portalled)}\n style={floatingStyle}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={option.value === value}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n option.value === value && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n handleSelect(option);\n }}\n >\n {option.label}\n </li>\n ))\n )}\n </ul>\n );\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div ref={setFieldNode} className={styles.field}>\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n className={styles.input}\n placeholder={placeholder}\n disabled={disabled}\n value={open ? query : selectedLabel}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n <span className={styles.caret} aria-hidden>\n ▾\n </span>\n </div>\n {open && (portal ? <Portal>{list}</Portal> : list)}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,98 +1,119 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
|
-
import t from "
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
2
|
+
import { Portal as t } from "../Portal/Portal.js";
|
|
3
|
+
import { useAnchorPosition as n } from "../Portal/anchor-position.js";
|
|
4
|
+
import r from "./Combobox.module.js";
|
|
5
|
+
import { useCallback as i, useEffect as a, useId as o, useMemo as s, useRef as c, useState as l } from "react";
|
|
6
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
5
7
|
//#region src/components/Combobox/Combobox.tsx
|
|
6
|
-
|
|
8
|
+
var f = 4;
|
|
9
|
+
function p(e, t) {
|
|
7
10
|
return !t || e.label.toLowerCase().includes(t.toLowerCase());
|
|
8
11
|
}
|
|
9
|
-
function
|
|
10
|
-
let
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}, [
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
function m({ options: m, value: h, onChange: g, label: _, placeholder: v = "Selecione", helperText: y, error: b, disabled: x, filter: S = p, emptyMessage: C = "Nenhuma opção encontrada", portal: w = !0, className: T }) {
|
|
13
|
+
let E = o(), D = c(null), O = c(null), [k, A] = l(!1), [j, M] = l(""), [N, P] = l(0), [F, I] = l(null), [L, R] = l(null), z = n({
|
|
14
|
+
anchor: F,
|
|
15
|
+
floating: L,
|
|
16
|
+
side: "bottom",
|
|
17
|
+
align: "start",
|
|
18
|
+
offset: f,
|
|
19
|
+
enabled: w && k,
|
|
20
|
+
matchWidth: !0
|
|
21
|
+
}), B = s(() => m.find((e) => e.value === h)?.label ?? "", [m, h]), V = s(() => m.filter((e) => S(e, j)), [
|
|
22
|
+
m,
|
|
23
|
+
S,
|
|
24
|
+
j
|
|
25
|
+
]), H = i(() => {
|
|
26
|
+
A(!1), M(""), P(0);
|
|
27
|
+
}, []), U = i((e) => {
|
|
28
|
+
e.disabled || (g(e.value), H());
|
|
29
|
+
}, [g, H]);
|
|
30
|
+
a(() => {
|
|
31
|
+
if (!k) return;
|
|
21
32
|
let e = (e) => {
|
|
22
|
-
|
|
33
|
+
let t = e.target;
|
|
34
|
+
D.current?.contains(t) || L?.contains(t) || H();
|
|
23
35
|
};
|
|
24
36
|
return window.addEventListener("mousedown", e), () => window.removeEventListener("mousedown", e);
|
|
25
|
-
}, [
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
}, [
|
|
38
|
+
k,
|
|
39
|
+
H,
|
|
40
|
+
L
|
|
41
|
+
]);
|
|
42
|
+
let W = (e) => {
|
|
43
|
+
if (e.key === "ArrowDown") e.preventDefault(), A(!0), P((e) => Math.min(V.length - 1, e + 1));
|
|
44
|
+
else if (e.key === "ArrowUp") e.preventDefault(), P((e) => Math.max(0, e - 1));
|
|
45
|
+
else if (e.key === "Enter") {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
let t = V[N];
|
|
48
|
+
t && U(t);
|
|
49
|
+
} else e.key === "Escape" && (k && e.preventDefault(), H());
|
|
50
|
+
}, G = /* @__PURE__ */ u("ul", {
|
|
51
|
+
ref: R,
|
|
52
|
+
id: `${E}-listbox`,
|
|
53
|
+
role: "listbox",
|
|
54
|
+
className: e(r.menu, w && r.portalled),
|
|
55
|
+
style: z,
|
|
56
|
+
children: V.length === 0 ? /* @__PURE__ */ u("li", {
|
|
57
|
+
className: r.empty,
|
|
58
|
+
children: C
|
|
59
|
+
}) : V.map((t, n) => /* @__PURE__ */ u("li", {
|
|
60
|
+
role: "option",
|
|
61
|
+
"aria-selected": t.value === h,
|
|
62
|
+
className: e(r.option, n === N && r.active, t.value === h && r.selected),
|
|
63
|
+
onMouseEnter: () => P(n),
|
|
64
|
+
onMouseDown: (e) => {
|
|
65
|
+
e.preventDefault(), U(t);
|
|
66
|
+
},
|
|
67
|
+
children: t.label
|
|
68
|
+
}, t.value))
|
|
69
|
+
});
|
|
70
|
+
return /* @__PURE__ */ d("div", {
|
|
71
|
+
ref: D,
|
|
72
|
+
className: e(r.wrapper, b && r.error, T),
|
|
28
73
|
children: [
|
|
29
|
-
|
|
30
|
-
htmlFor:
|
|
31
|
-
className:
|
|
32
|
-
children:
|
|
74
|
+
_ && /* @__PURE__ */ u("label", {
|
|
75
|
+
htmlFor: E,
|
|
76
|
+
className: r.label,
|
|
77
|
+
children: _
|
|
33
78
|
}),
|
|
34
|
-
/* @__PURE__ */
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
79
|
+
/* @__PURE__ */ d("div", {
|
|
80
|
+
ref: I,
|
|
81
|
+
className: r.field,
|
|
82
|
+
children: [/* @__PURE__ */ u("input", {
|
|
83
|
+
id: E,
|
|
84
|
+
ref: O,
|
|
39
85
|
type: "text",
|
|
40
86
|
role: "combobox",
|
|
41
|
-
"aria-expanded":
|
|
42
|
-
"aria-controls": `${
|
|
87
|
+
"aria-expanded": k,
|
|
88
|
+
"aria-controls": `${E}-listbox`,
|
|
43
89
|
"aria-autocomplete": "list",
|
|
44
|
-
className:
|
|
45
|
-
placeholder:
|
|
46
|
-
disabled:
|
|
47
|
-
value:
|
|
48
|
-
onFocus: () =>
|
|
90
|
+
className: r.input,
|
|
91
|
+
placeholder: v,
|
|
92
|
+
disabled: x,
|
|
93
|
+
value: k ? j : B,
|
|
94
|
+
onFocus: () => A(!0),
|
|
49
95
|
onChange: (e) => {
|
|
50
|
-
|
|
96
|
+
M(e.target.value), A(!0), P(0);
|
|
51
97
|
},
|
|
52
|
-
onKeyDown:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
else if (e.key === "Enter") {
|
|
56
|
-
e.preventDefault();
|
|
57
|
-
let t = M[k];
|
|
58
|
-
t && P(t);
|
|
59
|
-
} else e.key === "Escape" && N();
|
|
60
|
-
}
|
|
61
|
-
}), /* @__PURE__ */ c("span", {
|
|
62
|
-
className: t.caret,
|
|
98
|
+
onKeyDown: W
|
|
99
|
+
}), /* @__PURE__ */ u("span", {
|
|
100
|
+
className: r.caret,
|
|
63
101
|
"aria-hidden": !0,
|
|
64
102
|
children: "▾"
|
|
65
103
|
})]
|
|
66
104
|
}),
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}) : M.map((n, r) => /* @__PURE__ */ c("li", {
|
|
75
|
-
role: "option",
|
|
76
|
-
"aria-selected": n.value === f,
|
|
77
|
-
className: e(t.option, r === k && t.active, n.value === f && t.selected),
|
|
78
|
-
onMouseEnter: () => A(r),
|
|
79
|
-
onMouseDown: (e) => {
|
|
80
|
-
e.preventDefault(), P(n);
|
|
81
|
-
},
|
|
82
|
-
children: n.label
|
|
83
|
-
}, n.value))
|
|
84
|
-
}),
|
|
85
|
-
_ ? /* @__PURE__ */ c("span", {
|
|
86
|
-
className: t.errorText,
|
|
87
|
-
children: _
|
|
88
|
-
}) : g ? /* @__PURE__ */ c("span", {
|
|
89
|
-
className: t.helper,
|
|
90
|
-
children: g
|
|
105
|
+
k && (w ? /* @__PURE__ */ u(t, { children: G }) : G),
|
|
106
|
+
b ? /* @__PURE__ */ u("span", {
|
|
107
|
+
className: r.errorText,
|
|
108
|
+
children: b
|
|
109
|
+
}) : y ? /* @__PURE__ */ u("span", {
|
|
110
|
+
className: r.helper,
|
|
111
|
+
children: y
|
|
91
112
|
}) : null
|
|
92
113
|
]
|
|
93
114
|
});
|
|
94
115
|
}
|
|
95
116
|
//#endregion
|
|
96
|
-
export {
|
|
117
|
+
export { m as Combobox };
|
|
97
118
|
|
|
98
119
|
//# sourceMappingURL=Combobox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.js","names":[],"sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract (label, placeholder, helperText, error, disabled,\n * className) and the three combobox-specific knobs: filter, emptyMessage. The body\n * is the listbox — open state, active descendant, type-ahead and the ARIA wiring —\n * and those cannot be separated without passing the same active index back and\n * forth.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Combobox.module.css\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Custom filter — return true to keep the option. Default is case-insensitive substring match on label. */\n filter?: (option: ComboboxOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n className?: string;\n}\n\nfunction defaultFilter(option: ComboboxOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * Combobox — text input with a filterable dropdown of options.\n *\n * Selecting an option fires `onChange(value)`. Typing filters the list.\n * Keyboard: ArrowUp/ArrowDown to navigate, Enter to select, Esc to close.\n */\nexport function Combobox({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n className,\n}: ComboboxProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n\n const selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? \"\",\n [options, value],\n );\n\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const closeAndReset = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n setActiveIndex(0);\n }, []);\n\n const handleSelect = useCallback(\n (option: ComboboxOption): void => {\n if (option.disabled) return;\n onChange(option.value);\n closeAndReset();\n },\n [onChange, closeAndReset],\n );\n\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) {\n closeAndReset();\n }\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, closeAndReset]);\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) handleSelect(option);\n } else if (event.key === \"Escape\") {\n closeAndReset();\n }\n };\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div className={styles.field}>\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n className={styles.input}\n placeholder={placeholder}\n disabled={disabled}\n value={open ? query : selectedLabel}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n <span className={styles.caret} aria-hidden>\n ▾\n </span>\n </div>\n {open && (\n <ul id={`${id}-listbox`} role=\"listbox\" className={styles.menu}>\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={option.value === value}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n option.value === value && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n handleSelect(option);\n }}\n >\n {option.label}\n </li>\n ))\n )}\n </ul>\n )}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Combobox.js","names":[],"sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract (label, placeholder, helperText, error, disabled,\n * className) and the combobox-specific knobs: filter, emptyMessage, portal. The body\n * is the listbox — open state, active descendant, type-ahead and the ARIA wiring —\n * and those cannot be separated without passing the same active index back and\n * forth.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Combobox.module.css\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Custom filter — return true to keep the option. Default is case-insensitive substring match on label. */\n filter?: (option: ComboboxOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n /**\n * Render the list in a portal, positioned under the field. Default `true`.\n *\n * In flow, an ancestor with `overflow` other than `visible` clips the list —\n * in the last row of a `DataTable` none of it was visible (measured in Chrome\n * at 1440 px). In a portal it escapes the clip, takes the field's width,\n * flips above the field when there is no room below, and follows it on\n * scroll, resize and when the field or the list changes size.\n */\n portal?: boolean;\n className?: string;\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\nfunction defaultFilter(option: ComboboxOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * Combobox — text input with a filterable dropdown of options.\n *\n * Selecting an option fires `onChange(value)`. Typing filters the list.\n * Keyboard: ArrowUp/ArrowDown to navigate, Enter to select, Esc to close.\n */\nexport function Combobox({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: ComboboxProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n const [fieldNode, setFieldNode] = useState<HTMLDivElement | null>(null);\n const [listNode, setListNode] = useState<HTMLUListElement | null>(null);\n const floatingStyle = useAnchorPosition({\n anchor: fieldNode,\n floating: listNode,\n side: \"bottom\",\n align: \"start\",\n offset: LIST_OFFSET,\n enabled: portal && open,\n matchWidth: true,\n });\n\n const selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? \"\",\n [options, value],\n );\n\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const closeAndReset = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n setActiveIndex(0);\n }, []);\n\n const handleSelect = useCallback(\n (option: ComboboxOption): void => {\n if (option.disabled) return;\n onChange(option.value);\n closeAndReset();\n },\n [onChange, closeAndReset],\n );\n\n /**\n * Close on a press outside both the field and the list.\n *\n * The list is checked on its own because in a portal it is not inside the\n * field's wrapper: a press on its scrollbar, or on the empty message, would\n * otherwise count as outside and close it.\n */\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n const target = event.target as Node;\n if (rootRef.current?.contains(target) || listNode?.contains(target)) return;\n closeAndReset();\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, closeAndReset, listNode]);\n\n /**\n * Keyboard model of the input.\n *\n * `Escape` calls `preventDefault()` only while the list is open: that marks the\n * key as consumed, so an enclosing `Modal` or `Drawer` stays open and the next\n * `Escape` reaches it. With the list closed the key is left alone and dismisses\n * the enclosing layer, as the user expects.\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) handleSelect(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n closeAndReset();\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\n className={cn(styles.menu, portal && styles.portalled)}\n style={floatingStyle}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={option.value === value}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n option.value === value && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n handleSelect(option);\n }}\n >\n {option.label}\n </li>\n ))\n )}\n </ul>\n );\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div ref={setFieldNode} className={styles.field}>\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n className={styles.input}\n placeholder={placeholder}\n disabled={disabled}\n value={open ? query : selectedLabel}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n <span className={styles.caret} aria-hidden>\n ▾\n </span>\n </div>\n {open && (portal ? <Portal>{list}</Portal> : list)}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
require("../../styles/component/Combobox.css");
|
|
2
|
-
var e=`tempest_wrapper_TNvle`,t=`tempest_label_KVkZO`,n=`tempest_field_BenH0`,r=`tempest_input_-HTv1`,i=`tempest_caret_jRplQ`,a=`tempest_menu_zPQG3`,o=`tempest_option_vhhn5`,s=`tempest_active_XCgtX`,c=`tempest_selected_T6nm2`,l=`tempest_empty_Ahw5K`,u=`tempest_helper_OzB7V`,d=`tempest_errorText_X18sQ`,f=`tempest_error_bnVuM`,p={wrapper:e,label:t,field:n,input:r,caret:i,menu:a,option:o,active:s,selected:c,empty:l,helper:u,errorText:d,error:f};exports.active=s,exports.caret=i,exports.default=
|
|
2
|
+
var e=`tempest_wrapper_TNvle`,t=`tempest_label_KVkZO`,n=`tempest_field_BenH0`,r=`tempest_input_-HTv1`,i=`tempest_caret_jRplQ`,a=`tempest_menu_zPQG3`,o=`tempest_option_vhhn5`,s=`tempest_active_XCgtX`,c=`tempest_selected_T6nm2`,l=`tempest_empty_Ahw5K`,u=`tempest_helper_OzB7V`,d=`tempest_errorText_X18sQ`,f=`tempest_error_bnVuM`,p=`tempest_portalled_C9jFy`,m={wrapper:e,label:t,field:n,input:r,caret:i,menu:a,option:o,active:s,selected:c,empty:l,helper:u,errorText:d,error:f,portalled:p};exports.active=s,exports.caret=i,exports.default=m,exports.empty=l,exports.error=f,exports.errorText=d,exports.field=n,exports.helper=u,exports.input=r,exports.label=t,exports.menu=a,exports.option=o,exports.portalled=p,exports.selected=c,exports.wrapper=e;
|
|
3
3
|
//# sourceMappingURL=Combobox.module.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.module.cjs","names":[],"sources":["../../../src/components/Combobox/Combobox.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.input {\n width: 100%;\n height: var(--tempest-control-height-md);\n padding: 0 32px 0 var(--tempest-control-padding-md);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n box-shadow: var(--tempest-shadow-xs);\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.input:focus {\n outline: none;\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.input:disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.caret {\n position: absolute;\n right: 12px;\n pointer-events: none;\n color: var(--tempest-text-muted);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n.option.selected {\n font-weight: var(--tempest-weight-semibold);\n}\n\n.empty {\n padding: var(--tempest-space-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger);\n}\n\n.wrapper.error .input {\n border-color: var(--tempest-danger);\n}\n"],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"Combobox.module.cjs","names":[],"sources":["../../../src/components/Combobox/Combobox.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.input {\n width: 100%;\n height: var(--tempest-control-height-md);\n padding: 0 32px 0 var(--tempest-control-padding-md);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n box-shadow: var(--tempest-shadow-xs);\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.input:focus {\n outline: none;\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.input:disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.caret {\n position: absolute;\n right: 12px;\n pointer-events: none;\n color: var(--tempest-text-muted);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n.option.selected {\n font-weight: var(--tempest-weight-semibold);\n}\n\n.empty {\n padding: var(--tempest-space-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger);\n}\n\n.wrapper.error .input {\n border-color: var(--tempest-danger);\n}\n\n.portalled {\n font-family: var(--tempest-font-sans);\n right: auto;\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../../styles/component/Combobox.css";
|
|
2
2
|
//#region src/components/Combobox/Combobox.module.css
|
|
3
|
-
var e = "tempest_wrapper_TNvle", t = "tempest_label_KVkZO", n = "tempest_field_BenH0", r = "tempest_input_-HTv1", i = "tempest_caret_jRplQ", a = "tempest_menu_zPQG3", o = "tempest_option_vhhn5", s = "tempest_active_XCgtX", c = "tempest_selected_T6nm2", l = "tempest_empty_Ahw5K", u = "tempest_helper_OzB7V", d = "tempest_errorText_X18sQ", f = "tempest_error_bnVuM", p = {
|
|
3
|
+
var e = "tempest_wrapper_TNvle", t = "tempest_label_KVkZO", n = "tempest_field_BenH0", r = "tempest_input_-HTv1", i = "tempest_caret_jRplQ", a = "tempest_menu_zPQG3", o = "tempest_option_vhhn5", s = "tempest_active_XCgtX", c = "tempest_selected_T6nm2", l = "tempest_empty_Ahw5K", u = "tempest_helper_OzB7V", d = "tempest_errorText_X18sQ", f = "tempest_error_bnVuM", p = "tempest_portalled_C9jFy", m = {
|
|
4
4
|
wrapper: e,
|
|
5
5
|
label: t,
|
|
6
6
|
field: n,
|
|
@@ -13,9 +13,10 @@ var e = "tempest_wrapper_TNvle", t = "tempest_label_KVkZO", n = "tempest_field_B
|
|
|
13
13
|
empty: l,
|
|
14
14
|
helper: u,
|
|
15
15
|
errorText: d,
|
|
16
|
-
error: f
|
|
16
|
+
error: f,
|
|
17
|
+
portalled: p
|
|
17
18
|
};
|
|
18
19
|
//#endregion
|
|
19
|
-
export { s as active, i as caret,
|
|
20
|
+
export { s as active, i as caret, m as default, l as empty, f as error, d as errorText, n as field, u as helper, r as input, t as label, a as menu, o as option, p as portalled, c as selected, e as wrapper };
|
|
20
21
|
|
|
21
22
|
//# sourceMappingURL=Combobox.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.module.js","names":[],"sources":["../../../src/components/Combobox/Combobox.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.input {\n width: 100%;\n height: var(--tempest-control-height-md);\n padding: 0 32px 0 var(--tempest-control-padding-md);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n box-shadow: var(--tempest-shadow-xs);\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.input:focus {\n outline: none;\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.input:disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.caret {\n position: absolute;\n right: 12px;\n pointer-events: none;\n color: var(--tempest-text-muted);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n.option.selected {\n font-weight: var(--tempest-weight-semibold);\n}\n\n.empty {\n padding: var(--tempest-space-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger);\n}\n\n.wrapper.error .input {\n border-color: var(--tempest-danger);\n}\n"],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"Combobox.module.js","names":[],"sources":["../../../src/components/Combobox/Combobox.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.input {\n width: 100%;\n height: var(--tempest-control-height-md);\n padding: 0 32px 0 var(--tempest-control-padding-md);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n box-shadow: var(--tempest-shadow-xs);\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.input:focus {\n outline: none;\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.input:disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.caret {\n position: absolute;\n right: 12px;\n pointer-events: none;\n color: var(--tempest-text-muted);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n.option.selected {\n font-weight: var(--tempest-weight-semibold);\n}\n\n.empty {\n padding: var(--tempest-space-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger);\n}\n\n.wrapper.error .input {\n border-color: var(--tempest-danger);\n}\n\n.portalled {\n font-family: var(--tempest-font-sans);\n right: auto;\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("../Portal/
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../Portal/escape-layer.cjs"),n=require("../Portal/Portal.cjs"),r=require("./ContextMenu.module.cjs");let i=require("react"),a=require("react/jsx-runtime");var o=10;function s(e){return`separator`in e&&e.separator===!0}function c({items:c,children:u,className:d,trigger:f=`contextmenu`,longPressDelay:p=500,viewportMargin:m=8}){let[h,g]=(0,i.useState)(!1),[_,v]=(0,i.useState)({x:0,y:0}),[y,b]=(0,i.useState)(-1),x=(0,i.useId)(),[S,C]=(0,i.useState)(null),w=(0,i.useRef)([]),T=(0,i.useRef)(null),E=(0,i.useRef)(null),D=c.map((e,t)=>!s(e)&&!e.disabled?t:-1).filter(e=>e!==-1),O=(0,i.useCallback)(()=>{g(!1),b(-1)},[]),k=(0,i.useCallback)(e=>{v(e),b(-1),g(!0)},[]),A=(0,i.useCallback)(()=>{T.current!==null&&(clearTimeout(T.current),T.current=null),E.current=null},[]);(0,i.useEffect)(()=>A,[A]);let j=e=>{f!==`click`&&(e.preventDefault(),k({x:e.clientX,y:e.clientY}))},M=e=>{f!==`contextmenu`&&k({x:e.clientX,y:e.clientY})},N=e=>{if(p<=0||e.pointerType===`mouse`)return;let t={x:e.clientX,y:e.clientY};E.current=t,A(),E.current=t,T.current=setTimeout(()=>{T.current=null,E.current=null,l(),k(t)},p)},P=e=>{let t=E.current;t&&T.current!==null&&Math.hypot(e.clientX-t.x,e.clientY-t.y)>o&&A()};t.useEscapeLayer(h,O),(0,i.useEffect)(()=>{if(!h)return;let e=e=>{let t=t=>{e.preventDefault(),b(t),w.current[t]?.focus()};if(D.length===0)return;let n=D.indexOf(y);e.key===`ArrowDown`&&t(D[(n+1)%D.length]??-1),e.key===`ArrowUp`&&t(D[(n-1+D.length)%D.length]??-1),e.key===`Home`&&t(D[0]??-1),e.key===`End`&&t(D[D.length-1]??-1)},t=e=>{S&&!S.contains(e.target)&&O()};return window.addEventListener(`keydown`,e),window.addEventListener(`mousedown`,t),window.addEventListener(`resize`,O),window.addEventListener(`scroll`,O,!0),()=>{window.removeEventListener(`keydown`,e),window.removeEventListener(`mousedown`,t),window.removeEventListener(`resize`,O),window.removeEventListener(`scroll`,O,!0)}},[h,y,D,O,S]),(0,i.useLayoutEffect)(()=>{if(!h||!S)return;S.focus({preventScroll:!0});let{offsetWidth:e,offsetHeight:t}=S;if(e===0&&t===0)return;let n=window.innerWidth-e-m,r=window.innerHeight-t-m,i=Math.max(m,Math.min(_.x,n)),a=Math.max(m,Math.min(_.y,r));(i!==_.x||a!==_.y)&&v({x:i,y:a})},[h,S,_.x,_.y,m]);let F=e=>{e.onSelect?.(),O()};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(`span`,{className:r.default.root,onContextMenu:j,onClick:M,onPointerDown:N,onPointerMove:P,onPointerUp:A,onPointerCancel:A,children:u}),h&&(0,a.jsx)(n.Portal,{children:(0,a.jsx)(`ul`,{ref:C,id:x,role:`menu`,"aria-orientation":`vertical`,tabIndex:-1,className:e.cn(r.default.menu,d),style:{top:_.y,left:_.x},children:c.map((t,n)=>s(t)?(0,a.jsx)(`li`,{role:`separator`,className:r.default.separator,"aria-hidden":!0},`separator-${n}`):(0,a.jsx)(`li`,{role:`none`,children:(0,a.jsx)(`button`,{ref:e=>{w.current[n]=e},type:`button`,role:`menuitem`,className:e.cn(r.default.item,t.danger&&r.default.danger,y===n&&r.default.active),disabled:t.disabled,onClick:()=>F(t),onMouseEnter:()=>b(n),children:t.label})},`item-${n}`))})})]})}function l(){typeof window>`u`||window.addEventListener(`click`,e=>{e.preventDefault(),e.stopPropagation()},{capture:!0,once:!0})}exports.ContextMenu=c;
|
|
2
2
|
//# sourceMappingURL=ContextMenu.cjs.map
|