@iyulab/desktop-patterns 0.3.3 → 0.4.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
@@ -33,7 +33,7 @@ import '@iyulab/desktop-patterns'
33
33
  ## Usage
34
34
 
35
35
  ```html
36
- <dp-shell sidebar-open>
36
+ <dp-shell>
37
37
  <dp-sidebar slot="sidebar" active-id="overview" header="My App" id="nav"></dp-sidebar>
38
38
  <dp-toolbar slot="toolbar" heading="My App" show-toggle></dp-toolbar>
39
39
  <dp-page>
@@ -51,14 +51,23 @@ document.querySelector<HTMLElementTagNameMap['dp-sidebar']>('#nav')!.items = [
51
51
  { id: 'overview', icon: '■', label: 'Overview' },
52
52
  ]
53
53
 
54
+ const shell = document.querySelector('dp-shell')!
54
55
  document.querySelector('dp-sidebar')!.addEventListener('dp-sidebar-select', (e) => {
55
56
  // consumer owns activeId / routing
57
+ shell.sidebarOpen = false // a drawer gives way to what was picked
56
58
  })
57
59
  document.querySelector('dp-toolbar')!.addEventListener('dp-toolbar-toggle', () => {
58
- // consumer owns sidebar-open state on dp-shell
60
+ shell.sidebarOpen = !shell.sidebarOpen
59
61
  })
62
+ shell.addEventListener('dp-shell-sidebar-close', () => (shell.sidebarOpen = false)) // backdrop, Escape
60
63
  ```
61
64
 
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.
70
+
62
71
  ### Sidebar items: links, groups, icons, actions
63
72
 
