@unofficialbox/box-open-elements 0.27.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 +16 -1
- 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 +10 -0
- package/dist/components/forms/code-editor.js +54 -4
- 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/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/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/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/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
Browse the full catalog on the **[live docs site](https://unofficialbox.github.io/box-open-elements)** — live previews, an events/properties inspector, per-framework code snippets (React / Angular / Vue / Svelte / HTML), and the foundations pages.
|
|
14
14
|
|
|
15
|
-
The library is organized around three layers (see [taxonomy](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
15
|
+
The library is organized around three layers (see [taxonomy](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/taxonomy.md) for the canonical model):
|
|
16
16
|
|
|
17
17
|
- **Foundations** — design decisions as data: tokens, color, typography, iconography, accessibility, theming
|
|
18
18
|
- **Components** — accessible Web Components for single controls, organized by category
|
|
@@ -77,7 +77,7 @@ selection.toggleSelection("1");
|
|
|
77
77
|
- optimized component entrypoints: `@unofficialbox/box-open-elements/<name>`
|
|
78
78
|
- patterns: `@unofficialbox/box-open-elements/patterns/<area>` and `@unofficialbox/box-open-elements/patterns/<area>/<module>`
|
|
79
79
|
|
|
80
|
-
See the versioned [API guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
80
|
+
See the versioned [API guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/api-guidelines.md).
|
|
81
81
|
|
|
82
82
|
## Developing
|
|
83
83
|
|
|
@@ -91,17 +91,17 @@ bun run docs # build + serve the docs site at http://localhost:4600
|
|
|
91
91
|
|
|
92
92
|
## Documentation
|
|
93
93
|
|
|
94
|
-
The versioned docs index is at [docs/README.md](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
95
|
-
|
|
96
|
-
- [Taxonomy](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
97
|
-
- [Architecture](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
98
|
-
- [API Guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
99
|
-
- [Components catalog](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
100
|
-
- [Using with React, Angular, Vue, and Svelte](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
101
|
-
- [Design Tokens](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
102
|
-
- [Content Explorer](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
103
|
-
- [Box Server Integration](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
104
|
-
- [Agent UI guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.
|
|
94
|
+
The versioned docs index is at [docs/README.md](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/README.md). The most important entries:
|
|
95
|
+
|
|
96
|
+
- [Taxonomy](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/taxonomy.md)
|
|
97
|
+
- [Architecture](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/architecture.md)
|
|
98
|
+
- [API Guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/api-guidelines.md)
|
|
99
|
+
- [Components catalog](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/components/catalog.md) and [patterns catalog](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/patterns/catalog.md)
|
|
100
|
+
- [Using with React, Angular, Vue, and Svelte](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/integration/frameworks.md)
|
|
101
|
+
- [Design Tokens](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/foundations/tokens.md)
|
|
102
|
+
- [Content Explorer](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/patterns/content-explorer.md)
|
|
103
|
+
- [Box Server Integration](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/integration/box-server.md)
|
|
104
|
+
- [Agent UI guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/patterns/agent-ui-guidelines.md), [building blocks](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/patterns/agent-ui.md), and [motion](https://github.com/unofficialbox/box-open-elements/blob/v0.28.0/docs/foundations/motion.md)
|
|
105
105
|
|
|
106
106
|
## Contributing
|
|
107
107
|
|
|
@@ -21,6 +21,9 @@ export declare class Button extends BaseElement {
|
|
|
21
21
|
set disabled(value: boolean);
|
|
22
22
|
get label(): string;
|
|
23
23
|
set label(value: string);
|
|
24
|
+
/** Optional spoken name for repeated actions whose visible label is brief. */
|
|
25
|
+
get accessibleLabel(): string;
|
|
26
|
+
set accessibleLabel(value: string);
|
|
24
27
|
get size(): string;
|
|
25
28
|
set size(value: string);
|
|
26
29
|
get tone(): string;
|
|
@@ -188,7 +188,7 @@ export class Button extends BaseElement {
|
|
|
188
188
|
static tagName = DEFAULT_TAG_NAME;
|
|
189
189
|
static formAssociated = true;
|
|
190
190
|
static get observedAttributes() {
|
|
191
|
-
return ["disabled", "label", "size", "tone", "is-loading", "type"];
|
|
191
|
+
return ["accessible-label", "aria-label", "disabled", "label", "size", "tone", "is-loading", "type"];
|
|
192
192
|
}
|
|
193
193
|
buttonEl;
|
|
194
194
|
focus(options) { if (!this.disabled)
|
|
@@ -234,6 +234,16 @@ export class Button extends BaseElement {
|
|
|
234
234
|
set label(value) {
|
|
235
235
|
this.setAttribute("label", value);
|
|
236
236
|
}
|
|
237
|
+
/** Optional spoken name for repeated actions whose visible label is brief. */
|
|
238
|
+
get accessibleLabel() {
|
|
239
|
+
return this.getAttribute("accessible-label") ?? "";
|
|
240
|
+
}
|
|
241
|
+
set accessibleLabel(value) {
|
|
242
|
+
if (value)
|
|
243
|
+
this.setAttribute("accessible-label", value);
|
|
244
|
+
else
|
|
245
|
+
this.removeAttribute("accessible-label");
|
|
246
|
+
}
|
|
237
247
|
get size() {
|
|
238
248
|
return this.getAttribute("size") ?? "medium";
|
|
239
249
|
}
|
|
@@ -294,6 +304,11 @@ export class Button extends BaseElement {
|
|
|
294
304
|
this.buttonEl.dataset.tone = this.tone;
|
|
295
305
|
this.buttonEl.dataset.size = this.size;
|
|
296
306
|
this.labelEl.textContent = this.label;
|
|
307
|
+
const spokenLabel = this.accessibleLabel || this.getAttribute("aria-label");
|
|
308
|
+
if (spokenLabel)
|
|
309
|
+
this.buttonEl.setAttribute("aria-label", spokenLabel);
|
|
310
|
+
else
|
|
311
|
+
this.buttonEl.removeAttribute("aria-label");
|
|
297
312
|
const busy = this.isLoading;
|
|
298
313
|
this.spinnerEl.hidden = !busy;
|
|
299
314
|
this.buttonEl.setAttribute("aria-busy", String(busy));
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BaseElement } from "../../core/index.js";
|
|
2
|
+
/** Selectable resource information with independent slotted actions. Not a listbox option. */
|
|
3
|
+
export declare class ResourceRow extends BaseElement {
|
|
4
|
+
static readonly tagName = "box-resource-row";
|
|
5
|
+
static get observedAttributes(): string[];
|
|
6
|
+
private selectEl;
|
|
7
|
+
private labelEl;
|
|
8
|
+
private metaEl;
|
|
9
|
+
private statusEl;
|
|
10
|
+
private statusSlot;
|
|
11
|
+
get label(): string;
|
|
12
|
+
set label(value: string);
|
|
13
|
+
get meta(): string;
|
|
14
|
+
set meta(value: string);
|
|
15
|
+
get status(): string;
|
|
16
|
+
set status(value: string);
|
|
17
|
+
get value(): string;
|
|
18
|
+
set value(value: string);
|
|
19
|
+
get selected(): boolean;
|
|
20
|
+
set selected(value: boolean);
|
|
21
|
+
get active(): boolean;
|
|
22
|
+
set active(value: boolean);
|
|
23
|
+
get disabled(): boolean;
|
|
24
|
+
set disabled(value: boolean);
|
|
25
|
+
protected renderTemplate(): void;
|
|
26
|
+
protected setupListeners(): void;
|
|
27
|
+
protected update(): void;
|
|
28
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { BaseElement } from "../../core/index.js";
|
|
2
|
+
import { boeRadius } from "../../foundations/geometry/index.js";
|
|
3
|
+
const styles = `
|
|
4
|
+
:host { display:block; container-type:inline-size; font:inherit; color:var(--boe-token-text-text,#222); }
|
|
5
|
+
:host([hidden]) { display:none!important; }
|
|
6
|
+
[part="row"] { display:flex; align-items:center; gap:.75rem; min-inline-size:0; padding:.65rem; border:1px solid var(--boe-token-stroke-stroke,#dedede); border-radius:${boeRadius.med}; background:var(--boe-token-surface-surface,#fff); }
|
|
7
|
+
:host([selected]) [part="row"] { border-color:var(--boe-token-surface-surface-brand,#0061d5); background:color-mix(in srgb,var(--boe-token-surface-surface-brand,#0061d5) 7%,var(--boe-token-surface-surface,#fff)); }
|
|
8
|
+
:host([active]) [part="row"] { box-shadow:inset 0 0 0 1px var(--boe-token-surface-surface-brand,#0061d5); }
|
|
9
|
+
[part="select"] { flex:1 1 auto; min-inline-size:0; display:flex; align-items:center; gap:.65rem; padding:.25rem; border:0; border-radius:4px; background:none; color:inherit; font:inherit; text-align:start; cursor:pointer; }
|
|
10
|
+
[part="select"]:focus-visible { outline:2px solid var(--boe-token-surface-surface-brand,#0061d5); outline-offset:2px; }
|
|
11
|
+
[part="select"]:disabled { cursor:not-allowed; opacity:.55; }
|
|
12
|
+
[part="body"] { display:grid; gap:.15rem; min-inline-size:0; }
|
|
13
|
+
[part="label"] { font-weight:600; overflow-wrap:anywhere; }
|
|
14
|
+
[part="meta"] { color:var(--boe-token-text-text-secondary,#6f6f6f); font-size:.8em; white-space:pre-line; overflow-wrap:anywhere; }
|
|
15
|
+
[part="meta"]:empty, [part="status"]:empty { display:none; }
|
|
16
|
+
[part="trailing"] { flex:0 1 auto; display:flex; align-items:center; gap:.5rem; min-inline-size:0; }
|
|
17
|
+
[part="status"] { color:var(--boe-token-text-text-secondary,#6f6f6f); font-size:.8em; }
|
|
18
|
+
::slotted([slot="actions"]) { flex:none; }
|
|
19
|
+
@container (max-width: 320px) { [part="row"] { align-items:stretch; flex-wrap:wrap; } [part="trailing"] { inline-size:100%; justify-content:space-between; padding-inline-start:2.5rem; } }
|
|
20
|
+
@media (prefers-reduced-motion:reduce) { *,*::before,*::after { transition:none!important; } }
|
|
21
|
+
`;
|
|
22
|
+
/** Selectable resource information with independent slotted actions. Not a listbox option. */
|
|
23
|
+
export class ResourceRow extends BaseElement {
|
|
24
|
+
static tagName = "box-resource-row";
|
|
25
|
+
static get observedAttributes() { return ["label", "meta", "status", "value", "selected", "active", "disabled"]; }
|
|
26
|
+
selectEl;
|
|
27
|
+
labelEl;
|
|
28
|
+
metaEl;
|
|
29
|
+
statusEl;
|
|
30
|
+
statusSlot;
|
|
31
|
+
get label() { return this.getAttribute("label") ?? ""; }
|
|
32
|
+
set label(value) { this.setAttribute("label", value); }
|
|
33
|
+
get meta() { return this.getAttribute("meta") ?? ""; }
|
|
34
|
+
set meta(value) { this.setAttribute("meta", value); }
|
|
35
|
+
get status() { return this.getAttribute("status") ?? ""; }
|
|
36
|
+
set status(value) { this.setAttribute("status", value); }
|
|
37
|
+
get value() { return this.getAttribute("value") ?? this.label; }
|
|
38
|
+
set value(value) { this.setAttribute("value", value); }
|
|
39
|
+
get selected() { return this.hasAttribute("selected"); }
|
|
40
|
+
set selected(value) { this.toggleAttribute("selected", value); }
|
|
41
|
+
get active() { return this.hasAttribute("active"); }
|
|
42
|
+
set active(value) { this.toggleAttribute("active", value); }
|
|
43
|
+
get disabled() { return this.hasAttribute("disabled"); }
|
|
44
|
+
set disabled(value) { this.toggleAttribute("disabled", value); }
|
|
45
|
+
renderTemplate() {
|
|
46
|
+
this.shadowRoot.innerHTML = `<style>${styles}</style><div part="row"><button part="select" type="button"><slot name="icon"></slot><span part="body"><span part="label"></span><span part="meta"></span></span></button><span part="trailing"><span part="status"></span><slot name="status"></slot><slot name="actions"></slot></span></div>`;
|
|
47
|
+
this.selectEl = this.shadowRoot.querySelector('[part="select"]');
|
|
48
|
+
this.labelEl = this.shadowRoot.querySelector('[part="label"]');
|
|
49
|
+
this.metaEl = this.shadowRoot.querySelector('[part="meta"]');
|
|
50
|
+
this.statusEl = this.shadowRoot.querySelector('[part="status"]');
|
|
51
|
+
this.statusSlot = this.shadowRoot.querySelector('slot[name="status"]');
|
|
52
|
+
}
|
|
53
|
+
setupListeners() {
|
|
54
|
+
this.selectEl.addEventListener("click", () => this.dispatchEvent(new CustomEvent("select", { bubbles: true, composed: true, detail: { value: this.value } })));
|
|
55
|
+
this.statusSlot.addEventListener("slotchange", () => this.update());
|
|
56
|
+
}
|
|
57
|
+
update() {
|
|
58
|
+
this.labelEl.textContent = this.label;
|
|
59
|
+
this.metaEl.textContent = this.meta;
|
|
60
|
+
this.statusEl.textContent = this.status;
|
|
61
|
+
this.statusEl.hidden = this.statusSlot.assignedElements().length > 0;
|
|
62
|
+
this.selectEl.disabled = this.disabled;
|
|
63
|
+
this.selectEl.setAttribute("aria-pressed", String(this.selected));
|
|
64
|
+
this.selectEl.setAttribute("aria-label", this.label);
|
|
65
|
+
const slottedStatus = this.statusSlot.assignedNodes({ flatten: true }).map(node => node instanceof Element
|
|
66
|
+
? node.getAttribute("aria-label") ?? node.getAttribute("label") ?? node.textContent ?? ""
|
|
67
|
+
: node.textContent ?? "").map(value => value.trim()).filter(Boolean).join(" ");
|
|
68
|
+
const spokenStatus = this.status || slottedStatus;
|
|
69
|
+
if (this.meta || spokenStatus)
|
|
70
|
+
this.selectEl.setAttribute("aria-description", [this.meta.replaceAll("\n", ", "), spokenStatus].filter(Boolean).join("; "));
|
|
71
|
+
else
|
|
72
|
+
this.selectEl.removeAttribute("aria-description");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
ResourceRow.register();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BaseElement } from "../../core/index.js";
|
|
2
|
+
/** Host-controlled dry/live indicator. The host owns the mode-change destination. */
|
|
3
|
+
export declare class ModeIndicator extends BaseElement {
|
|
4
|
+
static readonly tagName = "box-mode-indicator";
|
|
5
|
+
static get observedAttributes(): string[];
|
|
6
|
+
private indicatorEl;
|
|
7
|
+
private stateEl;
|
|
8
|
+
private detailEl;
|
|
9
|
+
private readonly announcement;
|
|
10
|
+
private previousMode;
|
|
11
|
+
get mode(): "dry" | "live";
|
|
12
|
+
set mode(value: "dry" | "live");
|
|
13
|
+
get detail(): string;
|
|
14
|
+
set detail(value: string);
|
|
15
|
+
get namePrefix(): string;
|
|
16
|
+
set namePrefix(value: string);
|
|
17
|
+
get interactive(): boolean;
|
|
18
|
+
set interactive(value: boolean);
|
|
19
|
+
protected renderTemplate(): void;
|
|
20
|
+
protected update(): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { BaseElement } from "../../core/index.js";
|
|
2
|
+
import { FeedbackAnnouncement } from "../../foundations/a11y/announcer.js";
|
|
3
|
+
const styles = `
|
|
4
|
+
:host { display:inline-flex; max-inline-size:100%; font:inherit; color:var(--boe-token-text-text,#222); }
|
|
5
|
+
:host([hidden]) { display:none!important; }
|
|
6
|
+
[part="indicator"] { display:inline-flex; align-items:center; gap:.4rem; min-block-size:24px; min-inline-size:0; padding:.2rem .55rem; border-radius:999px; border:1px solid var(--boe-token-stroke-stroke,#dedede); background:var(--boe-token-surface-surface-secondary,#f4f4f4); color:inherit; font:inherit; text-align:start; }
|
|
7
|
+
button[part="indicator"] { cursor:pointer; }
|
|
8
|
+
button[part="indicator"]:focus-visible { outline:2px solid var(--boe-token-surface-surface-brand,#0061d5); outline-offset:2px; }
|
|
9
|
+
:host([mode="live"]) [part="indicator"] { border-color:var(--boe-token-text-status-text-error,#b92340); background:color-mix(in srgb,var(--boe-token-surface-status-surface-error,#ed3757) 9%,var(--boe-token-surface-surface,#fff)); }
|
|
10
|
+
[part="dot"] { flex:none; inline-size:.45rem; block-size:.45rem; border-radius:50%; background:var(--boe-token-text-text-secondary,#6f6f6f); }
|
|
11
|
+
:host([mode="live"]) [part="dot"] { background:var(--boe-token-text-status-text-error,#b92340); }
|
|
12
|
+
[part="state"] { font-weight:700; white-space:nowrap; }
|
|
13
|
+
[part="detail"] { min-inline-size:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
14
|
+
[part="detail"]:empty { display:none; }
|
|
15
|
+
:host([live-stripe][mode="live"]) { border-block-start:3px solid var(--boe-token-text-status-text-error,#b92340); }
|
|
16
|
+
@media (max-width:390px) { [part="detail"] { display:none; } }
|
|
17
|
+
`;
|
|
18
|
+
/** Host-controlled dry/live indicator. The host owns the mode-change destination. */
|
|
19
|
+
export class ModeIndicator extends BaseElement {
|
|
20
|
+
static tagName = "box-mode-indicator";
|
|
21
|
+
static get observedAttributes() { return ["mode", "detail", "name-prefix", "interactive", "live-stripe"]; }
|
|
22
|
+
indicatorEl;
|
|
23
|
+
stateEl;
|
|
24
|
+
detailEl;
|
|
25
|
+
announcement = new FeedbackAnnouncement();
|
|
26
|
+
previousMode = null;
|
|
27
|
+
get mode() { return this.getAttribute("mode") === "live" ? "live" : "dry"; }
|
|
28
|
+
set mode(value) { this.setAttribute("mode", value); }
|
|
29
|
+
get detail() { return this.getAttribute("detail") ?? ""; }
|
|
30
|
+
set detail(value) { this.setAttribute("detail", value); }
|
|
31
|
+
get namePrefix() { return this.getAttribute("name-prefix") ?? "Next run"; }
|
|
32
|
+
set namePrefix(value) { this.setAttribute("name-prefix", value); }
|
|
33
|
+
get interactive() { return this.hasAttribute("interactive"); }
|
|
34
|
+
set interactive(value) { this.toggleAttribute("interactive", value); }
|
|
35
|
+
renderTemplate() {
|
|
36
|
+
this.shadowRoot.innerHTML = `<style>${styles}</style><span part="indicator"><span part="dot" aria-hidden="true"></span><span part="state"></span><span part="detail"></span></span>`;
|
|
37
|
+
this.indicatorEl = this.shadowRoot.querySelector('[part="indicator"]');
|
|
38
|
+
this.stateEl = this.shadowRoot.querySelector('[part="state"]');
|
|
39
|
+
this.detailEl = this.shadowRoot.querySelector('[part="detail"]');
|
|
40
|
+
}
|
|
41
|
+
update() {
|
|
42
|
+
const mode = this.mode;
|
|
43
|
+
if (this.interactive !== (this.indicatorEl.tagName === "BUTTON")) {
|
|
44
|
+
const replacement = document.createElement(this.interactive ? "button" : "span");
|
|
45
|
+
replacement.setAttribute("part", "indicator");
|
|
46
|
+
if (!this.interactive)
|
|
47
|
+
replacement.setAttribute("role", "group");
|
|
48
|
+
if (replacement instanceof HTMLButtonElement)
|
|
49
|
+
replacement.type = "button";
|
|
50
|
+
replacement.append(...Array.from(this.indicatorEl.childNodes));
|
|
51
|
+
this.indicatorEl.replaceWith(replacement);
|
|
52
|
+
this.indicatorEl = replacement;
|
|
53
|
+
this.indicatorEl.addEventListener("click", () => { if (this.interactive)
|
|
54
|
+
this.dispatchEvent(new CustomEvent("activate", { bubbles: true, composed: true })); });
|
|
55
|
+
}
|
|
56
|
+
const state = mode === "live" ? "Live" : "Dry run";
|
|
57
|
+
if (!this.interactive)
|
|
58
|
+
this.indicatorEl.setAttribute("role", "group");
|
|
59
|
+
this.stateEl.textContent = state;
|
|
60
|
+
this.detailEl.textContent = this.detail ? `· ${this.detail}` : "";
|
|
61
|
+
this.indicatorEl.setAttribute("aria-label", `${this.namePrefix}: ${state}${this.detail ? `, ${this.detail}` : ""}`);
|
|
62
|
+
if (this.previousMode !== null && this.previousMode !== mode)
|
|
63
|
+
this.announcement.update(this, this.indicatorEl.getAttribute("aria-label") ?? "", "polite");
|
|
64
|
+
this.previousMode = mode;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
ModeIndicator.register();
|
|
@@ -46,7 +46,11 @@ export declare class ProgressSteps extends BaseElement {
|
|
|
46
46
|
private valueInternal;
|
|
47
47
|
private stepsEl;
|
|
48
48
|
private liveEl;
|
|
49
|
+
private compactToggleEl;
|
|
50
|
+
private compactExpanded;
|
|
49
51
|
private itemsSignature;
|
|
52
|
+
get compact(): boolean;
|
|
53
|
+
set compact(value: boolean);
|
|
50
54
|
get label(): string;
|
|
51
55
|
set label(value: string);
|
|
52
56
|
get items(): ProgressStepItem[];
|
|
@@ -59,6 +59,23 @@ const progressStepsStyles = `
|
|
|
59
59
|
font: inherit;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
[part="compact-toggle"] { display: none; }
|
|
63
|
+
@media (max-width: 600px) {
|
|
64
|
+
[part="compact-toggle"][data-enabled="true"] {
|
|
65
|
+
display: block;
|
|
66
|
+
width: 100%;
|
|
67
|
+
min-height: 36px;
|
|
68
|
+
text-align: left;
|
|
69
|
+
border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8);
|
|
70
|
+
border-radius: ${boeRadius.med};
|
|
71
|
+
background: var(--boe-token-surface-surface, #ffffff);
|
|
72
|
+
color: var(--boe-token-text-text, #222222);
|
|
73
|
+
font: inherit;
|
|
74
|
+
padding: 0.45rem 0.65rem;
|
|
75
|
+
}
|
|
76
|
+
[part="steps"][data-compact="true"][data-expanded="false"] { display: none; }
|
|
77
|
+
}
|
|
78
|
+
|
|
62
79
|
/* The host's own display would otherwise beat the UA rule for [hidden],
|
|
63
80
|
leaving the element on screen when a host hides it. */
|
|
64
81
|
:host([hidden]) {
|
|
@@ -214,12 +231,16 @@ const progressStepsStyles = `
|
|
|
214
231
|
export class ProgressSteps extends BaseElement {
|
|
215
232
|
static tagName = DEFAULT_TAG_NAME;
|
|
216
233
|
static get observedAttributes() {
|
|
217
|
-
return ["items", "label", "value"];
|
|
234
|
+
return ["compact", "items", "label", "value"];
|
|
218
235
|
}
|
|
219
236
|
valueInternal = "";
|
|
220
237
|
stepsEl;
|
|
221
238
|
liveEl;
|
|
239
|
+
compactToggleEl;
|
|
240
|
+
compactExpanded = false;
|
|
222
241
|
itemsSignature = "";
|
|
242
|
+
get compact() { return this.hasAttribute("compact"); }
|
|
243
|
+
set compact(value) { this.toggleAttribute("compact", Boolean(value)); }
|
|
223
244
|
get label() {
|
|
224
245
|
return this.getAttribute("label") ?? "Progress Steps";
|
|
225
246
|
}
|
|
@@ -270,13 +291,19 @@ export class ProgressSteps extends BaseElement {
|
|
|
270
291
|
}
|
|
271
292
|
this.shadowRoot.innerHTML = `
|
|
272
293
|
<style>${progressStepsStyles}</style>
|
|
273
|
-
<
|
|
294
|
+
<button type="button" part="compact-toggle" aria-controls="progress-steps-list" aria-expanded="false"></button>
|
|
295
|
+
<div id="progress-steps-list" part="steps" role="group"></div>
|
|
274
296
|
<div part="live" class="boe-sr-only" aria-live="polite"></div>
|
|
275
297
|
`;
|
|
276
298
|
this.stepsEl = this.shadowRoot.querySelector('[part="steps"]');
|
|
299
|
+
this.compactToggleEl = this.shadowRoot.querySelector('[part="compact-toggle"]');
|
|
277
300
|
this.liveEl = this.shadowRoot.querySelector('[part="live"]');
|
|
278
301
|
}
|
|
279
302
|
setupListeners() {
|
|
303
|
+
this.compactToggleEl.addEventListener("click", () => {
|
|
304
|
+
this.compactExpanded = !this.compactExpanded;
|
|
305
|
+
this.update();
|
|
306
|
+
});
|
|
280
307
|
this.stepsEl.addEventListener("click", event => {
|
|
281
308
|
const step = event.target.closest('[part="step"]');
|
|
282
309
|
if (!step || !this.stepsEl.contains(step)) {
|
|
@@ -397,6 +424,11 @@ export class ProgressSteps extends BaseElement {
|
|
|
397
424
|
}
|
|
398
425
|
this.stepsEl.setAttribute("aria-label", this.label);
|
|
399
426
|
const resolved = resolveStepStates(items, this.valueInternal);
|
|
427
|
+
this.compactToggleEl.dataset.enabled = String(this.compact);
|
|
428
|
+
this.compactToggleEl.setAttribute("aria-expanded", String(this.compactExpanded));
|
|
429
|
+
this.compactToggleEl.textContent = `Step ${activeIndex + 1} of ${items.length}: ${items[activeIndex]?.label ?? ""} · ${this.compactExpanded ? "Hide steps" : "Show steps"}`;
|
|
430
|
+
this.stepsEl.dataset.compact = String(this.compact);
|
|
431
|
+
this.stepsEl.dataset.expanded = String(this.compactExpanded);
|
|
400
432
|
const tabbable = this.tabbableIndex(resolved);
|
|
401
433
|
const signature = JSON.stringify(items);
|
|
402
434
|
if (signature !== this.itemsSignature) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BaseElement } from "../../core/index.js";
|
|
2
|
+
/** Finished-run verdict with a single action and one-time polite announcement. */
|
|
3
|
+
export declare class VerdictBanner extends BaseElement {
|
|
4
|
+
static readonly tagName = "box-verdict-banner";
|
|
5
|
+
static get observedAttributes(): string[];
|
|
6
|
+
private readonly headingId;
|
|
7
|
+
private readonly announcement;
|
|
8
|
+
private headingEl;
|
|
9
|
+
private reasonsEl;
|
|
10
|
+
private iconEl;
|
|
11
|
+
private reasonsValue;
|
|
12
|
+
private announced;
|
|
13
|
+
get tone(): "passed" | "missed" | "none";
|
|
14
|
+
set tone(value: "passed" | "missed" | "none");
|
|
15
|
+
get heading(): string;
|
|
16
|
+
set heading(value: string);
|
|
17
|
+
get headingLevel(): number;
|
|
18
|
+
set headingLevel(value: number);
|
|
19
|
+
get reasons(): string[];
|
|
20
|
+
set reasons(value: string[]);
|
|
21
|
+
protected renderTemplate(): void;
|
|
22
|
+
protected update(): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { BaseElement } from "../../core/index.js";
|
|
2
|
+
import { FeedbackAnnouncement } from "../../foundations/a11y/announcer.js";
|
|
3
|
+
import { boeRadius } from "../../foundations/geometry/index.js";
|
|
4
|
+
import { toneIcon } from "./tone.js";
|
|
5
|
+
const styles = `
|
|
6
|
+
:host { display:block; font:inherit; color:var(--boe-token-text-text,#222); container-type:inline-size; }
|
|
7
|
+
:host([hidden]) { display:none!important; }
|
|
8
|
+
[part="banner"] { display:grid; grid-template-columns:20px minmax(0,1fr) auto; align-items:start; gap:.7rem; padding:1rem; border-radius:${boeRadius.med}; border:1px solid var(--boe-token-stroke-stroke,#dedede); background:var(--boe-token-surface-surface-secondary,#f4f4f4); }
|
|
9
|
+
:host([tone="passed"]) [part="banner"] { border-color:var(--boe-token-text-status-text-success,#187657); background:color-mix(in srgb,var(--boe-token-surface-status-surface-success,#26c281) 9%,var(--boe-token-surface-surface,#fff)); }
|
|
10
|
+
:host([tone="missed"]) [part="banner"] { border-color:var(--boe-token-text-status-text-error,#b92340); background:color-mix(in srgb,var(--boe-token-surface-status-surface-error,#ed3757) 9%,var(--boe-token-surface-surface,#fff)); }
|
|
11
|
+
[part="icon"] { inline-size:20px; block-size:20px; color:var(--boe-token-text-text-secondary,#6f6f6f); }
|
|
12
|
+
:host([tone="passed"]) [part="icon"] { color:var(--boe-token-text-status-text-success,#187657); }
|
|
13
|
+
:host([tone="missed"]) [part="icon"] { color:var(--boe-token-text-status-text-error,#b92340); }
|
|
14
|
+
[part="icon"] svg { inline-size:100%; block-size:100%; }
|
|
15
|
+
[part="body"] { min-inline-size:0; }
|
|
16
|
+
[part="heading"] { margin:0; font:inherit; font-weight:700; overflow-wrap:anywhere; }
|
|
17
|
+
[part="reasons"] { margin:.45rem 0 0; padding-inline-start:1.2rem; overflow-wrap:anywhere; }
|
|
18
|
+
[part="reasons"]:empty { display:none; }
|
|
19
|
+
@container (max-width:320px) { [part="banner"] { grid-template-columns:20px minmax(0,1fr); } [part="action"] { grid-column:2; } }
|
|
20
|
+
`;
|
|
21
|
+
let nextHeadingId = 0;
|
|
22
|
+
/** Finished-run verdict with a single action and one-time polite announcement. */
|
|
23
|
+
export class VerdictBanner extends BaseElement {
|
|
24
|
+
static tagName = "box-verdict-banner";
|
|
25
|
+
static get observedAttributes() { return ["tone", "heading", "heading-level"]; }
|
|
26
|
+
headingId = `boe-verdict-${++nextHeadingId}`;
|
|
27
|
+
announcement = new FeedbackAnnouncement();
|
|
28
|
+
headingEl;
|
|
29
|
+
reasonsEl;
|
|
30
|
+
iconEl;
|
|
31
|
+
reasonsValue = [];
|
|
32
|
+
announced = false;
|
|
33
|
+
get tone() { const value = this.getAttribute("tone"); return value === "passed" || value === "missed" ? value : "none"; }
|
|
34
|
+
set tone(value) { this.setAttribute("tone", value); }
|
|
35
|
+
get heading() { return this.getAttribute("heading") ?? ""; }
|
|
36
|
+
set heading(value) { this.setAttribute("heading", value); }
|
|
37
|
+
get headingLevel() { const value = Number(this.getAttribute("heading-level") ?? 2); return Number.isInteger(value) && value >= 1 && value <= 6 ? value : 2; }
|
|
38
|
+
set headingLevel(value) { this.setAttribute("heading-level", String(value)); }
|
|
39
|
+
get reasons() { return [...this.reasonsValue]; }
|
|
40
|
+
set reasons(value) { this.reasonsValue = [...value]; if (this.isRendered)
|
|
41
|
+
this.update(); }
|
|
42
|
+
renderTemplate() {
|
|
43
|
+
this.shadowRoot.innerHTML = `<style>${styles}</style><section part="banner" role="group" aria-labelledby="${this.headingId}"><span part="icon" aria-hidden="true"></span><div part="body"><div part="heading" id="${this.headingId}" role="heading"></div><ul part="reasons"></ul></div><span part="action"><slot name="action"></slot></span></section>`;
|
|
44
|
+
this.headingEl = this.shadowRoot.querySelector('[part="heading"]');
|
|
45
|
+
this.reasonsEl = this.shadowRoot.querySelector('[part="reasons"]');
|
|
46
|
+
this.iconEl = this.shadowRoot.querySelector('[part="icon"]');
|
|
47
|
+
}
|
|
48
|
+
update() {
|
|
49
|
+
this.headingEl.textContent = this.heading;
|
|
50
|
+
this.headingEl.setAttribute("aria-level", String(this.headingLevel));
|
|
51
|
+
this.iconEl.innerHTML = toneIcon(this.tone === "passed" ? "success" : this.tone === "missed" ? "error" : "info");
|
|
52
|
+
this.reasonsEl.replaceChildren(...this.reasonsValue.map(reason => { const item = document.createElement("li"); item.textContent = reason; return item; }));
|
|
53
|
+
if (!this.announced && this.heading && this.tone !== "none") {
|
|
54
|
+
this.announced = true;
|
|
55
|
+
this.announcement.update(this, this.heading, "polite");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
VerdictBanner.register();
|
|
@@ -38,6 +38,7 @@ export declare class CodeEditor extends BaseElement {
|
|
|
38
38
|
private readonlyConfig;
|
|
39
39
|
private completionsConfig;
|
|
40
40
|
private wrapConfig;
|
|
41
|
+
private wrapIndentConfig;
|
|
41
42
|
private highlightsConfig;
|
|
42
43
|
private highlightsInternal;
|
|
43
44
|
private source?;
|
|
@@ -55,10 +56,19 @@ export declare class CodeEditor extends BaseElement {
|
|
|
55
56
|
}): void;
|
|
56
57
|
get wrap(): boolean;
|
|
57
58
|
set wrap(value: boolean);
|
|
59
|
+
get currentLineStyle(): "fill" | "border" | "none";
|
|
60
|
+
set currentLineStyle(value: "fill" | "border" | "none");
|
|
61
|
+
get hideHelp(): boolean;
|
|
62
|
+
set hideHelp(value: boolean);
|
|
63
|
+
get hideProblems(): boolean;
|
|
64
|
+
set hideProblems(value: boolean);
|
|
65
|
+
get fillHeight(): boolean;
|
|
66
|
+
set fillHeight(value: boolean);
|
|
58
67
|
get completionSource(): CodeCompletionSource | undefined;
|
|
59
68
|
set completionSource(value: CodeCompletionSource | undefined);
|
|
60
69
|
get highlights(): readonly CodeHighlight[];
|
|
61
70
|
set highlights(value: readonly CodeHighlight[]);
|
|
71
|
+
private wrapIndentExtension;
|
|
62
72
|
private highlightExtension;
|
|
63
73
|
get language(): string;
|
|
64
74
|
set language(value: string);
|
|
@@ -14,7 +14,7 @@ import { tags } from "@lezer/highlight";
|
|
|
14
14
|
export class CodeEditor extends BaseElement {
|
|
15
15
|
static tagName = "box-code-editor";
|
|
16
16
|
static get observedAttributes() {
|
|
17
|
-
return ["language", "readonly", "label", "value", "wrap"];
|
|
17
|
+
return ["current-line-style", "fill-height", "hide-help", "hide-problems", "language", "readonly", "label", "value", "wrap"];
|
|
18
18
|
}
|
|
19
19
|
valueInternal = "";
|
|
20
20
|
problemsInternal = [];
|
|
@@ -24,6 +24,7 @@ export class CodeEditor extends BaseElement {
|
|
|
24
24
|
readonlyConfig = new Compartment();
|
|
25
25
|
completionsConfig = new Compartment();
|
|
26
26
|
wrapConfig = new Compartment();
|
|
27
|
+
wrapIndentConfig = new Compartment();
|
|
27
28
|
highlightsConfig = new Compartment();
|
|
28
29
|
highlightsInternal = [];
|
|
29
30
|
source;
|
|
@@ -63,12 +64,48 @@ export class CodeEditor extends BaseElement {
|
|
|
63
64
|
}
|
|
64
65
|
get wrap() { return this.hasAttribute("wrap"); }
|
|
65
66
|
set wrap(value) { this.toggleAttribute("wrap", value); }
|
|
67
|
+
get currentLineStyle() {
|
|
68
|
+
const value = this.getAttribute("current-line-style");
|
|
69
|
+
return value === "border" || value === "none" ? value : "fill";
|
|
70
|
+
}
|
|
71
|
+
set currentLineStyle(value) { this.setAttribute("current-line-style", value); }
|
|
72
|
+
get hideHelp() { return this.hasAttribute("hide-help"); }
|
|
73
|
+
set hideHelp(value) { this.toggleAttribute("hide-help", value); }
|
|
74
|
+
get hideProblems() { return this.hasAttribute("hide-problems"); }
|
|
75
|
+
set hideProblems(value) { this.toggleAttribute("hide-problems", value); }
|
|
76
|
+
get fillHeight() { return this.hasAttribute("fill-height"); }
|
|
77
|
+
set fillHeight(value) { this.toggleAttribute("fill-height", value); }
|
|
66
78
|
get completionSource() { return this.source; }
|
|
67
79
|
set completionSource(value) { this.source = value; if (this.isRendered)
|
|
68
80
|
this.update(); }
|
|
69
81
|
get highlights() { return this.highlightsInternal.map(range => ({ ...range })); }
|
|
70
82
|
set highlights(value) { this.highlightsInternal = value.map(range => ({ ...range })); if (this.isRendered)
|
|
71
83
|
this.update(); }
|
|
84
|
+
wrapIndentExtension() {
|
|
85
|
+
return EditorView.decorations.of(view => {
|
|
86
|
+
if (!this.wrap)
|
|
87
|
+
return Decoration.none;
|
|
88
|
+
const ranges = [];
|
|
89
|
+
const seen = new Set();
|
|
90
|
+
for (const visible of view.visibleRanges) {
|
|
91
|
+
const first = view.state.doc.lineAt(visible.from).number;
|
|
92
|
+
const last = view.state.doc.lineAt(visible.to).number;
|
|
93
|
+
for (let number = first; number <= last; number++) {
|
|
94
|
+
const line = view.state.doc.line(number);
|
|
95
|
+
if (seen.has(line.from))
|
|
96
|
+
continue;
|
|
97
|
+
seen.add(line.from);
|
|
98
|
+
const whitespace = line.text.match(/^[\t ]*/)?.[0] ?? "";
|
|
99
|
+
const columns = Math.min(24, whitespace.replaceAll("\t", " ").length);
|
|
100
|
+
if (columns)
|
|
101
|
+
ranges.push(Decoration.line({ attributes: {
|
|
102
|
+
style: `padding-left:${columns}ch;text-indent:-${columns}ch`,
|
|
103
|
+
} }).range(line.from));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return Decoration.set(ranges, true);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
72
109
|
highlightExtension() {
|
|
73
110
|
return EditorView.decorations.of(view => {
|
|
74
111
|
const doc = view.state.doc;
|
|
@@ -179,17 +216,22 @@ export class CodeEditor extends BaseElement {
|
|
|
179
216
|
}
|
|
180
217
|
renderTemplate() {
|
|
181
218
|
this.shadowRoot.innerHTML = `<style>
|
|
182
|
-
:host{display:
|
|
219
|
+
:host{display:flex;flex-direction:column;min-width:0;min-height:0;color:var(--boe-token-text-text,#222);font:inherit}
|
|
183
220
|
:host([hidden]){display:none!important}
|
|
184
|
-
[part=editor]{border:1px solid var(--boe-token-stroke-stroke,#ddd);border-radius:8px;overflow:hidden}
|
|
221
|
+
[part=editor]{border:1px solid var(--boe-token-stroke-stroke,#ddd);border-radius:8px;overflow:hidden;min-height:0;flex:1 1 auto}
|
|
185
222
|
.cm-editor{background:var(--boe-code-background,var(--boe-token-surface-surface,#fff));color:var(--boe-code-foreground,var(--boe-token-text-text,#222))}
|
|
186
223
|
[part=editor]:focus-within{box-shadow:0 0 0 3px var(--boe-token-surface-surface-brand,#0061d5)}
|
|
187
224
|
.cm-editor.cm-focused{outline:none}
|
|
188
225
|
.cm-scroller{max-height:var(--boe-code-editor-height,420px);min-height:160px;overflow:auto;font-family:var(--boe-code-font-family,monospace);font-size:var(--boe-code-font-size,14px);line-height:var(--boe-code-line-height,1.6)}
|
|
226
|
+
:host([fill-height]){height:100%}
|
|
227
|
+
:host([fill-height]) [part=editor],:host([fill-height]) .cm-editor,:host([fill-height]) .cm-scroller{height:100%;max-height:none;min-height:0}
|
|
189
228
|
.cm-gutters,.cm-panels,.cm-tooltip{background:var(--boe-token-surface-surface-secondary,#fbfbfb)!important;color:var(--boe-code-foreground,var(--boe-token-text-text,#222))!important;border-color:var(--boe-token-stroke-stroke,#ddd)!important}
|
|
190
229
|
.cm-gutters{color:var(--boe-code-gutter,var(--boe-token-text-text,#222))!important;border-right:var(--boe-code-gutter-border,1px solid var(--boe-token-stroke-stroke,#ddd))!important}
|
|
191
230
|
.cm-cursor{border-left-color:var(--boe-code-caret,var(--boe-token-text-text,#222))}
|
|
192
231
|
.cm-activeLine,.cm-activeLineGutter{background:color-mix(in srgb,var(--boe-token-surface-surface-brand,#0061d5) 12%,transparent)!important}
|
|
232
|
+
:host([current-line-style=border]) .cm-activeLine{background:transparent!important;box-shadow:inset 0 1px var(--boe-code-current-line-border,var(--boe-token-stroke-stroke,#ddd)),inset 0 -1px var(--boe-code-current-line-border,var(--boe-token-stroke-stroke,#ddd))}
|
|
233
|
+
:host([current-line-style=none]) .cm-activeLine{background:transparent!important}
|
|
234
|
+
:host([current-line-style=border]) .cm-activeLineGutter,:host([current-line-style=none]) .cm-activeLineGutter{background:transparent!important}
|
|
193
235
|
.cm-activeLineGutter{color:var(--boe-code-gutter-active,var(--boe-code-gutter,var(--boe-token-text-text,#222)))!important}
|
|
194
236
|
.cm-selectionBackground,.cm-content ::selection{background:var(--boe-code-selection,color-mix(in srgb,var(--boe-token-surface-surface-brand,#0061d5) 12%,transparent))!important}
|
|
195
237
|
.boe-code-highlight{background:var(--boe-code-highlight-background,color-mix(in srgb,var(--boe-token-surface-surface-brand,#0061d5) 10%,transparent))!important;box-shadow:inset 3px 0 var(--boe-token-surface-surface-brand,#0061d5)}
|
|
@@ -206,6 +248,7 @@ export class CodeEditor extends BaseElement {
|
|
|
206
248
|
.cm-lint-marker-warning{background-image:radial-gradient(circle,var(--boe-token-text-status-text-warning,#9a6500) 45%,transparent 50%)!important}
|
|
207
249
|
.cm-lint-marker-info{background-image:radial-gradient(circle,var(--boe-token-surface-surface-brand,#0061d5) 45%,transparent 50%)!important}
|
|
208
250
|
[part=help],[part=problems]{font-size:13px;color:var(--boe-token-text-text-secondary,#666);margin:8px 0}
|
|
251
|
+
[part=help][hidden],[part=toolbar][hidden]{display:none}
|
|
209
252
|
[part=toolbar]{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
|
|
210
253
|
button{font:inherit;color:inherit;background:var(--boe-token-surface-surface,#fff);border:1px solid var(--boe-token-stroke-stroke,#ddd);border-radius:8px;padding:8px;min-height:36px}
|
|
211
254
|
button:focus-visible{outline:3px solid var(--boe-token-surface-surface-brand,#0061d5);outline-offset:2px}
|
|
@@ -297,6 +340,7 @@ export class CodeEditor extends BaseElement {
|
|
|
297
340
|
]),
|
|
298
341
|
this.completionsConfig.of(this.completionExtension()),
|
|
299
342
|
this.wrapConfig.of(this.wrap ? EditorView.lineWrapping : []),
|
|
343
|
+
this.wrapIndentConfig.of(this.wrapIndentExtension()),
|
|
300
344
|
this.highlightsConfig.of(this.highlightExtension()),
|
|
301
345
|
syntaxHighlighting(HighlightStyle.define([
|
|
302
346
|
{
|
|
@@ -388,6 +432,7 @@ export class CodeEditor extends BaseElement {
|
|
|
388
432
|
]),
|
|
389
433
|
this.completionsConfig.reconfigure(this.completionExtension()),
|
|
390
434
|
this.wrapConfig.reconfigure(this.wrap ? EditorView.lineWrapping : []),
|
|
435
|
+
this.wrapIndentConfig.reconfigure(this.wrapIndentExtension()),
|
|
391
436
|
this.highlightsConfig.reconfigure(this.highlightExtension()),
|
|
392
437
|
],
|
|
393
438
|
});
|
|
@@ -398,7 +443,12 @@ export class CodeEditor extends BaseElement {
|
|
|
398
443
|
}
|
|
399
444
|
this.view.contentDOM.setAttribute("aria-label", this.label);
|
|
400
445
|
this.view.contentDOM.setAttribute("tabindex", "0");
|
|
401
|
-
this.
|
|
446
|
+
if (this.hideHelp)
|
|
447
|
+
this.view.contentDOM.removeAttribute("aria-describedby");
|
|
448
|
+
else
|
|
449
|
+
this.view.contentDOM.setAttribute("aria-describedby", "editor-help");
|
|
450
|
+
this.shadowRoot.querySelector('[part=help]').hidden = this.hideHelp;
|
|
451
|
+
this.shadowRoot.querySelector('[part=toolbar]').hidden = this.hideProblems;
|
|
402
452
|
this.syncProblems();
|
|
403
453
|
}
|
|
404
454
|
flushChange() {
|