@iyulab/desktop-patterns 0.4.0 → 0.6.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 CHANGED
@@ -51,22 +51,58 @@ 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/breakpoints' // no elements registered
55
+
54
56
  const shell = document.querySelector('dp-shell')!
55
- document.querySelector('dp-sidebar')!.addEventListener('dp-sidebar-select', (e) => {
57
+ const sidebar = document.querySelector('dp-sidebar')!
58
+ const toolbar = document.querySelector('dp-toolbar')!
59
+ const wide = matchMedia(`(min-width: ${desktopMinWidth}px)`)
60
+ let collapsed = false // the consumer's own state: a wide window's sidebar folded to its rail
61
+
62
+ function sync() {
63
+ sidebar.collapsed = collapsed && wide.matches // a drawer always shows the whole sidebar
64
+ toolbar.expanded = wide.matches ? !collapsed : shell.sidebarOpen
65
+ }
66
+ wide.addEventListener('change', sync)
67
+ sidebar.addEventListener('dp-sidebar-select', () => {
56
68
  // consumer owns activeId / routing
57
- shell.sidebarOpen = false // a drawer gives way to what was picked
58
69
  })
59
- document.querySelector('dp-toolbar')!.addEventListener('dp-toolbar-toggle', () => {
60
- shell.sidebarOpen = !shell.sidebarOpen
70
+ sidebar.addEventListener('dp-sidebar-activate', () => {
71
+ shell.sidebarOpen = false // a drawer gives way to what was picked — the place already shown included
72
+ sync()
61
73
  })
62
- shell.addEventListener('dp-shell-sidebar-close', () => (shell.sidebarOpen = false)) // backdrop, Escape
74
+ toolbar.addEventListener('dp-toolbar-toggle', () => {
75
+ if (wide.matches) collapsed = !collapsed
76
+ else shell.sidebarOpen = !shell.sidebarOpen
77
+ sync()
78
+ })
79
+ shell.addEventListener('dp-shell-sidebar-close', () => { shell.sidebarOpen = false; sync() }) // backdrop, Escape
80
+ ```
81
+
82
+ Below the desktop breakpoint (`desktopMinWidth`, 1024px — `desktopMedia` for a component's styles)
83
+ the sidebar is a drawer over the content, open while `sidebar-open` is set; at and above it the
84
+ sidebar sits beside the content and `sidebar-open` changes nothing. The same toolbar toggle serves
85
+ both widths: in a narrow window it opens the drawer, in a wide one it folds the sidebar to its
86
+ collapsed rail (`dp-sidebar` `collapsed`). Give the toggle `expanded` so assistive technology hears
87
+ which state it is in. The consumer owns both states — start the drawer closed, close it when the
88
+ shell asks (backdrop click, Escape) and when a place is picked, or a narrow window stays covered.
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>
63
99
  ```
64
100
 
65
- Below the desktop breakpoint (1024px) the sidebar is a drawer over the content, open while
66
- `sidebar-open` is set; at and above it the sidebar always sits beside the content and `sidebar-open`
67
- changes nothing, and `dp-toolbar` hides its toggle there. The breakpoint is exported as
68
- `desktopMinWidth` (and `desktopMedia` for a component's styles). The consumer owns that state — start it closed, and close it when the shell asks
69
- (backdrop click, Escape) and when a place is picked, or a narrow window stays covered.
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`.
70
106
 
71
107
  ### Sidebar items: links, groups, icons, actions
72
108
 
@@ -91,6 +127,9 @@ nav.setAttribute('nav-label', 'Main') // names t
91
127
  items show, separated by a rule.
92
128
  - **Icons**: each item's icon is a slot named `icon-<id>` whose fallback is the item's `icon` text — slot any icon
93
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.
94
133
  - **Bottom items are actions**: each click fires `dp-sidebar-action` (`itemId`) — every time, so a failed action can
95
134
  be retried — and never moves `aria-current`. (In 0.2.x they fired `dp-sidebar-select` and took the active mark.)
96
135
 
@@ -108,7 +147,7 @@ import '@iyulab/desktop-patterns/tokens.css'
108
147
 
109
148
  Color/spacing/radius/typography all come from `desktop-compact`'s `--dc-*` tokens — this package adds
110
149
  only the structural dimensions `desktop-compact` has no concept of (`--dp-sidebar-width`,
111
- `--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
112
151
  values, so it still renders correctly even without either stylesheet.
113
152
 
114
153
  ## Platform boundary — drag regions
@@ -130,13 +169,14 @@ dp-toolbar[drag-region]::part(drag-handle) {
130
169
  `toolbar`, so ownership of the drag handle stays entirely with whatever renders there (typically
131
170
  `dp-toolbar`).
132
171
 
133
- ## Components (v1 — complete, 5/5)
172
+ ## Components
134
173
 
135
174
  | Component | Description |
136
175
  |---|---|
137
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 |
138
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 |
139
- | `dp-toolbar` | App-shell header bar — `heading`/`subtitle`, an optional sidebar drawer toggle (below the desktop breakpoint only), a right-side `actions` slot |
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 |
140
180
  | `dp-shell` | Top-level layout composing `sidebar`/`toolbar`/`banner`/main-content regions, with a responsive drawer (backdrop + overlay sidebar) below a 1024px breakpoint |
141
181
  | `dp-shortcut-overlay` | Display-only keyboard-shortcuts help panel — a `shortcuts` list + `open` flag; no registration/binding infrastructure (that stays the consumer's) |
142
182
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The desktop breakpoint, in CSS pixels. Below it `dp-shell`'s sidebar is a drawer over the
3
- * content; at and above it the sidebar always sits beside the content, so `dp-toolbar` hides the
4
- * toggle that opens the drawer.
3
+ * content; at and above it the sidebar sits beside the content, where a consumer may fold
4
+ * `dp-sidebar` to its collapsed rail instead.
5
5
  */
6
6
  export declare const desktopMinWidth = 1024;
7
7
  /** The media query for {@link desktopMinWidth}, for a component's styles. */
@@ -2,4 +2,4 @@ import { unsafeCSS as e } from "lit";
2
2
  //#region src/breakpoints.ts
3
3
  var t = 1024, n = e(`(min-width: ${t}px)`);
4
4
  //#endregion
5
- export { t as n, n as t };
5
+ export { n as desktopMedia, t as desktopMinWidth };
@@ -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). This component only dismisses itself (outside
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;
@@ -4,9 +4,9 @@ export declare class DpToolbarToggleEvent extends Event {
4
4
  }
5
5
  /**
6
6
  * App-shell header bar: a `heading` (optionally overridden per-view by
7
- * `subtitle`), an optional toggle for `dp-shell`'s sidebar drawer — shown
8
- * only below the desktop breakpoint, where the sidebar is a drawer — and a
9
- * right-side `actions` slot. Pure layout — this repo does not call any platform API.
7
+ * `subtitle`), an optional sidebar toggle — the consumer decides what it does
8
+ * (open `dp-shell`'s drawer in a narrow window, fold `dp-sidebar` to its rail
9
+ * in a wide one) — and a right-side `actions` slot. Pure layout — this repo does not call any platform API.
10
10
  *
11
11
  * Named `heading`, not `title` — matches dc-section-heading's own
12
12
  * established convention for this kind of headline text, and avoids
@@ -42,8 +42,9 @@ export declare class DpToolbar extends LitElement {
42
42
  static styles: import('lit').CSSResult;
43
43
  heading: string;
44
44
  subtitle: string;
45
- /** Shows the sidebar drawer toggle. It is hidden at and above the desktop breakpoint. */
46
45
  showToggle: boolean;
46
+ /** Whether the sidebar the toggle controls is shown in full — announced as the toggle's `aria-expanded`. */
47
+ expanded: boolean;
47
48
  /** Accessible name of the sidebar toggle — supply one in the app's language. */
48
49
  toggleLabel: string;
49
50
  dragRegion: boolean;
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,7 +1,8 @@
1
- import { n as e, t } from "./breakpoints-BjS_v3PV.js";
1
+ import { desktopMedia as e, desktopMinWidth as t } from "./breakpoints.js";
2
2
  import { DpPage as n } from "./page.js";
3
- import { DpSidebar as r, DpSidebarActionEvent as i, DpSidebarSelectEvent as a } from "./sidebar.js";
4
- import { DpToolbar as o, DpToolbarToggleEvent as s } from "./toolbar.js";
5
- import { DpShell as c, DpShellSidebarCloseEvent as l } from "./shell.js";
6
- import { DpShortcutOverlay as u, DpShortcutOverlayDismissEvent as d } from "./shortcut-overlay.js";
7
- export { n as DpPage, c as DpShell, l as DpShellSidebarCloseEvent, u as DpShortcutOverlay, d as DpShortcutOverlayDismissEvent, r as DpSidebar, i as DpSidebarActionEvent, a as DpSidebarSelectEvent, o as DpToolbar, s as DpToolbarToggleEvent, t as desktopMedia, e as desktopMinWidth };
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";
5
+ import { DpToolbar as c, DpToolbarToggleEvent as l } from "./toolbar.js";
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,4 +1,4 @@
1
- import { t as e } from "./breakpoints-BjS_v3PV.js";
1
+ import { desktopMedia as e } from "./breakpoints.js";
2
2
  import { t } from "./decorate-CafwLXg5.js";
3
3
  import { LitElement as n, css as r, html as i, nothing as a } from "lit";
4
4
  import { customElement as o, property as s } from "lit/decorators.js";
@@ -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.#e = (e) => {
15
- e.key === "Escape" && (e.preventDefault(), this.#t());
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.#e);
115
+ super.disconnectedCallback(), window.removeEventListener("keydown", this.#t);
112
116
  }
113
- #t() {
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.#t()}>
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.#t()}>×</button>
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
- }, d = class extends t {
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 u(e.id))}
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)) && t.id !== this.activeId) {
208
- if (!this.dispatchEvent(new l(t.id))) {
209
- e.preventDefault();
210
- return;
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.activeId = t.id;
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 })], d.prototype, "items", void 0), e([o({
230
+ e([o({ type: Array })], f.prototype, "items", void 0), e([o({
221
231
  type: Array,
222
232
  attribute: "bottom-items"
223
- })], d.prototype, "bottomItems", void 0), e([o({ attribute: "active-id" })], d.prototype, "activeId", void 0), e([o()], d.prototype, "header", void 0), e([o({ attribute: "nav-label" })], d.prototype, "navLabel", void 0), e([o({
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
- })], d.prototype, "collapsed", void 0), e([s()], d.prototype, "toggledGroups", void 0), d = e([a("dp-sidebar")], d);
236
+ })], f.prototype, "collapsed", void 0), e([s()], f.prototype, "toggledGroups", void 0), f = e([a("dp-sidebar")], f);
227
237
  //#endregion
228
- export { d as DpSidebar, u as DpSidebarActionEvent, l as DpSidebarSelectEvent };
238
+ export { f as DpSidebar, d as DpSidebarActionEvent, u as DpSidebarActivateEvent, l as DpSidebarSelectEvent };
package/dist/toolbar.js CHANGED
@@ -1,21 +1,20 @@
1
- import { t as e } from "./breakpoints-BjS_v3PV.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";
1
+ import { t as e } from "./decorate-CafwLXg5.js";
2
+ import { LitElement as t, css as n, html as r, nothing as i } from "lit";
3
+ import { customElement as a, property as o } from "lit/decorators.js";
5
4
  //#region src/components/toolbar/dp-toolbar.ts
6
- var c = class extends Event {
5
+ var s = class extends Event {
7
6
  constructor() {
8
7
  super("dp-toolbar-toggle", {
9
8
  bubbles: !0,
10
9
  composed: !0
11
10
  });
12
11
  }
13
- }, l = class extends n {
12
+ }, c = class extends t {
14
13
  constructor(...e) {
15
- super(...e), this.heading = "", this.subtitle = "", this.showToggle = !1, this.toggleLabel = "Toggle sidebar", this.dragRegion = !1;
14
+ super(...e), this.heading = "", this.subtitle = "", this.showToggle = !1, this.expanded = !1, this.toggleLabel = "Toggle sidebar", this.dragRegion = !1;
16
15
  }
17
16
  static {
18
- this.styles = r`
17
+ this.styles = n`
19
18
  :host {
20
19
  display: flex;
21
20
  align-items: center;
@@ -60,13 +59,6 @@ var c = class extends Event {
60
59
  outline: 2px solid var(--dc-color-accent, #2563eb);
61
60
  outline-offset: 2px;
62
61
  }
63
- /* At desktop width dp-shell's sidebar is always beside the content: a drawer toggle there
64
- would do nothing. */
65
- @media ${e} {
66
- .toggle {
67
- display: none;
68
- }
69
- }
70
62
  .drag-handle {
71
63
  flex: 1;
72
64
  align-self: stretch;
@@ -83,15 +75,16 @@ var c = class extends Event {
83
75
  super.connectedCallback(), this.setAttribute("role", "banner");
84
76
  }
85
77
  render() {
86
- return i`
78
+ return r`
87
79
  <div class="start">
88
- ${this.showToggle ? i`<button
80
+ ${this.showToggle ? r`<button
89
81
  class="toggle"
90
82
  aria-label=${this.toggleLabel}
91
- @click=${() => this.dispatchEvent(new c())}
83
+ aria-expanded=${this.expanded ? "true" : "false"}
84
+ @click=${() => this.dispatchEvent(new s())}
92
85
  >
93
86
  ☰
94
- </button>` : a}
87
+ </button>` : i}
95
88
  <span class="title">${this.subtitle || this.heading}</span>
96
89
  </div>
97
90
  <div class="drag-handle" part="drag-handle"></div>
@@ -99,13 +92,13 @@ var c = class extends Event {
99
92
  `;
100
93
  }
101
94
  };
102
- t([s()], l.prototype, "heading", void 0), t([s()], l.prototype, "subtitle", void 0), t([s({
95
+ e([o()], c.prototype, "heading", void 0), e([o()], c.prototype, "subtitle", void 0), e([o({
103
96
  type: Boolean,
104
97
  attribute: "show-toggle"
105
- })], l.prototype, "showToggle", void 0), t([s({ attribute: "toggle-label" })], l.prototype, "toggleLabel", void 0), t([s({
98
+ })], c.prototype, "showToggle", void 0), e([o({ type: Boolean })], c.prototype, "expanded", void 0), e([o({ attribute: "toggle-label" })], c.prototype, "toggleLabel", void 0), e([o({
106
99
  type: Boolean,
107
100
  reflect: !0,
108
101
  attribute: "drag-region"
109
- })], l.prototype, "dragRegion", void 0), l = t([o("dp-toolbar")], l);
102
+ })], c.prototype, "dragRegion", void 0), c = e([a("dp-toolbar")], c);
110
103
  //#endregion
111
- export { l as DpToolbar, c as DpToolbarToggleEvent };
104
+ export { c as DpToolbar, s as DpToolbarToggleEvent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/desktop-patterns",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
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",
@@ -77,6 +87,7 @@
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
@@ -11,4 +11,6 @@
11
11
  --dp-sidebar-width: 220px;
12
12
  --dp-sidebar-width-collapsed: 52px;
13
13
  --dp-header-height: 52px;
14
+ /* dp-list-detail's list pane: as wide as the sidebar, so navigation and list are equal columns. */
15
+ --dp-list-detail-list-width: var(--dp-sidebar-width);
14
16
  }