@iyulab/desktop-patterns 0.5.0 → 0.6.1
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 +27 -4
- package/dist/breakpoints.js +5 -0
- package/dist/components/list-detail/dp-list-detail.d.ts +25 -0
- package/dist/components/shortcut-overlay/dp-shortcut-overlay.d.ts +3 -1
- package/dist/components/sidebar/dp-sidebar.d.ts +10 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -5
- package/dist/list-detail.js +65 -0
- package/dist/shell.js +109 -2
- package/dist/shortcut-overlay.js +16 -7
- package/dist/sidebar.js +21 -11
- package/package.json +14 -3
- package/tokens.css +2 -0
- package/dist/dp-shell-CvV92B8B.js +0 -108
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ document.querySelector<HTMLElementTagNameMap['dp-sidebar']>('#nav')!.items = [
|
|
|
51
51
|
{ id: 'overview', icon: '■', label: 'Overview' },
|
|
52
52
|
]
|
|
53
53
|
|
|
54
|
-
import { desktopMinWidth } from '@iyulab/desktop-patterns'
|
|
54
|
+
import { desktopMinWidth } from '@iyulab/desktop-patterns/breakpoints' // no elements registered
|
|
55
55
|
|
|
56
56
|
const shell = document.querySelector('dp-shell')!
|
|
57
57
|
const sidebar = document.querySelector('dp-sidebar')!
|
|
@@ -66,7 +66,9 @@ function sync() {
|
|
|
66
66
|
wide.addEventListener('change', sync)
|
|
67
67
|
sidebar.addEventListener('dp-sidebar-select', () => {
|
|
68
68
|
// consumer owns activeId / routing
|
|
69
|
-
|
|
69
|
+
})
|
|
70
|
+
sidebar.addEventListener('dp-sidebar-activate', () => {
|
|
71
|
+
shell.sidebarOpen = false // a drawer gives way to what was picked — the place already shown included
|
|
70
72
|
sync()
|
|
71
73
|
})
|
|
72
74
|
toolbar.addEventListener('dp-toolbar-toggle', () => {
|
|
@@ -85,6 +87,23 @@ collapsed rail (`dp-sidebar` `collapsed`). Give the toggle `expanded` so assisti
|
|
|
85
87
|
which state it is in. The consumer owns both states — start the drawer closed, close it when the
|
|
86
88
|
shell asks (backdrop click, Escape) and when a place is picked, or a narrow window stays covered.
|
|
87
89
|
|
|
90
|
+
### A list beside the item picked from it
|
|
91
|
+
|
|
92
|
+
```html
|
|
93
|
+
<dp-page fill max-width="full">
|
|
94
|
+
<dp-list-detail>
|
|
95
|
+
<nav slot="list" aria-label="People">…</nav>
|
|
96
|
+
<article>…the person picked…</article>
|
|
97
|
+
</dp-list-detail>
|
|
98
|
+
</dp-page>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`dp-list-detail` puts the `list` slot beside the default slot, each scrolling on its own, the list
|
|
102
|
+
`--dp-list-detail-list-width` wide (by default the sidebar's width — navigation, list and item read as
|
|
103
|
+
1:1:rest). Below the desktop breakpoint there is room for one pane: the item while `detail-open` is
|
|
104
|
+
set, the list otherwise. The consumer owns `detail-open` — set it when an item is picked, and give
|
|
105
|
+
the item a way back that clears it, shown only below `desktopMinWidth`.
|
|
106
|
+
|
|
88
107
|
### Sidebar items: links, groups, icons, actions
|
|
89
108
|
|
|
90
109
|
```ts
|
|
@@ -108,6 +127,9 @@ nav.setAttribute('nav-label', 'Main') // names t
|
|
|
108
127
|
items show, separated by a rule.
|
|
109
128
|
- **Icons**: each item's icon is a slot named `icon-<id>` whose fallback is the item's `icon` text — slot any icon
|
|
110
129
|
element (`<svg slot="icon-overview">`) to replace it.
|
|
130
|
+
- **`dp-sidebar-activate`** fires on every pick of a navigation item, the active one included (after
|
|
131
|
+
`dp-sidebar-select` when the place changes; not when that was cancelled). Close a drawer on it: picking the
|
|
132
|
+
place already shown still means "take me there", and `dp-sidebar-select` does not fire for it.
|
|
111
133
|
- **Bottom items are actions**: each click fires `dp-sidebar-action` (`itemId`) — every time, so a failed action can
|
|
112
134
|
be retried — and never moves `aria-current`. (In 0.2.x they fired `dp-sidebar-select` and took the active mark.)
|
|
113
135
|
|
|
@@ -125,7 +147,7 @@ import '@iyulab/desktop-patterns/tokens.css'
|
|
|
125
147
|
|
|
126
148
|
Color/spacing/radius/typography all come from `desktop-compact`'s `--dc-*` tokens — this package adds
|
|
127
149
|
only the structural dimensions `desktop-compact` has no concept of (`--dp-sidebar-width`,
|
|
128
|
-
`--dp-sidebar-width-collapsed`, `--dp-header-height`). Every component ships with sane fallback
|
|
150
|
+
`--dp-sidebar-width-collapsed`, `--dp-header-height`, `--dp-list-detail-list-width`). Every component ships with sane fallback
|
|
129
151
|
values, so it still renders correctly even without either stylesheet.
|
|
130
152
|
|
|
131
153
|
## Platform boundary — drag regions
|
|
@@ -147,11 +169,12 @@ dp-toolbar[drag-region]::part(drag-handle) {
|
|
|
147
169
|
`toolbar`, so ownership of the drag handle stays entirely with whatever renders there (typically
|
|
148
170
|
`dp-toolbar`).
|
|
149
171
|
|
|
150
|
-
## Components
|
|
172
|
+
## Components
|
|
151
173
|
|
|
152
174
|
| Component | Description |
|
|
153
175
|
|---|---|
|
|
154
176
|
| `dp-page` | Scrollable content region with a centered, max-width column — the page-body every view renders into. With `fill` it does not scroll and its column is as tall as the region, for a view that scrolls its own parts (a list beside the item open) |
|
|
177
|
+
| `dp-list-detail` | A list beside the item picked from it (master-detail) — each pane scrolls on its own; one pane at a time below 1024px, the item while `detail-open` is set |
|
|
155
178
|
| `dp-sidebar` | App-shell navigation rail — expanded/collapsed states, an optional pinned bottom group, a `footer` slot for whatever a consumer wants to anchor there |
|
|
156
179
|
| `dp-toolbar` | App-shell header bar — `heading`/`subtitle`, an optional sidebar toggle (drawer in a narrow window, collapse in a wide one), a right-side `actions` slot |
|
|
157
180
|
| `dp-shell` | Top-level layout composing `sidebar`/`toolbar`/`banner`/main-content regions, with a responsive drawer (backdrop + overlay sidebar) below a 1024px breakpoint |
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* A list beside the item picked from it — the master-detail view of mail, files or records. The
|
|
4
|
+
* `list` slot holds what to pick from, the default slot what was picked. Each pane scrolls on its
|
|
5
|
+
* own, so the list stays where it was while the item is read.
|
|
6
|
+
*
|
|
7
|
+
* At desktop width the list sits at the start, `--dp-list-detail-list-width` wide (by default the
|
|
8
|
+
* sidebar's width, so navigation and list line up as equal columns), and the item takes the rest.
|
|
9
|
+
* Below it there is room for one pane: the item while `detail-open` is set, the list otherwise. The
|
|
10
|
+
* consumer owns `detail-open` — set it when an item is picked and clear it to go back to the list
|
|
11
|
+
* (a back control in the item, which it shows only while narrow).
|
|
12
|
+
*
|
|
13
|
+
* Give the view a definite height — inside `dp-page fill`, or any box with one.
|
|
14
|
+
*/
|
|
15
|
+
export declare class DpListDetail extends LitElement {
|
|
16
|
+
static styles: import('lit').CSSResult;
|
|
17
|
+
/** Below desktop width, show the picked item instead of the list. Above it both are always shown. */
|
|
18
|
+
detailOpen: boolean;
|
|
19
|
+
render(): import('lit-html').TemplateResult<1>;
|
|
20
|
+
}
|
|
21
|
+
declare global {
|
|
22
|
+
interface HTMLElementTagNameMap {
|
|
23
|
+
'dp-list-detail': DpListDetail;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -13,7 +13,9 @@ export declare class DpShortcutOverlayDismissEvent extends Event {
|
|
|
13
13
|
* `shift+/` shortcut and read from a module-level, framework-hook-specific
|
|
14
14
|
* registry; both are the consumer's responsibility here (see
|
|
15
15
|
* docs/superpowers/specs/2026-09-02-desktop-patterns-design.md, the
|
|
16
|
-
* dp-shortcut-overlay row).
|
|
16
|
+
* dp-shortcut-overlay row). While open it is a modal dialog: focus moves to
|
|
17
|
+
* its close button, Tab stays inside it, and focus goes back where it was once
|
|
18
|
+
* it closes. This component only dismisses itself (outside
|
|
17
19
|
* click / Escape) by asking — it dispatches `dp-shortcut-overlay-dismiss`
|
|
18
20
|
* rather than setting its own `open` to false, since `open` is the
|
|
19
21
|
* consumer's state to own, not this component's.
|
|
@@ -32,6 +32,16 @@ export declare class DpSidebarSelectEvent extends Event {
|
|
|
32
32
|
readonly itemId: string;
|
|
33
33
|
constructor(itemId: string);
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Fired whenever a navigation item is picked, the active one included — after `dp-sidebar-select`
|
|
37
|
+
* when the place changes, alone when the item picked is the one already active. Not cancelable.
|
|
38
|
+
* It is the moment a drawer holding the sidebar gives way: picking the place already shown still
|
|
39
|
+
* means "take me there".
|
|
40
|
+
*/
|
|
41
|
+
export declare class DpSidebarActivateEvent extends Event {
|
|
42
|
+
readonly itemId: string;
|
|
43
|
+
constructor(itemId: string);
|
|
44
|
+
}
|
|
35
45
|
/** Fired on every click of a bottom item — those are actions, not places. */
|
|
36
46
|
export declare class DpSidebarActionEvent extends Event {
|
|
37
47
|
readonly itemId: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './breakpoints.js';
|
|
2
2
|
export * from './components/page/dp-page.js';
|
|
3
|
+
export * from './components/list-detail/dp-list-detail.js';
|
|
3
4
|
export * from './components/sidebar/dp-sidebar.js';
|
|
4
5
|
export * from './components/toolbar/dp-toolbar.js';
|
|
5
6
|
export * from './components/shell/dp-shell.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { DpPage as
|
|
3
|
-
import {
|
|
1
|
+
import { desktopMedia as e, desktopMinWidth as t } from "./breakpoints.js";
|
|
2
|
+
import { DpPage as n } from "./page.js";
|
|
3
|
+
import { DpListDetail as r } from "./list-detail.js";
|
|
4
|
+
import { DpSidebar as i, DpSidebarActionEvent as a, DpSidebarActivateEvent as o, DpSidebarSelectEvent as s } from "./sidebar.js";
|
|
4
5
|
import { DpToolbar as c, DpToolbarToggleEvent as l } from "./toolbar.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
6
|
+
import { DpShell as u, DpShellSidebarCloseEvent as d } from "./shell.js";
|
|
7
|
+
import { DpShortcutOverlay as f, DpShortcutOverlayDismissEvent as p } from "./shortcut-overlay.js";
|
|
8
|
+
export { r as DpListDetail, n as DpPage, u as DpShell, d as DpShellSidebarCloseEvent, f as DpShortcutOverlay, p as DpShortcutOverlayDismissEvent, i as DpSidebar, a as DpSidebarActionEvent, o as DpSidebarActivateEvent, s as DpSidebarSelectEvent, c as DpToolbar, l as DpToolbarToggleEvent, e as desktopMedia, t as desktopMinWidth };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { desktopMedia as e } from "./breakpoints.js";
|
|
2
|
+
import { t } from "./decorate-CafwLXg5.js";
|
|
3
|
+
import { LitElement as n, css as r, html as i } from "lit";
|
|
4
|
+
import { customElement as a, property as o } from "lit/decorators.js";
|
|
5
|
+
//#region src/components/list-detail/dp-list-detail.ts
|
|
6
|
+
var s = class extends n {
|
|
7
|
+
constructor(...e) {
|
|
8
|
+
super(...e), this.detailOpen = !1;
|
|
9
|
+
}
|
|
10
|
+
static {
|
|
11
|
+
this.styles = r`
|
|
12
|
+
:host {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex: 1;
|
|
15
|
+
min-height: 0;
|
|
16
|
+
height: 100%;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
}
|
|
19
|
+
.list,
|
|
20
|
+
.detail {
|
|
21
|
+
min-width: 0;
|
|
22
|
+
min-height: 0;
|
|
23
|
+
overflow: auto;
|
|
24
|
+
box-sizing: border-box;
|
|
25
|
+
}
|
|
26
|
+
.list {
|
|
27
|
+
flex: 1;
|
|
28
|
+
}
|
|
29
|
+
.detail {
|
|
30
|
+
flex: 1;
|
|
31
|
+
display: none;
|
|
32
|
+
}
|
|
33
|
+
:host([detail-open]) .list {
|
|
34
|
+
display: none;
|
|
35
|
+
}
|
|
36
|
+
:host([detail-open]) .detail {
|
|
37
|
+
display: block;
|
|
38
|
+
}
|
|
39
|
+
@media ${e} {
|
|
40
|
+
:host([detail-open]) .list,
|
|
41
|
+
.list {
|
|
42
|
+
display: block;
|
|
43
|
+
flex: 0 0 var(--dp-list-detail-list-width, var(--dp-sidebar-width, 220px));
|
|
44
|
+
border-inline-end: 1px solid var(--dc-color-border, #e2e2e4);
|
|
45
|
+
}
|
|
46
|
+
.detail {
|
|
47
|
+
display: block;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
render() {
|
|
53
|
+
return i`
|
|
54
|
+
<div class="list" part="list"><slot name="list"></slot></div>
|
|
55
|
+
<div class="detail" part="detail"><slot></slot></div>
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
t([o({
|
|
60
|
+
type: Boolean,
|
|
61
|
+
reflect: !0,
|
|
62
|
+
attribute: "detail-open"
|
|
63
|
+
})], s.prototype, "detailOpen", void 0), s = t([a("dp-list-detail")], s);
|
|
64
|
+
//#endregion
|
|
65
|
+
export { s as DpListDetail };
|
package/dist/shell.js
CHANGED
|
@@ -1,2 +1,109 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { desktopMedia as e } from "./breakpoints.js";
|
|
2
|
+
import { t } from "./decorate-CafwLXg5.js";
|
|
3
|
+
import { LitElement as n, css as r, html as i, nothing as a } from "lit";
|
|
4
|
+
import { customElement as o, property as s } from "lit/decorators.js";
|
|
5
|
+
//#region src/components/shell/dp-shell.ts
|
|
6
|
+
var c = class extends Event {
|
|
7
|
+
constructor() {
|
|
8
|
+
super("dp-shell-sidebar-close", {
|
|
9
|
+
bubbles: !0,
|
|
10
|
+
composed: !0
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}, l = class extends n {
|
|
14
|
+
constructor(...e) {
|
|
15
|
+
super(...e), this.sidebarOpen = !1, this.onKeydown = (e) => {
|
|
16
|
+
e.key === "Escape" && !e.defaultPrevented && this.sidebarOpen && this.dispatchEvent(new c());
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
static {
|
|
20
|
+
this.styles = r`
|
|
21
|
+
:host {
|
|
22
|
+
display: flex;
|
|
23
|
+
width: 100vw;
|
|
24
|
+
height: 100vh;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
background: var(--dc-color-bg, #ffffff);
|
|
28
|
+
color: var(--dc-color-text, #1a1a1e);
|
|
29
|
+
font-family: var(--dc-font-family, system-ui, sans-serif);
|
|
30
|
+
}
|
|
31
|
+
.backdrop {
|
|
32
|
+
position: fixed;
|
|
33
|
+
inset: 0;
|
|
34
|
+
z-index: 30;
|
|
35
|
+
background: var(--dc-color-backdrop, rgba(0, 0, 0, 0.4));
|
|
36
|
+
}
|
|
37
|
+
.sidebar-region {
|
|
38
|
+
flex-shrink: 0;
|
|
39
|
+
display: none;
|
|
40
|
+
}
|
|
41
|
+
:host([sidebar-open]) .sidebar-region {
|
|
42
|
+
display: block;
|
|
43
|
+
position: fixed;
|
|
44
|
+
inset-block: 0;
|
|
45
|
+
inset-inline-start: 0;
|
|
46
|
+
z-index: 40;
|
|
47
|
+
}
|
|
48
|
+
@media ${e} {
|
|
49
|
+
/* Must match (or exceed) \`:host([sidebar-open]) .sidebar-region\`'s
|
|
50
|
+
specificity above (0,3,0) — a bare \`.sidebar-region\` here is only
|
|
51
|
+
(0,1,0), so on equal-or-narrower cascades the desktop override would
|
|
52
|
+
lose to the sidebar-open rule regardless of this media query
|
|
53
|
+
matching, leaving the sidebar permanently \`position: fixed\` (full-
|
|
54
|
+
height overlay) at any width once \`sidebar-open\` is set. Repeating
|
|
55
|
+
the \`:host([sidebar-open])\` prefix here, rather than only the bare
|
|
56
|
+
selector, is what actually lets desktop width win. */
|
|
57
|
+
:host([sidebar-open]) .sidebar-region,
|
|
58
|
+
.sidebar-region {
|
|
59
|
+
display: block;
|
|
60
|
+
position: relative;
|
|
61
|
+
inset: auto;
|
|
62
|
+
z-index: auto;
|
|
63
|
+
}
|
|
64
|
+
.backdrop {
|
|
65
|
+
display: none;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
.main-column {
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
flex: 1;
|
|
72
|
+
min-width: 0;
|
|
73
|
+
}
|
|
74
|
+
.toolbar-row {
|
|
75
|
+
flex-shrink: 0;
|
|
76
|
+
}
|
|
77
|
+
main {
|
|
78
|
+
flex: 1;
|
|
79
|
+
overflow: auto;
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
connectedCallback() {
|
|
84
|
+
super.connectedCallback(), document.addEventListener("keydown", this.onKeydown);
|
|
85
|
+
}
|
|
86
|
+
disconnectedCallback() {
|
|
87
|
+
document.removeEventListener("keydown", this.onKeydown), super.disconnectedCallback();
|
|
88
|
+
}
|
|
89
|
+
render() {
|
|
90
|
+
return i`
|
|
91
|
+
${this.sidebarOpen ? i`<div class="backdrop" @click=${() => this.dispatchEvent(new c())}></div>` : a}
|
|
92
|
+
<div class="sidebar-region"><slot name="sidebar"></slot></div>
|
|
93
|
+
<div class="main-column">
|
|
94
|
+
<div class="toolbar-row">
|
|
95
|
+
<slot name="toolbar"></slot>
|
|
96
|
+
</div>
|
|
97
|
+
<slot name="banner"></slot>
|
|
98
|
+
<main><slot></slot></main>
|
|
99
|
+
</div>
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
t([s({
|
|
104
|
+
type: Boolean,
|
|
105
|
+
reflect: !0,
|
|
106
|
+
attribute: "sidebar-open"
|
|
107
|
+
})], l.prototype, "sidebarOpen", void 0), l = t([o("dp-shell")], l);
|
|
108
|
+
//#endregion
|
|
109
|
+
export { l as DpShell, c as DpShellSidebarCloseEvent };
|
package/dist/shortcut-overlay.js
CHANGED
|
@@ -11,8 +11,8 @@ var c = class extends Event {
|
|
|
11
11
|
}
|
|
12
12
|
}, l = 0, u = class extends t {
|
|
13
13
|
constructor(...e) {
|
|
14
|
-
super(...e), this.shortcuts = [], this.open = !1, this.heading = "Keyboard shortcuts", this.emptyMessage = "No shortcuts registered.", this.closeLabel = "Close", this.headingId = `dp-shortcut-overlay-heading-${++l}`, this.#
|
|
15
|
-
e.key === "Escape" && (e.preventDefault(), this.#
|
|
14
|
+
super(...e), this.shortcuts = [], this.open = !1, this.heading = "Keyboard shortcuts", this.emptyMessage = "No shortcuts registered.", this.closeLabel = "Close", this.headingId = `dp-shortcut-overlay-heading-${++l}`, this.#t = (e) => {
|
|
15
|
+
e.key === "Escape" ? (e.preventDefault(), this.#r()) : e.key === "Tab" && (e.preventDefault(), this.#n()?.focus());
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
static {
|
|
@@ -104,18 +104,22 @@ var c = class extends Event {
|
|
|
104
104
|
`;
|
|
105
105
|
}
|
|
106
106
|
#e;
|
|
107
|
+
#t;
|
|
108
|
+
#n() {
|
|
109
|
+
return this.renderRoot.querySelector(".close");
|
|
110
|
+
}
|
|
107
111
|
updated(e) {
|
|
108
|
-
e.has("open") && (this.open ? window.addEventListener("keydown", this.#e) : window.removeEventListener("keydown", this.#e));
|
|
112
|
+
e.has("open") && (this.open ? (window.addEventListener("keydown", this.#t), this.#e = d(), this.#n()?.focus()) : (window.removeEventListener("keydown", this.#t), e.get("open") === !0 && this.#e?.focus(), this.#e = void 0));
|
|
109
113
|
}
|
|
110
114
|
disconnectedCallback() {
|
|
111
|
-
super.disconnectedCallback(), window.removeEventListener("keydown", this.#
|
|
115
|
+
super.disconnectedCallback(), window.removeEventListener("keydown", this.#t);
|
|
112
116
|
}
|
|
113
|
-
#
|
|
117
|
+
#r() {
|
|
114
118
|
this.dispatchEvent(new c());
|
|
115
119
|
}
|
|
116
120
|
render() {
|
|
117
121
|
return this.open ? r`
|
|
118
|
-
<div class="backdrop" @click=${() => this.#
|
|
122
|
+
<div class="backdrop" @click=${() => this.#r()}>
|
|
119
123
|
<div
|
|
120
124
|
class="panel"
|
|
121
125
|
role="dialog"
|
|
@@ -125,7 +129,7 @@ var c = class extends Event {
|
|
|
125
129
|
>
|
|
126
130
|
<div class="header">
|
|
127
131
|
<h2 id=${this.headingId}>${this.heading}</h2>
|
|
128
|
-
<button class="close" aria-label=${this.closeLabel} @click=${() => this.#
|
|
132
|
+
<button class="close" aria-label=${this.closeLabel} @click=${() => this.#r()}>×</button>
|
|
129
133
|
</div>
|
|
130
134
|
<div class="body">
|
|
131
135
|
${this.shortcuts.length === 0 ? r`<p class="empty">${this.emptyMessage}</p>` : r`
|
|
@@ -148,5 +152,10 @@ e([o({ type: Array })], u.prototype, "shortcuts", void 0), e([o({
|
|
|
148
152
|
type: Boolean,
|
|
149
153
|
reflect: !0
|
|
150
154
|
})], u.prototype, "open", void 0), e([o()], u.prototype, "heading", void 0), e([o({ attribute: "empty-message" })], u.prototype, "emptyMessage", void 0), e([o({ attribute: "close-label" })], u.prototype, "closeLabel", void 0), e([s()], u.prototype, "headingId", void 0), u = e([a("dp-shortcut-overlay")], u);
|
|
155
|
+
function d() {
|
|
156
|
+
let e = document.activeElement;
|
|
157
|
+
for (; e?.shadowRoot?.activeElement;) e = e.shadowRoot.activeElement;
|
|
158
|
+
return e instanceof HTMLElement && e !== document.body ? e : void 0;
|
|
159
|
+
}
|
|
151
160
|
//#endregion
|
|
152
161
|
export { u as DpShortcutOverlay, c as DpShortcutOverlayDismissEvent };
|
package/dist/sidebar.js
CHANGED
|
@@ -11,13 +11,20 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
|
|
|
11
11
|
}), this.itemId = e;
|
|
12
12
|
}
|
|
13
13
|
}, u = class extends Event {
|
|
14
|
+
constructor(e) {
|
|
15
|
+
super("dp-sidebar-activate", {
|
|
16
|
+
bubbles: !0,
|
|
17
|
+
composed: !0
|
|
18
|
+
}), this.itemId = e;
|
|
19
|
+
}
|
|
20
|
+
}, d = class extends Event {
|
|
14
21
|
constructor(e) {
|
|
15
22
|
super("dp-sidebar-action", {
|
|
16
23
|
bubbles: !0,
|
|
17
24
|
composed: !0
|
|
18
25
|
}), this.itemId = e;
|
|
19
26
|
}
|
|
20
|
-
},
|
|
27
|
+
}, f = class extends t {
|
|
21
28
|
constructor(...e) {
|
|
22
29
|
super(...e), this.items = [], this.bottomItems = [], this.activeId = "", this.header = "", this.navLabel = "", this.collapsed = !1, this.toggledGroups = /* @__PURE__ */ new Set();
|
|
23
30
|
}
|
|
@@ -194,7 +201,7 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
|
|
|
194
201
|
<button
|
|
195
202
|
class="item"
|
|
196
203
|
title=${this.collapsed ? e.label : i}
|
|
197
|
-
@click=${() => this.dispatchEvent(new
|
|
204
|
+
@click=${() => this.dispatchEvent(new d(e.id))}
|
|
198
205
|
>
|
|
199
206
|
${this.#r(e.id, e.icon)} ${this.collapsed ? i : r`<span class="label">${e.label}</span>`}
|
|
200
207
|
</button>
|
|
@@ -204,12 +211,15 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
|
|
|
204
211
|
return r`<span class="icon" aria-hidden="true"><slot name=${`icon-${e}`}>${t}</slot></span>`;
|
|
205
212
|
}
|
|
206
213
|
#i(e, t) {
|
|
207
|
-
if (!(t.href !== void 0 && (e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey))
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
214
|
+
if (!(t.href !== void 0 && (e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey))) {
|
|
215
|
+
if (t.id !== this.activeId) {
|
|
216
|
+
if (!this.dispatchEvent(new l(t.id))) {
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
this.activeId = t.id;
|
|
211
221
|
}
|
|
212
|
-
this.
|
|
222
|
+
this.dispatchEvent(new u(t.id));
|
|
213
223
|
}
|
|
214
224
|
}
|
|
215
225
|
#a(e) {
|
|
@@ -217,12 +227,12 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
|
|
|
217
227
|
t.delete(e) || t.add(e), this.toggledGroups = t;
|
|
218
228
|
}
|
|
219
229
|
};
|
|
220
|
-
e([o({ type: Array })],
|
|
230
|
+
e([o({ type: Array })], f.prototype, "items", void 0), e([o({
|
|
221
231
|
type: Array,
|
|
222
232
|
attribute: "bottom-items"
|
|
223
|
-
})],
|
|
233
|
+
})], f.prototype, "bottomItems", void 0), e([o({ attribute: "active-id" })], f.prototype, "activeId", void 0), e([o()], f.prototype, "header", void 0), e([o({ attribute: "nav-label" })], f.prototype, "navLabel", void 0), e([o({
|
|
224
234
|
type: Boolean,
|
|
225
235
|
reflect: !0
|
|
226
|
-
})],
|
|
236
|
+
})], f.prototype, "collapsed", void 0), e([s()], f.prototype, "toggledGroups", void 0), f = e([a("dp-sidebar")], f);
|
|
227
237
|
//#endregion
|
|
228
|
-
export {
|
|
238
|
+
export { f as DpSidebar, d as DpSidebarActionEvent, u as DpSidebarActivateEvent, l as DpSidebarSelectEvent };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/desktop-patterns",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -25,11 +25,21 @@
|
|
|
25
25
|
"import": "./dist/index.js",
|
|
26
26
|
"default": "./dist/index.js"
|
|
27
27
|
},
|
|
28
|
+
"./breakpoints": {
|
|
29
|
+
"types": "./dist/breakpoints.d.ts",
|
|
30
|
+
"import": "./dist/breakpoints.js",
|
|
31
|
+
"default": "./dist/breakpoints.js"
|
|
32
|
+
},
|
|
28
33
|
"./page": {
|
|
29
34
|
"types": "./dist/components/page/dp-page.d.ts",
|
|
30
35
|
"import": "./dist/page.js",
|
|
31
36
|
"default": "./dist/page.js"
|
|
32
37
|
},
|
|
38
|
+
"./list-detail": {
|
|
39
|
+
"types": "./dist/components/list-detail/dp-list-detail.d.ts",
|
|
40
|
+
"import": "./dist/list-detail.js",
|
|
41
|
+
"default": "./dist/list-detail.js"
|
|
42
|
+
},
|
|
33
43
|
"./sidebar": {
|
|
34
44
|
"types": "./dist/components/sidebar/dp-sidebar.d.ts",
|
|
35
45
|
"import": "./dist/sidebar.js",
|
|
@@ -67,16 +77,17 @@
|
|
|
67
77
|
"lit": "^3.3.3"
|
|
68
78
|
},
|
|
69
79
|
"peerDependencies": {
|
|
70
|
-
"@iyulab/desktop-compact": "^0.2.0 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0"
|
|
80
|
+
"@iyulab/desktop-compact": "^0.2.0 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0 || ^0.9.0"
|
|
71
81
|
},
|
|
72
82
|
"devDependencies": {
|
|
73
|
-
"@iyulab/desktop-compact": "^0.
|
|
83
|
+
"@iyulab/desktop-compact": "^0.9.0",
|
|
74
84
|
"@open-wc/testing": "^5.0.0",
|
|
75
85
|
"@storybook/web-components": "^10.6.0",
|
|
76
86
|
"@storybook/web-components-vite": "^10.6.0",
|
|
77
87
|
"@types/mocha": "^10.0.10",
|
|
78
88
|
"@web/dev-server-esbuild": "^2.0.0",
|
|
79
89
|
"@web/test-runner": "^1.0.0",
|
|
90
|
+
"@web/test-runner-commands": "^1.0.1",
|
|
80
91
|
"axe-core": "^4.13.0",
|
|
81
92
|
"storybook": "^10.6.0",
|
|
82
93
|
"typescript": "^6.0.3",
|
package/tokens.css
CHANGED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { t as e } from "./decorate-CafwLXg5.js";
|
|
2
|
-
import { LitElement as t, css as n, html as r, nothing as i, unsafeCSS as a } from "lit";
|
|
3
|
-
import { customElement as o, property as s } from "lit/decorators.js";
|
|
4
|
-
//#region src/breakpoints.ts
|
|
5
|
-
var c = 1024, l = a(`(min-width: ${c}px)`), u = class extends Event {
|
|
6
|
-
constructor() {
|
|
7
|
-
super("dp-shell-sidebar-close", {
|
|
8
|
-
bubbles: !0,
|
|
9
|
-
composed: !0
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
}, d = class extends t {
|
|
13
|
-
constructor(...e) {
|
|
14
|
-
super(...e), this.sidebarOpen = !1, this.onKeydown = (e) => {
|
|
15
|
-
e.key === "Escape" && !e.defaultPrevented && this.sidebarOpen && this.dispatchEvent(new u());
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
static {
|
|
19
|
-
this.styles = n`
|
|
20
|
-
:host {
|
|
21
|
-
display: flex;
|
|
22
|
-
width: 100vw;
|
|
23
|
-
height: 100vh;
|
|
24
|
-
overflow: hidden;
|
|
25
|
-
box-sizing: border-box;
|
|
26
|
-
background: var(--dc-color-bg, #ffffff);
|
|
27
|
-
color: var(--dc-color-text, #1a1a1e);
|
|
28
|
-
font-family: var(--dc-font-family, system-ui, sans-serif);
|
|
29
|
-
}
|
|
30
|
-
.backdrop {
|
|
31
|
-
position: fixed;
|
|
32
|
-
inset: 0;
|
|
33
|
-
z-index: 30;
|
|
34
|
-
background: var(--dc-color-backdrop, rgba(0, 0, 0, 0.4));
|
|
35
|
-
}
|
|
36
|
-
.sidebar-region {
|
|
37
|
-
flex-shrink: 0;
|
|
38
|
-
display: none;
|
|
39
|
-
}
|
|
40
|
-
:host([sidebar-open]) .sidebar-region {
|
|
41
|
-
display: block;
|
|
42
|
-
position: fixed;
|
|
43
|
-
inset-block: 0;
|
|
44
|
-
inset-inline-start: 0;
|
|
45
|
-
z-index: 40;
|
|
46
|
-
}
|
|
47
|
-
@media ${l} {
|
|
48
|
-
/* Must match (or exceed) \`:host([sidebar-open]) .sidebar-region\`'s
|
|
49
|
-
specificity above (0,3,0) — a bare \`.sidebar-region\` here is only
|
|
50
|
-
(0,1,0), so on equal-or-narrower cascades the desktop override would
|
|
51
|
-
lose to the sidebar-open rule regardless of this media query
|
|
52
|
-
matching, leaving the sidebar permanently \`position: fixed\` (full-
|
|
53
|
-
height overlay) at any width once \`sidebar-open\` is set. Repeating
|
|
54
|
-
the \`:host([sidebar-open])\` prefix here, rather than only the bare
|
|
55
|
-
selector, is what actually lets desktop width win. */
|
|
56
|
-
:host([sidebar-open]) .sidebar-region,
|
|
57
|
-
.sidebar-region {
|
|
58
|
-
display: block;
|
|
59
|
-
position: relative;
|
|
60
|
-
inset: auto;
|
|
61
|
-
z-index: auto;
|
|
62
|
-
}
|
|
63
|
-
.backdrop {
|
|
64
|
-
display: none;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
.main-column {
|
|
68
|
-
display: flex;
|
|
69
|
-
flex-direction: column;
|
|
70
|
-
flex: 1;
|
|
71
|
-
min-width: 0;
|
|
72
|
-
}
|
|
73
|
-
.toolbar-row {
|
|
74
|
-
flex-shrink: 0;
|
|
75
|
-
}
|
|
76
|
-
main {
|
|
77
|
-
flex: 1;
|
|
78
|
-
overflow: auto;
|
|
79
|
-
}
|
|
80
|
-
`;
|
|
81
|
-
}
|
|
82
|
-
connectedCallback() {
|
|
83
|
-
super.connectedCallback(), document.addEventListener("keydown", this.onKeydown);
|
|
84
|
-
}
|
|
85
|
-
disconnectedCallback() {
|
|
86
|
-
document.removeEventListener("keydown", this.onKeydown), super.disconnectedCallback();
|
|
87
|
-
}
|
|
88
|
-
render() {
|
|
89
|
-
return r`
|
|
90
|
-
${this.sidebarOpen ? r`<div class="backdrop" @click=${() => this.dispatchEvent(new u())}></div>` : i}
|
|
91
|
-
<div class="sidebar-region"><slot name="sidebar"></slot></div>
|
|
92
|
-
<div class="main-column">
|
|
93
|
-
<div class="toolbar-row">
|
|
94
|
-
<slot name="toolbar"></slot>
|
|
95
|
-
</div>
|
|
96
|
-
<slot name="banner"></slot>
|
|
97
|
-
<main><slot></slot></main>
|
|
98
|
-
</div>
|
|
99
|
-
`;
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
e([s({
|
|
103
|
-
type: Boolean,
|
|
104
|
-
reflect: !0,
|
|
105
|
-
attribute: "sidebar-open"
|
|
106
|
-
})], d.prototype, "sidebarOpen", void 0), d = e([o("dp-shell")], d);
|
|
107
|
-
//#endregion
|
|
108
|
-
export { c as i, u as n, l as r, d as t };
|