@iyulab/desktop-patterns 0.4.0 → 0.5.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,39 @@ 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'
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
69
  shell.sidebarOpen = false // a drawer gives way to what was picked
70
+ sync()
58
71
  })
59
- document.querySelector('dp-toolbar')!.addEventListener('dp-toolbar-toggle', () => {
60
- shell.sidebarOpen = !shell.sidebarOpen
72
+ toolbar.addEventListener('dp-toolbar-toggle', () => {
73
+ if (wide.matches) collapsed = !collapsed
74
+ else shell.sidebarOpen = !shell.sidebarOpen
75
+ sync()
61
76
  })
62
- shell.addEventListener('dp-shell-sidebar-close', () => (shell.sidebarOpen = false)) // backdrop, Escape
77
+ shell.addEventListener('dp-shell-sidebar-close', () => { shell.sidebarOpen = false; sync() }) // backdrop, Escape
63
78
  ```
64
79
 
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.
80
+ Below the desktop breakpoint (`desktopMinWidth`, 1024px — `desktopMedia` for a component's styles)
81
+ the sidebar is a drawer over the content, open while `sidebar-open` is set; at and above it the
82
+ sidebar sits beside the content and `sidebar-open` changes nothing. The same toolbar toggle serves
83
+ both widths: in a narrow window it opens the drawer, in a wide one it folds the sidebar to its
84
+ collapsed rail (`dp-sidebar` `collapsed`). Give the toggle `expanded` so assistive technology hears
85
+ which state it is in. The consumer owns both states — start the drawer closed, close it when the
86
+ shell asks (backdrop click, Escape) and when a place is picked, or a narrow window stays covered.
70
87
 
71
88
  ### Sidebar items: links, groups, icons, actions
72
89
 
@@ -136,7 +153,7 @@ dp-toolbar[drag-region]::part(drag-handle) {
136
153
  |---|---|
137
154
  | `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) |
138
155
  | `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 |
156
+ | `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
157
  | `dp-shell` | Top-level layout composing `sidebar`/`toolbar`/`banner`/main-content regions, with a responsive drawer (backdrop + overlay sidebar) below a 1024px breakpoint |
141
158
  | `dp-shortcut-overlay` | Display-only keyboard-shortcuts help panel — a `shortcuts` list + `open` flag; no registration/binding infrastructure (that stays the consumer's) |
142
159
 
@@ -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. */
@@ -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;
@@ -0,0 +1,108 @@
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 };
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
- import { n as e, t } from "./breakpoints-BjS_v3PV.js";
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";
1
+ import { i as e, n as t, r as n, t as r } from "./dp-shell-CvV92B8B.js";
2
+ import { DpPage as i } from "./page.js";
3
+ import { DpSidebar as a, DpSidebarActionEvent as o, DpSidebarSelectEvent as s } from "./sidebar.js";
4
+ import { DpToolbar as c, DpToolbarToggleEvent as l } from "./toolbar.js";
6
5
  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 };
6
+ export { i as DpPage, r as DpShell, t as DpShellSidebarCloseEvent, u as DpShortcutOverlay, d as DpShortcutOverlayDismissEvent, a as DpSidebar, o as DpSidebarActionEvent, s as DpSidebarSelectEvent, c as DpToolbar, l as DpToolbarToggleEvent, n as desktopMedia, e as desktopMinWidth };
package/dist/shell.js CHANGED
@@ -1,109 +1,2 @@
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";
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 };
1
+ import { n as e, t } from "./dp-shell-CvV92B8B.js";
2
+ export { t as DpShell, e as DpShellSidebarCloseEvent };
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.5.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,5 +0,0 @@
1
- import { unsafeCSS as e } from "lit";
2
- //#region src/breakpoints.ts
3
- var t = 1024, n = e(`(min-width: ${t}px)`);
4
- //#endregion
5
- export { t as n, n as t };