@iyulab/desktop-patterns 0.7.2 → 0.9.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
@@ -114,6 +114,7 @@ nav.items = [
114
114
  items: [{ id: 'runs', icon: '▶', label: 'Runs', href: '/runs' }],
115
115
  },
116
116
  ]
117
+ nav.pinnedItems = [{ id: 'settings', icon: '⚙', label: 'Settings' }] // places kept at the foot of the rail
117
118
  nav.bottomItems = [{ id: 'sign-out', icon: '⎋', label: 'Sign out' }] // actions, not places
118
119
  nav.setAttribute('nav-label', 'Main') // names the navigation landmark
119
120
  ```
@@ -130,6 +131,9 @@ nav.setAttribute('nav-label', 'Main') // names t
130
131
  - **`dp-sidebar-activate`** fires on every pick of a navigation item, the active one included (after
131
132
  `dp-sidebar-select` when the place changes; not when that was cancelled). Close a drawer on it: picking the
132
133
  place already shown still means "take me there", and `dp-sidebar-select` does not fire for it.
134
+ - **Pinned places** (`pinnedItems`) sit at the foot of the rail, inside the same navigation landmark and outside
135
+ the scrolling list. They are places like `items` — `dp-sidebar-select`, `aria-current`, `href` all apply — so a
136
+ settings or help page shown there is marked as the current page.
133
137
  - **Bottom items are actions**: each click fires `dp-sidebar-action` (`itemId`) — every time, so a failed action can
134
138
  be retried — and never moves `aria-current`. (In 0.2.x they fired `dp-sidebar-select` and took the active mark.)
135
139
 
@@ -200,6 +204,10 @@ npm run build # per-component ESM output, type declarations
200
204
  npm run storybook # interactive component browser
201
205
  ```
202
206
 
207
+ `tsc` is TypeScript 7 (`@typescript/native`), while `typescript` resolves to `@typescript/typescript6`:
208
+ TypeScript 7 has no JavaScript compiler API yet, and the declaration build reads one. The alias goes
209
+ once `vite-plugin-dts` runs on TypeScript 7 alone.
210
+
203
211
  `@iyulab/desktop-compact` is a **peer** dependency: this package reads its `--dc-*` tokens but never
204
212
  imports its code, so the consumer installs one copy and both packages share it. A regular dependency
205
213
  would give a consumer on a newer `desktop-compact` minor a second, unused copy nested under this
@@ -16,6 +16,12 @@ export declare class DpListDetail extends LitElement {
16
16
  static styles: import('lit').CSSResult;
17
17
  /** Below desktop width, show the picked item instead of the list. Above it both are always shown. */
18
18
  detailOpen: boolean;
19
+ /**
20
+ * At desktop width, fold the list away and give the item the whole width — for reading a wide item. The
21
+ * consumer owns it, with a control that brings the list back. Below desktop width it changes nothing:
22
+ * `detail-open` already picks the one pane shown.
23
+ */
24
+ listCollapsed: boolean;
19
25
  render(): import('lit-html').TemplateResult<1>;
20
26
  }