64
73
  ```ts
@@ -125,9 +134,9 @@ dp-toolbar[drag-region]::part(drag-handle) {
125
134
 
126
135
  | Component | Description |
127
136
  |---|---|
128
- | `dp-page` | Scrollable content region with a centered, max-width column — the page-body every view renders into |
137
+ | `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) |
129
138
  | `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 |
130
- | `dp-toolbar` | App-shell header bar — `heading`/`subtitle`, an optional collapse toggle, a right-side `actions` slot |
139
+ | `dp-toolbar` | App-shell header bar — `heading`/`subtitle`, an optional sidebar drawer toggle (below the desktop breakpoint only), a right-side `actions` slot |
131
140
  | `dp-shell` | Top-level layout composing `sidebar`/`toolbar`/`banner`/main-content regions, with a responsive drawer (backdrop + overlay sidebar) below a 1024px breakpoint |
132
141
  | `dp-shortcut-overlay` | Display-only keyboard-shortcuts help panel — a `shortcuts` list + `open` flag; no registration/binding infrastructure (that stays the consumer's) |
133
142
 
@@ -0,0 +1,5 @@
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 };
@@ -0,0 +1,8 @@
1
+ /**
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.
5
+ */
6
+ export declare const desktopMinWidth = 1024;
7
+ /** The media query for {@link desktopMinWidth}, for a component's styles. */
8
+ export declare const desktopMedia: import('lit').CSSResult;
@@ -4,10 +4,17 @@ export type PageMaxWidth = 'sm' | 'md' | 'lg' | 'xl' | 'full';
4
4
  * Scrollable content region with a centered, max-width column — the page-body
5
5
  * pattern every view in an app shell renders into. Pure layout: it has no
6
6
  * opinion on what it contains (default slot only).
7
+ *
8
+ * With `fill`, the page does not scroll: its column fills the region's height
9
+ * (a flex column), for a view that divides its own scrolling — a list beside
10
+ * the item open, where the list scrolls and the item stays in view. The
11
+ * slotted view sizes itself within that height (e.g. `flex: 1; min-height: 0`).
7
12
  */
8
13
  export declare class DpPage extends LitElement {
9
14
  static styles: import('lit').CSSResult;
10
15
  maxWidth: PageMaxWidth;
16
+ /** Fill the region's height instead of scrolling: the view inside divides its own scrolling. */
17
+ fill: boolean;
11
18
  render(): import('lit-html').TemplateResult<1>;
12
19
  }
13
20
  declare global {
@@ -28,7 +28,16 @@ export declare class DpShellSidebarCloseEvent extends Event {
28
28
  */
29
29
  export declare class DpShell extends LitElement {
30
30
  static styles: import('lit').CSSResult;
31
+ /**
32
+ * Whether the sidebar is open as a drawer below the desktop breakpoint. Above it the sidebar is
33
+ * always shown beside the content and this changes nothing. The consumer owns it: start it closed,
34
+ * and set it back to false on `dp-shell-sidebar-close` (backdrop click or Escape) and after a
35
+ * place is picked in the drawer.
36
+ */
31
37
  sidebarOpen: boolean;
38
+ private readonly onKeydown;
39
+ connectedCallback(): void;
40
+ disconnectedCallback(): void;
32
41
  render(): import('lit-html').TemplateResult<1>;
33
42
  }
34
43
  declare global {
@@ -4,8 +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 collapse/expand toggle, and a right-side
8
- * `actions` slot. Pure layout — this repo does not call any platform API.
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.
9
10
  *
10
11
  * Named `heading`, not `title` — matches dc-section-heading's own
11
12
  * established convention for this kind of headline text, and avoids
@@ -41,6 +42,7 @@ export declare class DpToolbar extends LitElement {
41
42
  static styles: import('lit').CSSResult;
42
43
  heading: string;
43
44
  subtitle: string;
45
+ /** Shows the sidebar drawer toggle. It is hidden at and above the desktop breakpoint. */
44
46
  showToggle: boolean;
45
47
  /** Accessible name of the sidebar toggle — supply one in the app's language. */
46
48
  toggleLabel: string;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './breakpoints.js';
1
2
  export * from './components/page/dp-page.js';
2
3
  export * from './components/sidebar/dp-sidebar.js';
3
4
  export * from './components/toolbar/dp-toolbar.js';
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
- import { DpPage as e } from "./page.js";
2
- import { DpSidebar as t, DpSidebarActionEvent as n, DpSidebarSelectEvent as r } from "./sidebar.js";
3
- import { DpToolbar as i, DpToolbarToggleEvent as a } from "./toolbar.js";
4
- import { DpShell as o, DpShellSidebarCloseEvent as s } from "./shell.js";
5
- import { DpShortcutOverlay as c, DpShortcutOverlayDismissEvent as l } from "./shortcut-overlay.js";
6
- export { e as DpPage, o as DpShell, s as DpShellSidebarCloseEvent, c as DpShortcutOverlay, l as DpShortcutOverlayDismissEvent, t as DpSidebar, n as DpSidebarActionEvent, r as DpSidebarSelectEvent, i as DpToolbar, a as DpToolbarToggleEvent };
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";
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 };
package/dist/page.js CHANGED
@@ -10,7 +10,7 @@ var o = {
10
10
  full: "none"
11
11
  }, s = class extends t {
12
12
  constructor(...e) {
13
- super(...e), this.maxWidth = "md";
13
+ super(...e), this.maxWidth = "md", this.fill = !1;
14
14
  }
15
15
  static {
16
16
  this.styles = n`
@@ -25,6 +25,14 @@ var o = {
25
25
  padding: var(--dc-space-6, 24px);
26
26
  box-sizing: border-box;
27
27
  }
