@iyulab/desktop-patterns 0.2.0 → 0.3.1

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
@@ -59,6 +59,32 @@ document.querySelector('dp-toolbar')!.addEventListener('dp-toolbar-toggle', () =
59
59
  })
60
60
  ```
61
61
 
62
+ ### Sidebar items: links, groups, icons, actions
63
+
64
+ ```ts
65
+ nav.items = [
66
+ { id: 'overview', icon: '■', label: 'Overview', href: '/overview' }, // href → rendered as <a>
67
+ {
68
+ id: 'work', label: 'Work', collapsed: false, // a group: a disclosure over its items
69
+ items: [{ id: 'runs', icon: '▶', label: 'Runs', href: '/runs' }],
70
+ },
71
+ ]
72
+ nav.bottomItems = [{ id: 'sign-out', icon: '⎋', label: 'Sign out' }] // actions, not places
73
+ nav.setAttribute('nav-label', 'Main') // names the navigation landmark
74
+ ```
75
+
76
+ - **`href`** renders the item as a link, so a router's link interception (or the browser) does the navigating.
77
+ `dp-sidebar-select` still fires and is cancelable — `preventDefault()` cancels the navigation and keeps the
78
+ active item. A modified click (Ctrl/⌘/Shift/Alt, middle button) opens the link elsewhere and selects nothing.
79
+ Items without `href` stay buttons and the consumer navigates on `dp-sidebar-select`.
80
+ - **Groups** (`{ id, label, items, icon?, collapsed? }`) render as a named `role="group"` under a disclosure
81
+ button (`aria-expanded`). A group holding the active item is always open. In the collapsed rail every group's
82
+ items show, separated by a rule.
83
+ - **Icons**: each item's icon is a slot named `icon-<id>` whose fallback is the item's `icon` text — slot any icon
84
+ element (`<svg slot="icon-overview">`) to replace it.
85
+ - **Bottom items are actions**: each click fires `dp-sidebar-action` (`itemId`) — every time, so a failed action can
86
+ be retried — and never moves `aria-current`. (In 0.2.x they fired `dp-sidebar-select` and took the active mark.)
87
+
62
88
  Every component takes its user-facing text as a plain attribute/property or slot — there is no
63
89
  built-in i18n layer. See each component's Storybook story (`npm run storybook`) for its full API.
64
90
 
@@ -25,6 +25,8 @@ export declare class DpShortcutOverlay extends LitElement {
25
25
  open: boolean;
26
26
  heading: string;
27
27
  emptyMessage: string;
28
+ /** Accessible name of the close button — supply one in the app's language. */
29
+ closeLabel: string;
28
30
  private headingId;
29
31
  updated(changed: Map<string, unknown>): void;
30
32
  disconnectedCallback(): void;
@@ -1,27 +1,65 @@
1
1
  import { LitElement } from 'lit';
2
+ /**
3
+ * One navigation entry. With `href` it renders as a link — a router (or the
4
+ * browser) does the navigating, exactly as it would for any other anchor on
5
+ * the page. Without one it renders as a button and the consumer navigates on
6
+ * `dp-sidebar-select`.
7
+ */
2
8
  export interface SidebarItem {
3
9
  id: string;
10
+ /** Text fallback for the `icon-<id>` slot. */
4
11
  icon: string;
5
12
  label: string;
13
+ href?: string;
14
+ }
15
+ /** A named set of items under one disclosure. */
16
+ export interface SidebarGroup {
17
+ id: string;
18
+ /** Text fallback for the `icon-<id>` slot. */
19
+ icon?: string;
20
+ label: string;
21
+ items: SidebarItem[];
22
+ /** Starts closed. A group holding the active item stays open regardless. */
23
+ collapsed?: boolean;
6
24
  }
25
+ export type SidebarEntry = SidebarItem | SidebarGroup;
26
+ /**
27
+ * Fired when a navigation item is chosen. Cancelable: calling
28
+ * `preventDefault()` keeps the active item where it was and, for a link,
29
+ * cancels the navigation too.
30
+ */
7
31
  export declare class DpSidebarSelectEvent extends Event {
8
32
  readonly itemId: string;
9
33
  constructor(itemId: string);
10
34
  }
35
+ /** Fired on every click of a bottom item — those are actions, not places. */
36
+ export declare class DpSidebarActionEvent extends Event {
37
+ readonly itemId: string;
38
+ constructor(itemId: string);
39
+ }
11
40
  /**
12
- * App-shell navigation rail: an icon+label list with an expanded/collapsed
13
- * state, an optional pinned bottom group, and a `footer` slot for whatever
14
- * the consumer wants to anchor at the bottom — a status badge, a version
15
- * string, anything; this repo doesn't know what.
41
+ * App-shell navigation rail: an icon+label list (flat, or grouped under
42
+ * disclosures) with an expanded/collapsed state, an optional pinned group of
43
+ * bottom actions, and a `footer` slot for whatever the consumer wants to
44
+ * anchor at the bottom — a status badge, a version string, anything; this
45
+ * repo doesn't know what.
46
+ *
47
+ * Every item's icon is a slot, `icon-<id>`, whose fallback is the item's
48
+ * `icon` text — so an icon element from any library can stand in for it.
16
49
  */
17
50
  export declare class DpSidebar extends LitElement {
18
51
  #private;
19
52
  static styles: import('lit').CSSResult;
20
- items: SidebarItem[];
53
+ items: SidebarEntry[];
54
+ /** Actions, not places — see `DpSidebarActionEvent`. */
21
55
  bottomItems: SidebarItem[];
22
56
  activeId: string;
23
57
  header: string;
58
+ /** Accessible name of the navigation landmark. Unset leaves it unnamed. */
59
+ navLabel: string;
24
60
  collapsed: boolean;
61
+ /** Groups whose open/closed state the user has flipped from their `collapsed` default. */
62
+ private toggledGroups;
25
63
  render(): import('lit-html').TemplateResult<1>;
26
64
  }
27
65
  declare global {
@@ -42,6 +42,8 @@ export declare class DpToolbar extends LitElement {
42
42
  heading: string;
43
43
  subtitle: string;
44
44
  showToggle: boolean;
45
+ /** Accessible name of the sidebar toggle — supply one in the app's language. */
46
+ toggleLabel: string;
45
47
  dragRegion: boolean;
46
48
  connectedCallback(): void;
47
49
  render(): import('lit-html').TemplateResult<1>;
@@ -0,0 +1,9 @@
1
+ //#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorate.js
2
+ function e(e, t, n, r) {
3
+ var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
4
+ if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
5
+ else for (var s = e.length - 1; s >= 0; s--) (o = e[s]) && (a = (i < 3 ? o(a) : i > 3 ? o(t, n, a) : o(t, n)) || a);
6
+ return i > 3 && a && Object.defineProperty(t, n, a), a;
7
+ }
8
+ //#endregion
9
+ export { e as t };
package/dist/index.js CHANGED
@@ -1,16 +1,6 @@
1
- import { DpPage as o } from "./page.js";
2
- import { DpSidebar as t, DpSidebarSelectEvent as l } from "./sidebar.js";
3
- import { DpToolbar as a, DpToolbarToggleEvent as S } from "./toolbar.js";
4
- import { DpShell as v, DpShellSidebarCloseEvent as b } from "./shell.js";
5
- import { DpShortcutOverlay as i, DpShortcutOverlayDismissEvent as x } from "./shortcut-overlay.js";
6
- export {
7
- o as DpPage,
8
- v as DpShell,
9
- b as DpShellSidebarCloseEvent,
10
- i as DpShortcutOverlay,
11
- x as DpShortcutOverlayDismissEvent,
12
- t as DpSidebar,
13
- l as DpSidebarSelectEvent,
14
- a as DpToolbar,
15
- S as DpToolbarToggleEvent
16
- };
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 };
package/dist/page.js CHANGED
@@ -1,30 +1,19 @@
1
- import { css as a, LitElement as m, html as x } from "lit";
2
- import { property as d, customElement as c } from "lit/decorators.js";
3
- var h = Object.defineProperty, u = Object.getOwnPropertyDescriptor, n = (l, e, s, r) => {
4
- for (var t = r > 1 ? void 0 : r ? u(e, s) : e, p = l.length - 1, i; p >= 0; p--)
5
- (i = l[p]) && (t = (r ? i(e, s, t) : i(t)) || t);
6
- return r && t && h(e, s, t), t;
7
- };
8
- const b = {
9
- sm: "672px",
10
- md: "768px",
11
- lg: "896px",
12
- xl: "1152px",
13
- full: "none"
14
- };
15
- let o = class extends m {
16
- constructor() {
17
- super(...arguments), this.maxWidth = "md";
18
- }
19
- render() {
20
- return x`
21
- <div class="inner" style="max-width: ${b[this.maxWidth]}">
22
- <slot></slot>
23
- </div>
24
- `;
25
- }
26
- };
27
- o.styles = a`
1
+ import { t as e } from "./decorate-CafwLXg5.js";
2
+ import { LitElement as t, css as n, html as r } from "lit";
3
+ import { customElement as i, property as a } from "lit/decorators.js";
4
+ //#region src/components/page/dp-page.ts
5
+ var o = {
6
+ sm: "672px",
7
+ md: "768px",
8
+ lg: "896px",
9
+ xl: "1152px",
10
+ full: "none"
11
+ }, s = class extends t {
12
+ constructor(...e) {
13
+ super(...e), this.maxWidth = "md";
14
+ }
15
+ static {
16
+ this.styles = n`
28
17
  :host {
29
18
  display: block;
30
19
  height: 100%;
@@ -37,12 +26,18 @@ o.styles = a`
37
26
  box-sizing: border-box;
38
27
  }
39
28
  `;
40
- n([
41
- d({ attribute: "max-width", reflect: !0 })
42
- ], o.prototype, "maxWidth", 2);
43
- o = n([
44
- c("dp-page")
45
- ], o);
46
- export {
47
- o as DpPage
29
+ }
30
+ render() {
31
+ return r`
32
+ <div class="inner" style="max-width: ${o[this.maxWidth]}">
33
+ <slot></slot>
34
+ </div>
35
+ `;
36
+ }
48
37
  };
38
+ e([a({
39
+ attribute: "max-width",
40
+ reflect: !0
41
+ })], s.prototype, "maxWidth", void 0), s = e([i("dp-page")], s);
42
+ //#endregion
43
+ export { s as DpPage };
package/dist/shell.js CHANGED
@@ -1,34 +1,20 @@
1
- import { css as p, LitElement as c, nothing as b, html as l } from "lit";
2
- import { property as h, customElement as f } from "lit/decorators.js";
3
- var m = Object.defineProperty, u = Object.getOwnPropertyDescriptor, d = (s, o, t, i) => {
4
- for (var e = i > 1 ? void 0 : i ? u(o, t) : o, a = s.length - 1, n; a >= 0; a--)
5
- (n = s[a]) && (e = (i ? n(o, t, e) : n(e)) || e);
6
- return i && e && m(o, t, e), e;
7
- };
8
- class v extends Event {
9
- constructor() {
10
- super("dp-shell-sidebar-close", { bubbles: !0, composed: !0 });
11
- }
12
- }
13
- let r = class extends c {
14
- constructor() {
15
- super(...arguments), this.sidebarOpen = !1;
16
- }
17
- render() {
18
- return l`
19
- ${this.sidebarOpen ? l`<div class="backdrop" @click=${() => this.dispatchEvent(new v())}></div>` : b}
20
- <div class="sidebar-region"><slot name="sidebar"></slot></div>
21
- <div class="main-column">
22
- <div class="toolbar-row">
23
- <slot name="toolbar"></slot>
24
- </div>
25
- <slot name="banner"></slot>
26
- <main><slot></slot></main>
27
- </div>
28
- `;
29
- }
30
- };
31
- r.styles = p`
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";
4
+ //#region src/components/shell/dp-shell.ts
5
+ var s = class extends Event {
6
+ constructor() {
7
+ super("dp-shell-sidebar-close", {
8
+ bubbles: !0,
9
+ composed: !0
10
+ });
11
+ }
12
+ }, c = class extends t {
13
+ constructor(...e) {
14
+ super(...e), this.sidebarOpen = !1;
15
+ }
16
+ static {
17
+ this.styles = n`
32
18
  :host {
33
19
  display: flex;
34
20
  width: 100vw;
@@ -90,13 +76,25 @@ r.styles = p`
90
76
  overflow: auto;
91
77
  }
92
78
  `;
93
- d([
94
- h({ type: Boolean, reflect: !0, attribute: "sidebar-open" })
95
- ], r.prototype, "sidebarOpen", 2);
96
- r = d([
97
- f("dp-shell")
98
- ], r);
99
- export {
100
- r as DpShell,
101
- v as DpShellSidebarCloseEvent
79
+ }
80
+ render() {
81
+ return r`
82
+ ${this.sidebarOpen ? r`<div class="backdrop" @click=${() => this.dispatchEvent(new s())}></div>` : i}
83
+ <div class="sidebar-region"><slot name="sidebar"></slot></div>
84
+ <div class="main-column">
85
+ <div class="toolbar-row">
86
+ <slot name="toolbar"></slot>
87
+ </div>
88
+ <slot name="banner"></slot>
89
+ <main><slot></slot></main>
90
+ </div>
91
+ `;
92
+ }
102
93
  };
94
+ e([o({
95
+ type: Boolean,
96
+ reflect: !0,
97
+ attribute: "sidebar-open"
98
+ })], c.prototype, "sidebarOpen", void 0), c = e([a("dp-shell")], c);
99
+ //#endregion
100
+ export { c as DpShell, s as DpShellSidebarCloseEvent };
@@ -1,68 +1,22 @@
1
- import { css as x, LitElement as b, nothing as w, html as d } from "lit";
2
- import { property as l, state as _, customElement as k } from "lit/decorators.js";
3
- var $ = Object.defineProperty, E = Object.getOwnPropertyDescriptor, g = (e) => {
4
- throw TypeError(e);
5
- }, a = (e, t, r, c) => {
6
- for (var s = c > 1 ? void 0 : c ? E(t, r) : t, h = e.length - 1, v; h >= 0; h--)
7
- (v = e[h]) && (s = (c ? v(t, r, s) : v(s)) || s);
8
- return c && s && $(t, r, s), s;
9
- }, m = (e, t, r) => t.has(e) || g("Cannot " + r), u = (e, t, r) => (m(e, t, "read from private field"), t.get(e)), y = (e, t, r) => t.has(e) ? g("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(e) : t.set(e, r), f = (e, t, r) => (m(e, t, "access private method"), r), i, n, p;
10
- class z extends Event {
11
- constructor() {
12
- super("dp-shortcut-overlay-dismiss", { bubbles: !0, composed: !0 });
13
- }
14
- }
15
- let C = 0, o = class extends b {
16
- constructor() {
17
- super(...arguments), y(this, n), this.shortcuts = [], this.open = !1, this.heading = "Keyboard shortcuts", this.emptyMessage = "No shortcuts registered.", this.headingId = `dp-shortcut-overlay-heading-${++C}`, y(this, i, (e) => {
18
- e.key === "Escape" && (e.preventDefault(), f(this, n, p).call(this));
19
- });
20
- }
21
- updated(e) {
22
- e.has("open") && (this.open ? window.addEventListener("keydown", u(this, i)) : window.removeEventListener("keydown", u(this, i)));
23
- }
24
- disconnectedCallback() {
25
- super.disconnectedCallback(), window.removeEventListener("keydown", u(this, i));
26
- }
27
- render() {
28
- return this.open ? d`
29
- <div class="backdrop" @click=${() => f(this, n, p).call(this)}>
30
- <div
31
- class="panel"
32
- role="dialog"
33
- aria-modal="true"
34
- aria-labelledby=${this.headingId}
35
- @click=${(e) => e.stopPropagation()}
36
- >
37
- <div class="header">
38
- <h2 id=${this.headingId}>${this.heading}</h2>
39
- <button class="close" aria-label="Close" @click=${() => f(this, n, p).call(this)}>×</button>
40
- </div>
41
- <div class="body">
42
- ${this.shortcuts.length === 0 ? d`<p class="empty">${this.emptyMessage}</p>` : d`
43
- <ul>
44
- ${this.shortcuts.map(
45
- (e) => d`
46
- <li>
47
- <span>${e.description}</span>
48
- <kbd>${e.combo}</kbd>
49
- </li>
50
- `
51
- )}
52
- </ul>
53
- `}
54
- </div>
55
- </div>
56
- </div>
57
- ` : w;
58
- }
59
- };
60
- i = /* @__PURE__ */ new WeakMap();
61
- n = /* @__PURE__ */ new WeakSet();
62
- p = function() {
63
- this.dispatchEvent(new z());
64
- };
65
- o.styles = x`
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, state as s } from "lit/decorators.js";
4
+ //#region src/components/shortcut-overlay/dp-shortcut-overlay.ts
5
+ var c = class extends Event {
6
+ constructor() {
7
+ super("dp-shortcut-overlay-dismiss", {
8
+ bubbles: !0,
9
+ composed: !0
10
+ });
11
+ }
12
+ }, l = 0, u = class extends t {
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());
16
+ };
17
+ }
18
+ static {
19
+ this.styles = n`
66
20
  :host {
67
21
  display: contents;
68
22
  font-family: var(--dc-font-family, system-ui, sans-serif);
@@ -148,25 +102,51 @@ o.styles = x`
148
102
  font-size: 10px;
149
103
  }
