@adia-ai/web-modules 0.7.8 → 0.7.10
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 +13 -0
- package/dist/everything.min.js +86 -86
- package/dist/web-modules.min.css +1 -1
- package/package.json +1 -1
- package/shell/embed-shell/embed-shell.a2ui.json +98 -0
- package/shell/embed-shell/embed-shell.css +100 -0
- package/shell/embed-shell/embed-shell.d.ts +32 -0
- package/shell/embed-shell/embed-shell.js +80 -0
- package/shell/embed-shell/embed-shell.yaml +74 -0
- package/shell/index.js +1 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <embed-shell> — embedded multi-surface shell.
|
|
3
|
+
*
|
|
4
|
+
* Composes a primary `[app]` surface with secondary `[panel="<name>"]` surfaces
|
|
5
|
+
* the consumer provides, and arranges them responsively. Peer of admin-shell /
|
|
6
|
+
* editor-shell / chat-shell. Light-DOM (P1). Content-agnostic: it orchestrates
|
|
7
|
+
* whatever `[app]` + `[panel]` children it's given — it owns layout, not content.
|
|
8
|
+
*
|
|
9
|
+
* Contract (clean attributes — ADR-0024, no data-*):
|
|
10
|
+
* <embed-shell>
|
|
11
|
+
* <x app>…</x> ← the primary surface (one)
|
|
12
|
+
* <y panel="chat">…</y> ← a secondary surface (any number)
|
|
13
|
+
* <z panel="settings">…</z>
|
|
14
|
+
* </embed-shell>
|
|
15
|
+
* …anywhere inside: <button opens="chat"> ← trigger (delegated)
|
|
16
|
+
* <button close> ← dismiss the open panel
|
|
17
|
+
*
|
|
18
|
+
* State (reflected, ADR-0023):
|
|
19
|
+
* embed-shell[panel="chat"] ← which panel is open ('' / absent = none)
|
|
20
|
+
* <y panel="chat" active> ← the open panel carries [active] (CSS hook)
|
|
21
|
+
*
|
|
22
|
+
* Events:
|
|
23
|
+
* embed:open (in) detail:{panel} ← request to open/toggle a panel
|
|
24
|
+
* embed:change(out) detail:{panel} ← emitted after the open panel changes
|
|
25
|
+
*
|
|
26
|
+
* Layout lives in embed-shell.css: ≥760px the open panel is a 50/50 column; <760px
|
|
27
|
+
* it's a cover sheet (translateY — identity at rest, so popovers inside still anchor).
|
|
28
|
+
*/
|
|
29
|
+
class EmbedShell extends HTMLElement {
|
|
30
|
+
#wired = false;
|
|
31
|
+
|
|
32
|
+
connectedCallback() {
|
|
33
|
+
if (this.#wired) return;
|
|
34
|
+
this.#wired = true;
|
|
35
|
+
this.addEventListener('click', (e) => {
|
|
36
|
+
const opener = e.target.closest?.('[opens]');
|
|
37
|
+
if (opener && this.contains(opener)) { this.toggle(opener.getAttribute('opens')); return; }
|
|
38
|
+
const closer = e.target.closest?.('[close]');
|
|
39
|
+
if (closer && this.contains(closer)) this.close();
|
|
40
|
+
});
|
|
41
|
+
this.addEventListener('embed:open', (e) => this.toggle(e.detail?.panel));
|
|
42
|
+
this.addEventListener('keydown', (e) => {
|
|
43
|
+
if (e.key === 'Escape' && this.panel) { e.stopPropagation(); this.close(); }
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** the open panel's name, or '' */
|
|
48
|
+
get panel() { return this.getAttribute('panel') || ''; }
|
|
49
|
+
|
|
50
|
+
toggle(name) { return this.panel === name ? this.close() : this.open(name); }
|
|
51
|
+
|
|
52
|
+
open(name) {
|
|
53
|
+
if (!name) return;
|
|
54
|
+
const el = this.querySelector(`:scope > [panel="${CSS.escape(name)}"]`);
|
|
55
|
+
if (!el) return; // consumer must provide a matching [panel] surface
|
|
56
|
+
for (const p of this.querySelectorAll(':scope > [panel][active]')) {
|
|
57
|
+
if (p !== el) p.removeAttribute('active');
|
|
58
|
+
}
|
|
59
|
+
el.setAttribute('active', ''); // CSS show/slide hook
|
|
60
|
+
this.setAttribute('panel', name); // reflected state
|
|
61
|
+
// focus the panel without scrolling the framed surface (preventScroll)
|
|
62
|
+
requestAnimationFrame(() =>
|
|
63
|
+
el.querySelector('[autofocus], [close], button-ui, [tabindex]')?.focus?.({ preventScroll: true }));
|
|
64
|
+
this.#emit(name);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
close() {
|
|
68
|
+
if (!this.panel) return;
|
|
69
|
+
this.querySelector(':scope > [panel][active]')?.removeAttribute('active');
|
|
70
|
+
this.removeAttribute('panel');
|
|
71
|
+
this.#emit('');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#emit(panel) {
|
|
75
|
+
this.dispatchEvent(new CustomEvent('embed:change', { detail: { panel }, bubbles: true }));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
customElements.define('embed-shell', EmbedShell);
|
|
80
|
+
export { EmbedShell };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
2
|
+
name: EmbedShell
|
|
3
|
+
tag: embed-shell
|
|
4
|
+
status: draft
|
|
5
|
+
component: AppShell
|
|
6
|
+
category: layout
|
|
7
|
+
version: 1
|
|
8
|
+
description: |
|
|
9
|
+
Embedded multi-surface shell — composes a primary [app] surface with secondary
|
|
10
|
+
[panel="<name>"] surfaces the consumer provides, arranging them responsively.
|
|
11
|
+
Peer of <admin-shell>. Content-agnostic: it orchestrates whatever [app] +
|
|
12
|
+
[panel] children it is given (it owns layout, not content), so any embedded app
|
|
13
|
+
supplies its own surfaces.
|
|
14
|
+
|
|
15
|
+
>=760px the open panel is an EQUAL 50/50 column beside the app and the frame
|
|
16
|
+
grows to fit; <760px it is a cover sheet that slides up over the app (identity
|
|
17
|
+
transform at rest, so popovers inside still anchor). Clean attribute contract
|
|
18
|
+
(ADR-0024, no data-*): [app], [panel="<name>"], trigger [opens="<name>"],
|
|
19
|
+
[close]; reflects [panel] on the shell + [active] on the open panel.
|
|
20
|
+
|
|
21
|
+
props:
|
|
22
|
+
panel:
|
|
23
|
+
type: string
|
|
24
|
+
reflect: true
|
|
25
|
+
description: |
|
|
26
|
+
The open panel's name (empty / absent = none). Reflected state. Set it by
|
|
27
|
+
clicking an [opens="<name>"] trigger, dispatching an `embed:open` event, or
|
|
28
|
+
calling .open(name) / .close() / .toggle(name).
|
|
29
|
+
|
|
30
|
+
events:
|
|
31
|
+
"embed:open":
|
|
32
|
+
description: Request to open or toggle a panel (consumed by the shell).
|
|
33
|
+
detail:
|
|
34
|
+
panel: string
|
|
35
|
+
"embed:change":
|
|
36
|
+
description: Fired after the open panel changes.
|
|
37
|
+
detail:
|
|
38
|
+
panel: string
|
|
39
|
+
|
|
40
|
+
slots:
|
|
41
|
+
default:
|
|
42
|
+
description: >-
|
|
43
|
+
Document-order children: one [app] primary surface + any number of
|
|
44
|
+
[panel="<name>"] secondary surfaces. The shell always shows [app] and reveals
|
|
45
|
+
the open [panel] (50/50 column >=760px, cover sheet <760px). Triggers
|
|
46
|
+
([opens], [close]) may live anywhere inside.
|
|
47
|
+
|
|
48
|
+
states:
|
|
49
|
+
- name: idle
|
|
50
|
+
description: No panel open — the app fills the frame.
|
|
51
|
+
- name: open
|
|
52
|
+
description: A panel is open ([panel="<name>"] reflected; the panel carries [active]).
|
|
53
|
+
|
|
54
|
+
traits:
|
|
55
|
+
- resizable
|
|
56
|
+
|
|
57
|
+
a2ui:
|
|
58
|
+
rules:
|
|
59
|
+
- >-
|
|
60
|
+
embed-shell takes exactly one [app] child + any number of [panel="<name>"]
|
|
61
|
+
children. It owns layout, not content — the consumer supplies the surfaces.
|
|
62
|
+
- >-
|
|
63
|
+
Open a panel with an [opens="<name>"] trigger anywhere inside, or an
|
|
64
|
+
`embed:open` event; dismiss with [close]. Do not toggle visibility manually —
|
|
65
|
+
the shell reflects [panel] + sets [active] on the open panel.
|
|
66
|
+
- >-
|
|
67
|
+
Use clean attributes (ADR-0024) — [app], [panel], [opens], [close] — not the
|
|
68
|
+
legacy data-* shapes.
|
|
69
|
+
|
|
70
|
+
keywords: [embed, shell, layout, panel, multi-pane, cover-sheet, app-shell]
|
|
71
|
+
synonyms:
|
|
72
|
+
embed: [embedded, shell, app-shell, multi-pane]
|
|
73
|
+
panel: [pane, sheet, drawer]
|
|
74
|
+
related: [AdminShell, Frame, Drawer]
|
package/shell/index.js
CHANGED