28
+ :host([fill]) {
29
+ overflow: hidden;
30
+ }
31
+ :host([fill]) .inner {
32
+ display: flex;
33
+ flex-direction: column;
34
+ height: 100%;
35
+ }
28
36
  `;
29
37
  }
30
38
  render() {
@@ -38,6 +46,9 @@ var o = {
38
46
  e([a({
39
47
  attribute: "max-width",
40
48
  reflect: !0
41
- })], s.prototype, "maxWidth", void 0), s = e([i("dp-page")], s);
49
+ })], s.prototype, "maxWidth", void 0), e([a({
50
+ type: Boolean,
51
+ reflect: !0
52
+ })], s.prototype, "fill", void 0), s = e([i("dp-page")], s);
42
53
  //#endregion
43
54
  export { s as DpPage };
package/dist/shell.js CHANGED
@@ -1,20 +1,23 @@
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";
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";
4
5
  //#region src/components/shell/dp-shell.ts
5
- var s = class extends Event {
6
+ var c = class extends Event {
6
7
  constructor() {
7
8
  super("dp-shell-sidebar-close", {
8
9
  bubbles: !0,
9
10
  composed: !0
10
11
  });
11
12
  }
12
- }, c = class extends t {
13
+ }, l = class extends n {
13
14
  constructor(...e) {
14
- super(...e), this.sidebarOpen = !1;
15
+ super(...e), this.sidebarOpen = !1, this.onKeydown = (e) => {
16
+ e.key === "Escape" && !e.defaultPrevented && this.sidebarOpen && this.dispatchEvent(new c());
17
+ };
15
18
  }
16
19
  static {
17
- this.styles = n`
20
+ this.styles = r`
18
21
  :host {
19
22
  display: flex;
20
23
  width: 100vw;
@@ -42,7 +45,7 @@ var s = class extends Event {
42
45
  inset-inline-start: 0;
43
46
  z-index: 40;
44
47
  }
45
- @media (min-width: 1024px) {
48
+ @media ${e} {
46
49
  /* Must match (or exceed) \`:host([sidebar-open]) .sidebar-region\`'s
47
50
  specificity above (0,3,0) — a bare \`.sidebar-region\` here is only
48
51
  (0,1,0), so on equal-or-narrower cascades the desktop override would
@@ -77,9 +80,15 @@ var s = class extends Event {
77
80
  }
78
81
  `;
79
82
  }
83
+ connectedCallback() {
84
+ super.connectedCallback(), document.addEventListener("keydown", this.onKeydown);
85
+ }
86
+ disconnectedCallback() {
87
+ document.removeEventListener("keydown", this.onKeydown), super.disconnectedCallback();
88
+ }
80
89
  render() {
81
- return r`
82
- ${this.sidebarOpen ? r`<div class="backdrop" @click=${() => this.dispatchEvent(new s())}></div>` : i}
90
+ return i`
91
+ ${this.sidebarOpen ? i`<div class="backdrop" @click=${() => this.dispatchEvent(new c())}></div>` : a}
83
92
  <div class="sidebar-region"><slot name="sidebar"></slot></div>
84
93
  <div class="main-column">
85
94
  <div class="toolbar-row">
@@ -91,10 +100,10 @@ var s = class extends Event {
91
100
  `;
92
101
  }
93
102
  };
94
- e([o({
103
+ t([s({
95
104
  type: Boolean,
96
105
  reflect: !0,
97
106
  attribute: "sidebar-open"
98
- })], c.prototype, "sidebarOpen", void 0), c = e([a("dp-shell")], c);
107
+ })], l.prototype, "sidebarOpen", void 0), l = t([o("dp-shell")], l);
99
108
  //#endregion
100
- export { c as DpShell, s as DpShellSidebarCloseEvent };
109
+ export { l as DpShell, c as DpShellSidebarCloseEvent };
package/dist/toolbar.js CHANGED
@@ -1,20 +1,21 @@
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";
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";
4
5
  //#region src/components/toolbar/dp-toolbar.ts
5
- var s = class extends Event {
6
+ var c = class extends Event {
6
7
  constructor() {
7
8
  super("dp-toolbar-toggle", {
8
9
  bubbles: !0,
9
10
  composed: !0
10
11
  });
11
12
  }
12
- }, c = class extends t {
13
+ }, l = class extends n {
13
14
  constructor(...e) {
14
15
  super(...e), this.heading = "", this.subtitle = "", this.showToggle = !1, this.toggleLabel = "Toggle sidebar", this.dragRegion = !1;
15
16
  }
16
17
  static {
17
- this.styles = n`
18
+ this.styles = r`
18
19
  :host {
19
20
  display: flex;
20
21
  align-items: center;
@@ -59,6 +60,13 @@ var s = class extends Event {
59
60
  outline: 2px solid var(--dc-color-accent, #2563eb);
60
61
  outline-offset: 2px;
61
62
  }
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
+ }
62
70
  .drag-handle {
63
71
  flex: 1;
64
72
  align-self: stretch;
@@ -75,15 +83,15 @@ var s = class extends Event {
75
83
  super.connectedCallback(), this.setAttribute("role", "banner");
76
84
  }
77
85
  render() {
78
- return r`
86
+ return i`
79
87
  <div class="start">
80
- ${this.showToggle ? r`<button
88
+ ${this.showToggle ? i`<button
81
89
  class="toggle"
82
90
  aria-label=${this.toggleLabel}
83
- @click=${() => this.dispatchEvent(new s())}
91
+ @click=${() => this.dispatchEvent(new c())}
84
92
  >
85
93
  ☰
86
- </button>` : i}
94
+ </button>` : a}
87
95
  <span class="title">${this.subtitle || this.heading}</span>
88
96
  </div>
89
97
  <div class="drag-handle" part="drag-handle"></div>
@@ -91,13 +99,13 @@ var s = class extends Event {
91
99
  `;
92
100
  }
93
101
  };
