@unofficialbox/box-open-elements 0.26.0 → 0.28.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 +13 -13
- package/dist/components/actions/button.d.ts +3 -0
- package/dist/components/actions/button.js +26 -7
- package/dist/components/collections/resource-row.d.ts +28 -0
- package/dist/components/collections/resource-row.js +75 -0
- package/dist/components/feedback/mode-indicator.d.ts +21 -0
- package/dist/components/feedback/mode-indicator.js +67 -0
- package/dist/components/feedback/progress-steps.d.ts +4 -0
- package/dist/components/feedback/progress-steps.js +34 -2
- package/dist/components/feedback/verdict-banner.d.ts +23 -0
- package/dist/components/feedback/verdict-banner.js +59 -0
- package/dist/components/forms/code-editor.d.ts +19 -0
- package/dist/components/forms/code-editor.js +102 -33
- package/dist/components/forms/combobox.d.ts +1 -0
- package/dist/components/forms/combobox.js +9 -5
- package/dist/components/forms/number-input.d.ts +1 -0
- package/dist/components/forms/number-input.js +11 -0
- package/dist/components/forms/text-area.d.ts +1 -0
- package/dist/components/forms/text-area.js +2 -0
- package/dist/components/forms/text-field.d.ts +3 -0
- package/dist/components/forms/text-field.js +15 -1
- package/dist/components/forms/tile-group.d.ts +6 -0
- package/dist/components/forms/tile-group.js +29 -2
- package/dist/components/layout/app-shell.js +13 -3
- package/dist/components/layout/nav-sidebar.js +14 -2
- package/dist/components/navigation/accordion.d.ts +3 -0
- package/dist/components/navigation/accordion.js +21 -5
- package/dist/components/output/code-block.d.ts +3 -0
- package/dist/components/output/code-block.js +12 -2
- package/dist/components/overlays/command-palette.js +4 -0
- package/dist/components/overlays/dialog.d.ts +6 -0
- package/dist/components/overlays/dialog.js +18 -2
- package/dist/components/overlays/drawer.d.ts +3 -0
- package/dist/components/overlays/drawer.js +11 -3
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/entries/mode-indicator.d.ts +1 -0
- package/dist/entries/mode-indicator.js +1 -0
- package/dist/entries/resource-row.d.ts +1 -0
- package/dist/entries/resource-row.js +1 -0
- package/dist/entries/verdict-banner.d.ts +1 -0
- package/dist/entries/verdict-banner.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/patterns/call-console/call-console.js +1 -1
- package/dist/patterns/flow-builder/flow-builder.d.ts +8 -0
- package/dist/patterns/flow-builder/flow-builder.js +34 -0
- package/dist/patterns/flow-builder/primitives.d.ts +14 -2
- package/dist/patterns/flow-builder/primitives.js +28 -5
- package/dist/patterns/form-wizard/form-wizard.js +18 -3
- package/dist/patterns/form-wizard/wizard-summary.js +11 -2
- package/dist/patterns/process-modeler/design.d.ts +1 -1
- package/dist/patterns/process-modeler/design.js +1 -1
- package/package.json +1 -1
|
@@ -40,6 +40,9 @@ export declare class CodeBlock extends BaseElement {
|
|
|
40
40
|
/** Accessible name for the copy button. */
|
|
41
41
|
get copyLabel(): string;
|
|
42
42
|
set copyLabel(value: string);
|
|
43
|
+
/** Accessible name for the scrollable code region. */
|
|
44
|
+
get label(): string;
|
|
45
|
+
set label(value: string);
|
|
43
46
|
private copy;
|
|
44
47
|
private markCopied;
|
|
45
48
|
disconnectedCallback(): void;
|
|
@@ -116,7 +116,7 @@ const codeStyles = `
|
|
|
116
116
|
export class CodeBlock extends BaseElement {
|
|
117
117
|
static tagName = DEFAULT_TAG_NAME;
|
|
118
118
|
static get observedAttributes() {
|
|
119
|
-
return ["code", "copy-label", "language"];
|
|
119
|
+
return ["code", "copy-label", "language", "label"];
|
|
120
120
|
}
|
|
121
121
|
preEl;
|
|
122
122
|
codeEl;
|
|
@@ -147,6 +147,16 @@ export class CodeBlock extends BaseElement {
|
|
|
147
147
|
set copyLabel(value) {
|
|
148
148
|
this.setAttribute("copy-label", value);
|
|
149
149
|
}
|
|
150
|
+
/** Accessible name for the scrollable code region. */
|
|
151
|
+
get label() {
|
|
152
|
+
return this.getAttribute("label") ?? "";
|
|
153
|
+
}
|
|
154
|
+
set label(value) {
|
|
155
|
+
if (value)
|
|
156
|
+
this.setAttribute("label", value);
|
|
157
|
+
else
|
|
158
|
+
this.removeAttribute("label");
|
|
159
|
+
}
|
|
150
160
|
async copy() {
|
|
151
161
|
const text = this.code || this.textContent?.trim() || "";
|
|
152
162
|
if (!text) {
|
|
@@ -222,7 +232,7 @@ export class CodeBlock extends BaseElement {
|
|
|
222
232
|
this.slotEl.textContent = "";
|
|
223
233
|
}
|
|
224
234
|
const language = this.language;
|
|
225
|
-
this.preEl.setAttribute("aria-label", language ? `${language} code` : "Source code");
|
|
235
|
+
this.preEl.setAttribute("aria-label", this.label || (language ? `${language} code` : "Source code"));
|
|
226
236
|
if (language) {
|
|
227
237
|
this.codeEl.setAttribute("data-language", language);
|
|
228
238
|
this.codeEl.setAttribute("class", `language-${language}`);
|
|
@@ -170,6 +170,10 @@ const elementStyles = `
|
|
|
170
170
|
white-space: nowrap;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
[part="option"][aria-selected="true"] [part="shortcut"] {
|
|
174
|
+
color: var(--boe-token-text-text, #1f1e1b);
|
|
175
|
+
}
|
|
176
|
+
|
|
173
177
|
[part="empty"] {
|
|
174
178
|
padding: 1.4rem 0.9rem;
|
|
175
179
|
text-align: center;
|
|
@@ -20,6 +20,12 @@ export declare class Dialog extends BaseElement {
|
|
|
20
20
|
set description(value: string);
|
|
21
21
|
get confirmLabel(): string;
|
|
22
22
|
set confirmLabel(value: string);
|
|
23
|
+
get confirmDisabled(): boolean;
|
|
24
|
+
set confirmDisabled(value: boolean);
|
|
25
|
+
get confirmBusy(): boolean;
|
|
26
|
+
set confirmBusy(value: boolean);
|
|
27
|
+
get confirmBusyLabel(): string;
|
|
28
|
+
set confirmBusyLabel(value: string);
|
|
23
29
|
/** Modal size — box-ui-elements Modal sizes. Default `medium`. */
|
|
24
30
|
get size(): "small" | "medium" | "large" | "fullscreen";
|
|
25
31
|
set size(value: "small" | "medium" | "large" | "fullscreen");
|
|
@@ -144,7 +144,7 @@ const dialogStyles = `
|
|
|
144
144
|
export class Dialog extends BaseElement {
|
|
145
145
|
static tagName = DEFAULT_TAG_NAME;
|
|
146
146
|
static get observedAttributes() {
|
|
147
|
-
return ["confirm-label", "description", "heading", "open", "size"];
|
|
147
|
+
return ["confirm-busy", "confirm-busy-label", "confirm-disabled", "confirm-label", "description", "heading", "open", "size"];
|
|
148
148
|
}
|
|
149
149
|
openValue = false;
|
|
150
150
|
wasOpen = false;
|
|
@@ -191,6 +191,17 @@ export class Dialog extends BaseElement {
|
|
|
191
191
|
set confirmLabel(value) {
|
|
192
192
|
this.setAttribute("confirm-label", value);
|
|
193
193
|
}
|
|
194
|
+
get confirmDisabled() { return this.hasAttribute("confirm-disabled"); }
|
|
195
|
+
set confirmDisabled(value) { this.toggleAttribute("confirm-disabled", Boolean(value)); }
|
|
196
|
+
get confirmBusy() { return this.hasAttribute("confirm-busy"); }
|
|
197
|
+
set confirmBusy(value) { this.toggleAttribute("confirm-busy", Boolean(value)); }
|
|
198
|
+
get confirmBusyLabel() { return this.getAttribute("confirm-busy-label") ?? ""; }
|
|
199
|
+
set confirmBusyLabel(value) {
|
|
200
|
+
if (value)
|
|
201
|
+
this.setAttribute("confirm-busy-label", value);
|
|
202
|
+
else
|
|
203
|
+
this.removeAttribute("confirm-busy-label");
|
|
204
|
+
}
|
|
194
205
|
/** Modal size — box-ui-elements Modal sizes. Default `medium`. */
|
|
195
206
|
get size() {
|
|
196
207
|
const size = this.getAttribute("size");
|
|
@@ -252,6 +263,8 @@ export class Dialog extends BaseElement {
|
|
|
252
263
|
return;
|
|
253
264
|
}
|
|
254
265
|
if (target.closest('[part="confirm"]')) {
|
|
266
|
+
if (this.confirmDisabled || this.confirmBusy)
|
|
267
|
+
return;
|
|
255
268
|
this.dispatchEvent(new CustomEvent("confirm", { bubbles: true, composed: true }));
|
|
256
269
|
this.close();
|
|
257
270
|
}
|
|
@@ -348,7 +361,10 @@ export class Dialog extends BaseElement {
|
|
|
348
361
|
this.descriptionEl.hidden = !description;
|
|
349
362
|
}
|
|
350
363
|
if (this.confirmEl) {
|
|
351
|
-
this.confirmEl.textContent = this.
|
|
364
|
+
this.confirmEl.textContent = this.confirmBusy && this.confirmBusyLabel
|
|
365
|
+
? this.confirmBusyLabel : this.confirmLabel;
|
|
366
|
+
this.confirmEl.disabled = this.confirmDisabled || this.confirmBusy;
|
|
367
|
+
this.confirmEl.setAttribute("aria-busy", String(this.confirmBusy));
|
|
352
368
|
}
|
|
353
369
|
if (justOpened) {
|
|
354
370
|
queueMicrotask(() => {
|
|
@@ -26,6 +26,9 @@ export declare class Drawer extends BaseElement {
|
|
|
26
26
|
/** Busy: a saving/loading veil over the body; Close stays reachable. */
|
|
27
27
|
get busy(): boolean;
|
|
28
28
|
set busy(value: boolean);
|
|
29
|
+
/** Hide only the built-in header control; Escape and backdrop still request dismissal. */
|
|
30
|
+
get hideCloseButton(): boolean;
|
|
31
|
+
set hideCloseButton(value: boolean);
|
|
29
32
|
get heading(): string;
|
|
30
33
|
set heading(value: string);
|
|
31
34
|
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
@@ -149,6 +149,8 @@ const drawerStyles = `
|
|
|
149
149
|
box-shadow: none;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
[part="close"][hidden] { display: none; }
|
|
153
|
+
|
|
152
154
|
${boeNeutralInteractiveStyles('[part="close"]')}
|
|
153
155
|
|
|
154
156
|
[part="body"] {
|
|
@@ -201,7 +203,7 @@ const DRAWER_SIZES = new Set(["small", "medium", "large", "full"]);
|
|
|
201
203
|
export class Drawer extends BaseElement {
|
|
202
204
|
static tagName = DEFAULT_TAG_NAME;
|
|
203
205
|
static get observedAttributes() {
|
|
204
|
-
return ["busy", "description", "heading", "open", "position", "size"];
|
|
206
|
+
return ["busy", "description", "heading", "hide-close-button", "open", "position", "size"];
|
|
205
207
|
}
|
|
206
208
|
openValue = false;
|
|
207
209
|
wasOpen = false;
|
|
@@ -255,6 +257,9 @@ export class Drawer extends BaseElement {
|
|
|
255
257
|
set busy(value) {
|
|
256
258
|
this.toggleAttribute("busy", Boolean(value));
|
|
257
259
|
}
|
|
260
|
+
/** Hide only the built-in header control; Escape and backdrop still request dismissal. */
|
|
261
|
+
get hideCloseButton() { return this.hasAttribute("hide-close-button"); }
|
|
262
|
+
set hideCloseButton(value) { this.toggleAttribute("hide-close-button", Boolean(value)); }
|
|
258
263
|
get heading() {
|
|
259
264
|
return this.getAttribute("heading") ?? "Drawer";
|
|
260
265
|
}
|
|
@@ -381,7 +386,7 @@ export class Drawer extends BaseElement {
|
|
|
381
386
|
this.hostEl.innerHTML = `
|
|
382
387
|
<style>${drawerStyles}</style>
|
|
383
388
|
<dialog part="backdrop" aria-labelledby="drawer-title">
|
|
384
|
-
<div part="drawer">
|
|
389
|
+
<div part="drawer" tabindex="-1">
|
|
385
390
|
<header part="header">
|
|
386
391
|
<div part="meta">
|
|
387
392
|
<h2 id="drawer-title"></h2>
|
|
@@ -421,6 +426,9 @@ export class Drawer extends BaseElement {
|
|
|
421
426
|
this.drawerEl = this.hostEl.querySelector('[part="drawer"]');
|
|
422
427
|
this.titleEl = this.hostEl.querySelector("#drawer-title");
|
|
423
428
|
this.descriptionEl = this.hostEl.querySelector('[part="description"]');
|
|
429
|
+
const closeButton = this.hostEl.querySelector('[part="close"]');
|
|
430
|
+
if (closeButton)
|
|
431
|
+
closeButton.hidden = this.hideCloseButton;
|
|
424
432
|
const isLeft = this.position === "left";
|
|
425
433
|
const isBottom = this.position === "bottom";
|
|
426
434
|
if (this.backdropEl) {
|
|
@@ -456,7 +464,7 @@ export class Drawer extends BaseElement {
|
|
|
456
464
|
}
|
|
457
465
|
if (justOpened) {
|
|
458
466
|
queueMicrotask(() => {
|
|
459
|
-
this.
|
|
467
|
+
(this.hideCloseButton ? this.drawerEl : closeButton)?.focus();
|
|
460
468
|
});
|
|
461
469
|
}
|
|
462
470
|
}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { Controller } from "./controller.js";
|
|
|
2
2
|
export { TypedEventEmitter } from "./event-emitter.js";
|
|
3
3
|
export type { EventMap, Unsubscribe } from "./event-emitter.js";
|
|
4
4
|
export { BaseElement } from "./element.js";
|
|
5
|
-
export { FORM_DESCRIPTION_ID, FORM_ERROR_MESSAGE_ID, FormAssociatedElement, boeFormFieldErrorStyles, boeFormFieldSupportStyles, formDataFromNamedValues, formDataFromRange, formDescriptionMarkup, formErrorMessageMarkup, getMirroredFormValue, rangeFromFormValue, stringValuesFromFormValue, } from "./form-associated.js";
|
|
5
|
+
export { FORM_DESCRIPTION_ID, FORM_ERROR_MESSAGE_ID, FORM_LABEL_ID, FormAssociatedElement, boeFormFieldErrorStyles, boeFormFieldSupportStyles, formDataFromNamedValues, formDataFromRange, formDescriptionMarkup, formErrorMessageMarkup, getMirroredFormValue, rangeFromFormValue, stringValuesFromFormValue, } from "./form-associated.js";
|
|
6
6
|
export type { FormValue } from "./form-associated.js";
|
|
7
7
|
export { resolveRowWindow, sameRowWindow } from "./virtualize.js";
|
|
8
8
|
export type { RowWindow, WindowMetrics } from "./virtualize.js";
|
package/dist/core/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { Controller } from "./controller.js";
|
|
2
2
|
export { TypedEventEmitter } from "./event-emitter.js";
|
|
3
3
|
export { BaseElement } from "./element.js";
|
|
4
|
-
export { FORM_DESCRIPTION_ID, FORM_ERROR_MESSAGE_ID, FormAssociatedElement, boeFormFieldErrorStyles, boeFormFieldSupportStyles, formDataFromNamedValues, formDataFromRange, formDescriptionMarkup, formErrorMessageMarkup, getMirroredFormValue, rangeFromFormValue, stringValuesFromFormValue, } from "./form-associated.js";
|
|
4
|
+
export { FORM_DESCRIPTION_ID, FORM_ERROR_MESSAGE_ID, FORM_LABEL_ID, FormAssociatedElement, boeFormFieldErrorStyles, boeFormFieldSupportStyles, formDataFromNamedValues, formDataFromRange, formDescriptionMarkup, formErrorMessageMarkup, getMirroredFormValue, rangeFromFormValue, stringValuesFromFormValue, } from "./form-associated.js";
|
|
5
5
|
export { resolveRowWindow, sameRowWindow } from "./virtualize.js";
|
|
6
6
|
export { buildOffsetIndex, offsetOfItem, resolveOffsetWindow } from "./offset-window.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../components/feedback/mode-indicator.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../components/feedback/mode-indicator.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../components/collections/resource-row.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../components/collections/resource-row.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../components/feedback/verdict-banner.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../components/feedback/verdict-banner.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export * from "./components/actions/toolbar.js";
|
|
|
24
24
|
export * from "./components/collections/card.js";
|
|
25
25
|
export * from "./components/collections/carousel.js";
|
|
26
26
|
export * from "./components/collections/datalist-item.js";
|
|
27
|
+
export * from "./components/collections/resource-row.js";
|
|
27
28
|
export * from "./components/collections/draggable-list.js";
|
|
28
29
|
export * from "./components/collections/grid-view.js";
|
|
29
30
|
export * from "./components/collections/pagination.js";
|
|
@@ -41,6 +42,7 @@ export * from "./components/feedback/empty-state.js";
|
|
|
41
42
|
export * from "./components/feedback/error-mask.js";
|
|
42
43
|
export * from "./components/feedback/help-text.js";
|
|
43
44
|
export * from "./components/feedback/indicator.js";
|
|
45
|
+
export * from "./components/feedback/mode-indicator.js";
|
|
44
46
|
export * from "./components/feedback/nudge.js";
|
|
45
47
|
export * from "./components/feedback/progress-bar.js";
|
|
46
48
|
export * from "./components/feedback/progress-ring.js";
|
|
@@ -49,6 +51,7 @@ export * from "./components/feedback/skeleton.js";
|
|
|
49
51
|
export * from "./components/feedback/spinner.js";
|
|
50
52
|
export * from "./components/feedback/path.js";
|
|
51
53
|
export * from "./components/feedback/toast.js";
|
|
54
|
+
export * from "./components/feedback/verdict-banner.js";
|
|
52
55
|
export * from "./components/files/drop-zone.js";
|
|
53
56
|
export * from "./components/identity/avatar.js";
|
|
54
57
|
export * from "./components/identity/contact-datalist-item.js";
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ export * from "./components/actions/toolbar.js";
|
|
|
27
27
|
export * from "./components/collections/card.js";
|
|
28
28
|
export * from "./components/collections/carousel.js";
|
|
29
29
|
export * from "./components/collections/datalist-item.js";
|
|
30
|
+
export * from "./components/collections/resource-row.js";
|
|
30
31
|
export * from "./components/collections/draggable-list.js";
|
|
31
32
|
export * from "./components/collections/grid-view.js";
|
|
32
33
|
export * from "./components/collections/pagination.js";
|
|
@@ -45,6 +46,7 @@ export * from "./components/feedback/empty-state.js";
|
|
|
45
46
|
export * from "./components/feedback/error-mask.js";
|
|
46
47
|
export * from "./components/feedback/help-text.js";
|
|
47
48
|
export * from "./components/feedback/indicator.js";
|
|
49
|
+
export * from "./components/feedback/mode-indicator.js";
|
|
48
50
|
export * from "./components/feedback/nudge.js";
|
|
49
51
|
export * from "./components/feedback/progress-bar.js";
|
|
50
52
|
export * from "./components/feedback/progress-ring.js";
|
|
@@ -53,6 +55,7 @@ export * from "./components/feedback/skeleton.js";
|
|
|
53
55
|
export * from "./components/feedback/spinner.js";
|
|
54
56
|
export * from "./components/feedback/path.js";
|
|
55
57
|
export * from "./components/feedback/toast.js";
|
|
58
|
+
export * from "./components/feedback/verdict-banner.js";
|
|
56
59
|
// Components: files
|
|
57
60
|
export * from "./components/files/drop-zone.js";
|
|
58
61
|
// Components: identity
|
|
@@ -86,7 +86,7 @@ export class CallConsole extends BaseElement {
|
|
|
86
86
|
<section part="console" aria-label="API call console">
|
|
87
87
|
<header><div part="heading"><h2></h2><small>Inspect requests, responses, and tool outcomes</small></div><span id="connection" role="status"></span><box-button id="retry" label="Reconnect" tone="neutral" hidden></box-button></header>
|
|
88
88
|
<div class="filters"><label class="search">Search <input id="search" type="search" placeholder="Method, URL, or summary"></label><label>Service <select id="service" aria-label="Service"><option value="">All services</option></select></label><label><input type="checkbox" id="errors"> Errors only</label><box-button id="clear" label="Clear calls" tone="neutral"></box-button></div>
|
|
89
|
-
<box-split-view resizable ratio="0.42" label="Calls and request inspector"><section slot="primary" aria-label="Calls"><div class="pane-heading"><h3>Requests</h3><span id="count" class="muted" role="status"></span></div><div id="list"></div><p id="list-empty" role="status" hidden></p></section><section aria-label="Request inspector"><div class="pane-heading"><h3>Inspector</h3><box-button data-copy="both" label="Copy both" tone="neutral"></box-button><span id="copy-status" role="status"></span></div><p id="detail-empty">Select a request to inspect its details.</p><div id="inspector" hidden><h3 id="summary"></h3><p id="metadata" class="muted"></p><p id="outcome" role="status" hidden></p><h3 class="code-heading">Request</h3><box-code-block id="request" copy-label="Copy request" wrap></box-code-block><h3 class="code-heading">Response</h3><box-code-block id="response" copy-label="Copy response" wrap></box-code-block></div></section></box-split-view></section>`;
|
|
89
|
+
<box-split-view resizable ratio="0.42" label="Calls and request inspector"><section slot="primary" aria-label="Calls"><div class="pane-heading"><h3>Requests</h3><span id="count" class="muted" role="status"></span></div><div id="list"></div><p id="list-empty" role="status" hidden></p></section><section aria-label="Request inspector"><div class="pane-heading"><h3>Inspector</h3><box-button data-copy="both" label="Copy both" tone="neutral"></box-button><span id="copy-status" role="status"></span></div><p id="detail-empty">Select a request to inspect its details.</p><div id="inspector" hidden><h3 id="summary"></h3><p id="metadata" class="muted"></p><p id="outcome" role="status" hidden></p><h3 class="code-heading">Request</h3><box-code-block id="request" label="Request" copy-label="Copy request" wrap></box-code-block><h3 class="code-heading">Response</h3><box-code-block id="response" label="Response" copy-label="Copy response" wrap></box-code-block></div></section></box-split-view></section>`;
|
|
90
90
|
}
|
|
91
91
|
setupListeners() {
|
|
92
92
|
this.shadowRoot.querySelector("#list").addEventListener("keydown", event => {
|
|
@@ -9,6 +9,10 @@ export interface ValidationOptions {
|
|
|
9
9
|
*/
|
|
10
10
|
showMessage?: boolean;
|
|
11
11
|
}
|
|
12
|
+
export interface FlowNodeFigure {
|
|
13
|
+
text: string;
|
|
14
|
+
tone?: "neutral" | "warning" | "error";
|
|
15
|
+
}
|
|
12
16
|
/**
|
|
13
17
|
* Host-owned document editing with accessible navigation and responsive inspection.
|
|
14
18
|
* `N` is the host's node type; it defaults to {@link FlowNode}, which the default
|
|
@@ -17,6 +21,7 @@ export interface ValidationOptions {
|
|
|
17
21
|
export declare class FlowBuilder<N extends FlowNodeBase = FlowNode> extends BaseElement {
|
|
18
22
|
static readonly tagName = "box-flow-builder";
|
|
19
23
|
private nodesValue;
|
|
24
|
+
private figuresValue;
|
|
20
25
|
private modelValue;
|
|
21
26
|
private catalogValue;
|
|
22
27
|
private selectedValue;
|
|
@@ -32,6 +37,9 @@ export declare class FlowBuilder<N extends FlowNodeBase = FlowNode> extends Base
|
|
|
32
37
|
get narrow(): boolean;
|
|
33
38
|
/** Focus a rendered card without changing selection or opening the editor. */
|
|
34
39
|
focusNode(node: N): void;
|
|
40
|
+
/** Update one live card figure without rebuilding the flow or moving focus. */
|
|
41
|
+
setNodeFigure(target: N | string | NodePath, figure: FlowNodeFigure | null): void;
|
|
42
|
+
private findNodeById;
|
|
35
43
|
private insertion?;
|
|
36
44
|
private invoker?;
|
|
37
45
|
private spine;
|
|
@@ -12,6 +12,7 @@ import { FLOW_SPINE_PARTS, InsertPoint, KIND_PICKER_PARTS } from "./primitives.j
|
|
|
12
12
|
export class FlowBuilder extends BaseElement {
|
|
13
13
|
static tagName = "box-flow-builder";
|
|
14
14
|
nodesValue = [];
|
|
15
|
+
figuresValue = new Map();
|
|
15
16
|
modelValue = defaultFlowModel;
|
|
16
17
|
catalogValue = [];
|
|
17
18
|
selectedValue = null;
|
|
@@ -27,6 +28,38 @@ export class FlowBuilder extends BaseElement {
|
|
|
27
28
|
get narrow() { return this.narrowValue; }
|
|
28
29
|
/** Focus a rendered card without changing selection or opening the editor. */
|
|
29
30
|
focusNode(node) { this.spine?.focusNode(node); }
|
|
31
|
+
/** Update one live card figure without rebuilding the flow or moving focus. */
|
|
32
|
+
setNodeFigure(target, figure) {
|
|
33
|
+
const node = typeof target === "string" ? this.findNodeById(target)
|
|
34
|
+
: Array.isArray(target) ? nodeAtPath({ body: this.nodes }, target)
|
|
35
|
+
: target;
|
|
36
|
+
if (!node)
|
|
37
|
+
return;
|
|
38
|
+
if (figure)
|
|
39
|
+
this.figuresValue.set(node, figure);
|
|
40
|
+
else
|
|
41
|
+
this.figuresValue.delete(node);
|
|
42
|
+
this.spine?.setNodeFigure(node, figure);
|
|
43
|
+
}
|
|
44
|
+
findNodeById(id) {
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
const visit = (nodes) => {
|
|
47
|
+
for (const node of nodes) {
|
|
48
|
+
if (seen.has(node))
|
|
49
|
+
continue;
|
|
50
|
+
seen.add(node);
|
|
51
|
+
if (node.id === id)
|
|
52
|
+
return node;
|
|
53
|
+
for (const child of this.model.children(node)) {
|
|
54
|
+
const found = visit(child.list);
|
|
55
|
+
if (found)
|
|
56
|
+
return found;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
};
|
|
61
|
+
return visit(this.nodes);
|
|
62
|
+
}
|
|
30
63
|
insertion;
|
|
31
64
|
invoker;
|
|
32
65
|
spine;
|
|
@@ -106,6 +139,7 @@ export class FlowBuilder extends BaseElement {
|
|
|
106
139
|
[hidden]{display:none!important} [part=inspector-heading]{font:inherit;font-weight:650;margin:0 0 12px}
|
|
107
140
|
</style><div part="error" hidden></div><div part="layout"><box-flow-spine exportparts="${FLOW_SPINE_PARTS.join(", ")}"></box-flow-spine><div part="inspector" role="region" aria-label="Add a step"><box-kind-picker exportparts="${KIND_PICKER_PARTS.join(", ")}"></box-kind-picker><div part="editor"></div></div></div><div part="picker-popup" hidden><box-kind-picker exportparts="${KIND_PICKER_PARTS.join(", ")}"></box-kind-picker></div><box-drawer position="bottom" size="large"></box-drawer>`;
|
|
108
141
|
this.spine = this.shadowRoot.querySelector("box-flow-spine");
|
|
142
|
+
this.spine.figures = this.figuresValue;
|
|
109
143
|
this.aside = this.shadowRoot.querySelector("[part=inspector]");
|
|
110
144
|
this.palette = this.aside.querySelector("box-kind-picker");
|
|
111
145
|
this.inspector = this.aside.querySelector("[part=editor]");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { type FlowInsertDetail, type FlowKind, type FlowModel, type FlowNodeBase } from "./model.js";
|
|
3
3
|
/** Parts a card exposes; the spine and builder forward them unchanged. */
|
|
4
|
-
export declare const FLOW_CARD_PARTS: readonly ["card", "icon", "title", "kind", "description", "status", "error"];
|
|
4
|
+
export declare const FLOW_CARD_PARTS: readonly ["card", "icon", "title", "kind", "description", "status", "figure", "error"];
|
|
5
5
|
/** Parts the spine exposes (its own plus forwarded card and insert-point parts). */
|
|
6
|
-
export declare const FLOW_SPINE_PARTS: readonly ["spine", "endpoint", "body", "branches", "branch", "branch-header", "branch-label", "branch-remove", "branch-add", "insert", "card", "icon", "title", "kind", "description", "status", "error"];
|
|
6
|
+
export declare const FLOW_SPINE_PARTS: readonly ["spine", "endpoint", "body", "branches", "branch", "branch-header", "branch-label", "branch-remove", "branch-add", "insert", "card", "icon", "title", "kind", "description", "status", "figure", "error"];
|
|
7
7
|
/** Parts a kind picker exposes. */
|
|
8
8
|
export declare const KIND_PICKER_PARTS: readonly ["choices", "group-heading", "choice", "choice-icon", "choice-label", "choice-description"];
|
|
9
9
|
export declare class FlowCard extends BaseElement {
|
|
@@ -13,6 +13,10 @@ export declare class FlowCard extends BaseElement {
|
|
|
13
13
|
model: FlowModel<FlowNodeBase>;
|
|
14
14
|
selected: boolean;
|
|
15
15
|
invalid: boolean;
|
|
16
|
+
figure: {
|
|
17
|
+
text: string;
|
|
18
|
+
tone?: "neutral" | "warning" | "error";
|
|
19
|
+
} | null;
|
|
16
20
|
protected renderTemplate(): void;
|
|
17
21
|
protected setupListeners(): void;
|
|
18
22
|
refresh(): void;
|
|
@@ -49,10 +53,18 @@ export declare class FlowSpine extends BaseElement {
|
|
|
49
53
|
endLabel: string;
|
|
50
54
|
headingLevel: number;
|
|
51
55
|
private cards;
|
|
56
|
+
figures: Map<FlowNodeBase, {
|
|
57
|
+
text: string;
|
|
58
|
+
tone?: "neutral" | "warning" | "error";
|
|
59
|
+
}>;
|
|
52
60
|
protected renderTemplate(): void;
|
|
53
61
|
refresh(): void;
|
|
54
62
|
/** Re-render one card after its fields change, without rebuilding the spine. */
|
|
55
63
|
refreshNode(node: FlowNodeBase): void;
|
|
64
|
+
setNodeFigure(node: FlowNodeBase, figure: {
|
|
65
|
+
text: string;
|
|
66
|
+
tone?: "neutral" | "warning" | "error";
|
|
67
|
+
} | null): void;
|
|
56
68
|
focusNode(node: FlowNodeBase): void;
|
|
57
69
|
select(node: FlowNodeBase | null, invalid?: FlowNodeBase | null): void;
|
|
58
70
|
protected update(): void;
|
|
@@ -9,7 +9,7 @@ const styles = `
|
|
|
9
9
|
`;
|
|
10
10
|
function emit(host, name, detail) { host.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true })); }
|
|
11
11
|
/** Parts a card exposes; the spine and builder forward them unchanged. */
|
|
12
|
-
export const FLOW_CARD_PARTS = ["card", "icon", "title", "kind", "description", "status", "error"];
|
|
12
|
+
export const FLOW_CARD_PARTS = ["card", "icon", "title", "kind", "description", "status", "figure", "error"];
|
|
13
13
|
/** Parts the spine exposes (its own plus forwarded card and insert-point parts). */
|
|
14
14
|
export const FLOW_SPINE_PARTS = ["spine", "endpoint", "body", "branches", "branch", "branch-header", "branch-label", "branch-remove", "branch-add", "insert", ...FLOW_CARD_PARTS];
|
|
15
15
|
/** Parts a kind picker exposes. */
|
|
@@ -21,15 +21,19 @@ export class FlowCard extends BaseElement {
|
|
|
21
21
|
model = defaultFlowModel;
|
|
22
22
|
selected = false;
|
|
23
23
|
invalid = false;
|
|
24
|
+
figure = null;
|
|
24
25
|
renderTemplate() {
|
|
25
26
|
this.shadowRoot.innerHTML = `<style>${styles}
|
|
26
27
|
button { width:100%; text-align:start; display:grid; gap:6px; padding:16px; border:1px solid var(--boe-token-stroke-stroke,#ddd); border-radius:var(--boe-profile-radius-med,8px); background:var(--boe-token-surface-surface,#fff); overflow-wrap:anywhere; }
|
|
27
28
|
button[aria-pressed=true] { border-color:var(--boe-token-surface-surface-brand,#0061d5); background:color-mix(in srgb,var(--boe-token-surface-surface-brand,#0061d5) 5%,var(--boe-token-surface-surface,#fff)); }
|
|
28
29
|
button[data-invalid=true] { border-color:var(--boe-token-text-status-text-error,#b92340); }
|
|
29
|
-
button.has-icon{grid-template-columns:20px minmax(0,1fr);column-gap:12px} button.has-icon [part=icon]{grid-column:1;grid-row:1/span
|
|
30
|
+
button.has-icon{grid-template-columns:20px minmax(0,1fr);column-gap:12px} button.has-icon [part=icon]{grid-column:1;grid-row:1/span 5} button.has-icon [part=title],button.has-icon [part=kind],button.has-icon [part=description],button.has-icon [part=status],button.has-icon [part=figure],button.has-icon [part=error]{grid-column:2}
|
|
30
31
|
[part=icon]{color:var(--boe-token-surface-surface-brand,#0061d5);width:20px;height:20px} [part=icon][data-tone=neutral]{color:var(--boe-token-text-text-secondary,#666)} [part=icon] svg{display:block;width:20px;height:20px} [part=title] { font-weight:650; } [part=kind],[part=description] { color:var(--boe-token-text-text-secondary,#666); } [part=error] { color:var(--boe-token-text-status-text-error,#b92340); }
|
|
32
|
+
[part=figure]{font-size:.78rem;font-variant-numeric:tabular-nums;color:var(--boe-token-text-text-secondary,#666);min-width:0;overflow-wrap:anywhere}
|
|
33
|
+
[part=figure][data-tone=warning]{color:var(--boe-token-text-status-text-warning,#805600)}
|
|
34
|
+
[part=figure][data-tone=error]{color:var(--boe-token-text-status-text-error,#b92340)}
|
|
31
35
|
[hidden] { display:none!important; }
|
|
32
|
-
</style><button type="button" part="card"><span part="icon" aria-hidden="true"></span><span part="title"></span><span part="kind"></span><span part="description"></span><span part="status"></span><span part="error">Needs attention</span></button>`;
|
|
36
|
+
</style><button type="button" part="card"><span part="icon" aria-hidden="true"></span><span part="title"></span><span part="kind"></span><span part="description"></span><span part="status"></span><span part="figure" hidden></span><span part="error">Needs attention</span></button>`;
|
|
33
37
|
}
|
|
34
38
|
setupListeners() { this.shadowRoot.querySelector("button").addEventListener("click", () => emit(this, "node-select", { node: this.node })); }
|
|
35
39
|
refresh() { if (this.isRendered)
|
|
@@ -41,7 +45,7 @@ export class FlowCard extends BaseElement {
|
|
|
41
45
|
const button = this.shadowRoot.querySelector("button");
|
|
42
46
|
button.setAttribute("aria-pressed", String(this.selected));
|
|
43
47
|
button.classList.toggle("has-icon", Boolean(kind?.icon));
|
|
44
|
-
button.setAttribute("aria-label", cardLabel(this.node, this.catalog, this.invalid, this.model));
|
|
48
|
+
button.setAttribute("aria-label", [cardLabel(this.node, this.catalog, this.invalid, this.model), this.figure?.text].filter(Boolean).join(". "));
|
|
45
49
|
button.dataset.invalid = String(this.invalid);
|
|
46
50
|
const iconEl = this.shadowRoot.querySelector("[part=icon]");
|
|
47
51
|
iconEl.replaceChildren(kind?.icon?.() ?? "");
|
|
@@ -53,6 +57,10 @@ export class FlowCard extends BaseElement {
|
|
|
53
57
|
element.hidden = !text;
|
|
54
58
|
}
|
|
55
59
|
this.shadowRoot.querySelector("[part=error]").hidden = !this.invalid;
|
|
60
|
+
const figureEl = this.shadowRoot.querySelector("[part=figure]");
|
|
61
|
+
figureEl.textContent = this.figure?.text ?? "";
|
|
62
|
+
figureEl.hidden = !this.figure?.text;
|
|
63
|
+
figureEl.dataset.tone = this.figure?.tone ?? "neutral";
|
|
56
64
|
}
|
|
57
65
|
}
|
|
58
66
|
export class InsertPoint extends BaseElement {
|
|
@@ -157,11 +165,23 @@ export class FlowSpine extends BaseElement {
|
|
|
157
165
|
endLabel = "Flow ends";
|
|
158
166
|
headingLevel = 3;
|
|
159
167
|
cards = new Map();
|
|
168
|
+
figures = new Map();
|
|
160
169
|
renderTemplate() { this.shadowRoot.innerHTML = `<style>${styles} [part=spine]{display:grid;gap:0;min-width:0} [part=branches]{display:flex;gap:16px;overflow-x:auto;max-width:100%;padding:8px} [part=branch]{flex:1 0 240px;min-width:0} [part=body]{margin:0 0 0 16px;border-inline-start:2px solid var(--boe-token-stroke-stroke,#ddd);padding:8px 12px} [part=endpoint]{text-align:center;font-weight:600;padding:12px} [part=branch-header]{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:1em 0} [part=branch-label]{font:inherit;font-weight:600;margin:0} [part=branch-remove],[part=branch-add]{border:1px solid var(--boe-token-stroke-stroke,#ddd);border-radius:16px;background:var(--boe-token-surface-surface,#fff);padding:4px 10px} [part=branch-add]{justify-self:center;margin:4px 0}</style><div part="spine"></div>`; }
|
|
161
170
|
refresh() { if (this.isRendered)
|
|
162
171
|
this.update(); }
|
|
163
172
|
/** Re-render one card after its fields change, without rebuilding the spine. */
|
|
164
173
|
refreshNode(node) { this.cards.get(node)?.refresh(); }
|
|
174
|
+
setNodeFigure(node, figure) {
|
|
175
|
+
if (figure)
|
|
176
|
+
this.figures.set(node, figure);
|
|
177
|
+
else
|
|
178
|
+
this.figures.delete(node);
|
|
179
|
+
const card = this.cards.get(node);
|
|
180
|
+
if (card) {
|
|
181
|
+
card.figure = figure;
|
|
182
|
+
card.refresh();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
165
185
|
focusNode(node) { this.cards.get(node)?.focus(); }
|
|
166
186
|
select(node, invalid = this.invalid) { this.selected = node; this.invalid = invalid; for (const [item, card] of this.cards) {
|
|
167
187
|
card.selected = item === node;
|
|
@@ -193,6 +213,7 @@ export class FlowSpine extends BaseElement {
|
|
|
193
213
|
card.model = this.model;
|
|
194
214
|
card.selected = node === this.selected;
|
|
195
215
|
card.invalid = node === this.invalid;
|
|
216
|
+
card.figure = this.figures.get(node) ?? null;
|
|
196
217
|
card.refresh();
|
|
197
218
|
fragment.append(card);
|
|
198
219
|
const nextAncestors = new Set([...ancestors, node]);
|
|
@@ -254,8 +275,10 @@ export class FlowSpine extends BaseElement {
|
|
|
254
275
|
end.textContent = this.endLabel;
|
|
255
276
|
root.replaceChildren(start, listView(this.nodes, "in the flow", new Set()), end);
|
|
256
277
|
for (const node of this.cards.keys())
|
|
257
|
-
if (!seen.has(node))
|
|
278
|
+
if (!seen.has(node)) {
|
|
258
279
|
this.cards.delete(node);
|
|
280
|
+
this.figures.delete(node);
|
|
281
|
+
}
|
|
259
282
|
}
|
|
260
283
|
}
|
|
261
284
|
FlowCard.register();
|
|
@@ -77,7 +77,7 @@ const elementStyles = `
|
|
|
77
77
|
font-size: 0.9rem;
|
|
78
78
|
background: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 10%, var(--boe-token-surface-surface, #ffffff));
|
|
79
79
|
border: 1px solid color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 34%, transparent);
|
|
80
|
-
color:
|
|
80
|
+
color: var(--boe-token-text-status-text-error, #b92340);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
[part="error"][hidden] {
|
|
@@ -338,6 +338,11 @@ export class FormWizard extends BaseElement {
|
|
|
338
338
|
}));
|
|
339
339
|
if (this.isRendered) {
|
|
340
340
|
this.update();
|
|
341
|
+
if (eventName === "stepChanged") {
|
|
342
|
+
queueMicrotask(() => {
|
|
343
|
+
this.panelsEl.querySelector('[part="panel"]:not([hidden])')?.focus();
|
|
344
|
+
});
|
|
345
|
+
}
|
|
341
346
|
}
|
|
342
347
|
}));
|
|
343
348
|
}
|
|
@@ -357,7 +362,7 @@ export class FormWizard extends BaseElement {
|
|
|
357
362
|
this.shadowRoot.innerHTML = `
|
|
358
363
|
<style>${elementStyles}</style>
|
|
359
364
|
<section part="wizard" aria-label="Form wizard">
|
|
360
|
-
<h2 part="title" hidden></h2>
|
|
365
|
+
<h2 id="wizard-title" part="title" hidden></h2>
|
|
361
366
|
<div part="layout">
|
|
362
367
|
<box-progress-steps part="rail"></box-progress-steps>
|
|
363
368
|
<div part="body">
|
|
@@ -376,6 +381,7 @@ export class FormWizard extends BaseElement {
|
|
|
376
381
|
`;
|
|
377
382
|
this.titleEl = this.shadowRoot.querySelector('[part="title"]');
|
|
378
383
|
this.railEl = this.shadowRoot.querySelector('[part="rail"]');
|
|
384
|
+
this.railEl.compact = true;
|
|
379
385
|
this.panelsEl = this.shadowRoot.querySelector('[part="panels"]');
|
|
380
386
|
this.errorEl = this.shadowRoot.querySelector('[part="error"]');
|
|
381
387
|
this.backEl = this.shadowRoot.querySelector('[part="back"]');
|
|
@@ -418,7 +424,7 @@ export class FormWizard extends BaseElement {
|
|
|
418
424
|
rebuildPanels(steps) {
|
|
419
425
|
this.panelsEl.innerHTML = steps
|
|
420
426
|
.map(step => `
|
|
421
|
-
<div part="panel" data-step-id="${escapeHtml(step.id)}" role="group" aria-label="${escapeHtml(step.label)}" hidden>
|
|
427
|
+
<div part="panel" data-step-id="${escapeHtml(step.id)}" role="group" aria-label="${escapeHtml(step.label)}" tabindex="-1" hidden>
|
|
422
428
|
<slot name="${escapeHtml(step.id)}"></slot>
|
|
423
429
|
</div>
|
|
424
430
|
`)
|
|
@@ -432,6 +438,15 @@ export class FormWizard extends BaseElement {
|
|
|
432
438
|
const steps = state?.steps ?? [];
|
|
433
439
|
this.titleEl.hidden = !this.heading;
|
|
434
440
|
this.titleEl.textContent = this.heading;
|
|
441
|
+
const wizard = this.shadowRoot?.querySelector('[part="wizard"]');
|
|
442
|
+
if (this.heading) {
|
|
443
|
+
wizard?.setAttribute("aria-labelledby", "wizard-title");
|
|
444
|
+
wizard?.removeAttribute("aria-label");
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
wizard?.removeAttribute("aria-labelledby");
|
|
448
|
+
wizard?.setAttribute("aria-label", "Form wizard");
|
|
449
|
+
}
|
|
435
450
|
const signature = JSON.stringify(steps.map(step => step.id));
|
|
436
451
|
if (signature !== this.panelsSignature) {
|
|
437
452
|
this.panelsSignature = signature;
|
|
@@ -11,6 +11,7 @@ const escapeHtml = (value) => value
|
|
|
11
11
|
const elementStyles = `
|
|
12
12
|
:host {
|
|
13
13
|
display: block;
|
|
14
|
+
container-type: inline-size;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
/* The host's own display would otherwise beat the UA rule for [hidden],
|
|
@@ -87,7 +88,7 @@ const elementStyles = `
|
|
|
87
88
|
margin: 0;
|
|
88
89
|
padding: 0 1rem 0.8rem;
|
|
89
90
|
display: grid;
|
|
90
|
-
grid-template-columns: minmax(8rem, 14rem) 1fr;
|
|
91
|
+
grid-template-columns: minmax(8rem, 14rem) minmax(0, 1fr);
|
|
91
92
|
gap: 0.3rem 1rem;
|
|
92
93
|
}
|
|
93
94
|
|
|
@@ -99,9 +100,17 @@ const elementStyles = `
|
|
|
99
100
|
|
|
100
101
|
[part="row-value"] {
|
|
101
102
|
margin: 0;
|
|
103
|
+
min-inline-size: 0;
|
|
104
|
+
max-inline-size: 100%;
|
|
105
|
+
overflow-x: auto;
|
|
102
106
|
font-size: 0.85rem;
|
|
103
107
|
color: var(--boe-token-text-text, #1f1e1b);
|
|
104
|
-
overflow-wrap:
|
|
108
|
+
overflow-wrap: normal;
|
|
109
|
+
word-break: normal;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@container (max-width: 32rem) {
|
|
113
|
+
[part="rows"] { grid-template-columns: minmax(0, 1fr); }
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
[part="row-value"][data-empty="true"] {
|