150
104
  `;
151
- a([
152
- l({ type: Array })
153
- ], o.prototype, "shortcuts", 2);
154
- a([
155
- l({ type: Boolean, reflect: !0 })
156
- ], o.prototype, "open", 2);
157
- a([
158
- l()
159
- ], o.prototype, "heading", 2);
160
- a([
161
- l({ attribute: "empty-message" })
162
- ], o.prototype, "emptyMessage", 2);
163
- a([
164
- _()
165
- ], o.prototype, "headingId", 2);
166
- o = a([
167
- k("dp-shortcut-overlay")
168
- ], o);
169
- export {
170
- o as DpShortcutOverlay,
171
- z as DpShortcutOverlayDismissEvent
105
+ }
106
+ #e;
107
+ updated(e) {
108
+ e.has("open") && (this.open ? window.addEventListener("keydown", this.#e) : window.removeEventListener("keydown", this.#e));
109
+ }
110
+ disconnectedCallback() {
111
+ super.disconnectedCallback(), window.removeEventListener("keydown", this.#e);
112
+ }
113
+ #t() {
114
+ this.dispatchEvent(new c());
115
+ }
116
+ render() {
117
+ return this.open ? r`
118
+ <div class="backdrop" @click=${() => this.#t()}>
119
+ <div
120
+ class="panel"
121
+ role="dialog"
122
+ aria-modal="true"
123
+ aria-labelledby=${this.headingId}
124
+ @click=${(e) => e.stopPropagation()}
125
+ >
126
+ <div class="header">
127
+ <h2 id=${this.headingId}>${this.heading}</h2>
128
+ <button class="close" aria-label=${this.closeLabel} @click=${() => this.#t()}>×</button>
129
+ </div>
130
+ <div class="body">
131
+ ${this.shortcuts.length === 0 ? r`<p class="empty">${this.emptyMessage}</p>` : r`
132
+ <ul>
133
+ ${this.shortcuts.map((e) => r`
134
+ <li>
135
+ <span>${e.description}</span>
136
+ <kbd>${e.combo}</kbd>
137
+ </li>
138
+ `)}
139
+ </ul>
140
+ `}
141
+ </div>
142
+ </div>
143
+ </div>
144
+ ` : i;
145
+ }
172
146
  };
147
+ e([o({ type: Array })], u.prototype, "shortcuts", void 0), e([o({
148
+ type: Boolean,
149
+ reflect: !0
150
+ })], 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);
151
+ //#endregion
152
+ export { u as DpShortcutOverlay, c as DpShortcutOverlayDismissEvent };
package/dist/sidebar.js CHANGED
@@ -1,51 +1,28 @@
1
- import { css as m, LitElement as x, nothing as c, html as i } from "lit";
2
- import { property as d, customElement as g } from "lit/decorators.js";
3
- var y = Object.defineProperty, w = Object.getOwnPropertyDescriptor, b = (e) => {
4
- throw TypeError(e);
5
- }, s = (e, t, a, l) => {
6
- for (var o = l > 1 ? void 0 : l ? w(t, a) : t, p = e.length - 1, h; p >= 0; p--)
7
- (h = e[p]) && (o = (l ? h(t, a, o) : h(o)) || o);
8
- return l && o && y(t, a, o), o;
9
- }, _ = (e, t, a) => t.has(e) || b("Cannot " + a), I = (e, t, a) => t.has(e) ? b("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(e) : t.set(e, a), v = (e, t, a) => (_(e, t, "access private method"), a), n, f, u;
10
- class $ extends Event {
11
- constructor(t) {
12
- super("dp-sidebar-select", { bubbles: !0, composed: !0 }), this.itemId = t;
13
- }
14
- }
15
- let r = class extends x {
16
- constructor() {
17
- super(...arguments), I(this, n), this.items = [], this.bottomItems = [], this.activeId = "", this.header = "", this.collapsed = !1;
18
- }
19
- render() {
20
- return i`
21
- <div class="header">
22
- <slot name="icon"></slot>
23
- ${!this.collapsed && this.header ? i`<span class="header-label">${this.header}</span>` : c}
24
- </div>
25
- <nav>${this.items.map((e) => v(this, n, f).call(this, e))}</nav>
26
- ${this.bottomItems.length > 0 ? i`<div class="bottom-group">${this.bottomItems.map((e) => v(this, n, f).call(this, e))}</div>` : c}
27
- <div class="footer"><slot name="footer"></slot></div>
28
- `;
29
- }
30
- };
31
- n = /* @__PURE__ */ new WeakSet();
32
- f = function(e) {
33
- const t = e.id === this.activeId;
34
- return i`
35
- <button
36
- title=${this.collapsed ? e.label : c}
37
- aria-current=${t ? "page" : c}
38
- @click=${() => v(this, n, u).call(this, e.id)}
39
- >
40
- <span class="icon" aria-hidden="true">${e.icon}</span>
41
- ${this.collapsed ? c : i`<span>${e.label}</span>`}
42
- </button>
43
- `;
44
- };
45
- u = function(e) {
46
- e !== this.activeId && (this.activeId = e, this.dispatchEvent(new $(e)));
47
- };
48
- r.styles = m`
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, state as s } from "lit/decorators.js";
4
+ //#region src/components/sidebar/dp-sidebar.ts
5
+ var c = (e) => Array.isArray(e.items), l = class extends Event {
6
+ constructor(e) {
7
+ super("dp-sidebar-select", {
8
+ bubbles: !0,
9
+ composed: !0,
10
+ cancelable: !0
11
+ }), this.itemId = e;
12
+ }
13
+ }, u = class extends Event {
14
+ constructor(e) {
15
+ super("dp-sidebar-action", {
16
+ bubbles: !0,
17
+ composed: !0
18
+ }), this.itemId = e;
19
+ }
20
+ }, d = class extends t {
21
+ constructor(...e) {
22
+ super(...e), this.items = [], this.bottomItems = [], this.activeId = "", this.header = "", this.navLabel = "", this.collapsed = !1, this.toggledGroups = /* @__PURE__ */ new Set();
23
+ }
24
+ static {
25
+ this.styles = n`
49
26
  :host {
50
27
  display: flex;
51
28
  flex-direction: column;
@@ -89,11 +66,12 @@ r.styles = m`
89
66
  border-top: 1px solid var(--dc-color-border, #e2e2e4);
90
67
  padding: var(--dc-space-1, 4px) 0;
91
68
  }
92
- button {
69
+ .item {
93
70
  appearance: none;
94
71
  background: none;
95
72
  border: none;
96
73
  border-left: 2px solid transparent;
74
+ box-sizing: border-box;
97
75
  width: 100%;
98
76
  display: flex;
99
77
  align-items: center;
@@ -103,12 +81,23 @@ r.styles = m`
103
81
  font-size: var(--dc-font-size-sm, 12px);
104
82
  color: var(--dc-color-text-secondary, #55555c);
105
83
  text-align: left;
84
+ text-decoration: none;
106
85
  cursor: pointer;
107
86
  }
108
- :host([collapsed]) button {
87
+ :host([collapsed]) .item {
109
88
  justify-content: center;
110
89
  padding: var(--dc-space-3, 12px) 0;
111
90
  }
91
+ /* Items inside an open group sit one step in, so the group reads as a set. */
92
+ :host(:not([collapsed])) .group-items .item {
93
+ padding-left: calc(var(--dc-space-3, 12px) * 2);
94
+ }
95
+ /* In the collapsed rail a group has no label to show; a rule marks where it starts. */
96
+ :host([collapsed]) .group + .group,
97
+ :host([collapsed]) .item + .group,
98
+ :host([collapsed]) .group + .item {
99
+ border-top: 1px solid var(--dc-color-border, #e2e2e4);
100
+ }
112
101
  /* The rail marker keeps the fill accent - it is a 3px bar, judged as a UI
113
102
  component (WCAG 1.4.11, 3:1). The label does not: an accent chosen to be
114
103
  read as a *fill* is not necessarily readable as 12px text on the hover
@@ -118,15 +107,15 @@ r.styles = m`
118
107
  find. --dc-color-accent-text lets a consumer supply the readable
119
108
  variant; falling back to the fill accent keeps every existing consumer
120
109
  rendering exactly as before. */
121
- button[aria-current='page'] {
110
+ .item[aria-current='page'] {
122
111
  border-left-color: var(--dc-color-accent, #2563eb);
123
112
  color: var(--dc-color-accent-text, var(--dc-color-accent, #2563eb));
124
113
  font-weight: var(--dc-font-weight-medium, 500);
125
114
  }
126
- button:hover {
115
+ .item:hover {
127
116
  background: var(--dc-color-surface-hover, #ececed);
128
117
  }
129
- button:focus-visible {
118
+ .item:focus-visible {
130
119
  outline: 2px solid var(--dc-color-accent, #2563eb);
131
120
  outline-offset: -2px;
132
121
  }
@@ -135,29 +124,105 @@ r.styles = m`
135
124
  width: 1.25em;
136
125
  text-align: center;
137
126
  }
127
+ .caret {
128
+ margin-left: auto;
129
+ transition: transform 0.15s;
130
+ }
131
+ .group-toggle[aria-expanded='false'] .caret {
132
+ transform: rotate(-90deg);
133
+ }
138
134
  .footer {
139
135
  border-top: 1px solid var(--dc-color-border, #e2e2e4);
140
136
  }
141
137
  `;
142
- s([
143
- d({ type: Array })
144
- ], r.prototype, "items", 2);
145
- s([
146
- d({ type: Array, attribute: "bottom-items" })
147
- ], r.prototype, "bottomItems", 2);
148
- s([
149
- d({ attribute: "active-id" })
150
- ], r.prototype, "activeId", 2);
151
- s([
152
- d()
153
- ], r.prototype, "header", 2);
154
- s([
155
- d({ type: Boolean, reflect: !0 })
156
- ], r.prototype, "collapsed", 2);
157
- r = s([
158
- g("dp-sidebar")
159
- ], r);
160
- export {
161
- r as DpSidebar,
162
- $ as DpSidebarSelectEvent
138
+ }
139
+ render() {
140
+ return r`
141
+ <div class="header">
142
+ <slot name="icon"></slot>
143
+ ${!this.collapsed && this.header ? r`<span class="header-label">${this.header}</span>` : i}
144
+ </div>
145
+ <nav aria-label=${this.navLabel || i}>
146
+ ${this.items.map((e) => c(e) ? this.#e(e) : this.#t(e))}
147
+ </nav>
148
+ ${this.bottomItems.length > 0 ? r`<div class="bottom-group">${this.bottomItems.map((e) => this.#n(e))}</div>` : i}
149
+ <div class="footer"><slot name="footer"></slot></div>
150
+ `;
151
+ }
152
+ #e(e) {
153
+ 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}`;
154
+ return r`
155
+ <div class="group">
156
+ ${this.collapsed ? i : r`<button
157
+ class="item group-toggle"
158
+ aria-expanded=${a ? "true" : "false"}
159
+ aria-controls=${o}
160
+ @click=${() => this.#a(e.id)}
161
+ >
162
+ ${this.#r(e.id, e.icon ?? "")}
163
+ <span class="label">${e.label}</span>
164
+ <span class="caret" aria-hidden="true">▾</span>
165
+ </button>`}
166
+ <div class="group-items" id=${o} role="group" aria-label=${e.label} ?hidden=${!a}>
167
+ ${e.items.map((e) => this.#t(e))}
168
+ </div>
169
+ </div>
170
+ `;
171
+ }
172
+ #t(e) {
173
+ let t = e.id === this.activeId, n = r`
174
+ ${this.#r(e.id, e.icon)} ${this.collapsed ? i : r`<span class="label">${e.label}</span>`}
175
+ `;
176
+ return e.href === void 0 ? r`<button
177
+ class="item"
178
+ title=${this.collapsed ? e.label : i}
179
+ aria-current=${t ? "page" : i}
180
+ @click=${(t) => this.#i(t, e)}
181
+ >
182
+ ${n}
183
+ </button>` : r`<a
184
+ class="item"
185
+ href=${e.href}
186
+ title=${this.collapsed ? e.label : i}
187
+ aria-current=${t ? "page" : i}
188
+ @click=${(t) => this.#i(t, e)}
189
+ >${n}</a
190
+ >`;
191
+ }
192
+ #n(e) {
193
+ return r`
194
+ <button
195
+ class="item"
196
+ title=${this.collapsed ? e.label : i}
197
+ @click=${() => this.dispatchEvent(new u(e.id))}
198
+ >
199
+ ${this.#r(e.id, e.icon)} ${this.collapsed ? i : r`<span class="label">${e.label}</span>`}
200
+ </button>
201
+ `;
202
+ }
203
+ #r(e, t) {
204
+ return r`<span class="icon" aria-hidden="true"><slot name=${`icon-${e}`}>${t}</slot></span>`;
205
+ }
206
+ #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;
211
+ }
212
+ this.activeId = t.id;
213
+ }
214
+ }
215
+ #a(e) {
216
+ let t = new Set(this.toggledGroups);
217
+ t.delete(e) || t.add(e), this.toggledGroups = t;
218
+ }
163
219
  };
220
+ e([o({ type: Array })], d.prototype, "items", void 0), e([o({
221
+ type: Array,
222
+ 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({
224
+ type: Boolean,
225
+ reflect: !0
226
+ })], d.prototype, "collapsed", void 0), e([s()], d.prototype, "toggledGroups", void 0), d = e([a("dp-sidebar")], d);
227
+ //#endregion
228
+ export { d as DpSidebar, u as DpSidebarActionEvent, l as DpSidebarSelectEvent };
package/dist/toolbar.js CHANGED
@@ -1,40 +1,20 @@
1
- import { css as p, LitElement as g, nothing as h, html as d } from "lit";
2
- import { property as s, customElement as v } from "lit/decorators.js";
3
- var f = Object.defineProperty, b = Object.getOwnPropertyDescriptor, o = (l, r, i, a) => {
4
- for (var e = a > 1 ? void 0 : a ? b(r, i) : r, n = l.length - 1, c; n >= 0; n--)
5
- (c = l[n]) && (e = (a ? c(r, i, e) : c(e)) || e);
6
- return a && e && f(r, i, e), e;
7
- };
8
- class u extends Event {
9
- constructor() {
10
- super("dp-toolbar-toggle", { bubbles: !0, composed: !0 });
11
- }
12
- }
13
- let t = class extends g {
14
- constructor() {
15
- super(...arguments), this.heading = "", this.subtitle = "", this.showToggle = !1, this.dragRegion = !1;
16
- }
17
- connectedCallback() {
18
- super.connectedCallback(), this.setAttribute("role", "banner");
19
- }
20
- render() {
21
- return d`
22
- <div class="start">
23
- ${this.showToggle ? d`<button
24
- class="toggle"
25
- aria-label="Toggle sidebar"
26
- @click=${() => this.dispatchEvent(new u())}
27
- >
28
- ☰
29
- </button>` : h}
30
- <span class="title">${this.subtitle || this.heading}</span>
31
- </div>
32
- <div class="drag-handle" part="drag-handle"></div>
33
- <div class="actions"><slot name="actions"></slot></div>
34
- `;
35
- }
36
- };
37
- t.styles = p`
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";
4
+ //#region src/components/toolbar/dp-toolbar.ts
5
+ var s = class extends Event {
6
+ constructor() {
7
+ super("dp-toolbar-toggle", {
8
+ bubbles: !0,
9
+ composed: !0
10
+ });
11
+ }
12
+ }, c = class extends t {
13
+ constructor(...e) {
14
+ super(...e), this.heading = "", this.subtitle = "", this.showToggle = !1, this.toggleLabel = "Toggle sidebar", this.dragRegion = !1;
15
+ }
16
+ static {
17
+ this.styles = n`
38
18
  :host {
39
19
  display: flex;
40
20
  align-items: center;
@@ -90,22 +70,34 @@ t.styles = p`
90
70
  gap: var(--dc-space-3, 12px);
91
71
  }
92
72
  `;
93
- o([
94
- s()
95
- ], t.prototype, "heading", 2);
96
- o([
97
- s()
98
- ], t.prototype, "subtitle", 2);
99
- o([
100
- s({ type: Boolean, attribute: "show-toggle" })
101
- ], t.prototype, "showToggle", 2);
102
- o([
103
- s({ type: Boolean, reflect: !0, attribute: "drag-region" })
104
- ], t.prototype, "dragRegion", 2);
105
- t = o([
106
- v("dp-toolbar")
107
- ], t);
108
- export {
109
- t as DpToolbar,
110
- u as DpToolbarToggleEvent
73
+ }
74
+ connectedCallback() {
75
+ super.connectedCallback(), this.setAttribute("role", "banner");
76
+ }
77
+ render() {
78
+ return r`
79
+ <div class="start">
80
+ ${this.showToggle ? r`<button
81
+ class="toggle"
82
+ aria-label=${this.toggleLabel}
83
+ @click=${() => this.dispatchEvent(new s())}
84
+ >
85
+ ☰
86
+ </button>` : i}
87
+ <span class="title">${this.subtitle || this.heading}</span>
88
+ </div>
89
+ <div class="drag-handle" part="drag-handle"></div>
90
+ <div class="actions"><slot name="actions"></slot></div>
91
+ `;
92
+ }
111
93
  };
94
+ e([o()], c.prototype, "heading", void 0), e([o()], c.prototype, "subtitle", void 0), e([o({
95
+ type: Boolean,
96
+ attribute: "show-toggle"
97
+ })], c.prototype, "showToggle", void 0), e([o({ attribute: "toggle-label" })], c.prototype, "toggleLabel", void 0), e([o({
98
+ type: Boolean,
99
+ reflect: !0,
100
+ attribute: "drag-region"
101
+ })], c.prototype, "dragRegion", void 0), c = e([a("dp-toolbar")], c);
102
+ //#endregion
103
+ 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.2.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -57,23 +57,23 @@
57
57
  "build-storybook": "storybook build"
58
58
  },
59
59
  "dependencies": {
60
- "lit": "^3.2.1"
60
+ "lit": "^3.3.3"
61
61
  },
62
62
  "peerDependencies": {
63
- "@iyulab/desktop-compact": "^0.2.0 || ^0.3.0"
63
+ "@iyulab/desktop-compact": "^0.2.0 || ^0.3.0 || ^0.4.0 || ^0.5.0"
64
64
  },
65
65
  "devDependencies": {
66
- "@iyulab/desktop-compact": "^0.3.0",
67
- "@open-wc/testing": "^4.0.0",
68
- "@storybook/addon-essentials": "^8.3.0",
69
- "@storybook/web-components": "^8.3.0",
70
- "@storybook/web-components-vite": "^8.3.0",
66
+ "@iyulab/desktop-compact": "^0.5.0",
67
+ "@open-wc/testing": "^5.0.0",
68
+ "@storybook/web-components": "^10.6.0",
69
+ "@storybook/web-components-vite": "^10.6.0",
71
70
  "@types/mocha": "^10.0.10",
72
- "@web/dev-server-esbuild": "^1.0.2",
73
- "@web/test-runner": "^0.19.0",
74
- "storybook": "^8.3.0",
75
- "typescript": "^5.6.3",
76
- "vite": "^5.4.10",
77
- "vite-plugin-dts": "^4.2.4"
71
+ "@web/dev-server-esbuild": "^2.0.0",
72
+ "@web/test-runner": "^1.0.0",
73
+ "axe-core": "^4.13.0",
74
+ "storybook": "^10.6.0",
75
+ "typescript": "^6.0.3",
76
+ "vite": "^8.3.1",
77
+ "vite-plugin-dts": "^5.1.1"
78
78
  }
79
79
  }