94
- e([o()], c.prototype, "heading", void 0), e([o()], c.prototype, "subtitle", void 0), e([o({
102
+ t([s()], l.prototype, "heading", void 0), t([s()], l.prototype, "subtitle", void 0), t([s({
95
103
  type: Boolean,
96
104
  attribute: "show-toggle"
97
- })], c.prototype, "showToggle", void 0), e([o({ attribute: "toggle-label" })], c.prototype, "toggleLabel", void 0), e([o({
105
+ })], l.prototype, "showToggle", void 0), t([s({ attribute: "toggle-label" })], l.prototype, "toggleLabel", void 0), t([s({
98
106
  type: Boolean,
99
107
  reflect: !0,
100
108
  attribute: "drag-region"
101
- })], c.prototype, "dragRegion", void 0), c = e([a("dp-toolbar")], c);
109
+ })], l.prototype, "dragRegion", void 0), l = t([o("dp-toolbar")], l);
102
110
  //#endregion
103
- export { c as DpToolbar, s as DpToolbarToggleEvent };
111
+ export { l as DpToolbar, c as DpToolbarToggleEvent };
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "@iyulab/desktop-patterns",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/iyulab/desktop-patterns.git"
9
+ },
10
+ "homepage": "https://github.com/iyulab/desktop-patterns#readme",
11
+ "bugs": "https://github.com/iyulab/desktop-patterns/issues",
6
12
  "engines": {
7
13
  "node": ">=22"
8
14
  },
@@ -48,6 +54,7 @@
48
54
  },
49
55
  "scripts": {
50
56
  "build": "vite build",
57
+ "prepack": "npm run build",
51
58
  "typecheck": "tsc --noEmit",
52
59
  "test": "web-test-runner \"src/**/*.test.ts\"",
53
60
  "test:watch": "web-test-runner \"src/**/*.test.ts\" --watch",
@@ -60,10 +67,10 @@
60
67
  "lit": "^3.3.3"
61
68
  },
62
69
  "peerDependencies": {
63
- "@iyulab/desktop-compact": "^0.2.0 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0"
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"
64
71
  },
65
72
  "devDependencies": {
66
- "@iyulab/desktop-compact": "^0.7.0",
73
+ "@iyulab/desktop-compact": "^0.8.2",
67
74
  "@open-wc/testing": "^5.0.0",
68
75
  "@storybook/web-components": "^10.6.0",
69
76
  "@storybook/web-components-vite": "^10.6.0",