tempest-react-sdk 0.66.0 → 0.68.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/README.md +97 -93
- package/dist/auth/create-tempest-auth.cjs +1 -1
- package/dist/auth/create-tempest-auth.cjs.map +1 -1
- package/dist/auth/create-tempest-auth.js +41 -40
- package/dist/auth/create-tempest-auth.js.map +1 -1
- package/dist/components/BottomSheet/BottomSheet.cjs +1 -1
- package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
- package/dist/components/BottomSheet/BottomSheet.js +35 -40
- package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
- package/dist/components/Card/Card.module.cjs.map +1 -1
- package/dist/components/Card/Card.module.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 +96 -74
- 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/Command/Command.cjs +1 -1
- package/dist/components/Command/Command.js +4 -4
- 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/DataTable/DataTable.cjs +1 -1
- package/dist/components/DataTable/DataTable.cjs.map +1 -1
- package/dist/components/DataTable/DataTable.js +138 -127
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/DataTable/use-dev-warnings.cjs +1 -1
- package/dist/components/DataTable/use-dev-warnings.cjs.map +1 -1
- package/dist/components/DataTable/use-dev-warnings.js +5 -3
- package/dist/components/DataTable/use-dev-warnings.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 +46 -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/FilterPanel/FilterPanel.cjs +2 -0
- package/dist/components/FilterPanel/FilterPanel.cjs.map +1 -0
- package/dist/components/FilterPanel/FilterPanel.js +75 -0
- package/dist/components/FilterPanel/FilterPanel.js.map +1 -0
- package/dist/components/FilterPanel/FilterPanel.module.cjs +3 -0
- package/dist/components/FilterPanel/FilterPanel.module.cjs.map +1 -0
- package/dist/components/FilterPanel/FilterPanel.module.js +12 -0
- package/dist/components/FilterPanel/FilterPanel.module.js.map +1 -0
- 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 +84 -87
- 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 +47 -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/Pagination/Pagination.cjs +1 -1
- package/dist/components/Pagination/Pagination.cjs.map +1 -1
- package/dist/components/Pagination/Pagination.js +31 -30
- package/dist/components/Pagination/Pagination.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 +37 -0
- package/dist/components/Portal/tab-bridge.js.map +1 -0
- package/dist/components/Select/CaretIcon.cjs +2 -0
- package/dist/components/Select/CaretIcon.cjs.map +1 -0
- package/dist/components/Select/CaretIcon.js +21 -0
- package/dist/components/Select/CaretIcon.js.map +1 -0
- package/dist/components/Select/Select.cjs +1 -1
- package/dist/components/Select/Select.cjs.map +1 -1
- package/dist/components/Select/Select.js +33 -47
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/Select.module.cjs +1 -1
- package/dist/components/Select/Select.module.cjs.map +1 -1
- package/dist/components/Select/Select.module.js +4 -3
- package/dist/components/Select/Select.module.js.map +1 -1
- package/dist/components/Select/options.cjs +2 -0
- package/dist/components/Select/options.cjs.map +1 -0
- package/dist/components/Select/options.js +25 -0
- package/dist/components/Select/options.js.map +1 -0
- package/dist/components/Sidebar/Sidebar.cjs +1 -1
- package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
- package/dist/components/Sidebar/Sidebar.js +49 -43
- package/dist/components/Sidebar/Sidebar.js.map +1 -1
- package/dist/components/Sidebar/active-nav-key.cjs +2 -0
- package/dist/components/Sidebar/active-nav-key.cjs.map +1 -0
- package/dist/components/Sidebar/active-nav-key.js +22 -0
- package/dist/components/Sidebar/active-nav-key.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/tab-sequence.cjs +2 -0
- package/dist/hooks/tab-sequence.cjs.map +1 -0
- package/dist/hooks/tab-sequence.js +53 -0
- package/dist/hooks/tab-sequence.js.map +1 -0
- package/dist/hooks/use-deep-memo.cjs +1 -1
- package/dist/hooks/use-deep-memo.cjs.map +1 -1
- package/dist/hooks/use-deep-memo.js +1 -1
- package/dist/hooks/use-deep-memo.js.map +1 -1
- package/dist/hooks/use-draft-filters.cjs +2 -0
- package/dist/hooks/use-draft-filters.cjs.map +1 -0
- package/dist/hooks/use-draft-filters.js +49 -0
- package/dist/hooks/use-draft-filters.js.map +1 -0
- 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 +31 -34
- 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/api-client.cjs +1 -1
- package/dist/http/api-client.cjs.map +1 -1
- package/dist/http/api-client.js +92 -80
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/credential-scope.cjs +1 -1
- package/dist/http/credential-scope.cjs.map +1 -1
- package/dist/http/credential-scope.js +5 -3
- package/dist/http/credential-scope.js.map +1 -1
- package/dist/http/csrf.cjs +2 -0
- package/dist/http/csrf.cjs.map +1 -0
- package/dist/http/csrf.js +46 -0
- package/dist/http/csrf.js.map +1 -0
- 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/http/resumable-upload.cjs +1 -1
- package/dist/http/resumable-upload.cjs.map +1 -1
- package/dist/http/resumable-upload.js +131 -123
- package/dist/http/resumable-upload.js.map +1 -1
- package/dist/http/upload-with-progress.cjs +1 -1
- package/dist/http/upload-with-progress.cjs.map +1 -1
- package/dist/http/upload-with-progress.js +41 -33
- package/dist/http/upload-with-progress.js.map +1 -1
- package/dist/query/use-paginated-query.cjs +1 -1
- package/dist/query/use-paginated-query.cjs.map +1 -1
- package/dist/query/use-paginated-query.js +47 -30
- package/dist/query/use-paginated-query.js.map +1 -1
- package/dist/styles/FilterPanel.css +2 -0
- package/dist/styles/actions.css +3 -0
- package/dist/styles/advanced.css +1 -0
- package/dist/styles/component/Card.css +1 -1
- package/dist/styles/component/Combobox.css +5 -3
- package/dist/styles/component/DropdownMenu.css +1 -0
- package/dist/styles/component/FilterPanel.css +5 -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/Select.css +13 -6
- package/dist/styles/component/Tooltip.css +1 -0
- package/dist/styles/core.css +4 -4
- package/dist/styles/data.css +4 -0
- package/dist/styles/forms.css +19 -9
- package/dist/styles/identity.css +1 -1
- package/dist/styles/manifest.json +6 -0
- package/dist/styles/navigation.css +2 -0
- package/dist/styles/tokens.css +4 -4
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +805 -35
- package/dist/tempest-react-sdk.js +347 -342
- package/dist/vite/tempest-csp.cjs +3 -0
- package/dist/vite/tempest-csp.cjs.map +1 -0
- package/dist/vite/tempest-csp.js +189 -0
- package/dist/vite/tempest-csp.js.map +1 -0
- package/dist/vite.cjs +1 -1
- package/dist/vite.d.ts +145 -0
- package/dist/vite.js +2 -1
- package/package.json +2 -2
- package/template/vite.config.ts +8 -1
- package/template-pwa/vite.config.ts +6 -0
|
@@ -1,82 +1,104 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
|
-
import t from "
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { useEscapeLayer as t } from "../Portal/escape-layer.js";
|
|
3
|
+
import { Portal as n } from "../Portal/Portal.js";
|
|
4
|
+
import { useAnchorPosition as r } from "../Portal/anchor-position.js";
|
|
5
|
+
import { usePortalTabOrder as i } from "../Portal/tab-bridge.js";
|
|
6
|
+
import a from "./NavigationMenu.module.js";
|
|
7
|
+
import { useCallback as o, useEffect as s, useId as c, useRef as l, useState as u } from "react";
|
|
8
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
5
9
|
//#region src/components/NavigationMenu/NavigationMenu.tsx
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
var p = 4;
|
|
11
|
+
function m({ items: m, portal: h = !0, className: g, ..._ }) {
|
|
12
|
+
let [v, y] = u(-1), [b, x] = u(-1), S = c(), C = l(null), w = o(() => {
|
|
13
|
+
y(-1), x(-1);
|
|
14
|
+
}, []), T = v === -1 ? b : v, [E, D] = u(null), [O, k] = u(null), A = h && T !== -1, j = r({
|
|
15
|
+
anchor: E,
|
|
16
|
+
floating: O,
|
|
17
|
+
side: "bottom",
|
|
18
|
+
align: "start",
|
|
19
|
+
offset: p,
|
|
20
|
+
enabled: A
|
|
21
|
+
});
|
|
22
|
+
i({
|
|
23
|
+
enabled: A,
|
|
24
|
+
anchor: E,
|
|
25
|
+
panel: O
|
|
26
|
+
}), t(T !== -1, w), s(() => {
|
|
27
|
+
if (T === -1) return;
|
|
12
28
|
let e = (e) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
_.current && !_.current.contains(e.target) && v();
|
|
29
|
+
let t = e.target;
|
|
30
|
+
C.current?.contains(t) || O?.contains(t) || w();
|
|
16
31
|
};
|
|
17
|
-
return window.addEventListener("
|
|
18
|
-
window.removeEventListener("
|
|
32
|
+
return window.addEventListener("mousedown", e), () => {
|
|
33
|
+
window.removeEventListener("mousedown", e);
|
|
19
34
|
};
|
|
20
|
-
}, [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
}, [
|
|
36
|
+
T,
|
|
37
|
+
w,
|
|
38
|
+
O
|
|
39
|
+
]);
|
|
40
|
+
let M = (e, t) => {
|
|
41
|
+
let n = () => {
|
|
42
|
+
e.onSelect?.(), w();
|
|
24
43
|
};
|
|
25
|
-
return e.href ? /* @__PURE__ */
|
|
44
|
+
return e.href ? /* @__PURE__ */ d("a", {
|
|
26
45
|
href: e.href,
|
|
27
|
-
role:
|
|
28
|
-
className:
|
|
29
|
-
onClick:
|
|
46
|
+
role: t === "menuitem" ? "menuitem" : void 0,
|
|
47
|
+
className: a.link,
|
|
48
|
+
onClick: n,
|
|
30
49
|
children: e.label
|
|
31
|
-
}) : /* @__PURE__ */
|
|
50
|
+
}) : /* @__PURE__ */ d("button", {
|
|
32
51
|
type: "button",
|
|
33
|
-
role:
|
|
34
|
-
className:
|
|
35
|
-
onClick:
|
|
52
|
+
role: t === "menuitem" ? "menuitem" : void 0,
|
|
53
|
+
className: a.link,
|
|
54
|
+
onClick: n,
|
|
36
55
|
children: e.label
|
|
37
56
|
});
|
|
38
57
|
};
|
|
39
|
-
return /* @__PURE__ */
|
|
40
|
-
ref:
|
|
41
|
-
className: e(
|
|
42
|
-
...
|
|
43
|
-
children: /* @__PURE__ */
|
|
44
|
-
className:
|
|
45
|
-
children:
|
|
46
|
-
if (!(
|
|
47
|
-
className:
|
|
48
|
-
children:
|
|
49
|
-
},
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
return /* @__PURE__ */ d("nav", {
|
|
59
|
+
ref: C,
|
|
60
|
+
className: e(a.root, g),
|
|
61
|
+
..._,
|
|
62
|
+
children: /* @__PURE__ */ d("ul", {
|
|
63
|
+
className: a.list,
|
|
64
|
+
children: m.map((t, r) => {
|
|
65
|
+
if (!(t.children && t.children.length > 0)) return /* @__PURE__ */ d("li", {
|
|
66
|
+
className: a.item,
|
|
67
|
+
children: M(t, "none")
|
|
68
|
+
}, r);
|
|
69
|
+
let i = T === r, o = `${S}-panel-${r}`, s = /* @__PURE__ */ d("ul", {
|
|
70
|
+
ref: k,
|
|
71
|
+
id: o,
|
|
72
|
+
role: "menu",
|
|
73
|
+
className: e(a.panel, h && a.portalled),
|
|
74
|
+
style: j,
|
|
75
|
+
children: t.children.map((e, t) => /* @__PURE__ */ d("li", {
|
|
76
|
+
role: "none",
|
|
77
|
+
className: a.subItem,
|
|
78
|
+
children: M(e, "menuitem")
|
|
79
|
+
}, t))
|
|
80
|
+
});
|
|
81
|
+
return /* @__PURE__ */ f("li", {
|
|
82
|
+
ref: i ? D : void 0,
|
|
83
|
+
className: a.item,
|
|
84
|
+
onMouseEnter: () => x(r),
|
|
85
|
+
onMouseLeave: () => x((e) => e === r ? -1 : e),
|
|
86
|
+
children: [/* @__PURE__ */ d("button", {
|
|
56
87
|
type: "button",
|
|
57
|
-
className:
|
|
88
|
+
className: a.trigger,
|
|
58
89
|
"aria-haspopup": "menu",
|
|
59
|
-
"aria-expanded":
|
|
60
|
-
"aria-controls":
|
|
61
|
-
onClick: () =>
|
|
62
|
-
onFocus: () =>
|
|
63
|
-
children:
|
|
64
|
-
}),
|
|
65
|
-
|
|
66
|
-
role: "menu",
|
|
67
|
-
className: t.panel,
|
|
68
|
-
children: e.children.map((e, n) => /* @__PURE__ */ s("li", {
|
|
69
|
-
role: "none",
|
|
70
|
-
className: t.subItem,
|
|
71
|
-
children: b(e, "menuitem")
|
|
72
|
-
}, n))
|
|
73
|
-
})]
|
|
74
|
-
}, n);
|
|
90
|
+
"aria-expanded": i,
|
|
91
|
+
"aria-controls": o,
|
|
92
|
+
onClick: () => y((e) => e === r ? -1 : r),
|
|
93
|
+
onFocus: () => x(r),
|
|
94
|
+
children: t.label
|
|
95
|
+
}), i && (h ? /* @__PURE__ */ d(n, { children: s }) : s)]
|
|
96
|
+
}, r);
|
|
75
97
|
})
|
|
76
98
|
})
|
|
77
99
|
});
|
|
78
100
|
}
|
|
79
101
|
//#endregion
|
|
80
|
-
export {
|
|
102
|
+
export { m as NavigationMenu };
|
|
81
103
|
|
|
82
104
|
//# sourceMappingURL=NavigationMenu.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavigationMenu.js","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — the body resolves the active item from the\n * current path, owns the open submenu and the hover-intent delay, all of which\n * decide the same highlighted row.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NavigationMenu.module.css\";\n\nexport interface NavigationMenuItem {\n /** Visible label for the entry. */\n label: ReactNode;\n /** When set, the entry renders as an anchor. */\n href?: string;\n /** Invoked when the entry is activated (click / Enter). */\n onSelect?: () => void;\n /** Nested entries — presence turns the item into a submenu trigger. */\n children?: NavigationMenuItem[];\n}\n\nexport interface NavigationMenuProps extends HTMLAttributes<HTMLElement> {\n /** Top-level navigation entries. */\n items: NavigationMenuItem[];\n}\n\n/**\n * Horizontal navigation menu with hover/click/focus dropdown submenus.\n *\n * - Top-level items render in a `<nav><ul>`.\n * - Items with `children` open a submenu panel (`role=\"menu\"`) on hover, focus,\n * or click. Only one panel is open at a time.\n * - Closes on outside click, Escape, or selecting a leaf entry.\n *\n * @example\n * <NavigationMenu\n * items={[\n * { label: \"Home\", href: \"/\" },\n * {\n * label: \"Products\",\n * children: [\n * { label: \"Analytics\", href: \"/analytics\" },\n * { label: \"Billing\", onSelect: () => openBilling() },\n * ],\n * },\n * ]}\n * />\n */\nexport function NavigationMenu({ items, className, ...props }: NavigationMenuProps) {\n const [clickedIndex, setClickedIndex] = useState<number>(-1);\n const [hovered, setHovered] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLElement>(null);\n\n const close = useCallback((): void => {\n setClickedIndex(-1);\n setHovered(-1);\n }, []);\n\n const openIndex = clickedIndex !== -1 ? clickedIndex : hovered;\n\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") close();\n };\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) close();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [openIndex, close]);\n\n const renderLeaf = (item: NavigationMenuItem, role: \"none\" | \"menuitem\"): ReactNode => {\n const handleClick = (): void => {\n item.onSelect?.();\n close();\n };\n if (item.href) {\n return (\n <a\n href={item.href}\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </a>\n );\n }\n return (\n <button\n type=\"button\"\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </button>\n );\n };\n\n return (\n <nav ref={rootRef} className={cn(styles.root, className)} {...props}>\n <ul className={styles.list}>\n {items.map((item, index) => {\n const hasChildren = !!item.children && item.children.length > 0;\n if (!hasChildren) {\n return (\n <li key={index} className={styles.item}>\n {renderLeaf(item, \"none\")}\n </li>\n );\n }\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <li\n key={index}\n className={styles.item}\n onMouseEnter={() => setHovered(index)}\n onMouseLeave={() => setHovered((prev) => (prev === index ? -1 : prev))}\n >\n <button\n type=\"button\"\n className={styles.trigger}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={panelId}\n onClick={() =>\n setClickedIndex((prev) => (prev === index ? -1 : index))\n }\n onFocus={() => setHovered(index)}\n >\n {item.label}\n </button>\n {open && (\n <ul id={panelId} role=\"menu\" className={styles.panel}>\n {item.children!.map((child, childIndex) => (\n <li key={childIndex} role=\"none\" className={styles.subItem}>\n {renderLeaf(child, \"menuitem\")}\n </li>\n ))}\n </ul>\n )}\n </li>\n );\n })}\n </ul>\n </nav>\n );\n}\n"],"mappings":";;;;;AAgDA,SAAgB,EAAe,EAAE,UAAO,cAAW,GAAG,KAA8B;CAChF,IAAM,CAAC,GAAc,KAAmB,EAAiB,EAAE,GACrD,CAAC,GAAS,KAAc,EAAiB,EAAE,GAC3C,IAAS,EAAM,GACf,IAAU,EAAoB,IAAI,GAElC,IAAQ,QAAwB;EAElC,AADA,EAAgB,EAAE,GAClB,EAAW,EAAE;CACjB,GAAG,CAAC,CAAC,GAEC,IAAY,MAAiB,KAAoB,IAAf;CAExC,QAAgB;EACZ,IAAI,MAAc,IAAI;EACtB,IAAM,KAAS,MAA+B;GAC1C,AAAI,EAAM,QAAQ,YAAU,EAAM;EACtC,GACM,KAAU,MAA4B;GACxC,AAAI,EAAQ,WAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,MAAc,KAAG,EAAM;EAClF;EAGA,OAFA,OAAO,iBAAiB,WAAW,CAAK,GACxC,OAAO,iBAAiB,aAAa,CAAM,SAC9B;GAET,AADA,OAAO,oBAAoB,WAAW,CAAK,GAC3C,OAAO,oBAAoB,aAAa,CAAM;EAClD;CACJ,GAAG,CAAC,GAAW,CAAK,CAAC;CAErB,IAAM,KAAc,GAA0B,MAAyC;EACnF,IAAM,UAA0B;GAE5B,AADA,EAAK,WAAW,GAChB,EAAM;EACV;EAaA,OAZI,EAAK,OAED,kBAAC,KAAD;GACI,MAAM,EAAK;GACX,MAAM,MAAS,aAAa,aAAa,KAAA;GACzC,WAAW,EAAO;GAClB,SAAS;GAER,UAAA,EAAK;EACP,CAAA,IAIP,kBAAC,UAAD;GACI,MAAK;GACL,MAAM,MAAS,aAAa,aAAa,KAAA;GACzC,WAAW,EAAO;GAClB,SAAS;GAER,UAAA,EAAK;EACF,CAAA;CAEhB;CAEA,OACI,kBAAC,OAAD;EAAK,KAAK;EAAS,WAAW,EAAG,EAAO,MAAM,CAAS;EAAG,GAAI;EAC1D,UAAA,kBAAC,MAAD;GAAI,WAAW,EAAO;GACjB,UAAA,EAAM,KAAK,GAAM,MAAU;IAExB,IAAI,EADkB,EAAK,YAAY,EAAK,SAAS,SAAS,IAE1D,OACI,kBAAC,MAAD;KAAgB,WAAW,EAAO;KAC7B,UAAA,EAAW,GAAM,MAAM;IACxB,GAFK,CAEL;IAGZ,IAAM,IAAO,MAAc,GACrB,IAAU,GAAG,EAAO,SAAS;IACnC,OACI,kBAAC,MAAD;KAEI,WAAW,EAAO;KAClB,oBAAoB,EAAW,CAAK;KACpC,oBAAoB,GAAY,MAAU,MAAS,IAAQ,KAAK,CAAK;KAJzE,UAAA,CAMI,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,iBAAc;MACd,iBAAe;MACf,iBAAe;MACf,eACI,GAAiB,MAAU,MAAS,IAAQ,KAAK,CAAM;MAE3D,eAAe,EAAW,CAAK;MAE9B,UAAA,EAAK;KACF,CAAA,GACP,KACG,kBAAC,MAAD;MAAI,IAAI;MAAS,MAAK;MAAO,WAAW,EAAO;MAC1C,UAAA,EAAK,SAAU,KAAK,GAAO,MACxB,kBAAC,MAAD;OAAqB,MAAK;OAAO,WAAW,EAAO;OAC9C,UAAA,EAAW,GAAO,UAAU;MAC7B,GAFK,CAEL,CACP;KACD,CAAA,CAER;IA3BK,GAAA,CA2BL;GAEZ,CAAC;EACD,CAAA;CACH,CAAA;AAEb"}
|
|
1
|
+
{"version":3,"file":"NavigationMenu.js","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — the body resolves the active item from the\n * current path, owns the open submenu and the hover-intent delay, all of which\n * decide the same highlighted row.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { useEscapeLayer } from \"@/components/Portal/escape-layer\";\nimport { usePortalTabOrder } from \"@/components/Portal/tab-bridge\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NavigationMenu.module.css\";\n\nexport interface NavigationMenuItem {\n /** Visible label for the entry. */\n label: ReactNode;\n /** When set, the entry renders as an anchor. */\n href?: string;\n /** Invoked when the entry is activated (click / Enter). */\n onSelect?: () => void;\n /** Nested entries — presence turns the item into a submenu trigger. */\n children?: NavigationMenuItem[];\n}\n\nexport interface NavigationMenuProps extends HTMLAttributes<HTMLElement> {\n /** Top-level navigation entries. */\n items: NavigationMenuItem[];\n /**\n * Render the open panel in a portal, positioned under its trigger. Default\n * `true`.\n *\n * In flow, an ancestor with `overflow` other than `visible` clips the panel,\n * and the SDK's own `Navbar` is one: its `nav` slot scrolls horizontally, and\n * `overflow-x: auto` forces `overflow-y` to `auto` too — a submenu showed\n * 0% of its panel there (measured in Chrome at 1440 px). In a portal the\n * panel escapes the clip, flips at the viewport edge, stays in the tab order\n * right after its trigger, and follows it on scroll and resize.\n */\n portal?: boolean;\n}\n\n/** Gap between trigger and panel, in px — the in-flow CSS uses the same 4 px. */\nconst PANEL_OFFSET = 4;\n\n/**\n * Horizontal navigation menu with hover/click/focus dropdown submenus.\n *\n * - Top-level items render in a `<nav><ul>`.\n * - Items with `children` open a submenu panel (`role=\"menu\"`) on hover, focus,\n * or click. Only one panel is open at a time.\n * - Closes on outside click, Escape, or selecting a leaf entry.\n *\n * @example\n * <NavigationMenu\n * items={[\n * { label: \"Home\", href: \"/\" },\n * {\n * label: \"Products\",\n * children: [\n * { label: \"Analytics\", href: \"/analytics\" },\n * { label: \"Billing\", onSelect: () => openBilling() },\n * ],\n * },\n * ]}\n * />\n */\nexport function NavigationMenu({ items, portal = true, className, ...props }: NavigationMenuProps) {\n const [clickedIndex, setClickedIndex] = useState<number>(-1);\n const [hovered, setHovered] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLElement>(null);\n\n const close = useCallback((): void => {\n setClickedIndex(-1);\n setHovered(-1);\n }, []);\n\n const openIndex = clickedIndex !== -1 ? clickedIndex : hovered;\n\n const [anchorNode, setAnchorNode] = useState<HTMLLIElement | null>(null);\n const [panelNode, setPanelNode] = useState<HTMLUListElement | null>(null);\n const floating = portal && openIndex !== -1;\n const floatingStyle = useAnchorPosition({\n anchor: anchorNode,\n floating: panelNode,\n side: \"bottom\",\n align: \"start\",\n offset: PANEL_OFFSET,\n enabled: floating,\n });\n usePortalTabOrder({ enabled: floating, anchor: anchorNode, panel: panelNode });\n\n useEscapeLayer(openIndex !== -1, close);\n\n /**\n * Close on a press outside both the menu and the open panel.\n *\n * The panel is checked on its own because in a portal it is not inside the\n * menu's root: a press on one of its entries would otherwise count as\n * outside, close the panel on `mousedown`, and swallow the entry's click.\n */\n useEffect(() => {\n if (openIndex === -1) return;\n const onDown = (event: MouseEvent): void => {\n const target = event.target as Node;\n if (rootRef.current?.contains(target) || panelNode?.contains(target)) return;\n close();\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [openIndex, close, panelNode]);\n\n const renderLeaf = (item: NavigationMenuItem, role: \"none\" | \"menuitem\"): ReactNode => {\n const handleClick = (): void => {\n item.onSelect?.();\n close();\n };\n if (item.href) {\n return (\n <a\n href={item.href}\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </a>\n );\n }\n return (\n <button\n type=\"button\"\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </button>\n );\n };\n\n return (\n <nav ref={rootRef} className={cn(styles.root, className)} {...props}>\n <ul className={styles.list}>\n {items.map((item, index) => {\n const hasChildren = !!item.children && item.children.length > 0;\n if (!hasChildren) {\n return (\n <li key={index} className={styles.item}>\n {renderLeaf(item, \"none\")}\n </li>\n );\n }\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n const panel = (\n <ul\n ref={setPanelNode}\n id={panelId}\n role=\"menu\"\n className={cn(styles.panel, portal && styles.portalled)}\n style={floatingStyle}\n >\n {item.children!.map((child, childIndex) => (\n <li key={childIndex} role=\"none\" className={styles.subItem}>\n {renderLeaf(child, \"menuitem\")}\n </li>\n ))}\n </ul>\n );\n return (\n <li\n key={index}\n ref={open ? setAnchorNode : undefined}\n className={styles.item}\n onMouseEnter={() => setHovered(index)}\n onMouseLeave={() => setHovered((prev) => (prev === index ? -1 : prev))}\n >\n <button\n type=\"button\"\n className={styles.trigger}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={panelId}\n onClick={() =>\n setClickedIndex((prev) => (prev === index ? -1 : index))\n }\n onFocus={() => setHovered(index)}\n >\n {item.label}\n </button>\n {open && (portal ? <Portal>{panel}</Portal> : panel)}\n </li>\n );\n })}\n </ul>\n </nav>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
require("../../styles/component/NavigationMenu.css");
|
|
2
|
-
var e=`tempest_root_Nosqs`,t=`tempest_list_v9FNC`,n=`tempest_item_9RzZ7`,r=`tempest_link_67ULr`,i=`tempest_trigger_2gBDu`,a=`tempest_panel_N2ujp`,o=`tempest_tempest-nav-menu-in_8tNCR`,s=`tempest_subItem_LjtO7`,c={root:e,list:t,item:n,link:r,trigger:i,panel:a,tempestNavMenuIn:o,subItem:s};exports.default=
|
|
2
|
+
var e=`tempest_root_Nosqs`,t=`tempest_list_v9FNC`,n=`tempest_item_9RzZ7`,r=`tempest_link_67ULr`,i=`tempest_trigger_2gBDu`,a=`tempest_panel_N2ujp`,o=`tempest_tempest-nav-menu-in_8tNCR`,s=`tempest_subItem_LjtO7`,c=`tempest_portalled_2V2JB`,l={root:e,list:t,item:n,link:r,trigger:i,panel:a,tempestNavMenuIn:o,subItem:s,portalled:c};exports.default=l,exports.item=n,exports.link=r,exports.list=t,exports.panel=a,exports.portalled=c,exports.root=e,exports.subItem=s,exports.tempestNavMenuIn=o,exports.trigger=i;
|
|
3
3
|
//# sourceMappingURL=NavigationMenu.module.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavigationMenu.module.cjs","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.module.css"],"sourcesContent":[".root {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n}\n\n.list {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.item {\n position: relative;\n}\n\n.link,\n.trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n text-decoration: none;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .link:hover,\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.link:focus-visible,\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\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 padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-nav-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.subItem {\n display: block;\n}\n\n.subItem .link {\n width: 100%;\n}\n\n@keyframes tempest-nav-menu-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n"],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"NavigationMenu.module.cjs","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.module.css"],"sourcesContent":[".root {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n}\n\n.list {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.item {\n position: relative;\n}\n\n.link,\n.trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n text-decoration: none;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .link:hover,\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.link:focus-visible,\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\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 padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-nav-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.subItem {\n display: block;\n}\n\n.subItem .link {\n width: 100%;\n}\n\n@keyframes tempest-nav-menu-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n\n.portalled {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../../styles/component/NavigationMenu.css";
|
|
2
2
|
//#region src/components/NavigationMenu/NavigationMenu.module.css
|
|
3
|
-
var e = "tempest_root_Nosqs", t = "tempest_list_v9FNC", n = "tempest_item_9RzZ7", r = "tempest_link_67ULr", i = "tempest_trigger_2gBDu", a = "tempest_panel_N2ujp", o = "tempest_tempest-nav-menu-in_8tNCR", s = "tempest_subItem_LjtO7", c = {
|
|
3
|
+
var e = "tempest_root_Nosqs", t = "tempest_list_v9FNC", n = "tempest_item_9RzZ7", r = "tempest_link_67ULr", i = "tempest_trigger_2gBDu", a = "tempest_panel_N2ujp", o = "tempest_tempest-nav-menu-in_8tNCR", s = "tempest_subItem_LjtO7", c = "tempest_portalled_2V2JB", l = {
|
|
4
4
|
root: e,
|
|
5
5
|
list: t,
|
|
6
6
|
item: n,
|
|
@@ -8,9 +8,10 @@ var e = "tempest_root_Nosqs", t = "tempest_list_v9FNC", n = "tempest_item_9RzZ7"
|
|
|
8
8
|
trigger: i,
|
|
9
9
|
panel: a,
|
|
10
10
|
tempestNavMenuIn: o,
|
|
11
|
-
subItem: s
|
|
11
|
+
subItem: s,
|
|
12
|
+
portalled: c
|
|
12
13
|
};
|
|
13
14
|
//#endregion
|
|
14
|
-
export {
|
|
15
|
+
export { l as default, n as item, r as link, t as list, a as panel, c as portalled, e as root, s as subItem, o as tempestNavMenuIn, i as trigger };
|
|
15
16
|
|
|
16
17
|
//# sourceMappingURL=NavigationMenu.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavigationMenu.module.js","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.module.css"],"sourcesContent":[".root {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n}\n\n.list {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.item {\n position: relative;\n}\n\n.link,\n.trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n text-decoration: none;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .link:hover,\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.link:focus-visible,\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\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 padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-nav-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.subItem {\n display: block;\n}\n\n.subItem .link {\n width: 100%;\n}\n\n@keyframes tempest-nav-menu-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n"],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"NavigationMenu.module.js","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.module.css"],"sourcesContent":[".root {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n}\n\n.list {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.item {\n position: relative;\n}\n\n.link,\n.trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n text-decoration: none;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .link:hover,\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.link:focus-visible,\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\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 padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-nav-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.subItem {\n display: block;\n}\n\n.subItem .link {\n width: 100%;\n}\n\n@keyframes tempest-nav-menu-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n\n.portalled {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("./Pagination.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("./Pagination.module.cjs");let n=require("react"),r=require("react/jsx-runtime");var i=[10,25,50,100];function a(e,t,n){if(t<=n+4)return Array.from({length:t},(e,t)=>t+1);let r=Math.max(2,e-Math.floor(n/2)),i=Math.min(t-1,r+n-1),a=[1];r>2&&a.push(`...`);for(let e=r;e<=i;e++)a.push(e);return i<t-1&&a.push(`...`),a.push(t),a}function o({page:o,totalPages:s,onPageChange:c,pageSize:l,onPageSizeChange:u,pageSizeOptions:d=i,totalItems:f,siblingCount:p=3,compactOnMobile:m=!0,className:h}){let g=(0,n.useMemo)(()=>a(o,s,p),[o,s,p]),_=(0,n.useMemo)(()=>l===void 0||d.includes(l)?d:[...d,l].sort((e,t)=>e-t),[l,d]),v=(0,n.useRef)(null);return(0,n.useEffect)(()=>{m||v.current?.scrollIntoView?.({block:`nearest`,inline:`nearest`})},[o,m]),s<=1&&!u?null:(0,r.jsxs)(`div`,{className:e.cn(t.default.wrapper,h),"data-compact":String(m),children:[(0,r.jsx)(`div`,{className:t.default.summary,children:typeof f==`number`?`${f} resultado${f===1?``:`s`}`:`Página ${o} de ${s}`}),(0,r.jsxs)(`div`,{className:t.default.controls,children:[(0,r.jsx)(`button`,{type:`button`,className:t.default.page,onClick:()=>c(Math.max(1,o-1)),disabled:o<=1,"aria-label":`Página anterior`,children:`‹`}),g.map((n,i)=>n===`...`?(0,r.jsx)(`span`,{className:e.cn(t.default.ellipsis,t.default.numeric),children:`…`},`ellipsis-${i}`):(0,r.jsx)(`button`,{type:`button`,ref:n===o?v:void 0,className:e.cn(t.default.page,t.default.numeric,n===o&&t.default.active),onClick:()=>c(n),"aria-current":n===o?`page`:void 0,children:n},n)),(0,r.jsx)(`button`,{type:`button`,className:t.default.page,onClick:()=>c(Math.min(s,o+1)),disabled:o>=s,"aria-label":`Próxima página`,children:`›`}),u&&(0,r.jsx)(`select`,{className:t.default.sizeSelect,value:l,onChange:e=>u(Number(e.target.value)),"aria-label":`Itens por página`,children:_.map(e=>(0,r.jsxs)(`option`,{value:e,children:[e,` / página`]},e))})]})]})}exports.Pagination=o;
|
|
2
2
|
//# sourceMappingURL=Pagination.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pagination.cjs","names":[],"sources":["../../../src/components/Pagination/Pagination.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — page/totalPages/onPageChange is the pager,\n * pageSize/onPageSizeChange/ pageSizeOptions is the size control that sits in the\n * same bar, and totalItems plus siblingCount decide the copy and how many page\n * buttons are rendered. They ship together because a footer that pages but cannot\n * resize is half a footer.\n */\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Pagination.module.css\";\n\nexport interface PaginationProps {\n page: number;\n totalPages: number;\n onPageChange: (page: number) => void;\n pageSize?: number;\n onPageSizeChange?: (size: number) => void;\n pageSizeOptions?: number[];\n /** Total items count; if provided, renders the summary text. */\n totalItems?: number;\n /** Pages kept around the current one; ~`siblingCount + 4` buttons render. Default 3. */\n siblingCount?: number;\n /**\n * Below 640px, drop to `‹` / `›` plus the summary and hide the numbered\n * buttons and the size select. Default `true`.\n *\n * The default suits a desktop-first app, and costs the thing pagination is\n * for on a phone: reaching page 7 becomes six taps on \"next\" instead of one\n * tap on `7`. Pass `false` in a mobile-first app and the numbers stay at\n * every width — the row scrolls horizontally by itself rather than pushing\n * the page wider, and the current page is scrolled into view when it\n * changes.\n */\n compactOnMobile?: boolean;\n className?: string;\n}\n\nfunction buildRange(page: number, totalPages: number, siblings: number): (number | \"...\")[] {\n const totalSlots = siblings + 4;\n if (totalPages <= totalSlots) {\n return Array.from({ length: totalPages }, (_, i) => i + 1);\n }\n const start = Math.max(2, page - Math.floor(siblings / 2));\n const end = Math.min(totalPages - 1, start + siblings - 1);\n const range: (number | \"...\")[] = [1];\n if (start > 2) range.push(\"...\");\n for (let i = start; i <= end; i++) range.push(i);\n if (end < totalPages - 1) range.push(\"...\");\n range.push(totalPages);\n return range;\n}\n\n/**\n * Numeric pagination controls. Pair with {@link usePagination} for state.\n *\n * Narrow screens collapse to `‹` / `›` plus the summary by default, which turns\n * random access into sequential access — fine for a desktop-first app, wrong for\n * one whose traffic is phones. `compactOnMobile={false}` keeps the numbers at\n * every width: the row scrolls on its own instead of widening the page, and the\n * effect below keeps the current page inside the visible part of that row.\n *\n * The scroll is why this is one boolean rather than a `variant`. Simply\n * un-hiding the buttons would push a nine-button row past a 360px viewport and\n * give the whole document a horizontal scrollbar — the opt-out has to carry its\n * own layout, so it does.\n */\nexport function Pagination({\n page,\n totalPages,\n onPageChange,\n pageSize,\n onPageSizeChange,\n pageSizeOptions =
|
|
1
|
+
{"version":3,"file":"Pagination.cjs","names":[],"sources":["../../../src/components/Pagination/Pagination.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — page/totalPages/onPageChange is the pager,\n * pageSize/onPageSizeChange/ pageSizeOptions is the size control that sits in the\n * same bar, and totalItems plus siblingCount decide the copy and how many page\n * buttons are rendered. They ship together because a footer that pages but cannot\n * resize is half a footer.\n */\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Pagination.module.css\";\n\nexport interface PaginationProps {\n page: number;\n totalPages: number;\n onPageChange: (page: number) => void;\n pageSize?: number;\n onPageSizeChange?: (size: number) => void;\n /**\n * Sizes offered in the selector. Default `[10, 25, 50, 100]`.\n *\n * The current `pageSize` is always offered too, merged in ascending order. A\n * `<select>` whose value matches no option displays its first option, so\n * `pageSize={20}` against the default list used to read \"10 / página\" over a\n * table showing 20 rows.\n */\n pageSizeOptions?: number[];\n /** Total items count; if provided, renders the summary text. */\n totalItems?: number;\n /** Pages kept around the current one; ~`siblingCount + 4` buttons render. Default 3. */\n siblingCount?: number;\n /**\n * Below 640px, drop to `‹` / `›` plus the summary and hide the numbered\n * buttons and the size select. Default `true`.\n *\n * The default suits a desktop-first app, and costs the thing pagination is\n * for on a phone: reaching page 7 becomes six taps on \"next\" instead of one\n * tap on `7`. Pass `false` in a mobile-first app and the numbers stay at\n * every width — the row scrolls horizontally by itself rather than pushing\n * the page wider, and the current page is scrolled into view when it\n * changes.\n */\n compactOnMobile?: boolean;\n className?: string;\n}\n\nconst DEFAULT_PAGE_SIZE_OPTIONS: number[] = [10, 25, 50, 100];\n\nfunction buildRange(page: number, totalPages: number, siblings: number): (number | \"...\")[] {\n const totalSlots = siblings + 4;\n if (totalPages <= totalSlots) {\n return Array.from({ length: totalPages }, (_, i) => i + 1);\n }\n const start = Math.max(2, page - Math.floor(siblings / 2));\n const end = Math.min(totalPages - 1, start + siblings - 1);\n const range: (number | \"...\")[] = [1];\n if (start > 2) range.push(\"...\");\n for (let i = start; i <= end; i++) range.push(i);\n if (end < totalPages - 1) range.push(\"...\");\n range.push(totalPages);\n return range;\n}\n\n/**\n * Numeric pagination controls. Pair with {@link usePagination} for state.\n *\n * Narrow screens collapse to `‹` / `›` plus the summary by default, which turns\n * random access into sequential access — fine for a desktop-first app, wrong for\n * one whose traffic is phones. `compactOnMobile={false}` keeps the numbers at\n * every width: the row scrolls on its own instead of widening the page, and the\n * effect below keeps the current page inside the visible part of that row.\n *\n * The scroll is why this is one boolean rather than a `variant`. Simply\n * un-hiding the buttons would push a nine-button row past a 360px viewport and\n * give the whole document a horizontal scrollbar — the opt-out has to carry its\n * own layout, so it does.\n */\nexport function Pagination({\n page,\n totalPages,\n onPageChange,\n pageSize,\n onPageSizeChange,\n pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,\n totalItems,\n siblingCount = 3,\n compactOnMobile = true,\n className,\n}: PaginationProps) {\n const pages = useMemo(\n () => buildRange(page, totalPages, siblingCount),\n [page, totalPages, siblingCount],\n );\n const sizes = useMemo(\n () =>\n pageSize === undefined || pageSizeOptions.includes(pageSize)\n ? pageSizeOptions\n : [...pageSizeOptions, pageSize].sort((a, b) => a - b),\n [pageSize, pageSizeOptions],\n );\n const activePage = useRef<HTMLButtonElement | null>(null);\n\n useEffect(() => {\n if (compactOnMobile) return;\n activePage.current?.scrollIntoView?.({ block: \"nearest\", inline: \"nearest\" });\n }, [page, compactOnMobile]);\n\n if (totalPages <= 1 && !onPageSizeChange) return null;\n\n return (\n <div className={cn(styles.wrapper, className)} data-compact={String(compactOnMobile)}>\n <div className={styles.summary}>\n {typeof totalItems === \"number\"\n ? `${totalItems} resultado${totalItems === 1 ? \"\" : \"s\"}`\n : `Página ${page} de ${totalPages}`}\n </div>\n <div className={styles.controls}>\n <button\n type=\"button\"\n className={styles.page}\n onClick={() => onPageChange(Math.max(1, page - 1))}\n disabled={page <= 1}\n aria-label=\"Página anterior\"\n >\n ‹\n </button>\n {pages.map((entry, index) =>\n entry === \"...\" ? (\n <span\n key={`ellipsis-${index}`}\n className={cn(styles.ellipsis, styles.numeric)}\n >\n …\n </span>\n ) : (\n <button\n type=\"button\"\n key={entry}\n ref={entry === page ? activePage : undefined}\n className={cn(\n styles.page,\n styles.numeric,\n entry === page && styles.active,\n )}\n onClick={() => onPageChange(entry)}\n aria-current={entry === page ? \"page\" : undefined}\n >\n {entry}\n </button>\n ),\n )}\n <button\n type=\"button\"\n className={styles.page}\n onClick={() => onPageChange(Math.min(totalPages, page + 1))}\n disabled={page >= totalPages}\n aria-label=\"Próxima página\"\n >\n ›\n </button>\n {onPageSizeChange && (\n <select\n className={styles.sizeSelect}\n value={pageSize}\n onChange={(event) => onPageSizeChange(Number(event.target.value))}\n aria-label=\"Itens por página\"\n >\n {sizes.map((opt) => (\n <option key={opt} value={opt}>\n {opt} / página\n </option>\n ))}\n </select>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -3,71 +3,72 @@ import t from "./Pagination.module.js";
|
|
|
3
3
|
import { useEffect as n, useMemo as r, useRef as i } from "react";
|
|
4
4
|
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/Pagination/Pagination.tsx
|
|
6
|
-
|
|
6
|
+
var s = [
|
|
7
|
+
10,
|
|
8
|
+
25,
|
|
9
|
+
50,
|
|
10
|
+
100
|
|
11
|
+
];
|
|
12
|
+
function c(e, t, n) {
|
|
7
13
|
if (t <= n + 4) return Array.from({ length: t }, (e, t) => t + 1);
|
|
8
14
|
let r = Math.max(2, e - Math.floor(n / 2)), i = Math.min(t - 1, r + n - 1), a = [1];
|
|
9
15
|
r > 2 && a.push("...");
|
|
10
16
|
for (let e = r; e <= i; e++) a.push(e);
|
|
11
17
|
return i < t - 1 && a.push("..."), a.push(t), a;
|
|
12
18
|
}
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
25,
|
|
16
|
-
50,
|
|
17
|
-
100
|
|
18
|
-
], totalItems: m, siblingCount: h = 3, compactOnMobile: g = !0, className: _ }) {
|
|
19
|
-
let v = r(() => s(c, l, h), [
|
|
20
|
-
c,
|
|
19
|
+
function l({ page: l, totalPages: u, onPageChange: d, pageSize: f, onPageSizeChange: p, pageSizeOptions: m = s, totalItems: h, siblingCount: g = 3, compactOnMobile: _ = !0, className: v }) {
|
|
20
|
+
let y = r(() => c(l, u, g), [
|
|
21
21
|
l,
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
u,
|
|
23
|
+
g
|
|
24
|
+
]), b = r(() => f === void 0 || m.includes(f) ? m : [...m, f].sort((e, t) => e - t), [f, m]), x = i(null);
|
|
24
25
|
return n(() => {
|
|
25
|
-
|
|
26
|
+
_ || x.current?.scrollIntoView?.({
|
|
26
27
|
block: "nearest",
|
|
27
28
|
inline: "nearest"
|
|
28
29
|
});
|
|
29
|
-
}, [
|
|
30
|
-
className: e(t.wrapper,
|
|
31
|
-
"data-compact": String(
|
|
30
|
+
}, [l, _]), u <= 1 && !p ? null : /* @__PURE__ */ o("div", {
|
|
31
|
+
className: e(t.wrapper, v),
|
|
32
|
+
"data-compact": String(_),
|
|
32
33
|
children: [/* @__PURE__ */ a("div", {
|
|
33
34
|
className: t.summary,
|
|
34
|
-
children: typeof
|
|
35
|
+
children: typeof h == "number" ? `${h} resultado${h === 1 ? "" : "s"}` : `Página ${l} de ${u}`
|
|
35
36
|
}), /* @__PURE__ */ o("div", {
|
|
36
37
|
className: t.controls,
|
|
37
38
|
children: [
|
|
38
39
|
/* @__PURE__ */ a("button", {
|
|
39
40
|
type: "button",
|
|
40
41
|
className: t.page,
|
|
41
|
-
onClick: () =>
|
|
42
|
-
disabled:
|
|
42
|
+
onClick: () => d(Math.max(1, l - 1)),
|
|
43
|
+
disabled: l <= 1,
|
|
43
44
|
"aria-label": "Página anterior",
|
|
44
45
|
children: "‹"
|
|
45
46
|
}),
|
|
46
|
-
|
|
47
|
+
y.map((n, r) => n === "..." ? /* @__PURE__ */ a("span", {
|
|
47
48
|
className: e(t.ellipsis, t.numeric),
|
|
48
49
|
children: "…"
|
|
49
50
|
}, `ellipsis-${r}`) : /* @__PURE__ */ a("button", {
|
|
50
51
|
type: "button",
|
|
51
|
-
ref: n ===
|
|
52
|
-
className: e(t.page, t.numeric, n ===
|
|
53
|
-
onClick: () =>
|
|
54
|
-
"aria-current": n ===
|
|
52
|
+
ref: n === l ? x : void 0,
|
|
53
|
+
className: e(t.page, t.numeric, n === l && t.active),
|
|
54
|
+
onClick: () => d(n),
|
|
55
|
+
"aria-current": n === l ? "page" : void 0,
|
|
55
56
|
children: n
|
|
56
57
|
}, n)),
|
|
57
58
|
/* @__PURE__ */ a("button", {
|
|
58
59
|
type: "button",
|
|
59
60
|
className: t.page,
|
|
60
|
-
onClick: () =>
|
|
61
|
-
disabled:
|
|
61
|
+
onClick: () => d(Math.min(u, l + 1)),
|
|
62
|
+
disabled: l >= u,
|
|
62
63
|
"aria-label": "Próxima página",
|
|
63
64
|
children: "›"
|
|
64
65
|
}),
|
|
65
|
-
|
|
66
|
+
p && /* @__PURE__ */ a("select", {
|
|
66
67
|
className: t.sizeSelect,
|
|
67
|
-
value:
|
|
68
|
-
onChange: (e) =>
|
|
68
|
+
value: f,
|
|
69
|
+
onChange: (e) => p(Number(e.target.value)),
|
|
69
70
|
"aria-label": "Itens por página",
|
|
70
|
-
children:
|
|
71
|
+
children: b.map((e) => /* @__PURE__ */ o("option", {
|
|
71
72
|
value: e,
|
|
72
73
|
children: [e, " / página"]
|
|
73
74
|
}, e))
|
|
@@ -77,6 +78,6 @@ function c({ page: c, totalPages: l, onPageChange: u, pageSize: d, onPageSizeCha
|
|
|
77
78
|
});
|
|
78
79
|
}
|
|
79
80
|
//#endregion
|
|
80
|
-
export {
|
|
81
|
+
export { l as Pagination };
|
|
81
82
|
|
|
82
83
|
//# sourceMappingURL=Pagination.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pagination.js","names":[],"sources":["../../../src/components/Pagination/Pagination.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — page/totalPages/onPageChange is the pager,\n * pageSize/onPageSizeChange/ pageSizeOptions is the size control that sits in the\n * same bar, and totalItems plus siblingCount decide the copy and how many page\n * buttons are rendered. They ship together because a footer that pages but cannot\n * resize is half a footer.\n */\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Pagination.module.css\";\n\nexport interface PaginationProps {\n page: number;\n totalPages: number;\n onPageChange: (page: number) => void;\n pageSize?: number;\n onPageSizeChange?: (size: number) => void;\n pageSizeOptions?: number[];\n /** Total items count; if provided, renders the summary text. */\n totalItems?: number;\n /** Pages kept around the current one; ~`siblingCount + 4` buttons render. Default 3. */\n siblingCount?: number;\n /**\n * Below 640px, drop to `‹` / `›` plus the summary and hide the numbered\n * buttons and the size select. Default `true`.\n *\n * The default suits a desktop-first app, and costs the thing pagination is\n * for on a phone: reaching page 7 becomes six taps on \"next\" instead of one\n * tap on `7`. Pass `false` in a mobile-first app and the numbers stay at\n * every width — the row scrolls horizontally by itself rather than pushing\n * the page wider, and the current page is scrolled into view when it\n * changes.\n */\n compactOnMobile?: boolean;\n className?: string;\n}\n\nfunction buildRange(page: number, totalPages: number, siblings: number): (number | \"...\")[] {\n const totalSlots = siblings + 4;\n if (totalPages <= totalSlots) {\n return Array.from({ length: totalPages }, (_, i) => i + 1);\n }\n const start = Math.max(2, page - Math.floor(siblings / 2));\n const end = Math.min(totalPages - 1, start + siblings - 1);\n const range: (number | \"...\")[] = [1];\n if (start > 2) range.push(\"...\");\n for (let i = start; i <= end; i++) range.push(i);\n if (end < totalPages - 1) range.push(\"...\");\n range.push(totalPages);\n return range;\n}\n\n/**\n * Numeric pagination controls. Pair with {@link usePagination} for state.\n *\n * Narrow screens collapse to `‹` / `›` plus the summary by default, which turns\n * random access into sequential access — fine for a desktop-first app, wrong for\n * one whose traffic is phones. `compactOnMobile={false}` keeps the numbers at\n * every width: the row scrolls on its own instead of widening the page, and the\n * effect below keeps the current page inside the visible part of that row.\n *\n * The scroll is why this is one boolean rather than a `variant`. Simply\n * un-hiding the buttons would push a nine-button row past a 360px viewport and\n * give the whole document a horizontal scrollbar — the opt-out has to carry its\n * own layout, so it does.\n */\nexport function Pagination({\n page,\n totalPages,\n onPageChange,\n pageSize,\n onPageSizeChange,\n pageSizeOptions =
|
|
1
|
+
{"version":3,"file":"Pagination.js","names":[],"sources":["../../../src/components/Pagination/Pagination.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — page/totalPages/onPageChange is the pager,\n * pageSize/onPageSizeChange/ pageSizeOptions is the size control that sits in the\n * same bar, and totalItems plus siblingCount decide the copy and how many page\n * buttons are rendered. They ship together because a footer that pages but cannot\n * resize is half a footer.\n */\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Pagination.module.css\";\n\nexport interface PaginationProps {\n page: number;\n totalPages: number;\n onPageChange: (page: number) => void;\n pageSize?: number;\n onPageSizeChange?: (size: number) => void;\n /**\n * Sizes offered in the selector. Default `[10, 25, 50, 100]`.\n *\n * The current `pageSize` is always offered too, merged in ascending order. A\n * `<select>` whose value matches no option displays its first option, so\n * `pageSize={20}` against the default list used to read \"10 / página\" over a\n * table showing 20 rows.\n */\n pageSizeOptions?: number[];\n /** Total items count; if provided, renders the summary text. */\n totalItems?: number;\n /** Pages kept around the current one; ~`siblingCount + 4` buttons render. Default 3. */\n siblingCount?: number;\n /**\n * Below 640px, drop to `‹` / `›` plus the summary and hide the numbered\n * buttons and the size select. Default `true`.\n *\n * The default suits a desktop-first app, and costs the thing pagination is\n * for on a phone: reaching page 7 becomes six taps on \"next\" instead of one\n * tap on `7`. Pass `false` in a mobile-first app and the numbers stay at\n * every width — the row scrolls horizontally by itself rather than pushing\n * the page wider, and the current page is scrolled into view when it\n * changes.\n */\n compactOnMobile?: boolean;\n className?: string;\n}\n\nconst DEFAULT_PAGE_SIZE_OPTIONS: number[] = [10, 25, 50, 100];\n\nfunction buildRange(page: number, totalPages: number, siblings: number): (number | \"...\")[] {\n const totalSlots = siblings + 4;\n if (totalPages <= totalSlots) {\n return Array.from({ length: totalPages }, (_, i) => i + 1);\n }\n const start = Math.max(2, page - Math.floor(siblings / 2));\n const end = Math.min(totalPages - 1, start + siblings - 1);\n const range: (number | \"...\")[] = [1];\n if (start > 2) range.push(\"...\");\n for (let i = start; i <= end; i++) range.push(i);\n if (end < totalPages - 1) range.push(\"...\");\n range.push(totalPages);\n return range;\n}\n\n/**\n * Numeric pagination controls. Pair with {@link usePagination} for state.\n *\n * Narrow screens collapse to `‹` / `›` plus the summary by default, which turns\n * random access into sequential access — fine for a desktop-first app, wrong for\n * one whose traffic is phones. `compactOnMobile={false}` keeps the numbers at\n * every width: the row scrolls on its own instead of widening the page, and the\n * effect below keeps the current page inside the visible part of that row.\n *\n * The scroll is why this is one boolean rather than a `variant`. Simply\n * un-hiding the buttons would push a nine-button row past a 360px viewport and\n * give the whole document a horizontal scrollbar — the opt-out has to carry its\n * own layout, so it does.\n */\nexport function Pagination({\n page,\n totalPages,\n onPageChange,\n pageSize,\n onPageSizeChange,\n pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,\n totalItems,\n siblingCount = 3,\n compactOnMobile = true,\n className,\n}: PaginationProps) {\n const pages = useMemo(\n () => buildRange(page, totalPages, siblingCount),\n [page, totalPages, siblingCount],\n );\n const sizes = useMemo(\n () =>\n pageSize === undefined || pageSizeOptions.includes(pageSize)\n ? pageSizeOptions\n : [...pageSizeOptions, pageSize].sort((a, b) => a - b),\n [pageSize, pageSizeOptions],\n );\n const activePage = useRef<HTMLButtonElement | null>(null);\n\n useEffect(() => {\n if (compactOnMobile) return;\n activePage.current?.scrollIntoView?.({ block: \"nearest\", inline: \"nearest\" });\n }, [page, compactOnMobile]);\n\n if (totalPages <= 1 && !onPageSizeChange) return null;\n\n return (\n <div className={cn(styles.wrapper, className)} data-compact={String(compactOnMobile)}>\n <div className={styles.summary}>\n {typeof totalItems === \"number\"\n ? `${totalItems} resultado${totalItems === 1 ? \"\" : \"s\"}`\n : `Página ${page} de ${totalPages}`}\n </div>\n <div className={styles.controls}>\n <button\n type=\"button\"\n className={styles.page}\n onClick={() => onPageChange(Math.max(1, page - 1))}\n disabled={page <= 1}\n aria-label=\"Página anterior\"\n >\n ‹\n </button>\n {pages.map((entry, index) =>\n entry === \"...\" ? (\n <span\n key={`ellipsis-${index}`}\n className={cn(styles.ellipsis, styles.numeric)}\n >\n …\n </span>\n ) : (\n <button\n type=\"button\"\n key={entry}\n ref={entry === page ? activePage : undefined}\n className={cn(\n styles.page,\n styles.numeric,\n entry === page && styles.active,\n )}\n onClick={() => onPageChange(entry)}\n aria-current={entry === page ? \"page\" : undefined}\n >\n {entry}\n </button>\n ),\n )}\n <button\n type=\"button\"\n className={styles.page}\n onClick={() => onPageChange(Math.min(totalPages, page + 1))}\n disabled={page >= totalPages}\n aria-label=\"Próxima página\"\n >\n ›\n </button>\n {onPageSizeChange && (\n <select\n className={styles.sizeSelect}\n value={pageSize}\n onChange={(event) => onPageSizeChange(Number(event.target.value))}\n aria-label=\"Itens por página\"\n >\n {sizes.map((opt) => (\n <option key={opt} value={opt}>\n {opt} / página\n </option>\n ))}\n </select>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AA6CA,IAAM,IAAsC;CAAC;CAAI;CAAI;CAAI;AAAG;AAE5D,SAAS,EAAW,GAAc,GAAoB,GAAsC;CAExF,IAAI,KADe,IAAW,GAE1B,OAAO,MAAM,KAAK,EAAE,QAAQ,EAAW,IAAI,GAAG,MAAM,IAAI,CAAC;CAE7D,IAAM,IAAQ,KAAK,IAAI,GAAG,IAAO,KAAK,MAAM,IAAW,CAAC,CAAC,GACnD,IAAM,KAAK,IAAI,IAAa,GAAG,IAAQ,IAAW,CAAC,GACnD,IAA4B,CAAC,CAAC;CACpC,AAAI,IAAQ,KAAG,EAAM,KAAK,KAAK;CAC/B,KAAK,IAAI,IAAI,GAAO,KAAK,GAAK,KAAK,EAAM,KAAK,CAAC;CAG/C,OAFI,IAAM,IAAa,KAAG,EAAM,KAAK,KAAK,GAC1C,EAAM,KAAK,CAAU,GACd;AACX;AAgBA,SAAgB,EAAW,EACvB,SACA,eACA,iBACA,aACA,qBACA,qBAAkB,GAClB,eACA,kBAAe,GACf,qBAAkB,IAClB,gBACgB;CAChB,IAAM,IAAQ,QACJ,EAAW,GAAM,GAAY,CAAY,GAC/C;EAAC;EAAM;EAAY;CAAY,CACnC,GACM,IAAQ,QAEN,MAAa,KAAA,KAAa,EAAgB,SAAS,CAAQ,IACrD,IACA,CAAC,GAAG,GAAiB,CAAQ,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC,GAC7D,CAAC,GAAU,CAAe,CAC9B,GACM,IAAa,EAAiC,IAAI;CASxD,OAPA,QAAgB;EACR,KACJ,EAAW,SAAS,iBAAiB;GAAE,OAAO;GAAW,QAAQ;EAAU,CAAC;CAChF,GAAG,CAAC,GAAM,CAAe,CAAC,GAEtB,KAAc,KAAK,CAAC,IAAyB,OAG7C,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,SAAS,CAAS;EAAG,gBAAc,OAAO,CAAe;EAAnF,UAAA,CACI,kBAAC,OAAD;GAAK,WAAW,EAAO;GAClB,UAAA,OAAO,KAAe,WACjB,GAAG,EAAW,YAAY,MAAe,IAAI,KAAK,QAClD,UAAU,EAAK,MAAM;EAC1B,CAAA,GACL,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAvB,UAAA;IACI,kBAAC,UAAD;KACI,MAAK;KACL,WAAW,EAAO;KAClB,eAAe,EAAa,KAAK,IAAI,GAAG,IAAO,CAAC,CAAC;KACjD,UAAU,KAAQ;KAClB,cAAW;KACd,UAAA;IAEO,CAAA;IACP,EAAM,KAAK,GAAO,MACf,MAAU,QACN,kBAAC,QAAD;KAEI,WAAW,EAAG,EAAO,UAAU,EAAO,OAAO;KAChD,UAAA;IAEK,GAJG,YAAY,GAIf,IAEN,kBAAC,UAAD;KACI,MAAK;KAEL,KAAK,MAAU,IAAO,IAAa,KAAA;KACnC,WAAW,EACP,EAAO,MACP,EAAO,SACP,MAAU,KAAQ,EAAO,MAC7B;KACA,eAAe,EAAa,CAAK;KACjC,gBAAc,MAAU,IAAO,SAAS,KAAA;KAEvC,UAAA;IACG,GAXC,CAWD,CAEhB;IACA,kBAAC,UAAD;KACI,MAAK;KACL,WAAW,EAAO;KAClB,eAAe,EAAa,KAAK,IAAI,GAAY,IAAO,CAAC,CAAC;KAC1D,UAAU,KAAQ;KAClB,cAAW;KACd,UAAA;IAEO,CAAA;IACP,KACG,kBAAC,UAAD;KACI,WAAW,EAAO;KAClB,OAAO;KACP,WAAW,MAAU,EAAiB,OAAO,EAAM,OAAO,KAAK,CAAC;KAChE,cAAW;KAEV,UAAA,EAAM,KAAK,MACR,kBAAC,UAAD;MAAkB,OAAO;MAAzB,UAAA,CACK,GAAI,WACD;KAFK,GAAA,CAEL,CACX;IACG,CAAA;GAEX;EACJ,CAAA,CAAA;;AAEb"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("./Popover.module.cjs");let
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../Portal/escape-layer.cjs"),n=require("../Portal/Portal.cjs"),r=require("../Portal/anchor-position.cjs"),i=require("../Portal/tab-bridge.cjs"),a=require("./Popover.module.cjs");let o=require("react"),s=require("react/jsx-runtime");var c=8;function l({trigger:l,children:u,placement:d=`bottom`,portal:f=!0,open:p,onOpenChange:m,defaultOpen:h=!1,closeOnEsc:g=!0,closeOnOutsideClick:_=!0,className:v}){let y=p!==void 0,[b,x]=(0,o.useState)(h),S=y?p:b,C=(0,o.useId)(),[w,T]=(0,o.useState)(null),[E,D]=(0,o.useState)(null),O=r.useAnchorPosition({anchor:w,floating:E,side:d,align:`center`,offset:c,enabled:f&&S});i.usePortalTabOrder({enabled:f&&S,anchor:w,panel:E});let k=(0,o.useCallback)(e=>{y||x(e),m?.(e)},[y,m]);t.useEscapeLayer(S&&g,()=>k(!1)),(0,o.useEffect)(()=>{if(!S)return;let e=e=>{if(!_)return;let t=e.target;w?.contains(t)||E?.contains(t)||k(!1)};return window.addEventListener(`mousedown`,e),()=>{window.removeEventListener(`mousedown`,e)}},[S,_,k,w,E]);let A=e=>{l.props.onClick?.(e),k(!S)},j={...l,props:{...l.props,onClick:A,"aria-expanded":S,"aria-controls":C}},M=(0,s.jsx)(`div`,{ref:D,id:C,role:`dialog`,className:e.cn(a.default.popover,f?a.default.portalled:a.default[d],v),style:O,children:u});return(0,s.jsxs)(`span`,{ref:T,className:a.default.root,children:[j,S&&(f?(0,s.jsx)(n.Portal,{children:M}):M)]})}exports.Popover=l;
|
|
2
2
|
//# sourceMappingURL=Popover.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Popover.cjs","names":[],"sources":["../../../src/components/Popover/Popover.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — trigger and children are the two halves, placement\n * is where it lands, and open/onOpenChange/defaultOpen is the standard controlled-\n * or-not pair. closeOnEsc and closeOnOutsideClick are separate because a popover\n * holding a form wants one and not the other.\n */\nimport { useCallback, useEffect, useId,
|
|
1
|
+
{"version":3,"file":"Popover.cjs","names":[],"sources":["../../../src/components/Popover/Popover.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — trigger and children are the two halves, placement\n * is where it lands, portal is whether it escapes the tree, and open/onOpenChange/defaultOpen is the standard controlled-\n * or-not pair. closeOnEsc and closeOnOutsideClick are separate because a popover\n * holding a form wants one and not the other.\n */\nimport { useCallback, useEffect, useId, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { useEscapeLayer } from \"@/components/Portal/escape-layer\";\nimport { usePortalTabOrder } from \"@/components/Portal/tab-bridge\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Popover.module.css\";\n\nexport type PopoverPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\n\nexport interface PopoverProps {\n /** Trigger element. Receives `onClick`/`aria-expanded`/`aria-controls`. */\n trigger: ReactElement<{\n onClick?: (e: React.MouseEvent) => void;\n \"aria-expanded\"?: boolean;\n \"aria-controls\"?: string;\n }>;\n children: ReactNode;\n placement?: PopoverPlacement;\n /**\n * Render the panel in a portal, positioned against the trigger. Default `true`.\n *\n * In flow, any ancestor with `overflow` other than `visible` clips the panel —\n * inside a `DataTable` cell only 3% of it was visible (measured in Chrome at\n * 1440 px). In a portal it escapes the clip, flips to the opposite side when\n * the requested one has no room, and follows the trigger on scroll and resize.\n * `false` keeps the in-flow panel.\n */\n portal?: boolean;\n /** Controlled open state. */\n open?: boolean;\n /** Called when the user toggles or dismisses. */\n onOpenChange?: (open: boolean) => void;\n /** Default open state for uncontrolled usage. */\n defaultOpen?: boolean;\n /** Close on Escape. Default true. */\n closeOnEsc?: boolean;\n /** Close on outside click. Default true. */\n closeOnOutsideClick?: boolean;\n className?: string;\n}\n\n/** Gap between trigger and panel, in px — the in-flow CSS uses the same 8 px. */\nconst POPOVER_OFFSET = 8;\n\n/**\n * Lightweight popover. Renders a panel anchored to a trigger, dismissed on\n * outside click / Escape.\n *\n * Portalled by default, with viewport-aware placement: the panel flips to the\n * opposite side when the requested one overflows, and is clamped inside the\n * viewport otherwise.\n */\nexport function Popover({\n trigger,\n children,\n placement = \"bottom\",\n portal = true,\n open,\n onOpenChange,\n defaultOpen = false,\n closeOnEsc = true,\n closeOnOutsideClick = true,\n className,\n}: PopoverProps) {\n const isControlled = open !== undefined;\n const [internalOpen, setInternalOpen] = useState<boolean>(defaultOpen);\n const isOpen = isControlled ? open : internalOpen;\n const id = useId();\n const [rootNode, setRootNode] = useState<HTMLSpanElement | null>(null);\n const [panelNode, setPanelNode] = useState<HTMLDivElement | null>(null);\n const floatingStyle = useAnchorPosition({\n anchor: rootNode,\n floating: panelNode,\n side: placement,\n align: \"center\",\n offset: POPOVER_OFFSET,\n enabled: portal && isOpen,\n });\n usePortalTabOrder({ enabled: portal && isOpen, anchor: rootNode, panel: panelNode });\n\n const setOpen = useCallback(\n (next: boolean): void => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n\n useEscapeLayer(isOpen && closeOnEsc, () => setOpen(false));\n\n /**\n * Dismiss on a press outside both trigger and panel.\n *\n * The panel is checked on its own because in a portal it is not inside the\n * trigger's wrapper, and a press inside it — on a form field, say — would\n * otherwise count as outside.\n */\n useEffect(() => {\n if (!isOpen) return;\n const onDown = (event: MouseEvent): void => {\n if (!closeOnOutsideClick) return;\n const target = event.target as Node;\n if (rootNode?.contains(target) || panelNode?.contains(target)) return;\n setOpen(false);\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [isOpen, closeOnOutsideClick, setOpen, rootNode, panelNode]);\n\n const handleTriggerClick = (event: React.MouseEvent): void => {\n trigger.props.onClick?.(event);\n setOpen(!isOpen);\n };\n\n const triggerClone = {\n ...trigger,\n props: {\n ...trigger.props,\n onClick: handleTriggerClick,\n \"aria-expanded\": isOpen,\n \"aria-controls\": id,\n },\n } as ReactElement;\n\n const panel = (\n <div\n ref={setPanelNode}\n id={id}\n role=\"dialog\"\n className={cn(styles.popover, portal ? styles.portalled : styles[placement], className)}\n style={floatingStyle}\n >\n {children}\n </div>\n );\n\n return (\n <span ref={setRootNode} className={styles.root}>\n {triggerClone}\n {isOpen && (portal ? <Portal>{panel}</Portal> : panel)}\n </span>\n );\n}\n"],"mappings":"yRAkDA,IAAM,EAAiB,EAUvB,SAAgB,EAAQ,CACpB,UACA,WACA,YAAY,SACZ,SAAS,GACT,OACA,eACA,cAAc,GACd,aAAa,GACb,sBAAsB,GACtB,aACa,CACb,IAAM,EAAe,IAAS,IAAA,GACxB,CAAC,EAAc,IAAA,EAAmB,EAAA,SAAA,CAAkB,CAAW,EAC/D,EAAS,EAAe,EAAO,EAC/B,GAAA,EAAK,EAAA,MAAA,CAAM,EACX,CAAC,EAAU,IAAA,EAAe,EAAA,SAAA,CAAiC,IAAI,EAC/D,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAAgC,IAAI,EAChE,EAAgB,EAAA,kBAAkB,CACpC,OAAQ,EACR,SAAU,EACV,KAAM,EACN,MAAO,SACP,OAAQ,EACR,QAAS,GAAU,CACvB,CAAC,EACD,EAAA,kBAAkB,CAAE,QAAS,GAAU,EAAQ,OAAQ,EAAU,MAAO,CAAU,CAAC,EAEnF,IAAM,GAAA,EAAU,EAAA,YAAA,CACX,GAAwB,CAChB,GAAc,EAAgB,CAAI,EACvC,IAAe,CAAI,CACvB,EACA,CAAC,EAAc,CAAY,CAC/B,EAEA,EAAA,eAAe,GAAU,MAAkB,EAAQ,EAAK,CAAC,GASzD,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAQ,OACb,IAAM,EAAU,GAA4B,CACxC,GAAI,CAAC,EAAqB,OAC1B,IAAM,EAAS,EAAM,OACjB,GAAU,SAAS,CAAM,GAAK,GAAW,SAAS,CAAM,GAC5D,EAAQ,EAAK,CACjB,EAEA,OADA,OAAO,iBAAiB,YAAa,CAAM,MAC9B,CACT,OAAO,oBAAoB,YAAa,CAAM,CAClD,CACJ,EAAG,CAAC,EAAQ,EAAqB,EAAS,EAAU,CAAS,CAAC,EAE9D,IAAM,EAAsB,GAAkC,CAC1D,EAAQ,MAAM,UAAU,CAAK,EAC7B,EAAQ,CAAC,CAAM,CACnB,EAEM,EAAe,CACjB,GAAG,EACH,MAAO,CACH,GAAG,EAAQ,MACX,QAAS,EACT,gBAAiB,EACjB,gBAAiB,CACrB,CACJ,EAEM,GACF,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,IAAK,EACD,KACJ,KAAK,SACL,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,EAAS,EAAA,QAAO,UAAY,EAAA,QAAO,GAAY,CAAS,EACtF,MAAO,EAEN,UACA,CAAA,EAGT,OACI,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,IAAK,EAAa,UAAW,EAAA,QAAO,KAA1C,SAAA,CACK,EACA,IAAW,GAAS,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAA,SAAS,CAAc,CAAA,EAAI,EAC9C,GAEd"}
|