@adia-ai/web-modules 0.3.3 → 0.3.4
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/CHANGELOG.md +36 -0
- package/chat/chat-shell/chat-shell.examples.html +85 -0
- package/chat/chat-shell/chat-shell.html +42 -0
- package/editor/editor-shell/editor-shell.examples.html +71 -0
- package/editor/editor-shell/editor-shell.html +42 -0
- package/package.json +1 -1
- package/shell/admin-command/admin-command.a2ui.json +102 -0
- package/shell/admin-command/admin-command.examples.html +83 -0
- package/shell/admin-command/admin-command.html +42 -0
- package/shell/admin-command/admin-command.js +161 -0
- package/shell/admin-command/admin-command.test.js +115 -0
- package/shell/admin-command/admin-command.yaml +102 -0
- package/shell/admin-content/admin-content.a2ui.json +73 -0
- package/shell/admin-content/admin-content.examples.html +33 -0
- package/shell/admin-content/admin-content.html +42 -0
- package/shell/admin-content/admin-content.yaml +63 -0
- package/shell/admin-page/admin-page.a2ui.json +74 -0
- package/shell/admin-page/admin-page.examples.html +37 -0
- package/shell/admin-page/admin-page.html +42 -0
- package/shell/admin-page/admin-page.yaml +61 -0
- package/shell/admin-page-body/admin-page-body.a2ui.json +62 -0
- package/shell/admin-page-body/admin-page-body.examples.html +34 -0
- package/shell/admin-page-body/admin-page-body.html +42 -0
- package/shell/admin-page-body/admin-page-body.yaml +49 -0
- package/shell/admin-page-header/admin-page-header.a2ui.json +62 -0
- package/shell/admin-page-header/admin-page-header.examples.html +34 -0
- package/shell/admin-page-header/admin-page-header.html +42 -0
- package/shell/admin-page-header/admin-page-header.yaml +47 -0
- package/shell/admin-scroll/admin-scroll.a2ui.json +62 -0
- package/shell/admin-scroll/admin-scroll.examples.html +31 -0
- package/shell/admin-scroll/admin-scroll.html +42 -0
- package/shell/admin-scroll/admin-scroll.yaml +51 -0
- package/shell/admin-shell/admin-shell.a2ui.json +0 -10
- package/shell/admin-shell/admin-shell.css +1 -0
- package/shell/admin-shell/admin-shell.examples.html +61 -5
- package/shell/admin-shell/admin-shell.js +165 -121
- package/shell/admin-shell/admin-shell.yaml +6 -6
- package/shell/admin-shell/css/admin-shell.bespoke.css +198 -0
- package/shell/admin-shell/css/admin-shell.tokens.css +10 -0
- package/shell/admin-sidebar/admin-sidebar.a2ui.json +138 -0
- package/shell/admin-sidebar/admin-sidebar.examples.html +76 -0
- package/shell/admin-sidebar/admin-sidebar.html +47 -0
- package/shell/admin-sidebar/admin-sidebar.js +227 -0
- package/shell/admin-sidebar/admin-sidebar.test.js +123 -0
- package/shell/admin-sidebar/admin-sidebar.yaml +140 -0
- package/shell/admin-statusbar/admin-statusbar.a2ui.json +81 -0
- package/shell/admin-statusbar/admin-statusbar.examples.html +29 -0
- package/shell/admin-statusbar/admin-statusbar.html +42 -0
- package/shell/admin-statusbar/admin-statusbar.yaml +68 -0
- package/shell/admin-topbar/admin-topbar.a2ui.json +83 -0
- package/shell/admin-topbar/admin-topbar.examples.html +31 -0
- package/shell/admin-topbar/admin-topbar.html +42 -0
- package/shell/admin-topbar/admin-topbar.yaml +75 -0
- package/shell/index.js +2 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Edit this file; run `npm run build:components` to regenerate a2ui.json.
|
|
2
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
3
|
+
name: AdminScroll
|
|
4
|
+
tag: admin-scroll
|
|
5
|
+
component: AdminScroll
|
|
6
|
+
category: layout
|
|
7
|
+
version: 1
|
|
8
|
+
description: |
|
|
9
|
+
Module-tier shell scroll surface. CSS-only — no behavior, no JS.
|
|
10
|
+
Sits inside <admin-content> as the central scrollable region.
|
|
11
|
+
Single-axis scroll (vertical), scroll-contained so wheel/touch
|
|
12
|
+
events don't propagate to the document.
|
|
13
|
+
|
|
14
|
+
Replaces the legacy <section> child of <main> at shell-tier per
|
|
15
|
+
ADR-0023.
|
|
16
|
+
|
|
17
|
+
props: {}
|
|
18
|
+
|
|
19
|
+
events: {}
|
|
20
|
+
|
|
21
|
+
slots:
|
|
22
|
+
default:
|
|
23
|
+
description: >-
|
|
24
|
+
Scrollable content — typically <admin-page> wrapping the
|
|
25
|
+
page-tier sticky bands + body.
|
|
26
|
+
|
|
27
|
+
states:
|
|
28
|
+
- name: idle
|
|
29
|
+
description: Default, the only state.
|
|
30
|
+
|
|
31
|
+
traits: []
|
|
32
|
+
|
|
33
|
+
a2ui:
|
|
34
|
+
rules:
|
|
35
|
+
- >-
|
|
36
|
+
admin-scroll is the bespoke replacement for the legacy <section>
|
|
37
|
+
child of <main> inside admin-shell. Single child convention —
|
|
38
|
+
typically wraps an <admin-page> for sticky-band layout.
|
|
39
|
+
|
|
40
|
+
keywords:
|
|
41
|
+
- admin-scroll
|
|
42
|
+
- scroll-region
|
|
43
|
+
- scroll-container
|
|
44
|
+
|
|
45
|
+
synonyms:
|
|
46
|
+
scroll-region: [scroll-area, viewport-content]
|
|
47
|
+
|
|
48
|
+
related:
|
|
49
|
+
- AdminShell
|
|
50
|
+
- AdminContent
|
|
51
|
+
- AdminPage
|
|
@@ -97,16 +97,6 @@
|
|
|
97
97
|
{
|
|
98
98
|
"description": "Default, interactive shell.",
|
|
99
99
|
"name": "idle"
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
"description": "Leading sidebar is collapsed; content expands.",
|
|
103
|
-
"attribute": "data-sidebar-leading-collapsed",
|
|
104
|
-
"name": "collapsed-leading"
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
"description": "Trailing sidebar (inspector) is collapsed.",
|
|
108
|
-
"attribute": "data-sidebar-trailing-collapsed",
|
|
109
|
-
"name": "collapsed-trailing"
|
|
110
100
|
}
|
|
111
101
|
],
|
|
112
102
|
"synonyms": {
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
</header>
|
|
10
10
|
|
|
11
11
|
<section data-section>
|
|
12
|
-
<h2 variant="section">Basic shape</h2>
|
|
13
|
-
<p data-note>Author supplies the DOM (sidebars + main column + optional command dialog); the shell binds the interactions.</p>
|
|
12
|
+
<h2 variant="section">Basic shape (legacy)</h2>
|
|
13
|
+
<p data-note>Author supplies the DOM (sidebars + main column + optional command dialog); the shell binds the interactions. This is the original raw-HTML authoring shape — still fully supported.</p>
|
|
14
14
|
<code-ui language="html"><admin-shell mode="rounded">
|
|
15
15
|
<aside data-sidebar="leading">
|
|
16
16
|
<header>…</header>
|
|
@@ -40,6 +40,62 @@
|
|
|
40
40
|
</admin-shell></code-ui>
|
|
41
41
|
</section>
|
|
42
42
|
|
|
43
|
+
<section data-section>
|
|
44
|
+
<h2 variant="section">Basic shape (bespoke — recommended)</h2>
|
|
45
|
+
<p data-note>The bespoke shape uses module-namespaced custom elements (<code><admin-sidebar></code>, <code><admin-content></code>, etc.) per <a href="../../../../.brain/adrs/0023-bespoke-shell-tier-children.md">ADR-0023</a>. Each child owns its own behavior + state attributes; queryable from outside via <code>:has(admin-sidebar[collapsed])</code>.</p>
|
|
46
|
+
<code-ui language="html"><admin-shell mode="rounded">
|
|
47
|
+
<admin-sidebar slot="leading" resizable collapsible>
|
|
48
|
+
<admin-topbar slot="header">
|
|
49
|
+
<span slot="heading">Workspace</span>
|
|
50
|
+
</admin-topbar>
|
|
51
|
+
<nav-ui>…</nav-ui>
|
|
52
|
+
<admin-statusbar slot="footer">
|
|
53
|
+
<span>User</span>
|
|
54
|
+
</admin-statusbar>
|
|
55
|
+
<div data-resize></div>
|
|
56
|
+
</admin-sidebar>
|
|
57
|
+
|
|
58
|
+
<admin-content>
|
|
59
|
+
<admin-topbar slot="header">
|
|
60
|
+
<button-ui data-sidebar-toggle="leading" icon="sidebar" variant="ghost" size="sm"></button-ui>
|
|
61
|
+
<breadcrumb-ui slot="heading">…</breadcrumb-ui>
|
|
62
|
+
</admin-topbar>
|
|
63
|
+
<admin-scroll>
|
|
64
|
+
<admin-page>
|
|
65
|
+
<admin-page-header>
|
|
66
|
+
<header-ui><span slot="heading">Page Title</span></header-ui>
|
|
67
|
+
</admin-page-header>
|
|
68
|
+
<admin-page-body>
|
|
69
|
+
<section-ui>…content…</section-ui>
|
|
70
|
+
</admin-page-body>
|
|
71
|
+
</admin-page>
|
|
72
|
+
</admin-scroll>
|
|
73
|
+
<admin-statusbar slot="footer">
|
|
74
|
+
<span>Status</span>
|
|
75
|
+
</admin-statusbar>
|
|
76
|
+
</admin-content>
|
|
77
|
+
|
|
78
|
+
<admin-command>
|
|
79
|
+
<command-ui placeholder="Search..."></command-ui>
|
|
80
|
+
</admin-command>
|
|
81
|
+
</admin-shell></code-ui>
|
|
82
|
+
</section>
|
|
83
|
+
|
|
84
|
+
<section data-section>
|
|
85
|
+
<h2 variant="section">State as attribute</h2>
|
|
86
|
+
<p>Every queryable state is reflected on the relevant child element. Style cross-cuts via <code>:has()</code>:</p>
|
|
87
|
+
<code-ui language="css">/* Theme tweak when leading sidebar is collapsed */
|
|
88
|
+
admin-shell:has(admin-sidebar[slot="leading"][collapsed]) admin-content {
|
|
89
|
+
/* … */
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Show focus indicator while command palette is open */
|
|
93
|
+
admin-shell:has(admin-command[open]) {
|
|
94
|
+
/* … */
|
|
95
|
+
}</code-ui>
|
|
96
|
+
<p data-note>JS reads the same attribute: <code>shell.querySelector('admin-sidebar[slot="leading"]').hasAttribute('collapsed')</code>. No threshold math, no <code>getBoundingClientRect</code>.</p>
|
|
97
|
+
</section>
|
|
98
|
+
|
|
43
99
|
<section data-section>
|
|
44
100
|
<h2 variant="section">Properties</h2>
|
|
45
101
|
<table>
|
|
@@ -55,9 +111,9 @@
|
|
|
55
111
|
<table>
|
|
56
112
|
<thead><tr><th>Affordance</th><th>Author markup</th><th>What the shell does</th></tr></thead>
|
|
57
113
|
<tbody>
|
|
58
|
-
<tr><td>Sidebar toggle</td><td><code>[data-sidebar-toggle="leading"]</code> / <code>[data-sidebar-toggle="trailing"]</code></td><td>Click flips <code>[
|
|
59
|
-
<tr><td>Sidebar resize</td><td><code><div data-resize></div></code> inside the sidebar</td><td>Pointer-drag resizes the aside; bounded by <code
|
|
60
|
-
<tr><td>Command palette</td><td><code><dialog data-command></code>
|
|
114
|
+
<tr><td>Sidebar toggle</td><td><code>[data-sidebar-toggle="leading"]</code> / <code>[data-sidebar-toggle="trailing"]</code></td><td>Click flips <code>[collapsed]</code> on the corresponding sidebar. Persists in <code>localStorage</code>.</td></tr>
|
|
115
|
+
<tr><td>Sidebar resize</td><td><code><div data-resize></div></code> inside the sidebar</td><td>Pointer-drag resizes the aside; bounded by <code>--page-sidebar-min-width</code> / <code>--page-sidebar-max-width</code> CSS custom properties. Snaps below threshold to collapsed.</td></tr>
|
|
116
|
+
<tr><td>Command palette</td><td><code><dialog data-command></code> as a child of the shell</td><td><code>Cmd+K</code> / <code>Ctrl+K</code> calls <code>showModal()</code> on the inner dialog. Forwards <code>command-select</code> events from the inner <code><command-ui></code>.</td></tr>
|
|
61
117
|
<tr><td>Responsive collapse</td><td>(automatic)</td><td>ResizeObserver collapses sidebars when viewport drops below the breakpoint; restores on widen.</td></tr>
|
|
62
118
|
</tbody>
|
|
63
119
|
</table>
|
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* <admin-shell mode="rounded borderless">
|
|
3
|
-
* <
|
|
4
|
-
* <
|
|
5
|
-
* <
|
|
6
|
-
* <
|
|
3
|
+
* <admin-sidebar slot="leading" resizable collapsible>…</admin-sidebar>
|
|
4
|
+
* <admin-content>…</admin-content>
|
|
5
|
+
* <admin-sidebar slot="trailing" resizable collapsible>…</admin-sidebar>
|
|
6
|
+
* <admin-command><command-ui></command-ui></admin-command>
|
|
7
7
|
* </admin-shell>
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* The component auto-wires four JS behaviors that CSS can't handle:
|
|
9
|
+
* Module-tier app shell. Coordinates bespoke shell-tier children;
|
|
10
|
+
* does NOT centralize their behavior. Each child owns its own
|
|
11
|
+
* concern (resize/collapse on <admin-sidebar>, palette wiring on
|
|
12
|
+
* <admin-command>).
|
|
14
13
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* Backwards compatibility: legacy authoring shapes still work —
|
|
15
|
+
* <aside data-sidebar="leading"> and <dialog data-command> are
|
|
16
|
+
* recognized + wired the same as their bespoke equivalents. Mixed
|
|
17
|
+
* markup (some bespoke, some legacy) renders identically. See
|
|
18
|
+
* ADR-0023 for the migration window.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* Host responsibilities (the only behavior that stays here):
|
|
21
|
+
* 1. [mode] reflection — "rounded", "borderless", or both
|
|
22
|
+
* 2. [data-sidebar-toggle="leading|trailing"] click forwarding to
|
|
23
|
+
* the matching sidebar's .toggle() (works for bespoke + legacy)
|
|
24
|
+
* 3. [data-command-trigger] click forwarding to the inner palette's
|
|
25
|
+
* .show() (works for bespoke + legacy)
|
|
26
|
+
* 4. Re-emit child events as host events for backwards compat
|
|
27
|
+
* (sidebar-toggle, sidebar-resize, command-select all bubble
|
|
28
|
+
* already, but consumers who listened on the host get them)
|
|
22
29
|
*/
|
|
23
30
|
|
|
24
31
|
import { UIElement } from '../../../web-components/core/element.js';
|
|
25
32
|
|
|
26
|
-
const SNAP_THRESHOLD = 96;
|
|
27
|
-
const SNAP_MIN_USABLE = 160;
|
|
28
|
-
|
|
29
33
|
class AdminShell extends UIElement {
|
|
30
34
|
static properties = {
|
|
31
35
|
mode: { type: String, default: '', reflect: true },
|
|
@@ -33,107 +37,161 @@ class AdminShell extends UIElement {
|
|
|
33
37
|
|
|
34
38
|
static template = () => null;
|
|
35
39
|
|
|
36
|
-
#sidebarWidths = new Map();
|
|
37
|
-
#resizeCleanups = [];
|
|
38
|
-
#sidebarRO = null;
|
|
39
40
|
#cmdKeyHandler = null;
|
|
41
|
+
#legacyResizeCleanups = [];
|
|
42
|
+
#legacySidebarRO = null;
|
|
43
|
+
#legacySidebarWidths = new Map();
|
|
40
44
|
|
|
41
45
|
connected() {
|
|
42
|
-
this.#
|
|
43
|
-
this.#
|
|
44
|
-
this.#
|
|
45
|
-
this.#setupResizeObserver();
|
|
46
|
+
this.#wireToggleButtons();
|
|
47
|
+
this.#wireCommandTriggers();
|
|
48
|
+
this.#setupLegacyShapes();
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
disconnected() {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
this.#
|
|
52
|
-
|
|
53
|
-
// Clean up ResizeObserver
|
|
54
|
-
this.#sidebarRO?.disconnect();
|
|
55
|
-
this.#sidebarRO = null;
|
|
56
|
-
|
|
57
|
-
// Clean up Cmd+K
|
|
52
|
+
for (const cleanup of this.#legacyResizeCleanups) cleanup();
|
|
53
|
+
this.#legacyResizeCleanups = [];
|
|
54
|
+
this.#legacySidebarRO?.disconnect();
|
|
55
|
+
this.#legacySidebarRO = null;
|
|
58
56
|
if (this.#cmdKeyHandler) {
|
|
59
57
|
document.removeEventListener('keydown', this.#cmdKeyHandler);
|
|
60
58
|
this.#cmdKeyHandler = null;
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
//
|
|
62
|
+
// Selector that matches BOTH bespoke <admin-sidebar slot> AND legacy
|
|
63
|
+
// shapes. Used by toggle-button wiring + ResizeObserver setup.
|
|
64
|
+
static #SIDEBAR_SEL =
|
|
65
|
+
':is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"], ' +
|
|
66
|
+
'[data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"])';
|
|
65
67
|
|
|
66
|
-
// Selector that matches both legacy raw-HTML and slot-vocabulary forms.
|
|
67
|
-
static #SIDEBAR_SEL = ':is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"])';
|
|
68
|
-
|
|
69
|
-
// Helper: read sidebar name from either authoring shape.
|
|
70
68
|
#sidebarName(el) {
|
|
71
|
-
return
|
|
69
|
+
return (
|
|
70
|
+
el.getAttribute('name') ??
|
|
71
|
+
el.getAttribute('data-sidebar') ??
|
|
72
|
+
el.getAttribute('slot')
|
|
73
|
+
);
|
|
72
74
|
}
|
|
73
75
|
|
|
74
|
-
#
|
|
75
|
-
|
|
76
|
+
#findSidebar(name) {
|
|
77
|
+
return this.querySelector(
|
|
78
|
+
':is(' +
|
|
79
|
+
`admin-sidebar[slot="${name}"], admin-sidebar[name="${name}"], ` +
|
|
80
|
+
`[data-sidebar="${name}"], aside-ui[slot="${name}"]` +
|
|
81
|
+
')'
|
|
82
|
+
);
|
|
76
83
|
}
|
|
77
84
|
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
// ── 1. Toggle buttons — works for bespoke + legacy ──
|
|
86
|
+
|
|
87
|
+
#wireToggleButtons() {
|
|
88
|
+
for (const btn of this.querySelectorAll('[data-sidebar-toggle]')) {
|
|
89
|
+
const name = btn.getAttribute('data-sidebar-toggle');
|
|
90
|
+
btn.addEventListener('click', () => {
|
|
91
|
+
const sidebar = this.#findSidebar(name);
|
|
92
|
+
if (!sidebar) return;
|
|
93
|
+
|
|
94
|
+
// Bespoke <admin-sidebar> has a public toggle() method
|
|
95
|
+
if (typeof sidebar.toggle === 'function') {
|
|
96
|
+
sidebar.toggle();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Legacy: replicate the old in-place toggle on raw HTML
|
|
101
|
+
this.#legacyToggle(sidebar, name);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── 2. Command triggers — works for bespoke + legacy ──
|
|
107
|
+
|
|
108
|
+
#wireCommandTriggers() {
|
|
109
|
+
const command = this.querySelector('admin-command');
|
|
110
|
+
const legacyDialog = command ? null : this.querySelector('dialog[data-command]');
|
|
111
|
+
|
|
112
|
+
if (!command && !legacyDialog) return;
|
|
113
|
+
|
|
114
|
+
// Bespoke path: trigger calls .show() on <admin-command>
|
|
115
|
+
if (command) {
|
|
116
|
+
for (const trigger of this.querySelectorAll('[data-command-trigger]')) {
|
|
117
|
+
trigger.addEventListener('click', (e) => {
|
|
118
|
+
e.stopPropagation();
|
|
119
|
+
command.show();
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
// <admin-command> owns its own keyboard shortcut; nothing more
|
|
123
|
+
// for the host to wire. We DO re-listen for command-select to
|
|
124
|
+
// wire navigation if a <nav-ui> is present.
|
|
125
|
+
this.#wireCommandSelectToNav(command);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Legacy path: replicate the old wiring around <dialog data-command>
|
|
130
|
+
this.#wireLegacyCommand(legacyDialog);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#wireCommandSelectToNav(command) {
|
|
134
|
+
const nav = this.querySelector('nav-ui');
|
|
135
|
+
if (!nav) return;
|
|
136
|
+
command.addEventListener('command-select', (e) => {
|
|
137
|
+
const item = nav.querySelector(`nav-item-ui[value="${e.detail.value}"]`);
|
|
138
|
+
if (item) nav.select(item);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── 3. Legacy shape wiring (raw <aside data-sidebar>, <dialog data-command>) ──
|
|
143
|
+
|
|
144
|
+
#setupLegacyShapes() {
|
|
145
|
+
const legacySidebars = this.querySelectorAll(
|
|
146
|
+
':is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"])'
|
|
147
|
+
);
|
|
148
|
+
if (legacySidebars.length === 0) return;
|
|
149
|
+
|
|
150
|
+
this.#restoreLegacyWidths(legacySidebars);
|
|
151
|
+
this.#setupLegacyResizeHandles(legacySidebars);
|
|
152
|
+
this.#setupLegacyResizeObserver(legacySidebars);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
#restoreLegacyWidths(sidebars) {
|
|
156
|
+
for (const sidebar of sidebars) {
|
|
80
157
|
const name = this.#sidebarName(sidebar);
|
|
81
158
|
try {
|
|
82
159
|
const saved = localStorage.getItem(`adia-sidebar-${name}`);
|
|
83
160
|
if (saved) {
|
|
84
161
|
sidebar.style.width = saved;
|
|
85
|
-
// Only store as the "previous expanded width" if it's actually expanded.
|
|
86
|
-
// If collapsed, keep the default expanded width so toggle can restore it.
|
|
87
162
|
const w = parseFloat(saved);
|
|
88
|
-
if (isNaN(w)
|
|
89
|
-
this.#
|
|
163
|
+
if (!isNaN(w) && w > 96) {
|
|
164
|
+
this.#legacySidebarWidths.set(name, saved);
|
|
90
165
|
}
|
|
91
166
|
}
|
|
92
167
|
} catch {}
|
|
93
168
|
}
|
|
94
169
|
}
|
|
95
170
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const isCollapsed = sidebar.getBoundingClientRect().width <= SNAP_THRESHOLD;
|
|
108
|
-
|
|
109
|
-
if (isCollapsed) {
|
|
110
|
-
// Expand: restore previous width
|
|
111
|
-
const prev = this.#sidebarWidths.get(sidebarName);
|
|
112
|
-
sidebar.style.width = prev || '';
|
|
113
|
-
this.#persistSidebar(sidebarName, prev || '');
|
|
114
|
-
} else {
|
|
115
|
-
// Collapse: save current width, set to min
|
|
116
|
-
this.#sidebarWidths.set(sidebarName, sidebar.style.width || getComputedStyle(sidebar).width);
|
|
117
|
-
const minW = getComputedStyle(sidebar).minWidth;
|
|
118
|
-
sidebar.style.width = minW;
|
|
119
|
-
this.#persistSidebar(sidebarName, minW);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
this.dispatchEvent(new CustomEvent('sidebar-toggle', {
|
|
123
|
-
bubbles: true,
|
|
124
|
-
detail: { sidebar: sidebarName, expanded: !isCollapsed },
|
|
125
|
-
}));
|
|
126
|
-
});
|
|
171
|
+
#legacyToggle(sidebar, name) {
|
|
172
|
+
const isCollapsed = sidebar.getBoundingClientRect().width <= 96;
|
|
173
|
+
if (isCollapsed) {
|
|
174
|
+
const prev = this.#legacySidebarWidths.get(name);
|
|
175
|
+
sidebar.style.width = prev || '';
|
|
176
|
+
try { localStorage.setItem(`adia-sidebar-${name}`, prev || ''); } catch {}
|
|
177
|
+
} else {
|
|
178
|
+
this.#legacySidebarWidths.set(name, sidebar.style.width || getComputedStyle(sidebar).width);
|
|
179
|
+
const minW = getComputedStyle(sidebar).minWidth;
|
|
180
|
+
sidebar.style.width = minW;
|
|
181
|
+
try { localStorage.setItem(`adia-sidebar-${name}`, minW); } catch {}
|
|
127
182
|
}
|
|
183
|
+
this.dispatchEvent(new CustomEvent('sidebar-toggle', {
|
|
184
|
+
bubbles: true,
|
|
185
|
+
detail: { sidebar: name, name, expanded: !isCollapsed },
|
|
186
|
+
}));
|
|
128
187
|
}
|
|
129
188
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
const isLeading = sidebarName === 'leading';
|
|
189
|
+
#setupLegacyResizeHandles(sidebars) {
|
|
190
|
+
for (const sidebar of sidebars) {
|
|
191
|
+
const handle = sidebar.querySelector(':scope > [data-resize]');
|
|
192
|
+
if (!handle) continue;
|
|
193
|
+
const name = this.#sidebarName(sidebar);
|
|
194
|
+
const isLeading = name === 'leading';
|
|
137
195
|
|
|
138
196
|
const onPointerDown = (e) => {
|
|
139
197
|
e.preventDefault();
|
|
@@ -156,18 +214,17 @@ class AdminShell extends UIElement {
|
|
|
156
214
|
handle.removeEventListener('pointermove', onMove);
|
|
157
215
|
handle.removeEventListener('pointerup', onUp);
|
|
158
216
|
|
|
159
|
-
// Snap logic
|
|
160
217
|
const w = sidebar.getBoundingClientRect().width;
|
|
161
|
-
if (w <=
|
|
218
|
+
if (w <= 96) {
|
|
162
219
|
sidebar.style.width = getComputedStyle(sidebar).minWidth;
|
|
163
|
-
} else if (w <
|
|
164
|
-
sidebar.style.width =
|
|
220
|
+
} else if (w < 160) {
|
|
221
|
+
sidebar.style.width = '160px';
|
|
165
222
|
}
|
|
223
|
+
try { localStorage.setItem(`adia-sidebar-${name}`, sidebar.style.width); } catch {}
|
|
166
224
|
|
|
167
|
-
this.#persistSidebar(sidebarName, sidebar.style.width);
|
|
168
225
|
this.dispatchEvent(new CustomEvent('sidebar-resize', {
|
|
169
226
|
bubbles: true,
|
|
170
|
-
detail: { sidebar:
|
|
227
|
+
detail: { sidebar: name, name, width: sidebar.getBoundingClientRect().width },
|
|
171
228
|
}));
|
|
172
229
|
};
|
|
173
230
|
|
|
@@ -176,16 +233,24 @@ class AdminShell extends UIElement {
|
|
|
176
233
|
};
|
|
177
234
|
|
|
178
235
|
handle.addEventListener('pointerdown', onPointerDown);
|
|
179
|
-
this.#
|
|
236
|
+
this.#legacyResizeCleanups.push(() => handle.removeEventListener('pointerdown', onPointerDown));
|
|
180
237
|
}
|
|
181
238
|
}
|
|
182
239
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
240
|
+
#setupLegacyResizeObserver(sidebars) {
|
|
241
|
+
this.#legacySidebarRO = new ResizeObserver((entries) => {
|
|
242
|
+
for (const entry of entries) {
|
|
243
|
+
const sidebar = entry.target;
|
|
244
|
+
const narrow = entry.contentBoxSize[0].inlineSize <= 96;
|
|
245
|
+
for (const sel of sidebar.querySelectorAll('select-ui')) {
|
|
246
|
+
sel.setAttribute('placement', narrow ? 'right' : 'bottom-start');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
for (const sb of sidebars) this.#legacySidebarRO.observe(sb);
|
|
251
|
+
}
|
|
188
252
|
|
|
253
|
+
#wireLegacyCommand(dialog) {
|
|
189
254
|
const cmdEl = dialog.querySelector('command-ui');
|
|
190
255
|
const nav = this.querySelector('nav-ui');
|
|
191
256
|
|
|
@@ -193,13 +258,11 @@ class AdminShell extends UIElement {
|
|
|
193
258
|
dialog.showModal();
|
|
194
259
|
if (cmdEl) { cmdEl.open = true; cmdEl.value = ''; cmdEl.focus(); }
|
|
195
260
|
};
|
|
196
|
-
|
|
197
261
|
const closeCmd = () => {
|
|
198
262
|
dialog.close();
|
|
199
263
|
if (cmdEl) cmdEl.open = false;
|
|
200
264
|
};
|
|
201
265
|
|
|
202
|
-
// Trigger elements
|
|
203
266
|
for (const trigger of this.querySelectorAll('[data-command-trigger]')) {
|
|
204
267
|
trigger.addEventListener('click', (e) => {
|
|
205
268
|
e.stopPropagation();
|
|
@@ -207,12 +270,10 @@ class AdminShell extends UIElement {
|
|
|
207
270
|
});
|
|
208
271
|
}
|
|
209
272
|
|
|
210
|
-
// Backdrop click closes
|
|
211
273
|
dialog.addEventListener('click', (e) => {
|
|
212
274
|
if (e.target === dialog) closeCmd();
|
|
213
275
|
});
|
|
214
276
|
|
|
215
|
-
// Command-n events
|
|
216
277
|
if (cmdEl) {
|
|
217
278
|
cmdEl.addEventListener('dismiss', closeCmd);
|
|
218
279
|
cmdEl.addEventListener('select', (e) => {
|
|
@@ -228,7 +289,8 @@ class AdminShell extends UIElement {
|
|
|
228
289
|
});
|
|
229
290
|
}
|
|
230
291
|
|
|
231
|
-
// Cmd+K
|
|
292
|
+
// Cmd+K listener — only for legacy shape (bespoke <admin-command>
|
|
293
|
+
// owns its own listener)
|
|
232
294
|
this.#cmdKeyHandler = (e) => {
|
|
233
295
|
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
234
296
|
e.preventDefault();
|
|
@@ -237,24 +299,6 @@ class AdminShell extends UIElement {
|
|
|
237
299
|
};
|
|
238
300
|
document.addEventListener('keydown', this.#cmdKeyHandler);
|
|
239
301
|
}
|
|
240
|
-
|
|
241
|
-
// ── 4. ResizeObserver for select placement ─────────────────
|
|
242
|
-
|
|
243
|
-
#setupResizeObserver() {
|
|
244
|
-
this.#sidebarRO = new ResizeObserver((entries) => {
|
|
245
|
-
for (const entry of entries) {
|
|
246
|
-
const sidebar = entry.target;
|
|
247
|
-
const narrow = entry.contentBoxSize[0].inlineSize <= SNAP_THRESHOLD;
|
|
248
|
-
for (const sel of sidebar.querySelectorAll('select-ui')) {
|
|
249
|
-
sel.setAttribute('placement', narrow ? 'right' : 'bottom-start');
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
for (const sb of this.querySelectorAll(AdminShell.#SIDEBAR_SEL)) {
|
|
255
|
-
this.#sidebarRO.observe(sb);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
302
|
}
|
|
259
303
|
|
|
260
304
|
customElements.define('admin-shell', AdminShell);
|
|
@@ -66,12 +66,12 @@ slots:
|
|
|
66
66
|
states:
|
|
67
67
|
- name: idle
|
|
68
68
|
description: Default, interactive shell.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
|
|
70
|
+
# Note: collapsed state has moved to <admin-sidebar>'s [collapsed]
|
|
71
|
+
# reflected attribute (per ADR-0023). External consumers should query
|
|
72
|
+
# admin-sidebar[slot="leading"][collapsed] / [slot="trailing"][collapsed]
|
|
73
|
+
# directly. The previous data-sidebar-{name}-collapsed attributes on
|
|
74
|
+
# <admin-shell> are not set; new code should style via :has().
|
|
75
75
|
|
|
76
76
|
traits: []
|
|
77
77
|
|