@hyperpackai/hyperui 0.2.0 → 0.3.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 +13 -0
- package/dist/components/Accordion/index.d.ts +6 -0
- package/dist/components/Accordion/index.d.ts.map +1 -1
- package/dist/components/Accordion/index.js +65 -9
- package/dist/components/Autocomplete/index.d.ts +12 -2
- package/dist/components/Autocomplete/index.d.ts.map +1 -1
- package/dist/components/Autocomplete/index.js +148 -24
- package/dist/components/Backdrop/index.d.ts +2 -1
- package/dist/components/Backdrop/index.d.ts.map +1 -1
- package/dist/components/Backdrop/index.js +6 -3
- package/dist/components/Checkbox/index.d.ts +1 -0
- package/dist/components/Checkbox/index.d.ts.map +1 -1
- package/dist/components/Checkbox/index.js +6 -2
- package/dist/components/DashboardLayout/index.d.ts +13 -0
- package/dist/components/DashboardLayout/index.d.ts.map +1 -1
- package/dist/components/DashboardLayout/index.js +50 -7
- package/dist/components/DataTable/index.d.ts +43 -0
- package/dist/components/DataTable/index.d.ts.map +1 -1
- package/dist/components/DataTable/index.js +126 -21
- package/dist/components/Dialog/index.d.ts +9 -3
- package/dist/components/Dialog/index.d.ts.map +1 -1
- package/dist/components/Dialog/index.js +46 -30
- package/dist/components/Drawer/index.d.ts +11 -3
- package/dist/components/Drawer/index.d.ts.map +1 -1
- package/dist/components/Drawer/index.js +66 -11
- package/dist/components/DropdownMenu/index.d.ts +5 -3
- package/dist/components/DropdownMenu/index.d.ts.map +1 -1
- package/dist/components/DropdownMenu/index.js +56 -13
- package/dist/components/FocusTrap/index.d.ts.map +1 -1
- package/dist/components/FocusTrap/index.js +34 -32
- package/dist/components/Input/index.d.ts +2 -0
- package/dist/components/Input/index.d.ts.map +1 -1
- package/dist/components/Input/index.js +18 -4
- package/dist/components/Menu/index.d.ts +6 -2
- package/dist/components/Menu/index.d.ts.map +1 -1
- package/dist/components/Menu/index.js +50 -15
- package/dist/components/Modal/index.d.ts +3 -1
- package/dist/components/Modal/index.d.ts.map +1 -1
- package/dist/components/Modal/index.js +27 -9
- package/dist/components/NestedNavbar/index.d.ts +33 -0
- package/dist/components/NestedNavbar/index.d.ts.map +1 -0
- package/dist/components/NestedNavbar/index.js +435 -0
- package/dist/components/NestedSidebar/index.d.ts +48 -0
- package/dist/components/NestedSidebar/index.d.ts.map +1 -0
- package/dist/components/NestedSidebar/index.js +368 -0
- package/dist/components/Popover/index.d.ts +11 -3
- package/dist/components/Popover/index.d.ts.map +1 -1
- package/dist/components/Popover/index.js +45 -9
- package/dist/components/Radio/index.d.ts +26 -1
- package/dist/components/Radio/index.d.ts.map +1 -1
- package/dist/components/Radio/index.js +61 -2
- package/dist/components/Select/index.d.ts +5 -0
- package/dist/components/Select/index.d.ts.map +1 -1
- package/dist/components/Select/index.js +22 -5
- package/dist/components/Sheet/index.d.ts +9 -3
- package/dist/components/Sheet/index.d.ts.map +1 -1
- package/dist/components/Sheet/index.js +48 -23
- package/dist/components/Sidebar/index.d.ts +20 -1
- package/dist/components/Sidebar/index.d.ts.map +1 -1
- package/dist/components/Sidebar/index.js +285 -8
- package/dist/components/SpeedDial/index.d.ts +10 -0
- package/dist/components/SpeedDial/index.d.ts.map +1 -1
- package/dist/components/SpeedDial/index.js +61 -11
- package/dist/components/Switch/index.d.ts +2 -0
- package/dist/components/Switch/index.d.ts.map +1 -1
- package/dist/components/Switch/index.js +6 -2
- package/dist/components/Tabs/index.d.ts +3 -0
- package/dist/components/Tabs/index.d.ts.map +1 -1
- package/dist/components/Tabs/index.js +47 -8
- package/dist/components/TextField/index.d.ts +2 -0
- package/dist/components/TextField/index.d.ts.map +1 -1
- package/dist/components/TextField/index.js +12 -4
- package/dist/components/Textarea/index.d.ts +5 -0
- package/dist/components/Textarea/index.d.ts.map +1 -1
- package/dist/components/Textarea/index.js +21 -4
- package/dist/components/Transition/index.d.ts +14 -0
- package/dist/components/Transition/index.d.ts.map +1 -0
- package/dist/components/Transition/index.js +49 -0
- package/dist/components/TransitionGroup/index.d.ts +16 -0
- package/dist/components/TransitionGroup/index.d.ts.map +1 -0
- package/dist/components/TransitionGroup/index.js +95 -0
- package/dist/components/data.d.ts +81 -16
- package/dist/components/data.d.ts.map +1 -1
- package/dist/components/data.js +163 -31
- package/dist/components/enterprise.d.ts +85 -26
- package/dist/components/enterprise.d.ts.map +1 -1
- package/dist/components/enterprise.js +211 -36
- package/dist/components/index.d.ts +21 -13
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +7 -2
- package/dist/portal.d.ts.map +1 -1
- package/dist/portal.js +3 -0
- package/dist/theme/index.d.ts +5 -6
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +30 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +11 -0
- package/package.json +6 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,13 @@ const TABLE_CSS = `
|
|
|
5
5
|
border: 1px solid color-mix(in srgb, var(--hu-border) 82%, transparent);
|
|
6
6
|
background: var(--hu-bg); box-shadow: var(--hu-shadow-sm);
|
|
7
7
|
}
|
|
8
|
+
.hu-table-wrap--sticky { max-height: var(--hu-table-max-height, 560px); }
|
|
8
9
|
.hu-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--hu-font-size-sm); min-width: 560px; }
|
|
10
|
+
.hu-table-caption {
|
|
11
|
+
caption-side: top; text-align: left; padding: var(--hu-space-3) var(--hu-space-4);
|
|
12
|
+
font-size: var(--hu-font-size-sm); font-weight: var(--hu-font-weight-semibold); color: var(--hu-text);
|
|
13
|
+
background: var(--hu-bg); border-bottom: 1px solid var(--hu-border);
|
|
14
|
+
}
|
|
9
15
|
.hu-table th {
|
|
10
16
|
padding: 10px 16px; text-align: left; font-size: 11px; font-weight: var(--hu-font-weight-semibold);
|
|
11
17
|
color: var(--hu-text-3); text-transform: uppercase; letter-spacing: .05em;
|
|
@@ -13,47 +19,146 @@ const TABLE_CSS = `
|
|
|
13
19
|
border-bottom: 1px solid var(--hu-border);
|
|
14
20
|
white-space: nowrap;
|
|
15
21
|
}
|
|
22
|
+
.hu-table--sticky-header thead th { position: sticky; top: 0; z-index: 1; }
|
|
16
23
|
.hu-table th.hu-table-sortable { cursor: pointer; user-select: none; }
|
|
17
24
|
.hu-table th.hu-table-sortable:hover { background: var(--hu-bg-3); color: var(--hu-text); }
|
|
25
|
+
.hu-table th.hu-table-sortable:focus-visible, .hu-table tbody tr[tabindex]:focus-visible { outline: 2px solid var(--hu-primary); outline-offset: -2px; }
|
|
18
26
|
.hu-table-sort-icon { display: inline-flex; align-items: center; margin-left: 4px; opacity: .4; vertical-align: middle; }
|
|
19
27
|
.hu-table-sort-icon--asc, .hu-table-sort-icon--desc { opacity: 1; color: var(--hu-primary); }
|
|
20
28
|
.hu-table td { padding: 12px 16px; border-bottom: 1px solid var(--hu-border); color: var(--hu-text); vertical-align: middle; background: var(--hu-bg); }
|
|
21
29
|
.hu-table tr:last-child td { border-bottom: none; }
|
|
22
30
|
.hu-table tbody tr { transition: background var(--hu-duration) var(--hu-ease); }
|
|
23
31
|
.hu-table tbody tr:hover td { background: color-mix(in srgb, var(--hu-primary-bg) 28%, var(--hu-bg-2)); }
|
|
32
|
+
.hu-table tbody tr[aria-selected="true"] td { background: color-mix(in srgb, var(--hu-primary-bg) 45%, var(--hu-bg)); }
|
|
33
|
+
.hu-table tbody tr[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }
|
|
34
|
+
.hu-table tbody tr[aria-disabled="true"]:hover td { background: var(--hu-bg); }
|
|
24
35
|
.hu-table-empty { text-align: center; padding: var(--hu-space-12) var(--hu-space-6); color: var(--hu-text-3); }
|
|
25
36
|
.hu-table-footer { display: flex; align-items: center; justify-content: space-between; gap: var(--hu-space-3); flex-wrap: wrap; padding: var(--hu-space-3) var(--hu-space-4); border-top: 1px solid var(--hu-border); background: var(--hu-bg-2); font-size: var(--hu-font-size-xs); color: var(--hu-text-3); }
|
|
26
37
|
.hu-table-checkbox-col { width: 40px; padding-left: 16px !important; }
|
|
27
38
|
.hu-table input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--hu-primary); }
|
|
39
|
+
.hu-table--compact th { padding: 7px 12px; }
|
|
40
|
+
.hu-table--compact td { padding: 8px 12px; }
|
|
41
|
+
.hu-table--comfortable th { padding: 13px 18px; }
|
|
42
|
+
.hu-table--comfortable td { padding: 16px 18px; }
|
|
28
43
|
`;
|
|
44
|
+
function toCssUnit(value) {
|
|
45
|
+
return typeof value === "number" ? `${value}px` : value;
|
|
46
|
+
}
|
|
29
47
|
export function DataTable(props) {
|
|
30
48
|
injectCSS("hu-table", TABLE_CSS);
|
|
31
|
-
const { columns, rows, rowKey, sortColumn, sortDir, onSort, onRowClick, emptyMessage = "No data available.", footer, selectable, selectedRows, onSelectRow, onSelectAll } = props;
|
|
32
|
-
const
|
|
33
|
-
|
|
49
|
+
const { columns, rows, rowKey, sortColumn, sortDir, onSort, onRowClick, emptyMessage = "No data available.", emptyState, loading, loadingState, error, errorState, footer, selectable, selectedRows, onSelectRow, onSelectAll, density = "default" } = props;
|
|
50
|
+
const rowId = (row, index) => rowKey?.(row) ?? String(row["id"] ?? index);
|
|
51
|
+
const rowKeys = rows.map(rowId);
|
|
52
|
+
const stateColSpan = columns.length + (selectable ? 1 : 0);
|
|
53
|
+
const sortDirectionFor = (key) => sortColumn === key && sortDir === "asc" ? "desc" : "asc";
|
|
54
|
+
const handleSort = (key) => {
|
|
55
|
+
const direction = sortDirectionFor(key);
|
|
56
|
+
onSort?.(key);
|
|
57
|
+
props.onSortChange?.({ key, direction });
|
|
58
|
+
};
|
|
59
|
+
const handleSortKeyDown = (event, key) => {
|
|
60
|
+
if (event.key !== "Enter" && event.key !== " ")
|
|
61
|
+
return;
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
handleSort(key);
|
|
64
|
+
};
|
|
65
|
+
const handleRowAction = (row, key, index) => {
|
|
66
|
+
if (props.isRowDisabled?.(row, index))
|
|
67
|
+
return;
|
|
68
|
+
onRowClick?.(row);
|
|
69
|
+
props.onRowAction?.({ row, key, index });
|
|
70
|
+
};
|
|
71
|
+
const handleRowKeyDown = (event, row, key, index) => {
|
|
72
|
+
if (event.key !== "Enter" && event.key !== " ")
|
|
73
|
+
return;
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
handleRowAction(row, key, index);
|
|
76
|
+
};
|
|
77
|
+
const handleSelectAll = (selected) => {
|
|
78
|
+
onSelectAll?.(selected);
|
|
79
|
+
props.onSelectAllChange?.({ selected, keys: rowKeys, rows });
|
|
80
|
+
};
|
|
81
|
+
const handleSelectRow = (row, key, index, selected) => {
|
|
82
|
+
if (props.isRowDisabled?.(row, index))
|
|
83
|
+
return;
|
|
84
|
+
onSelectRow?.(key, selected);
|
|
85
|
+
props.onSelectionChange?.({ row, key, selected });
|
|
86
|
+
};
|
|
87
|
+
const sortIcon = (dir) => h("svg", {
|
|
88
|
+
width: "12",
|
|
89
|
+
height: "12",
|
|
90
|
+
viewBox: "0 0 12 12",
|
|
91
|
+
fill: "currentColor",
|
|
92
|
+
"aria-hidden": "true"
|
|
93
|
+
}, dir === "asc"
|
|
94
|
+
? h("path", { d: "M6 3l4 5H2l4-5z" })
|
|
34
95
|
: dir === "desc"
|
|
35
|
-
?
|
|
36
|
-
:
|
|
37
|
-
const allSelected = rows.length > 0 &&
|
|
38
|
-
|
|
96
|
+
? h("path", { d: "M6 9L2 4h8L6 9z" })
|
|
97
|
+
: h("path", { d: "M6 3l3 3H3l3-3zM6 9L3 6h6L6 9z", opacity: ".4" }));
|
|
98
|
+
const allSelected = rows.length > 0 && rowKeys.every((key) => selectedRows?.has(key));
|
|
99
|
+
const wrapperStyle = [
|
|
100
|
+
props.maxHeight !== undefined && `--hu-table-max-height:${toCssUnit(props.maxHeight)}`,
|
|
101
|
+
props.style
|
|
102
|
+
].filter(Boolean).join(";") || undefined;
|
|
103
|
+
return h("div", {
|
|
104
|
+
id: props.id,
|
|
105
|
+
class: cn("hu-table-wrap", props.stickyHeader && "hu-table-wrap--sticky", props.class),
|
|
106
|
+
style: wrapperStyle,
|
|
107
|
+
"data-testid": props.testId
|
|
108
|
+
}, h("table", {
|
|
109
|
+
class: cn("hu-table", density !== "default" && `hu-table--${density}`, props.stickyHeader && "hu-table--sticky-header"),
|
|
110
|
+
role: "table",
|
|
111
|
+
"aria-label": props["aria-label"],
|
|
112
|
+
"aria-labelledby": props["aria-labelledby"]
|
|
113
|
+
}, props.caption && h("caption", { class: "hu-table-caption" }, props.caption), h("thead", {}, h("tr", {}, selectable && h("th", { class: "hu-table-checkbox-col" }, h("input", {
|
|
114
|
+
type: "checkbox",
|
|
115
|
+
checked: allSelected,
|
|
116
|
+
"aria-label": "Select all rows",
|
|
117
|
+
"aria-checked": allSelected ? "true" : "false",
|
|
118
|
+
onChange: (e) => handleSelectAll(e.target.checked)
|
|
119
|
+
})), ...columns.map((col) => h("th", {
|
|
120
|
+
id: col.id ?? `hu-table-col-${col.key}`,
|
|
39
121
|
key: col.key,
|
|
40
122
|
class: col.sortable ? "hu-table-sortable" : undefined,
|
|
41
123
|
style: [col.width && `width:${col.width}`, col.align && `text-align:${col.align}`].filter(Boolean).join(";"),
|
|
42
|
-
|
|
124
|
+
tabindex: col.sortable ? "0" : undefined,
|
|
125
|
+
"aria-sort": sortColumn === col.key ? (sortDir === "desc" ? "descending" : "ascending") : undefined,
|
|
126
|
+
"aria-label": col.headerLabel,
|
|
127
|
+
onClick: col.sortable ? () => handleSort(col.key) : undefined,
|
|
128
|
+
onKeyDown: col.sortable ? (event) => handleSortKeyDown(event, col.key) : undefined
|
|
43
129
|
}, col.header, col.sortable && h("span", {
|
|
44
130
|
class: cn("hu-table-sort-icon", sortColumn === col.key && `hu-table-sort-icon--${sortDir}`),
|
|
45
|
-
innerHTML: sortSVG(sortColumn === col.key ? sortDir ?? "" : ""),
|
|
46
131
|
"aria-hidden": "true"
|
|
47
|
-
}))))), h("tbody", {},
|
|
48
|
-
? h("tr", {}, h("td", { colspan:
|
|
49
|
-
:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
132
|
+
}, sortIcon(sortColumn === col.key ? sortDir ?? "" : "")))))), h("tbody", {}, loading
|
|
133
|
+
? h("tr", {}, h("td", { colspan: stateColSpan, class: "hu-table-empty" }, loadingState ?? "Loading..."))
|
|
134
|
+
: error || errorState
|
|
135
|
+
? h("tr", {}, h("td", { colspan: stateColSpan, class: "hu-table-empty", role: "alert" }, errorState ?? error))
|
|
136
|
+
: rows.length === 0
|
|
137
|
+
? h("tr", {}, h("td", { colspan: stateColSpan, class: "hu-table-empty" }, emptyState ?? emptyMessage))
|
|
138
|
+
: rows.map((row, i) => {
|
|
139
|
+
const key = rowId(row, i);
|
|
140
|
+
const disabled = props.isRowDisabled?.(row, i) ?? false;
|
|
141
|
+
const selected = selectedRows?.has(key) ?? false;
|
|
142
|
+
const actionable = (onRowClick || props.onRowAction) && !disabled;
|
|
143
|
+
return h("tr", {
|
|
144
|
+
key,
|
|
145
|
+
style: actionable ? "cursor:pointer" : undefined,
|
|
146
|
+
tabindex: actionable ? "0" : undefined,
|
|
147
|
+
"aria-label": props.getRowLabel?.(row, i),
|
|
148
|
+
"aria-selected": selectable ? (selected ? "true" : "false") : undefined,
|
|
149
|
+
"aria-disabled": disabled ? "true" : undefined,
|
|
150
|
+
onClick: actionable ? () => handleRowAction(row, key, i) : undefined,
|
|
151
|
+
onKeyDown: actionable ? (event) => handleRowKeyDown(event, row, key, i) : undefined
|
|
152
|
+
}, selectable && h("td", { class: "hu-table-checkbox-col" }, h("input", {
|
|
153
|
+
type: "checkbox", checked: selected,
|
|
154
|
+
disabled,
|
|
155
|
+
"aria-label": `Select row ${i + 1}`,
|
|
156
|
+
onChange: (e) => { e.stopPropagation(); handleSelectRow(row, key, i, e.target.checked); }
|
|
157
|
+
})), ...columns.map((col) => h("td", {
|
|
158
|
+
key: col.key,
|
|
159
|
+
headers: col.id ?? `hu-table-col-${col.key}`,
|
|
160
|
+
style: col.align ? `text-align:${col.align}` : undefined,
|
|
161
|
+
"aria-label": col.cellLabel?.(row, i)
|
|
162
|
+
}, col.render ? col.render(row, i) : row[col.key])));
|
|
163
|
+
}))), footer && h("div", { class: "hu-table-footer" }, footer));
|
|
59
164
|
}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { type Signal } from "@hyperpackai/hyperion";
|
|
2
2
|
import { type VNode } from "../../theme/index.js";
|
|
3
|
+
export type DialogCloseReason = "backdrop" | "escape" | "close-button" | "programmatic";
|
|
4
|
+
export type DialogCloseButtonRenderer = (close: (reason?: DialogCloseReason) => void) => unknown;
|
|
3
5
|
export interface DialogProps {
|
|
4
|
-
open
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
open?: boolean | Signal<boolean>;
|
|
7
|
+
defaultOpen?: boolean;
|
|
8
|
+
onClose?: (reason?: DialogCloseReason) => void;
|
|
9
|
+
onOpenChange?: (open: boolean, reason?: DialogCloseReason) => void;
|
|
7
10
|
title?: string;
|
|
8
11
|
description?: string;
|
|
12
|
+
id?: string;
|
|
9
13
|
size?: "sm" | "md" | "lg" | "xl" | "full";
|
|
10
14
|
closeOnBackdrop?: boolean;
|
|
11
15
|
closeOnEscape?: boolean;
|
|
12
16
|
showClose?: boolean;
|
|
17
|
+
closeIcon?: unknown;
|
|
18
|
+
closeButton?: unknown | DialogCloseButtonRenderer;
|
|
13
19
|
footer?: unknown;
|
|
14
20
|
footerAlign?: "start" | "end" | "between";
|
|
15
21
|
class?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAsDpE,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,QAAQ,GAAG,cAAc,GAAG,cAAc,CAAC;AACxF,MAAM,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB,KAAK,IAAI,KAAK,OAAO,CAAC;AAEjG,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC/C,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAC1C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,GAAG,yBAAyB,CAAC;IAClD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAID,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,KAAK,CA4EhD"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { signal } from "@hyperpackai/hyperion";
|
|
1
2
|
import { injectCSS, cn, h } from "../../theme/index.js";
|
|
2
3
|
import { renderInPortal } from "../../portal.js";
|
|
3
4
|
import { FocusTrap } from "../FocusTrap/index.js";
|
|
@@ -50,47 +51,62 @@ const DIALOG_CSS = `
|
|
|
50
51
|
.hu-dialog-footer--start { justify-content: flex-start; }
|
|
51
52
|
.hu-dialog-footer--between { justify-content: space-between; }
|
|
52
53
|
`;
|
|
54
|
+
let dialogId = 0;
|
|
53
55
|
export function Dialog(props) {
|
|
54
56
|
injectCSS("hu-dialog", DIALOG_CSS);
|
|
55
|
-
const { title, description, size = "md", closeOnBackdrop = true, closeOnEscape = true, showClose = true, footer, footerAlign = "end", onClose, onOpenChange, children } = props;
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
const { title, description, size = "md", closeOnBackdrop = true, closeOnEscape = true, showClose = true, footer, footerAlign = "end", closeIcon, closeButton, onClose, onOpenChange, children } = props;
|
|
58
|
+
const generatedId = props.id ?? `hu-dialog-${++dialogId}`;
|
|
59
|
+
const titleId = title ? `${generatedId}-title` : undefined;
|
|
60
|
+
const descriptionId = description ? `${generatedId}-description` : undefined;
|
|
61
|
+
const signalOpen = getSignalProp(props.open);
|
|
62
|
+
const internalOpen = signal(props.defaultOpen ?? false);
|
|
63
|
+
const isControlled = typeof props.open === "boolean" || !!signalOpen;
|
|
64
|
+
const isOpen = () => signalOpen ? signalOpen.value : typeof props.open === "boolean" ? props.open : internalOpen.value;
|
|
65
|
+
const close = (reason = "programmatic") => {
|
|
66
|
+
if (signalOpen)
|
|
67
|
+
signalOpen.value = false;
|
|
68
|
+
else if (!isControlled)
|
|
69
|
+
internalOpen.value = false;
|
|
70
|
+
onOpenChange?.(false, reason);
|
|
71
|
+
onClose?.(reason);
|
|
72
|
+
};
|
|
73
|
+
const defaultCloseIcon = h("svg", {
|
|
74
|
+
viewBox: "0 0 16 16",
|
|
75
|
+
fill: "none",
|
|
76
|
+
stroke: "currentColor",
|
|
77
|
+
"stroke-width": "2",
|
|
78
|
+
"aria-hidden": "true"
|
|
79
|
+
}, h("path", { d: "M2 2l12 12M14 2L2 14" }));
|
|
80
|
+
const renderedCloseButton = () => {
|
|
81
|
+
if (typeof closeButton === "function")
|
|
82
|
+
return closeButton(close);
|
|
83
|
+
if (closeButton)
|
|
84
|
+
return closeButton;
|
|
85
|
+
return h("button", {
|
|
86
|
+
class: "hu-dialog-close",
|
|
87
|
+
type: "button",
|
|
88
|
+
onClick: () => close("close-button"),
|
|
89
|
+
"aria-label": "Close dialog"
|
|
90
|
+
}, closeIcon ?? defaultCloseIcon);
|
|
65
91
|
};
|
|
66
|
-
const closeSVG = `<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 2l12 12M14 2L2 14"/></svg>`;
|
|
67
92
|
const renderDialog = () => h("div", {
|
|
68
93
|
class: "hu-dialog-backdrop", role: "presentation", tabIndex: -1,
|
|
69
94
|
onClick: closeOnBackdrop ? (e) => {
|
|
70
95
|
if (e.target.classList.contains("hu-dialog-backdrop")) {
|
|
71
|
-
|
|
72
|
-
onClose?.("backdrop");
|
|
96
|
+
close("backdrop");
|
|
73
97
|
}
|
|
74
|
-
} : undefined
|
|
75
|
-
|
|
76
|
-
}, FocusTrap({ active: true, restoreFocus: true, autoFocus: true, onEscape: closeOnEscape ? () => {
|
|
77
|
-
onOpenChange?.(false, "escape");
|
|
78
|
-
onClose?.("escape");
|
|
79
|
-
} : undefined, children: h("div", {
|
|
98
|
+
} : undefined
|
|
99
|
+
}, FocusTrap({ active: true, restoreFocus: true, autoFocus: true, onEscape: closeOnEscape ? () => close("escape") : undefined, children: h("div", {
|
|
80
100
|
class: cn("hu-dialog", `hu-dialog--${size}`, props.class),
|
|
81
101
|
role: "dialog", "aria-modal": "true",
|
|
82
|
-
"aria-labelledby":
|
|
83
|
-
"aria-describedby":
|
|
84
|
-
}, (title || showClose) && h("div", { class: "hu-dialog-header" }, title && h("div", {}, h("h2", { id:
|
|
85
|
-
class: "hu-dialog-close",
|
|
86
|
-
onClick: () => {
|
|
87
|
-
onOpenChange?.(false, "close-button");
|
|
88
|
-
onClose?.("close-button");
|
|
89
|
-
},
|
|
90
|
-
"aria-label": "Close dialog", innerHTML: closeSVG
|
|
91
|
-
})), h("div", { class: "hu-dialog-body" }, children), footer && h("div", { class: cn("hu-dialog-footer", `hu-dialog-footer--${footerAlign}`) }, footer))
|
|
102
|
+
"aria-labelledby": titleId,
|
|
103
|
+
"aria-describedby": descriptionId
|
|
104
|
+
}, (title || showClose) && h("div", { class: "hu-dialog-header" }, title && h("div", {}, h("h2", { id: titleId, class: "hu-dialog-title" }, title), description && h("p", { id: descriptionId, class: "hu-dialog-desc" }, description)), showClose && renderedCloseButton()), h("div", { class: "hu-dialog-body" }, children), footer && h("div", { class: cn("hu-dialog-footer", `hu-dialog-footer--${footerAlign}`) }, footer))
|
|
92
105
|
}));
|
|
93
|
-
if (
|
|
106
|
+
if (typeof props.open === "boolean")
|
|
94
107
|
return isOpen() ? renderInPortal(renderDialog(), "dialog") : h("span", { "aria-hidden": "true" });
|
|
95
108
|
return (() => isOpen() ? renderInPortal(renderDialog(), "dialog") : h("span", { "aria-hidden": "true" }));
|
|
96
109
|
}
|
|
110
|
+
function getSignalProp(value) {
|
|
111
|
+
return typeof value === "object" && value != null && "peek" in value ? value : undefined;
|
|
112
|
+
}
|
|
@@ -1,15 +1,23 @@
|
|
|
1
|
+
import { type Signal } from "@hyperpackai/hyperion";
|
|
1
2
|
import { type VNode } from "../../theme/index.js";
|
|
2
3
|
export type DrawerAnchor = "left" | "right" | "top" | "bottom";
|
|
3
4
|
export type DrawerVariant = "temporary" | "permanent" | "persistent";
|
|
4
5
|
export type DrawerSize = "sm" | "md" | "lg";
|
|
6
|
+
export type DrawerCloseReason = "backdrop" | "escape" | "close-button";
|
|
7
|
+
export type DrawerCloseButtonRenderer = (close: (reason?: DrawerCloseReason) => void) => unknown;
|
|
5
8
|
export interface DrawerProps {
|
|
6
|
-
open
|
|
9
|
+
open?: boolean | Signal<boolean>;
|
|
10
|
+
defaultOpen?: boolean;
|
|
7
11
|
anchor?: DrawerAnchor;
|
|
8
12
|
variant?: DrawerVariant;
|
|
9
13
|
size?: DrawerSize;
|
|
10
14
|
title?: string;
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
closeIcon?: unknown;
|
|
16
|
+
closeButton?: unknown | DrawerCloseButtonRenderer;
|
|
17
|
+
closeOnBackdrop?: boolean;
|
|
18
|
+
closeOnEscape?: boolean;
|
|
19
|
+
onClose?: (reason?: DrawerCloseReason) => void;
|
|
20
|
+
onOpenChange?: (open: boolean, reason?: DrawerCloseReason) => void;
|
|
13
21
|
class?: string;
|
|
14
22
|
children?: unknown;
|
|
15
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Drawer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Drawer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AA2DpE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;AAC/D,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;AACrE,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAC5C,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,QAAQ,GAAG,cAAc,CAAC;AACvE,MAAM,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB,KAAK,IAAI,KAAK,OAAO,CAAC;AAEjG,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,GAAG,yBAAyB,CAAC;IAClD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC/C,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,KAAK,GAAG,IAAI,CA0EvD"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { signal } from "@hyperpackai/hyperion";
|
|
1
2
|
import { injectCSS, cn, h } from "../../theme/index.js";
|
|
2
3
|
import { renderInPortal } from "../../portal.js";
|
|
4
|
+
import { FocusTrap } from "../FocusTrap/index.js";
|
|
3
5
|
const CSS = `
|
|
4
6
|
.hu-drawer-root { }
|
|
5
7
|
|
|
@@ -56,22 +58,75 @@ const CSS = `
|
|
|
56
58
|
`;
|
|
57
59
|
export function Drawer(props) {
|
|
58
60
|
injectCSS("hu-drawer", CSS);
|
|
59
|
-
const {
|
|
60
|
-
const
|
|
61
|
+
const { anchor = "left", variant = "temporary", size = "md", title, closeOnBackdrop = true, closeOnEscape = true, onClose, onOpenChange } = props;
|
|
62
|
+
const uncontrolledOpen = signal(props.defaultOpen ?? false);
|
|
63
|
+
const isSignalOpen = typeof props.open === "object" && props.open != null && "peek" in props.open;
|
|
64
|
+
const isControlled = typeof props.open === "boolean" || isSignalOpen;
|
|
65
|
+
const isOpen = () => {
|
|
66
|
+
if (typeof props.open === "boolean")
|
|
67
|
+
return props.open;
|
|
68
|
+
if (isSignalOpen)
|
|
69
|
+
return props.open.value;
|
|
70
|
+
return uncontrolledOpen.value;
|
|
71
|
+
};
|
|
72
|
+
const close = (reason = "close-button") => {
|
|
73
|
+
if (!isControlled)
|
|
74
|
+
uncontrolledOpen.value = false;
|
|
75
|
+
if (isSignalOpen)
|
|
76
|
+
props.open.value = false;
|
|
61
77
|
onOpenChange?.(false, reason);
|
|
62
78
|
onClose?.(reason);
|
|
63
79
|
};
|
|
64
|
-
|
|
80
|
+
const open = isOpen();
|
|
81
|
+
if (!open && variant === "temporary" && isControlled && typeof props.open === "boolean")
|
|
65
82
|
return null;
|
|
66
|
-
if (!open && variant !== "permanent")
|
|
83
|
+
if (!open && variant !== "permanent" && isControlled && typeof props.open === "boolean")
|
|
67
84
|
return null;
|
|
68
|
-
const
|
|
69
|
-
|
|
85
|
+
const closeIcon = props.closeIcon ?? h("svg", {
|
|
86
|
+
viewBox: "0 0 16 16",
|
|
87
|
+
width: "16",
|
|
88
|
+
height: "16",
|
|
89
|
+
fill: "none",
|
|
90
|
+
stroke: "currentColor",
|
|
91
|
+
"stroke-width": "2",
|
|
92
|
+
"aria-hidden": "true"
|
|
93
|
+
}, h("path", { d: "M2 2l12 12M14 2L2 14" }));
|
|
94
|
+
const showDefaultClose = props.closeButton === undefined && Boolean(onClose ?? onOpenChange ?? isSignalOpen ?? !isControlled);
|
|
95
|
+
const showCloseButton = props.closeButton !== null && (props.closeButton !== undefined || showDefaultClose);
|
|
96
|
+
const panel = () => h("div", {
|
|
70
97
|
class: cn("hu-drawer", `hu-drawer--${anchor}`, `hu-drawer--${variant}`, size !== "md" && `hu-drawer--${size}`, props.class),
|
|
71
98
|
role: "dialog",
|
|
72
|
-
"aria-modal": variant === "temporary" ? "true" : undefined
|
|
73
|
-
}, (title ||
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
99
|
+
"aria-modal": variant === "temporary" ? "true" : undefined,
|
|
100
|
+
}, (title || showCloseButton) && h("div", { class: "hu-drawer__header" }, title && h("span", { class: "hu-drawer__title" }, title), showCloseButton && renderDrawerCloseButton(props.closeButton, close, closeIcon)), h("div", { class: "hu-drawer__content" }, props.children));
|
|
101
|
+
const renderClosed = () => null;
|
|
102
|
+
const renderDrawer = () => {
|
|
103
|
+
if (!isOpen() && variant === "temporary")
|
|
104
|
+
return renderClosed();
|
|
105
|
+
if (!isOpen() && variant !== "permanent")
|
|
106
|
+
return renderClosed();
|
|
107
|
+
if (variant !== "temporary")
|
|
108
|
+
return panel();
|
|
109
|
+
return renderInPortal(h("div", { class: "hu-drawer-root" }, h("div", { class: "hu-drawer-backdrop", onClick: closeOnBackdrop ? () => close("backdrop") : undefined }), FocusTrap({
|
|
110
|
+
active: true,
|
|
111
|
+
restoreFocus: true,
|
|
112
|
+
autoFocus: true,
|
|
113
|
+
onEscape: closeOnEscape ? () => close("escape") : undefined,
|
|
114
|
+
children: panel()
|
|
115
|
+
})), "drawer");
|
|
116
|
+
};
|
|
117
|
+
if (isControlled && typeof props.open === "boolean")
|
|
118
|
+
return renderDrawer();
|
|
119
|
+
return renderDrawer;
|
|
120
|
+
}
|
|
121
|
+
function renderDrawerCloseButton(closeButton, close, closeIcon) {
|
|
122
|
+
if (typeof closeButton === "function")
|
|
123
|
+
return closeButton(close);
|
|
124
|
+
if (closeButton != null)
|
|
125
|
+
return closeButton;
|
|
126
|
+
return h("button", {
|
|
127
|
+
type: "button",
|
|
128
|
+
class: "hu-drawer__close",
|
|
129
|
+
onClick: () => close("close-button"),
|
|
130
|
+
"aria-label": "Close drawer"
|
|
131
|
+
}, closeIcon);
|
|
77
132
|
}
|
|
@@ -11,14 +11,16 @@ export interface MenuItem {
|
|
|
11
11
|
onClick?: () => void;
|
|
12
12
|
children?: MenuItem[];
|
|
13
13
|
}
|
|
14
|
+
export type DropdownMenuCloseReason = "escape" | "item-select" | "programmatic" | "trigger";
|
|
14
15
|
export interface DropdownMenuProps {
|
|
15
|
-
open
|
|
16
|
+
open?: boolean | Signal<boolean>;
|
|
17
|
+
defaultOpen?: boolean;
|
|
16
18
|
items: MenuItem[];
|
|
17
19
|
align?: "left" | "right";
|
|
18
20
|
side?: "top" | "bottom";
|
|
19
21
|
trigger?: unknown;
|
|
20
|
-
onClose?: () => void;
|
|
21
|
-
onOpenChange?: (open: boolean) => void;
|
|
22
|
+
onClose?: (reason?: DropdownMenuCloseReason) => void;
|
|
23
|
+
onOpenChange?: (open: boolean, reason?: DropdownMenuCloseReason) => void;
|
|
22
24
|
class?: string;
|
|
23
25
|
}
|
|
24
26
|
export declare function DropdownMenu(props: DropdownMenuProps): VNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/DropdownMenu/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/DropdownMenu/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAmCpE,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;IACtC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,aAAa,GAAG,cAAc,GAAG,SAAS,CAAC;AAE5F,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACrD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,KAAK,CAsF5D"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { signal } from "@hyperpackai/hyperion";
|
|
1
2
|
import { injectCSS, cn, h } from "../../theme/index.js";
|
|
2
3
|
const DROPDOWN_CSS = `
|
|
3
4
|
.hu-dropdown { position: relative; display: inline-block; }
|
|
@@ -34,23 +35,59 @@ const DROPDOWN_CSS = `
|
|
|
34
35
|
export function DropdownMenu(props) {
|
|
35
36
|
injectCSS("hu-dropdown", DROPDOWN_CSS);
|
|
36
37
|
const { items, align = "left", side = "bottom", trigger, onClose, onOpenChange } = props;
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
const internalOpen = signal(props.defaultOpen ?? false);
|
|
39
|
+
const signalOpen = getSignalProp(props.open);
|
|
40
|
+
const isControlled = typeof props.open === "boolean" || !!signalOpen;
|
|
41
|
+
const isOpen = () => signalOpen ? signalOpen.value : typeof props.open === "boolean" ? props.open : internalOpen.value;
|
|
42
|
+
const setOpen = (next, reason = "programmatic") => {
|
|
43
|
+
if (signalOpen)
|
|
44
|
+
signalOpen.value = next;
|
|
45
|
+
else if (!isControlled)
|
|
46
|
+
internalOpen.value = next;
|
|
47
|
+
onOpenChange?.(next, reason);
|
|
48
|
+
if (!next)
|
|
49
|
+
onClose?.(reason);
|
|
50
|
+
};
|
|
51
|
+
const close = (reason = "programmatic") => {
|
|
52
|
+
setOpen(false, reason);
|
|
53
|
+
};
|
|
54
|
+
const toggle = () => setOpen(!isOpen(), "trigger");
|
|
55
|
+
const handleMenuKeyDown = (e) => {
|
|
56
|
+
if (e.key === "Escape") {
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
close("escape");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(e.key))
|
|
62
|
+
return;
|
|
63
|
+
const menu = e.currentTarget;
|
|
64
|
+
const items = Array.from(menu.querySelectorAll('[role="menuitem"]:not(:disabled)'));
|
|
65
|
+
if (!items.length)
|
|
66
|
+
return;
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
const activeIndex = items.indexOf(document.activeElement);
|
|
69
|
+
const lastIndex = items.length - 1;
|
|
70
|
+
if (e.key === "Home") {
|
|
71
|
+
items[0]?.focus();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (e.key === "End") {
|
|
75
|
+
items[lastIndex]?.focus();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const delta = e.key === "ArrowDown" ? 1 : -1;
|
|
79
|
+
const nextIndex = activeIndex === -1
|
|
80
|
+
? (delta === 1 ? 0 : lastIndex)
|
|
81
|
+
: (activeIndex + delta + items.length) % items.length;
|
|
82
|
+
items[nextIndex]?.focus();
|
|
44
83
|
};
|
|
45
84
|
const menuClass = `hu-dropdown-menu--${side}-${align}`;
|
|
46
85
|
const renderMenu = () => isOpen()
|
|
47
86
|
? h("ul", {
|
|
48
87
|
class: cn("hu-dropdown-menu", menuClass),
|
|
49
88
|
role: "menu",
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
close();
|
|
53
|
-
}
|
|
89
|
+
tabindex: "-1",
|
|
90
|
+
onKeyDown: handleMenuKeyDown
|
|
54
91
|
}, ...items.map((item, i) => {
|
|
55
92
|
if (item.type === "separator")
|
|
56
93
|
return h("li", { key: `sep-${i}`, class: "hu-dropdown-separator", role: "separator" });
|
|
@@ -59,9 +96,15 @@ export function DropdownMenu(props) {
|
|
|
59
96
|
return h("li", { key: item.id ?? item.label, role: "none" }, h("button", {
|
|
60
97
|
class: cn("hu-dropdown-item", item.destructive && "hu-dropdown-item--destructive", item.disabled && "hu-dropdown-item--disabled"),
|
|
61
98
|
role: "menuitem", disabled: item.disabled,
|
|
62
|
-
|
|
99
|
+
tabindex: "-1",
|
|
100
|
+
onClick: () => { item.onClick?.(); close("item-select"); }
|
|
63
101
|
}, item.icon && h("span", { class: "hu-dropdown-item-icon", "aria-hidden": "true" }, item.icon), item.label, item.shortcut && h("span", { class: "hu-dropdown-item-shortcut" }, item.shortcut)));
|
|
64
102
|
}))
|
|
65
103
|
: null;
|
|
66
|
-
return h("div", { class: cn("hu-dropdown", props.class) }, trigger
|
|
104
|
+
return h("div", { class: cn("hu-dropdown", props.class) }, trigger && (isControlled
|
|
105
|
+
? trigger
|
|
106
|
+
: h("div", { onClick: toggle, style: "display:contents;" }, trigger)), typeof props.open === "boolean" ? renderMenu() : renderMenu);
|
|
107
|
+
}
|
|
108
|
+
function getSignalProp(value) {
|
|
109
|
+
return typeof value === "object" && value != null && "peek" in value ? value : undefined;
|
|
67
110
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/FocusTrap/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAK,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAWrD,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/FocusTrap/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAK,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAWrD,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK,CAiEtD"}
|