21
27
  declare global {
@@ -3,7 +3,8 @@ import { LitElement } from 'lit';
3
3
  * The top of a page or of a detail pane: a small eyebrow naming where you are, the heading (an
4
4
  * `<h2>`), a line describing it, and the page's own actions — ruled off from what follows. It
5
5
  * stands on its own rather than being part of `dp-page`, so a pane inside a page can have one.
6
- * Container-sized: the actions sit beside the heading when there is room and drop under it when not.
6
+ * The heading keeps a readable width: the actions sit beside it when there is room and wrap under it
7
+ * when not, however wide they are, so a heading is never squeezed to a column of single letters.
7
8
  */
8
9
  export declare class DpPageHeader extends LitElement {
9
10
  static styles: import('lit').CSSResult;
@@ -61,7 +61,13 @@ export declare class DpSidebar extends LitElement {
61
61
  #private;
62
62
  static styles: import('lit').CSSResult;
63
63
  items: SidebarEntry[];
64
- /** Actions, not places — see `DpSidebarActionEvent`. */
64
+ /**
65
+ * Places pinned to the foot of the rail — a settings or help page an app keeps out of its main
66
+ * list. They are navigation like `items` (same `dp-sidebar-select`, `aria-current`, links) and
67
+ * sit inside the same landmark, below the scrolling list.
68
+ */
69
+ pinnedItems: SidebarItem[];
70
+ /** Actions, not places — see `DpSidebarActionEvent`. A place belongs in `pinnedItems`. */
65
71
  bottomItems: SidebarItem[];
66
72
  activeId: string;
67
73
  header: string;
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorate.js
1
+ //#region \0@oxc-project+runtime@0.152.0/helpers/esm/decorate.js
2
2
  function e(e, t, n, r) {
3
3
  var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
4
4
  if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
@@ -1,11 +1,11 @@
1
1
  import { desktopMedia as e } from "./breakpoints.js";
2
- import { t } from "./decorate-CafwLXg5.js";
2
+ import { t } from "./decorate-BJVyhdRd.js";
3
3
  import { LitElement as n, css as r, html as i } from "lit";
4
4
  import { customElement as a, property as o } from "lit/decorators.js";
5
5
  //#region src/components/list-detail/dp-list-detail.ts
6
6
  var s = class extends n {
7
7
  constructor(...e) {
8
- super(...e), this.detailOpen = !1;
8
+ super(...e), this.detailOpen = !1, this.listCollapsed = !1;
9
9
  }
10
10
  static {
11
11
  this.styles = r`
@@ -46,6 +46,11 @@ var s = class extends n {
46
46
  .detail {
47
47
  display: block;
48
48
  }
49
+ /* Folded away: the item takes the whole width until the list is wanted again. */
50
+ :host([list-collapsed]) .list,
51
+ :host([list-collapsed][detail-open]) .list {
52
+ display: none;
53
+ }
49
54
  }
50
55
  /* On paper only the item picked is printed, at its full length. */
51
56
  @media print {
@@ -75,6 +80,10 @@ t([o({
75
80
  type: Boolean,
76
81
  reflect: !0,
77
82
  attribute: "detail-open"
78
- })], s.prototype, "detailOpen", void 0), s = t([a("dp-list-detail")], s);
83
+ })], s.prototype, "detailOpen", void 0), t([o({
84
+ type: Boolean,
85
+ reflect: !0,
86
+ attribute: "list-collapsed"
87
+ })], s.prototype, "listCollapsed", void 0), s = t([a("dp-list-detail")], s);
79
88
  //#endregion
80
89
  export { s as DpListDetail };
@@ -1,4 +1,4 @@
1
- import { t as e } from "./decorate-CafwLXg5.js";
1
+ import { t as e } from "./decorate-BJVyhdRd.js";
2
2
  import { LitElement as t, css as n, html as r, nothing as i } from "lit";
3
3
  import { customElement as a, property as o } from "lit/decorators.js";
4
4
  //#region src/components/page-header/dp-page-header.ts
@@ -15,22 +15,29 @@ var s = class extends t {
15
15
  border-bottom: 1px solid var(--dc-page-rule, var(--dc-color-border, #e2e2e4));
16
16
  font-family: var(--dc-font-family, system-ui, sans-serif);
17
17
  }
18
- .grid {
19
- display: grid;
20
- grid-template-columns: minmax(0, 1fr) auto;
21
- column-gap: var(--dc-space-4, 16px);
22
- row-gap: var(--dc-space-1, 4px);
18
+ /* The heading claims a readable width first; actions that do not fit beside it wrap under it. */
19
+ .row {
20
+ display: flex;
21
+ flex-wrap: wrap;
23
22
  align-items: center;
23
+ gap: var(--dc-space-1, 4px) var(--dc-space-4, 16px);
24
+ }
25
+ .main {
26
+ flex: 1 1 20em;
27
+ min-width: 0;
28
+ display: flex;
29
+ flex-direction: column;
30
+ gap: var(--dc-space-1, 4px);
24
31
  }
25
32
  .eyebrow {
26
- grid-column: 1 / -1;
33
+ display: block;
34
+ margin-bottom: var(--dc-space-1, 4px);
27
35
  font-size: var(--dc-page-eyebrow-size, var(--dc-font-size-xs, 11px));
28
36
  font-weight: var(--dc-font-weight-semibold, 600);
29
37
  letter-spacing: 0.04em;
30
38
  color: var(--dc-page-eyebrow-color, var(--dc-color-accent-text, #1d4ed8));
31
39
  }
32
40
  .title {
33
- grid-column: 1;
34
41
  display: flex;
35
42
  flex-wrap: wrap;
36
43
  align-items: center;
@@ -46,29 +53,22 @@ var s = class extends t {
46
53
  overflow-wrap: anywhere;
47
54
  }
48
55
  .description {
49
- grid-column: 1;
50
56
  margin: 0;
51
57
  color: var(--dc-page-description-color, var(--dc-color-text-secondary, #55555c));
52
58
  }
53
59
  .actions {
54
- grid-column: 2;
55
- grid-row: 2 / span 2;
60
+ flex: 0 1 auto;
61
+ margin-inline-start: auto;
56
62
  display: flex;
57
63
  flex-wrap: wrap;
58
64
  gap: var(--dc-space-2, 8px);
59
65
  align-items: center;
60
66
  justify-content: flex-end;
61
67
  }
62
- :host(:not([eyebrow])) .actions,
63
- :host([eyebrow='']) .actions {
64
- grid-row: 1 / span 2;
65
- }
66
68
  @container (max-width: 480px) {
67
- .actions,
68
- :host(:not([eyebrow])) .actions,
69
- :host([eyebrow='']) .actions {
70
- grid-column: 1;
71
- grid-row: auto;
69
+ .actions {
70
+ flex-basis: 100%;
71
+ margin-inline-start: 0;
72
72
  justify-content: flex-start;
73
73
  margin-top: var(--dc-space-2, 8px);
74
74
  }
@@ -76,12 +76,14 @@ var s = class extends t {
76
76
  `;
77
77
  }
78
78
  render() {
79
- return r`<div class="grid">
80
- ${this.eyebrow ? r`<span class="eyebrow">${this.eyebrow}</span>` : i}
81
- <div class="title"><h2 part="heading">${this.heading}</h2><slot name="heading-extra"></slot></div>
82
- ${this.description ? r`<p class="description">${this.description}</p>` : i}
83
- <div class="actions"><slot name="actions"></slot></div>
84
- </div>`;
79
+ return r`${this.eyebrow ? r`<span class="eyebrow">${this.eyebrow}</span>` : i}
80
+ <div class="row">
81
+ <div class="main">
82
+ <div class="title"><h2 part="heading">${this.heading}</h2><slot name="heading-extra"></slot></div>
83
+ ${this.description ? r`<p class="description">${this.description}</p>` : i}
84
+ </div>
85
+ <div class="actions"><slot name="actions"></slot></div>
86
+ </div>`;
85
87
  }
86
88
  };
87
89
  e([o({ reflect: !0 })], s.prototype, "eyebrow", void 0), e([o()], s.prototype, "heading", void 0), e([o()], s.prototype, "description", void 0), s = e([a("dp-page-header")], s);
package/dist/page.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as e } from "./decorate-CafwLXg5.js";
1
+ import { t as e } from "./decorate-BJVyhdRd.js";
2
2
  import { LitElement as t, css as n, html as r } from "lit";
3
3
  import { customElement as i, property as a } from "lit/decorators.js";
4
4
  //#region src/components/page/dp-page.ts
package/dist/shell.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { desktopMedia as e } from "./breakpoints.js";
2
- import { t } from "./decorate-CafwLXg5.js";
2
+ import { t } from "./decorate-BJVyhdRd.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";
5
5
  //#region src/components/shell/dp-shell.ts
@@ -1,4 +1,4 @@
1
- import { t as e } from "./decorate-CafwLXg5.js";
1
+ import { t as e } from "./decorate-BJVyhdRd.js";
2
2
  import { LitElement as t, css as n, html as r, nothing as i } from "lit";
3
3
  import { customElement as a, property as o, state as s } from "lit/decorators.js";
4
4
  //#region src/components/shortcut-overlay/dp-shortcut-overlay.ts
package/dist/sidebar.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as e } from "./decorate-CafwLXg5.js";
1
+ import { t as e } from "./decorate-BJVyhdRd.js";
2
2
  import { LitElement as t, css as n, html as r, nothing as i } from "lit";
3
3
  import { customElement as a, property as o, state as s } from "lit/decorators.js";
4
4
  //#region src/components/sidebar/dp-sidebar.ts
@@ -26,7 +26,7 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
26
26
  }
27
27
  }, f = class extends t {
28
28
  constructor(...e) {
29
- super(...e), this.items = [], this.bottomItems = [], this.activeId = "", this.header = "", this.navLabel = "", this.collapsed = !1, this.toggledGroups = /* @__PURE__ */ new Set();
29
+ super(...e), this.items = [], this.pinnedItems = [], this.bottomItems = [], this.activeId = "", this.header = "", this.navLabel = "", this.collapsed = !1, this.toggledGroups = /* @__PURE__ */ new Set();
30
30
  }
31
31
  static {
32
32
  this.styles = n`
@@ -63,10 +63,22 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
63
63
  color: var(--dc-color-text-secondary, #55555c);
64
64
  }
65
65
  nav {
66
+ flex: 1;
67
+ min-height: 0;
68
+ display: flex;
69
+ flex-direction: column;
70
+ }
71
+ .nav-items {
66
72
  flex: 1;
67
73
  overflow-y: auto;
68
74
  padding: var(--dc-space-1, 4px) 0;
69
75
  }
76
+ /* Places pinned to the foot of the rail stay in view however long the list above scrolls. */
77
+ .pinned {
78
+ flex-shrink: 0;
79
+ border-top: 1px solid var(--dc-color-border, #e2e2e4);
80
+ padding: var(--dc-space-1, 4px) 0;
81
+ }
70
82
  .bottom-group {
71
83
  border-top: 1px solid var(--dc-color-border, #e2e2e4);
72
84
  padding: var(--dc-space-1, 4px) 0;
@@ -93,10 +105,19 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
93
105
  justify-content: center;
94
106
  padding: var(--dc-space-3, 12px) 0;
95
107
  }
96
- /* Items inside an open group sit one step in, so the group reads as a set. */
97
- :host(:not([collapsed])) .group-items .item {
108
+ /* A group with an icon is a parent item: its items sit one step in, so it reads as a set. */
109
+ :host(:not([collapsed])) .group:not(.heading) .group-items .item {
98
110
  padding-left: calc(var(--dc-space-3, 12px) * 2);
99
111
  }
112
+ /* A group without one is a heading over its items: smaller and quieter than they are, its label flush
113
+ with their icons, and the items at the rail's own indent - the heading, not an indent, makes the set. */
114
+ .group.heading > .group-toggle {
115
+ padding-top: var(--dc-space-3, 12px);
116
+ padding-bottom: var(--dc-space-1, 4px);
117
+ font-size: var(--dp-sidebar-group-size, var(--dc-font-size-xs, 11px));
118
+ font-weight: var(--dc-font-weight-semibold, 600);
119
+ color: var(--dc-color-text-muted, #6b6b73);
120
+ }
100
121
  /* In the collapsed rail a group has no label to show; a rule marks where it starts. */
101
122
  :host([collapsed]) .group + .group,
102
123
  :host([collapsed]) .item + .group,
@@ -153,23 +174,26 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
153
174
  ${!this.collapsed && this.header ? r`<span class="header-label">${this.header}</span>` : i}
154
175
  </div>
155
176
  <nav aria-label=${this.navLabel || i}>
156
- ${this.items.map((e) => c(e) ? this.#e(e) : this.#t(e))}
177
+ <div class="nav-items">
178
+ ${this.items.map((e) => c(e) ? this.#e(e) : this.#t(e))}
179
+ </div>
180
+ ${this.pinnedItems.length > 0 ? r`<div class="pinned">${this.pinnedItems.map((e) => this.#t(e))}</div>` : i}
157
181
  </nav>
158
182
  ${this.bottomItems.length > 0 ? r`<div class="bottom-group">${this.bottomItems.map((e) => this.#n(e))}</div>` : i}
159
183
  <div class="footer"><slot name="footer"></slot></div>
160
184
  `;
161
185
  }
162
186
  #e(e) {
163
- let t = e.items.some((e) => e.id === this.activeId), n = e.collapsed === !0, a = this.collapsed || t || n === this.toggledGroups.has(e.id), o = `group-${e.id}`;
187
+ let t = e.items.some((e) => e.id === this.activeId), n = e.collapsed === !0, a = this.collapsed || t || n === this.toggledGroups.has(e.id), o = `group-${e.id}`, s = !e.icon;
164
188
  return r`
165
- <div class="group">
189
+ <div class=${s ? "group heading" : "group"}>
166
190
  ${this.collapsed ? i : r`<button
167
191
  class="item group-toggle"
168
192
  aria-expanded=${a ? "true" : "false"}
169
193
  aria-controls=${o}
170
194
  @click=${() => this.#a(e.id)}
171
195
  >
172
- ${this.#r(e.id, e.icon ?? "")}
196
+ ${s ? i : this.#r(e.id, e.icon ?? "")}
173
197
  <span class="label">${e.label}</span>
174
198
  <span class="caret" aria-hidden="true">▾</span>
175
199
  </button>`}
@@ -231,6 +255,9 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
231
255
  }
232
256
  };
233
257
  e([o({ type: Array })], f.prototype, "items", void 0), e([o({
258
+ type: Array,
259
+ attribute: "pinned-items"
260
+ })], f.prototype, "pinnedItems", void 0), e([o({
234
261
  type: Array,
235
262
  attribute: "bottom-items"
236
263
  })], 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({
package/dist/toolbar.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as e } from "./decorate-CafwLXg5.js";
1
+ import { t as e } from "./decorate-BJVyhdRd.js";
2
2
  import { LitElement as t, css as n, html as r, nothing as i } from "lit";
3
3
  import { customElement as a, property as o } from "lit/decorators.js";
4
4
  //#region src/components/toolbar/dp-toolbar.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/desktop-patterns",
3
- "version": "0.7.2",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -82,21 +82,22 @@
82
82
  "lit": "^3.3.3"
83
83
  },
84
84
  "peerDependencies": {
85
- "@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 || ^0.10.0 || ^0.11.0"
85
+ "@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 || ^0.10.0 || ^0.11.0 || ^0.12.0"
86
86
  },
87
87
  "devDependencies": {
88
- "@iyulab/desktop-compact": "^0.11.0",
88
+ "@iyulab/desktop-compact": "^0.12.0",
89
89
  "@open-wc/testing": "^5.0.0",
90
90
  "@storybook/web-components": "^10.6.0",
91
91
  "@storybook/web-components-vite": "^10.6.0",
92
92
  "@types/mocha": "^10.0.10",
93
+ "@typescript/native": "npm:typescript@^7.0.2",
93
94
  "@web/dev-server-esbuild": "^2.0.0",
94
95
  "@web/test-runner": "^1.0.0",
95
96
  "@web/test-runner-commands": "^1.0.1",
96
97
  "axe-core": "^4.13.0",
97
98
  "storybook": "^10.6.0",
98
- "typescript": "^6.0.3",
99
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
99
100
  "vite": "^8.3.1",
100
- "vite-plugin-dts": "^5.1.1"
101
+ "vite-plugin-dts": "^5.1.2"
101
102
  }
102
103
  }
package/tokens.css CHANGED
@@ -22,6 +22,11 @@
22
22
  syntax: '*';
23
23
  inherits: true;
24
24
  }
25
+ /* dp-sidebar's heading size for a group without an icon. Unset, the sidebar uses var(--dc-font-size-xs, 11px). */
26
+ @property --dp-sidebar-group-size {
27
+ syntax: '*';
28
+ inherits: true;
29
+ }
25
30
  :root {
26
31
  --dp-sidebar-width: 220px;
27
32
  --dp-sidebar-width-collapsed: 52px;