@iyulab/desktop-patterns 0.8.0 → 0.10.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
@@ -115,7 +115,10 @@ nav.items = [
115
115
  },
116
116
  ]
117
117
  nav.pinnedItems = [{ id: 'settings', icon: '⚙', label: 'Settings' }] // places kept at the foot of the rail
118
- nav.bottomItems = [{ id: 'sign-out', icon: '⎋', label: 'Sign out' }] // actions, not places
118
+ nav.bottomItems = [
119
+ { id: 'dense', icon: '▤', label: 'Dense mode', pressed: false }, // a toggle: aria-pressed, owned by you
120
+ { id: 'sign-out', icon: '⎋', label: 'Sign out' }, // actions, not places
121
+ ]
119
122
  nav.setAttribute('nav-label', 'Main') // names the navigation landmark
120
123
  ```
121
124
 
@@ -136,6 +139,11 @@ nav.setAttribute('nav-label', 'Main') // names t
136
139
  settings or help page shown there is marked as the current page.
137
140
  - **Bottom items are actions**: each click fires `dp-sidebar-action` (`itemId`) — every time, so a failed action can
138
141
  be retried — and never moves `aria-current`. (In 0.2.x they fired `dp-sidebar-select` and took the active mark.)
142
+ - **Toggle actions**: a bottom action with `pressed` (`{ id, icon, label, pressed: true | false }`) is a setting it
143
+ turns on and off. The button carries `aria-pressed`, and an on toggle gets a quiet ground and an accent icon
144
+ (`--dp-sidebar-pressed-bg`, `--dp-sidebar-pressed-icon-color`) that still shows in the collapsed rail. Name it after
145
+ the setting ("Dense mode"), not the click ("Turn dense mode on"). The sidebar does not flip it — change the value
146
+ on `dp-sidebar-action` and set `bottomItems` again.
139
147
 
140
148
  Every component takes its user-facing text as a plain attribute/property or slot — there is no
141
149
  built-in i18n layer. See each component's Storybook story (`npm run storybook`) for its full API.
@@ -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 {
@@ -12,6 +12,19 @@ export interface SidebarItem {
12
12
  label: string;
13
13
  href?: string;
14
14
  }
15
+ /**
16
+ * One bottom action. With `pressed` it is a toggle — a setting the action turns on and off (a dense
17
+ * or touch mode, a theme): the button carries `aria-pressed` and, when on, a quiet ground and an accent
18
+ * icon. Its `label` then names the setting, not what a click would do. The sidebar does not flip it;
19
+ * the consumer owns the state, changes it on `dp-sidebar-action`, and passes the new value back.
20
+ */
21
+ export interface SidebarAction {
22
+ id: string;
23
+ /** Text fallback for the `icon-<id>` slot. */
24
+ icon: string;
25
+ label: string;
26
+ pressed?: boolean;
27
+ }
15
28
  /** A named set of items under one disclosure. */
16
29
  export interface SidebarGroup {
17
30
  id: string;
@@ -68,7 +81,7 @@ export declare class DpSidebar extends LitElement {
68
81
  */
69
82
  pinnedItems: SidebarItem[];
70
83
  /** Actions, not places — see `DpSidebarActionEvent`. A place belongs in `pinnedItems`. */
71
- bottomItems: SidebarItem[];
84
+ bottomItems: SidebarAction[];
72
85
  activeId: string;
73
86
  header: string;
74
87
  /** Accessible name of the navigation landmark. Unset leaves it unnamed. */
@@ -5,7 +5,7 @@ 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 };
package/dist/sidebar.js CHANGED
@@ -105,10 +105,19 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
105
105
  justify-content: center;
106
106
  padding: var(--dc-space-3, 12px) 0;
107
107
  }
108
- /* Items inside an open group sit one step in, so the group reads as a set. */
109
- :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 {
110
110
  padding-left: calc(var(--dc-space-3, 12px) * 2);
111
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
+ }
112
121
  /* In the collapsed rail a group has no label to show; a rule marks where it starts. */
113
122
  :host([collapsed]) .group + .group,
114
123
  :host([collapsed]) .item + .group,
@@ -137,6 +146,17 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
137
146
  .item[aria-current='page']:hover {
138
147
  background: var(--dc-selection-bg, var(--dc-color-surface-hover, #ececed));
139
148
  }
149
+ /* A toggle that is on. Not a place, so not the rail marker or the current-page colour of the label:
150
+ a quiet ground (--dp-sidebar-pressed-bg) and the accent on the icon, so it still reads in the
151
+ collapsed rail, where only the icon shows. Declared after :hover so hovering keeps the ground. */
152
+ .item[aria-pressed='true'],
153
+ .item[aria-pressed='true']:hover {
154
+ background: var(--dp-sidebar-pressed-bg, var(--dc-selection-bg, var(--dc-color-surface-hover, #ececed)));
155
+ color: var(--dc-color-text, #1d1d1f);
156
+ }
157
+ .item[aria-pressed='true'] .icon {
158
+ color: var(--dp-sidebar-pressed-icon-color, var(--dc-color-accent-text, var(--dc-color-accent, #2563eb)));
159
+ }
140
160
  .item:focus-visible {
141
161
  outline: 2px solid var(--dc-color-accent, #2563eb);
142
162
  outline-offset: -2px;
@@ -175,16 +195,16 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
175
195
  `;
176
196
  }
177
197
  #e(e) {
178
- 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}`;
198
+ 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;
179
199
  return r`
180
- <div class="group">
200
+ <div class=${s ? "group heading" : "group"}>
181
201
  ${this.collapsed ? i : r`<button
182
202
  class="item group-toggle"
183
203
  aria-expanded=${a ? "true" : "false"}
184
204
  aria-controls=${o}
185
205
  @click=${() => this.#a(e.id)}
186
206
  >
187
- ${this.#r(e.id, e.icon ?? "")}
207
+ ${s ? i : this.#r(e.id, e.icon ?? "")}
188
208
  <span class="label">${e.label}</span>
189
209
  <span class="caret" aria-hidden="true">▾</span>
190
210
  </button>`}
@@ -219,6 +239,7 @@ var c = (e) => Array.isArray(e.items), l = class extends Event {
219
239
  <button
220
240
  class="item"
221
241
  title=${this.collapsed ? e.label : i}
242
+ aria-pressed=${e.pressed === void 0 ? i : e.pressed ? "true" : "false"}
222
243
  @click=${() => this.dispatchEvent(new d(e.id))}
223
244
  >
224
245
  ${this.#r(e.id, e.icon)} ${this.collapsed ? i : r`<span class="label">${e.label}</span>`}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/desktop-patterns",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -82,10 +82,10 @@
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",
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;