@hyperpackai/hyperui 0.1.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 +69 -61
- 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 +8 -3
|
@@ -4,11 +4,20 @@ const DASHBOARD_LAYOUT_CSS = `
|
|
|
4
4
|
min-height: 100dvh; display: grid; color: var(--hu-text); background: var(--hu-bg-2);
|
|
5
5
|
grid-template-areas:
|
|
6
6
|
"sidebar header"
|
|
7
|
-
"sidebar main"
|
|
7
|
+
"sidebar main"
|
|
8
|
+
"sidebar footer";
|
|
8
9
|
grid-template-columns: var(--hu-dashboard-sidebar, 280px) minmax(0, 1fr);
|
|
9
|
-
grid-template-rows: auto minmax(0, 1fr);
|
|
10
|
+
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
10
11
|
}
|
|
11
12
|
.hu-dashboard-layout--collapsed { grid-template-columns: var(--hu-dashboard-sidebar-collapsed, 72px) minmax(0, 1fr); }
|
|
13
|
+
.hu-dashboard-layout__skip {
|
|
14
|
+
position: fixed; inset-inline-start: var(--hu-space-3); inset-block-start: var(--hu-space-3);
|
|
15
|
+
z-index: calc(var(--hu-z-toast) + 1); transform: translateY(-150%);
|
|
16
|
+
padding: var(--hu-space-2) var(--hu-space-3); border-radius: var(--hu-radius);
|
|
17
|
+
background: var(--hu-primary); color: var(--hu-primary-text); text-decoration: none;
|
|
18
|
+
font-weight: var(--hu-font-weight-semibold);
|
|
19
|
+
}
|
|
20
|
+
.hu-dashboard-layout__skip:focus { transform: translateY(0); outline: 2px solid var(--hu-primary-text); outline-offset: 2px; }
|
|
12
21
|
.hu-dashboard-layout__header {
|
|
13
22
|
grid-area: header; min-width: 0; position: sticky; inset-block-start: 0;
|
|
14
23
|
z-index: var(--hu-z-sticky); background: var(--hu-bg);
|
|
@@ -26,10 +35,15 @@ const DASHBOARD_LAYOUT_CSS = `
|
|
|
26
35
|
border-inline-start: 1px solid var(--hu-border); background: var(--hu-bg);
|
|
27
36
|
min-width: 0; overflow: auto;
|
|
28
37
|
}
|
|
38
|
+
.hu-dashboard-layout__footer {
|
|
39
|
+
grid-area: footer; min-width: 0; background: var(--hu-bg);
|
|
40
|
+
border-block-start: 1px solid var(--hu-border);
|
|
41
|
+
}
|
|
29
42
|
.hu-dashboard-layout--with-aside {
|
|
30
43
|
grid-template-areas:
|
|
31
44
|
"sidebar header header"
|
|
32
|
-
"sidebar main aside"
|
|
45
|
+
"sidebar main aside"
|
|
46
|
+
"sidebar footer aside";
|
|
33
47
|
grid-template-columns: var(--hu-dashboard-sidebar, 280px) minmax(0, 1fr) var(--hu-dashboard-aside, 320px);
|
|
34
48
|
}
|
|
35
49
|
@media (max-width: 768px) {
|
|
@@ -37,9 +51,10 @@ const DASHBOARD_LAYOUT_CSS = `
|
|
|
37
51
|
.hu-dashboard-layout--with-aside {
|
|
38
52
|
grid-template-areas:
|
|
39
53
|
"header"
|
|
40
|
-
"main"
|
|
54
|
+
"main"
|
|
55
|
+
"footer";
|
|
41
56
|
grid-template-columns: minmax(0, 1fr);
|
|
42
|
-
grid-template-rows: auto minmax(0, 1fr);
|
|
57
|
+
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
43
58
|
}
|
|
44
59
|
.hu-dashboard-layout__sidebar,
|
|
45
60
|
.hu-dashboard-layout__aside { display: none; }
|
|
@@ -60,8 +75,36 @@ export function DashboardLayout(props) {
|
|
|
60
75
|
styles.push(`--hu-dashboard-aside:${toCssUnit(props.asideWidth)}`);
|
|
61
76
|
if (props.padding !== undefined)
|
|
62
77
|
styles.push(`--hu-dashboard-padding:${toCssUnit(props.padding)}`);
|
|
78
|
+
if (props.style)
|
|
79
|
+
styles.push(props.style);
|
|
80
|
+
const mainId = props.skipLinkTarget?.replace(/^#/, "") ?? (props.id ? `${props.id}-main` : "hu-dashboard-main");
|
|
63
81
|
return h("div", {
|
|
82
|
+
id: props.id,
|
|
64
83
|
class: cn("hu-dashboard-layout", props.collapsed && "hu-dashboard-layout--collapsed", props.aside != null && "hu-dashboard-layout--with-aside", props.class),
|
|
65
|
-
style: styles.join(";") || undefined
|
|
66
|
-
|
|
84
|
+
style: styles.join(";") || undefined,
|
|
85
|
+
"data-testid": props.testId
|
|
86
|
+
}, props.skipLinkLabel !== null && h("a", {
|
|
87
|
+
class: "hu-dashboard-layout__skip",
|
|
88
|
+
href: `#${mainId}`
|
|
89
|
+
}, props.skipLinkLabel ?? "Skip to main content"), props.header && h("header", {
|
|
90
|
+
class: "hu-dashboard-layout__header",
|
|
91
|
+
"aria-label": props.headerLabel
|
|
92
|
+
}, props.header), props.sidebar && h("aside", {
|
|
93
|
+
class: "hu-dashboard-layout__sidebar",
|
|
94
|
+
"aria-label": props.sidebarLabel ?? "Primary navigation"
|
|
95
|
+
}, props.sidebar), h("main", {
|
|
96
|
+
id: mainId,
|
|
97
|
+
class: "hu-dashboard-layout__main",
|
|
98
|
+
tabindex: "-1",
|
|
99
|
+
"aria-label": props.mainLabel
|
|
100
|
+
}, props.children), props.aside && h("aside", {
|
|
101
|
+
class: "hu-dashboard-layout__aside",
|
|
102
|
+
"aria-label": props.asideLabel ?? "Secondary panel"
|
|
103
|
+
}, props.aside), props.footer && h("footer", {
|
|
104
|
+
class: "hu-dashboard-layout__footer",
|
|
105
|
+
"aria-label": props.footerLabel
|
|
106
|
+
}, props.footer));
|
|
107
|
+
}
|
|
108
|
+
export function AppShell(props) {
|
|
109
|
+
return DashboardLayout(props);
|
|
67
110
|
}
|
|
@@ -2,26 +2,69 @@ import { type VNode } from "../../theme/index.js";
|
|
|
2
2
|
export interface TableColumn<T> {
|
|
3
3
|
key: string;
|
|
4
4
|
header: string;
|
|
5
|
+
id?: string;
|
|
5
6
|
width?: string;
|
|
6
7
|
sortable?: boolean;
|
|
7
8
|
render?: (row: T, i: number) => unknown;
|
|
8
9
|
align?: "left" | "center" | "right";
|
|
10
|
+
headerLabel?: string;
|
|
11
|
+
cellLabel?: (row: T, i: number) => string;
|
|
9
12
|
}
|
|
13
|
+
export interface DataTableSortEvent {
|
|
14
|
+
key: string;
|
|
15
|
+
direction: "asc" | "desc";
|
|
16
|
+
}
|
|
17
|
+
export interface DataTableRowEvent<T> {
|
|
18
|
+
row: T;
|
|
19
|
+
key: string;
|
|
20
|
+
index: number;
|
|
21
|
+
}
|
|
22
|
+
export interface DataTableSelectionEvent<T> {
|
|
23
|
+
row: T;
|
|
24
|
+
key: string;
|
|
25
|
+
selected: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface DataTableSelectAllEvent<T> {
|
|
28
|
+
selected: boolean;
|
|
29
|
+
keys: string[];
|
|
30
|
+
rows: T[];
|
|
31
|
+
}
|
|
32
|
+
export type DataTableDensity = "compact" | "default" | "comfortable";
|
|
10
33
|
export interface DataTableProps<T extends Record<string, unknown>> {
|
|
34
|
+
id?: string;
|
|
11
35
|
columns: TableColumn<T>[];
|
|
12
36
|
rows: T[];
|
|
13
37
|
rowKey?: (row: T) => string;
|
|
38
|
+
caption?: string;
|
|
39
|
+
"aria-label"?: string;
|
|
40
|
+
"aria-labelledby"?: string;
|
|
41
|
+
density?: DataTableDensity;
|
|
42
|
+
stickyHeader?: boolean;
|
|
43
|
+
maxHeight?: string | number;
|
|
14
44
|
sortColumn?: string;
|
|
15
45
|
sortDir?: "asc" | "desc";
|
|
16
46
|
onSort?: (key: string) => void;
|
|
47
|
+
onSortChange?: (event: DataTableSortEvent) => void;
|
|
17
48
|
onRowClick?: (row: T) => void;
|
|
49
|
+
onRowAction?: (event: DataTableRowEvent<T>) => void;
|
|
50
|
+
isRowDisabled?: (row: T, index: number) => boolean;
|
|
51
|
+
getRowLabel?: (row: T, index: number) => string;
|
|
18
52
|
emptyMessage?: string;
|
|
53
|
+
emptyState?: unknown;
|
|
54
|
+
loading?: boolean;
|
|
55
|
+
loadingState?: unknown;
|
|
56
|
+
error?: unknown;
|
|
57
|
+
errorState?: unknown;
|
|
19
58
|
footer?: unknown;
|
|
20
59
|
selectable?: boolean;
|
|
21
60
|
selectedRows?: Set<string>;
|
|
22
61
|
onSelectRow?: (key: string, selected: boolean) => void;
|
|
62
|
+
onSelectionChange?: (event: DataTableSelectionEvent<T>) => void;
|
|
23
63
|
onSelectAll?: (selected: boolean) => void;
|
|
64
|
+
onSelectAllChange?: (event: DataTableSelectAllEvent<T>) => void;
|
|
24
65
|
class?: string;
|
|
66
|
+
style?: string;
|
|
67
|
+
testId?: string;
|
|
25
68
|
}
|
|
26
69
|
export declare function DataTable<T extends Record<string, unknown>>(props: DataTableProps<T>): VNode;
|
|
27
70
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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":"AAAA,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AA6CpE,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,GAAG,EAAE,CAAC,CAAC;IACP,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC;IACxC,GAAG,EAAE,CAAC,CAAC;IACP,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC;IACxC,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,CAAC,EAAE,CAAC;CACX;AAED,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;AAErE,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1B,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACnD,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC;IAC9B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IACpD,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACnD,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IACvD,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAChE,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1C,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,KAAK,CAyJ5F"}
|
|
@@ -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
|
}
|