@unofficialbox/box-open-elements 0.24.0 → 0.25.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 +1 -0
- package/dist/components/actions/button.js +13 -0
- package/dist/components/forms/checkbox.d.ts +1 -0
- package/dist/components/forms/checkbox.js +2 -0
- package/dist/components/forms/code-editor.d.ts +28 -3
- package/dist/components/forms/code-editor.js +71 -6
- package/dist/components/forms/combobox.js +7 -8
- package/dist/components/forms/number-input.d.ts +1 -0
- package/dist/components/forms/number-input.js +4 -1
- package/dist/components/forms/select.d.ts +1 -0
- package/dist/components/forms/select.js +3 -1
- package/dist/components/forms/text-area.js +5 -7
- package/dist/components/forms/text-field.d.ts +1 -0
- package/dist/components/forms/text-field.js +2 -0
- package/dist/components/navigation/accordion.d.ts +7 -0
- package/dist/components/navigation/accordion.js +31 -8
- package/dist/components/output/code-block.js +3 -1
- package/dist/components/overlays/drawer.js +2 -2
- package/dist/foundations/geometry/tokens.d.ts +2 -2
- package/dist/foundations/geometry/tokens.js +3 -3
- package/dist/foundations/icons/box-iconography.generated.d.ts +472 -1
- package/dist/foundations/icons/box-iconography.generated.js +473 -2
- package/dist/patterns/flow-builder/flow-builder.d.ts +5 -1
- package/dist/patterns/flow-builder/flow-builder.js +18 -7
- package/dist/patterns/flow-builder/primitives.js +1 -1
- package/dist/patterns/process-modeler/bridge.d.ts +10 -0
- package/dist/patterns/process-modeler/bridge.js +173 -0
- package/dist/patterns/process-modeler/geometry.d.ts +18 -0
- package/dist/patterns/process-modeler/geometry.js +307 -0
- package/dist/patterns/process-modeler/index.d.ts +2 -0
- package/dist/patterns/process-modeler/index.js +2 -0
- package/dist/patterns/process-modeler/model.d.ts +41 -1
- package/dist/patterns/process-modeler/process-modeler.d.ts +48 -0
- package/dist/patterns/process-modeler/process-modeler.js +709 -63
- package/package.json +4 -3
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.25.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.25.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.25.0/docs/README.md). The most important entries:
|
|
95
|
+
|
|
96
|
+
- [Taxonomy](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/taxonomy.md)
|
|
97
|
+
- [Architecture](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/architecture.md)
|
|
98
|
+
- [API Guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/api-guidelines.md)
|
|
99
|
+
- [Components catalog](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/components/catalog.md) and [patterns catalog](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/patterns/catalog.md)
|
|
100
|
+
- [Using with React, Angular, Vue, and Svelte](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/integration/frameworks.md)
|
|
101
|
+
- [Design Tokens](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/foundations/tokens.md)
|
|
102
|
+
- [Content Explorer](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/patterns/content-explorer.md)
|
|
103
|
+
- [Box Server Integration](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/integration/box-server.md)
|
|
104
|
+
- [Agent UI guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/patterns/agent-ui-guidelines.md), [building blocks](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/patterns/agent-ui.md), and [motion](https://github.com/unofficialbox/box-open-elements/blob/v0.25.0/docs/foundations/motion.md)
|
|
105
105
|
|
|
106
106
|
## Contributing
|
|
107
107
|
|
|
@@ -74,6 +74,17 @@ const buttonStyles = `
|
|
|
74
74
|
box-shadow: none;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
button[data-tone="text"], button[data-tone="text"]:hover:not(:disabled),
|
|
78
|
+
button[data-tone="text"]:active:not(:disabled), button[data-tone="text"]:focus-visible {
|
|
79
|
+
background: transparent;
|
|
80
|
+
border-color: transparent;
|
|
81
|
+
color: var(--boe-token-surface-surface-brand, #0061d5);
|
|
82
|
+
padding-inline: 0;
|
|
83
|
+
white-space: normal;
|
|
84
|
+
text-align: start;
|
|
85
|
+
}
|
|
86
|
+
button[data-tone="text"]:hover:not(:disabled) { text-decoration: underline; }
|
|
87
|
+
|
|
77
88
|
button[data-tone="neutral"]:hover:not(:disabled) {
|
|
78
89
|
background: color-mix(in srgb, var(--boe-token-surface-surface, #ffffff) 97%, black 3%);
|
|
79
90
|
border-color: ${boeControl.buttonBorder};
|
|
@@ -176,6 +187,8 @@ export class Button extends BaseElement {
|
|
|
176
187
|
return ["disabled", "label", "size", "tone", "is-loading", "type"];
|
|
177
188
|
}
|
|
178
189
|
buttonEl;
|
|
190
|
+
focus(options) { if (!this.disabled)
|
|
191
|
+
this.buttonEl?.focus(options); }
|
|
179
192
|
spinnerEl;
|
|
180
193
|
iconEl;
|
|
181
194
|
labelEl;
|
|
@@ -105,6 +105,8 @@ export class Checkbox extends FormAssociatedElement {
|
|
|
105
105
|
indeterminateInternal = false;
|
|
106
106
|
valueInternal = DEFAULT_VALUE;
|
|
107
107
|
inputEl;
|
|
108
|
+
focus(options) { if (!this.disabled)
|
|
109
|
+
this.inputEl?.focus(options); }
|
|
108
110
|
labelEl;
|
|
109
111
|
descriptionEl;
|
|
110
112
|
fieldEl;
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
|
-
import { type Completion } from "@codemirror/autocomplete";
|
|
2
|
+
import { type Completion, type CompletionSource } from "@codemirror/autocomplete";
|
|
3
3
|
export interface CodeProblem {
|
|
4
4
|
/** One-based line and column positions. */
|
|
5
|
-
line
|
|
5
|
+
line?: number;
|
|
6
|
+
/** UTF-16 offsets; when supplied, these take precedence over line/column. */
|
|
7
|
+
from?: number;
|
|
8
|
+
to?: number;
|
|
6
9
|
col?: number;
|
|
7
10
|
endCol?: number;
|
|
8
11
|
message: string;
|
|
9
12
|
tone: "error" | "warning" | "info";
|
|
10
13
|
}
|
|
11
14
|
export type CodeCompletion = Completion;
|
|
15
|
+
export type CodeCompletionSource = CompletionSource;
|
|
16
|
+
export interface CodeHighlight {
|
|
17
|
+
from: number;
|
|
18
|
+
to: number;
|
|
19
|
+
}
|
|
12
20
|
export interface CodeSelection {
|
|
13
21
|
anchor: number;
|
|
14
22
|
head: number;
|
|
@@ -24,6 +32,10 @@ export declare class CodeEditor extends BaseElement {
|
|
|
24
32
|
private languageConfig;
|
|
25
33
|
private readonlyConfig;
|
|
26
34
|
private completionsConfig;
|
|
35
|
+
private wrapConfig;
|
|
36
|
+
private highlightsConfig;
|
|
37
|
+
private highlightsInternal;
|
|
38
|
+
private source?;
|
|
27
39
|
private timer?;
|
|
28
40
|
private pending;
|
|
29
41
|
private syncing;
|
|
@@ -32,6 +44,17 @@ export declare class CodeEditor extends BaseElement {
|
|
|
32
44
|
private escapeUntil;
|
|
33
45
|
get value(): string;
|
|
34
46
|
set value(value: string);
|
|
47
|
+
/** Host replacements are silent and excluded from the typing undo stack. */
|
|
48
|
+
setValue(value: string, options?: {
|
|
49
|
+
addToHistory?: boolean;
|
|
50
|
+
}): void;
|
|
51
|
+
get wrap(): boolean;
|
|
52
|
+
set wrap(value: boolean);
|
|
53
|
+
get completionSource(): CodeCompletionSource | undefined;
|
|
54
|
+
set completionSource(value: CodeCompletionSource | undefined);
|
|
55
|
+
get highlights(): readonly CodeHighlight[];
|
|
56
|
+
set highlights(value: readonly CodeHighlight[]);
|
|
57
|
+
private highlightExtension;
|
|
35
58
|
get language(): string;
|
|
36
59
|
set language(value: string);
|
|
37
60
|
get readonly(): boolean;
|
|
@@ -44,7 +67,9 @@ export declare class CodeEditor extends BaseElement {
|
|
|
44
67
|
set completions(value: readonly CodeCompletion[]);
|
|
45
68
|
get selection(): CodeSelection;
|
|
46
69
|
set selection(value: CodeSelection);
|
|
47
|
-
revealLine(line: number
|
|
70
|
+
revealLine(line: number, options?: {
|
|
71
|
+
center?: boolean;
|
|
72
|
+
}): void;
|
|
48
73
|
focus(): void;
|
|
49
74
|
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
50
75
|
connectedCallback(): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { basicSetup } from "codemirror";
|
|
3
|
-
import { Compartment, EditorSelection, EditorState, Prec, } from "@codemirror/state";
|
|
4
|
-
import { EditorView, keymap } from "@codemirror/view";
|
|
3
|
+
import { Compartment, EditorSelection, EditorState, Prec, Transaction, } from "@codemirror/state";
|
|
4
|
+
import { Decoration, EditorView, keymap } from "@codemirror/view";
|
|
5
5
|
import { indentWithTab } from "@codemirror/commands";
|
|
6
6
|
import { javascript } from "@codemirror/lang-javascript";
|
|
7
7
|
import { go } from "@codemirror/lang-go";
|
|
@@ -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"];
|
|
17
|
+
return ["language", "readonly", "label", "value", "wrap"];
|
|
18
18
|
}
|
|
19
19
|
valueInternal = "";
|
|
20
20
|
problemsInternal = [];
|
|
@@ -23,6 +23,10 @@ export class CodeEditor extends BaseElement {
|
|
|
23
23
|
languageConfig = new Compartment();
|
|
24
24
|
readonlyConfig = new Compartment();
|
|
25
25
|
completionsConfig = new Compartment();
|
|
26
|
+
wrapConfig = new Compartment();
|
|
27
|
+
highlightsConfig = new Compartment();
|
|
28
|
+
highlightsInternal = [];
|
|
29
|
+
source;
|
|
26
30
|
timer;
|
|
27
31
|
pending = false;
|
|
28
32
|
syncing = false;
|
|
@@ -39,6 +43,48 @@ export class CodeEditor extends BaseElement {
|
|
|
39
43
|
if (this.isRendered)
|
|
40
44
|
this.update();
|
|
41
45
|
}
|
|
46
|
+
/** Host replacements are silent and excluded from the typing undo stack. */
|
|
47
|
+
setValue(value, options = {}) {
|
|
48
|
+
if (!options.addToHistory || !this.view) {
|
|
49
|
+
this.value = value;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
clearTimeout(this.timer);
|
|
53
|
+
this.pending = false;
|
|
54
|
+
this.syncing = true;
|
|
55
|
+
try {
|
|
56
|
+
this.view.dispatch({ changes: { from: 0, to: this.view.state.doc.length, insert: value } });
|
|
57
|
+
this.valueInternal = value;
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
this.syncing = false;
|
|
61
|
+
}
|
|
62
|
+
this.syncProblems();
|
|
63
|
+
}
|
|
64
|
+
get wrap() { return this.hasAttribute("wrap"); }
|
|
65
|
+
set wrap(value) { this.toggleAttribute("wrap", value); }
|
|
66
|
+
get completionSource() { return this.source; }
|
|
67
|
+
set completionSource(value) { this.source = value; if (this.isRendered)
|
|
68
|
+
this.update(); }
|
|
69
|
+
get highlights() { return this.highlightsInternal.map(range => ({ ...range })); }
|
|
70
|
+
set highlights(value) { this.highlightsInternal = value.map(range => ({ ...range })); if (this.isRendered)
|
|
71
|
+
this.update(); }
|
|
72
|
+
highlightExtension() {
|
|
73
|
+
return EditorView.decorations.of(view => {
|
|
74
|
+
const doc = view.state.doc;
|
|
75
|
+
const lines = new Set();
|
|
76
|
+
for (const range of this.highlightsInternal) {
|
|
77
|
+
if (!Number.isFinite(range.from) || !Number.isFinite(range.to))
|
|
78
|
+
continue;
|
|
79
|
+
const from = Math.max(0, Math.min(doc.length, range.from));
|
|
80
|
+
const to = Math.max(from, Math.min(doc.length, range.to));
|
|
81
|
+
for (let line = doc.lineAt(from).number; line <= doc.lineAt(Math.max(from, to - 1)).number; line++) {
|
|
82
|
+
lines.add(doc.line(line).from);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return Decoration.set([...lines].sort((a, b) => a - b).map(from => Decoration.line({ class: "boe-code-highlight", attributes: { part: "highlight" } }).range(from)));
|
|
86
|
+
});
|
|
87
|
+
}
|
|
42
88
|
get language() {
|
|
43
89
|
return this.getAttribute("language") ?? "typescript";
|
|
44
90
|
}
|
|
@@ -92,11 +138,13 @@ export class CodeEditor extends BaseElement {
|
|
|
92
138
|
scrollIntoView: true,
|
|
93
139
|
});
|
|
94
140
|
}
|
|
95
|
-
revealLine(line) {
|
|
141
|
+
revealLine(line, options = {}) {
|
|
96
142
|
if (!this.view)
|
|
97
143
|
return;
|
|
98
144
|
const target = this.view.state.doc.line(Math.max(1, Math.min(this.view.state.doc.lines, Math.floor(line) || 1)));
|
|
99
145
|
this.selection = { anchor: target.from, head: target.from };
|
|
146
|
+
if (options.center)
|
|
147
|
+
this.view.dispatch({ effects: EditorView.scrollIntoView(target.from, { y: "center" }) });
|
|
100
148
|
this.view.focus();
|
|
101
149
|
}
|
|
102
150
|
focus() {
|
|
@@ -128,6 +176,7 @@ export class CodeEditor extends BaseElement {
|
|
|
128
176
|
.cm-gutters,.cm-panels,.cm-tooltip{background:var(--boe-token-surface-surface-secondary,#fbfbfb)!important;color:var(--boe-token-text-text,#222)!important;border-color:var(--boe-token-stroke-stroke,#ddd)!important}
|
|
129
177
|
.cm-cursor{border-left-color:var(--boe-token-text-text,#222)}
|
|
130
178
|
.cm-activeLine,.cm-activeLineGutter,.cm-selectionBackground{background:color-mix(in srgb,var(--boe-token-surface-surface-brand,#0061d5) 12%,transparent)!important}
|
|
179
|
+
.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)}
|
|
131
180
|
.cm-matchingBracket,.cm-selectionMatch{background:color-mix(in srgb,var(--boe-token-surface-surface-brand,#0061d5) 15%,transparent)!important;color:inherit!important}
|
|
132
181
|
.cm-nonmatchingBracket{color:var(--boe-token-text-status-text-error,#b92340)!important}
|
|
133
182
|
.cm-searchMatch{background:color-mix(in srgb,var(--boe-token-text-status-text-warning,#9a6500) 20%,transparent);outline:none}
|
|
@@ -183,6 +232,8 @@ export class CodeEditor extends BaseElement {
|
|
|
183
232
|
: javascript({ typescript: this.language === "typescript" });
|
|
184
233
|
}
|
|
185
234
|
completionExtension() {
|
|
235
|
+
if (this.source)
|
|
236
|
+
return autocompletion({ override: [this.source] });
|
|
186
237
|
return autocompletion({
|
|
187
238
|
override: [
|
|
188
239
|
(context) => {
|
|
@@ -229,6 +280,8 @@ export class CodeEditor extends BaseElement {
|
|
|
229
280
|
EditorView.editable.of(!this.readonly),
|
|
230
281
|
]),
|
|
231
282
|
this.completionsConfig.of(this.completionExtension()),
|
|
283
|
+
this.wrapConfig.of(this.wrap ? EditorView.lineWrapping : []),
|
|
284
|
+
this.highlightsConfig.of(this.highlightExtension()),
|
|
232
285
|
syntaxHighlighting(HighlightStyle.define([
|
|
233
286
|
{
|
|
234
287
|
tag: [
|
|
@@ -270,6 +323,10 @@ export class CodeEditor extends BaseElement {
|
|
|
270
323
|
},
|
|
271
324
|
])),
|
|
272
325
|
EditorView.updateListener.of((update) => {
|
|
326
|
+
if (update.selectionSet && !this.syncing && !update.startState.selection.eq(update.state.selection)) {
|
|
327
|
+
const { anchor, head } = update.state.selection.main;
|
|
328
|
+
this.dispatchEvent(new CustomEvent("selection-changed", { detail: { anchor, head }, bubbles: true, composed: true }));
|
|
329
|
+
}
|
|
273
330
|
if (update.docChanged && !this.syncing) {
|
|
274
331
|
this.valueInternal = update.state.doc.toString();
|
|
275
332
|
this.pending = true;
|
|
@@ -295,6 +352,7 @@ export class CodeEditor extends BaseElement {
|
|
|
295
352
|
clearTimeout(this.timer);
|
|
296
353
|
this.pending = false;
|
|
297
354
|
this.view.dispatch({
|
|
355
|
+
annotations: Transaction.addToHistory.of(false),
|
|
298
356
|
changes: {
|
|
299
357
|
from: 0,
|
|
300
358
|
to: this.view.state.doc.length,
|
|
@@ -310,6 +368,8 @@ export class CodeEditor extends BaseElement {
|
|
|
310
368
|
EditorView.editable.of(!this.readonly),
|
|
311
369
|
]),
|
|
312
370
|
this.completionsConfig.reconfigure(this.completionExtension()),
|
|
371
|
+
this.wrapConfig.reconfigure(this.wrap ? EditorView.lineWrapping : []),
|
|
372
|
+
this.highlightsConfig.reconfigure(this.highlightExtension()),
|
|
313
373
|
],
|
|
314
374
|
});
|
|
315
375
|
}
|
|
@@ -338,10 +398,15 @@ export class CodeEditor extends BaseElement {
|
|
|
338
398
|
return [];
|
|
339
399
|
const doc = this.view.state.doc;
|
|
340
400
|
return this.problems
|
|
341
|
-
.filter((problem) => Number.isInteger(problem.line) &&
|
|
401
|
+
.filter((problem) => Number.isFinite(problem.from) || (Number.isInteger(problem.line) &&
|
|
342
402
|
problem.line >= 1 &&
|
|
343
|
-
problem.line <= doc.lines)
|
|
403
|
+
problem.line <= doc.lines))
|
|
344
404
|
.map((problem) => {
|
|
405
|
+
if (Number.isFinite(problem.from)) {
|
|
406
|
+
const from = Math.max(0, Math.min(doc.length, Math.floor(problem.from)));
|
|
407
|
+
const to = Math.max(from, Math.min(doc.length, Number.isFinite(problem.to) ? Math.floor(problem.to) : from + 1));
|
|
408
|
+
return { from, to, message: problem.message, severity: problem.tone };
|
|
409
|
+
}
|
|
345
410
|
const line = doc.line(problem.line);
|
|
346
411
|
const column = Number.isFinite(problem.col) ? problem.col : 1;
|
|
347
412
|
const endColumn = Number.isFinite(problem.endCol)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FormAssociatedElement, boeFormFieldErrorStyles, formErrorMessageMarkup, } from "../../core/index.js";
|
|
2
2
|
import { boeNeutralInteractiveStyles } from "../../foundations/tokens/interaction.js";
|
|
3
3
|
import { boeMotionDuration, boeMotionEasing } from "../../foundations/motion/index.js";
|
|
4
|
-
import { boeOverlay, boeRadius } from "../../foundations/geometry/index.js";
|
|
4
|
+
import { boeControl, boeOverlay, boeRadius } from "../../foundations/geometry/index.js";
|
|
5
5
|
import { parsePlacement, trackAnchor, } from "../../foundations/overlay/index.js";
|
|
6
6
|
const DEFAULT_TAG_NAME = "box-combobox";
|
|
7
7
|
const escapeHtml = (value) => value
|
|
@@ -29,11 +29,9 @@ const comboboxStyles = `
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
[part="label"] {
|
|
32
|
-
font-size:
|
|
32
|
+
font-size: ${boeControl.fontSize};
|
|
33
33
|
font-weight: 700;
|
|
34
|
-
|
|
35
|
-
text-transform: uppercase;
|
|
36
|
-
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
34
|
+
color: var(--boe-token-text-text, #222222);
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
[part="control"] {
|
|
@@ -48,7 +46,7 @@ const comboboxStyles = `
|
|
|
48
46
|
font: inherit;
|
|
49
47
|
color: var(--boe-token-text-text, #222222);
|
|
50
48
|
padding: 0.45rem 0.7rem;
|
|
51
|
-
border: 1px solid
|
|
49
|
+
border: 1px solid ${boeControl.inputBorder};
|
|
52
50
|
border-radius: ${boeRadius.control};
|
|
53
51
|
background: var(--boe-token-surface-surface, #ffffff);
|
|
54
52
|
transition:
|
|
@@ -62,6 +60,7 @@ const comboboxStyles = `
|
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
${boeNeutralInteractiveStyles('[part="input"]')}
|
|
63
|
+
[part="input"]:hover:not(:disabled) { border-color: ${boeControl.inputBorder}; }
|
|
65
64
|
|
|
66
65
|
[part="input"]:focus-visible {
|
|
67
66
|
border-color: var(--boe-token-surface-surface-brand, #0061d5);
|
|
@@ -126,8 +125,8 @@ const comboboxStyles = `
|
|
|
126
125
|
padding: 6px 10px 2px;
|
|
127
126
|
font-size: 0.72rem;
|
|
128
127
|
font-weight: 700;
|
|
129
|
-
letter-spacing:
|
|
130
|
-
text-transform:
|
|
128
|
+
letter-spacing: normal;
|
|
129
|
+
text-transform: none;
|
|
131
130
|
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
132
131
|
}
|
|
133
132
|
|
|
@@ -41,7 +41,7 @@ const numberInputStyles = `
|
|
|
41
41
|
font: inherit;
|
|
42
42
|
color: var(--boe-token-text-text, #222222);
|
|
43
43
|
padding: 0.45rem 0.7rem;
|
|
44
|
-
border: 1px solid
|
|
44
|
+
border: 1px solid ${boeControl.inputBorder};
|
|
45
45
|
border-radius: ${boeRadius.control};
|
|
46
46
|
background: var(--boe-token-surface-surface, #ffffff);
|
|
47
47
|
transition:
|
|
@@ -55,6 +55,7 @@ const numberInputStyles = `
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
${boeNeutralInteractiveStyles('[part="input"]')}
|
|
58
|
+
[part="input"]:hover:not(:disabled) { border-color: ${boeControl.inputBorder}; }
|
|
58
59
|
|
|
59
60
|
[part="input"]:focus-visible {
|
|
60
61
|
border-color: var(--boe-token-surface-surface-brand, #0061d5);
|
|
@@ -79,6 +80,8 @@ export class NumberInput extends FormAssociatedElement {
|
|
|
79
80
|
}
|
|
80
81
|
valueInternal = 0;
|
|
81
82
|
inputEl;
|
|
83
|
+
focus(options) { if (!this.disabled)
|
|
84
|
+
this.inputEl?.focus(options); }
|
|
82
85
|
labelEl;
|
|
83
86
|
errorEl;
|
|
84
87
|
descriptionEl;
|
|
@@ -64,7 +64,7 @@ const selectStyles = `
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
[part="select"]:hover:not(:disabled) {
|
|
67
|
-
border-color:
|
|
67
|
+
border-color: ${boeControl.inputBorder};
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
[part="select"]:focus-visible {
|
|
@@ -150,6 +150,8 @@ export class Select extends FormAssociatedElement {
|
|
|
150
150
|
valueInternal = "";
|
|
151
151
|
valuesInternal = [];
|
|
152
152
|
selectEl;
|
|
153
|
+
focus(options) { if (!this.disabled)
|
|
154
|
+
this.selectEl?.focus(options); }
|
|
153
155
|
labelEl;
|
|
154
156
|
descriptionEl;
|
|
155
157
|
errorEl;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FormAssociatedElement, boeFormFieldErrorStyles, formErrorMessageMarkup, } from "../../core/index.js";
|
|
2
2
|
import { boeMotionDuration, boeMotionEasing } from "../../foundations/motion/index.js";
|
|
3
|
-
import { boeRadius } from "../../foundations/geometry/index.js";
|
|
3
|
+
import { boeControl, boeRadius } from "../../foundations/geometry/index.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-text-area";
|
|
5
5
|
const textAreaStyles = `
|
|
6
6
|
:host {
|
|
@@ -21,11 +21,9 @@ const textAreaStyles = `
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
[part="label"] {
|
|
24
|
-
font-size:
|
|
24
|
+
font-size: ${boeControl.fontSize};
|
|
25
25
|
font-weight: 700;
|
|
26
|
-
|
|
27
|
-
text-transform: uppercase;
|
|
28
|
-
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
26
|
+
color: var(--boe-token-text-text, #222222);
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
[part="textarea"] {
|
|
@@ -33,7 +31,7 @@ const textAreaStyles = `
|
|
|
33
31
|
font: inherit;
|
|
34
32
|
color: var(--boe-token-text-text, #222222);
|
|
35
33
|
padding: 0.45rem 0.7rem;
|
|
36
|
-
border: 1px solid
|
|
34
|
+
border: 1px solid ${boeControl.inputBorder};
|
|
37
35
|
border-radius: ${boeRadius.control};
|
|
38
36
|
background: var(--boe-token-surface-surface, #ffffff);
|
|
39
37
|
resize: vertical;
|
|
@@ -49,7 +47,7 @@ const textAreaStyles = `
|
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
[part="textarea"]:hover:not(:disabled) {
|
|
52
|
-
border-color:
|
|
50
|
+
border-color: ${boeControl.inputBorder};
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
[part="textarea"]:focus-visible {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
type BoxAccordionItem = {
|
|
3
3
|
content?: string;
|
|
4
|
+
summary?: string;
|
|
4
5
|
label: string;
|
|
5
6
|
value: string;
|
|
6
7
|
};
|
|
@@ -11,6 +12,12 @@ export declare class Accordion extends BaseElement {
|
|
|
11
12
|
get borderless(): boolean;
|
|
12
13
|
set borderless(value: boolean);
|
|
13
14
|
private valueInternal;
|
|
15
|
+
private valuesInternal;
|
|
16
|
+
get multiple(): boolean;
|
|
17
|
+
set multiple(value: boolean);
|
|
18
|
+
get values(): string[];
|
|
19
|
+
set values(value: string[]);
|
|
20
|
+
private isOpen;
|
|
14
21
|
private lastItemsJson;
|
|
15
22
|
private accordionEl;
|
|
16
23
|
get label(): string;
|
|
@@ -66,6 +66,10 @@ const accordionStyles = `
|
|
|
66
66
|
[part="heading"] {
|
|
67
67
|
font-weight: 600;
|
|
68
68
|
}
|
|
69
|
+
[part="summary"] {
|
|
70
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
71
|
+
font-weight: 400;
|
|
72
|
+
}
|
|
69
73
|
|
|
70
74
|
[part="indicator"] {
|
|
71
75
|
inline-size: 1.35rem;
|
|
@@ -103,7 +107,7 @@ const accordionStyles = `
|
|
|
103
107
|
export class Accordion extends BaseElement {
|
|
104
108
|
static tagName = DEFAULT_TAG_NAME;
|
|
105
109
|
static get observedAttributes() {
|
|
106
|
-
return ["items", "label", "value", "borderless"];
|
|
110
|
+
return ["items", "label", "value", "values", "multiple", "borderless"];
|
|
107
111
|
}
|
|
108
112
|
/** Flat variant with no outer card border/background. */
|
|
109
113
|
get borderless() {
|
|
@@ -113,6 +117,12 @@ export class Accordion extends BaseElement {
|
|
|
113
117
|
this.toggleAttribute("borderless", Boolean(value));
|
|
114
118
|
}
|
|
115
119
|
valueInternal = "";
|
|
120
|
+
valuesInternal = [];
|
|
121
|
+
get multiple() { return this.hasAttribute("multiple"); }
|
|
122
|
+
set multiple(value) { this.toggleAttribute("multiple", value); }
|
|
123
|
+
get values() { return [...this.valuesInternal]; }
|
|
124
|
+
set values(value) { this.setAttribute("values", JSON.stringify([...new Set(value)])); }
|
|
125
|
+
isOpen(value) { return this.multiple ? this.valuesInternal.includes(value) : value === this.valueInternal; }
|
|
116
126
|
lastItemsJson = "";
|
|
117
127
|
accordionEl;
|
|
118
128
|
get label() {
|
|
@@ -151,12 +161,21 @@ export class Accordion extends BaseElement {
|
|
|
151
161
|
if (name === "value") {
|
|
152
162
|
this.valueInternal = this.getAttribute("value") ?? "";
|
|
153
163
|
}
|
|
164
|
+
if (name === "values") {
|
|
165
|
+
try {
|
|
166
|
+
const values = JSON.parse(newValue ?? "[]");
|
|
167
|
+
this.valuesInternal = Array.isArray(values) ? [...new Set(values.filter((v) => typeof v === "string"))] : [];
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
this.valuesInternal = [];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
154
173
|
super.attributeChangedCallback(name, oldValue, newValue);
|
|
155
174
|
}
|
|
156
|
-
renderItemsMarkup(items
|
|
175
|
+
renderItemsMarkup(items) {
|
|
157
176
|
return items
|
|
158
177
|
.map(item => {
|
|
159
|
-
const isOpen = item.value
|
|
178
|
+
const isOpen = this.isOpen(item.value);
|
|
160
179
|
const panelId = `panel-${escapeHtml(item.value)}`;
|
|
161
180
|
const triggerId = `trigger-${escapeHtml(item.value)}`;
|
|
162
181
|
return `
|
|
@@ -170,7 +189,7 @@ export class Accordion extends BaseElement {
|
|
|
170
189
|
aria-expanded="${String(isOpen)}"
|
|
171
190
|
aria-controls="${panelId}"
|
|
172
191
|
>
|
|
173
|
-
|
|
192
|
+
<span part="label">${escapeHtml(item.label)}${item.summary ? ` <span part="summary">${escapeHtml(item.summary)}</span>` : ""}</span>
|
|
174
193
|
<span part="indicator" aria-hidden="true">${isOpen ? "−" : "+"}</span>
|
|
175
194
|
</button>
|
|
176
195
|
</h3>
|
|
@@ -206,6 +225,11 @@ export class Accordion extends BaseElement {
|
|
|
206
225
|
if (!nextValue) {
|
|
207
226
|
return;
|
|
208
227
|
}
|
|
228
|
+
if (this.multiple) {
|
|
229
|
+
this.values = this.isOpen(nextValue) ? this.valuesInternal.filter(value => value !== nextValue) : [...this.valuesInternal, nextValue];
|
|
230
|
+
this.dispatchEvent(new CustomEvent("values-changed", { detail: { values: this.values }, bubbles: true, composed: true }));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
209
233
|
if (nextValue === this.valueInternal) {
|
|
210
234
|
this.valueInternal = "";
|
|
211
235
|
this.setAttribute("value", "");
|
|
@@ -233,22 +257,21 @@ export class Accordion extends BaseElement {
|
|
|
233
257
|
}
|
|
234
258
|
const items = this.items;
|
|
235
259
|
const itemsJson = this.getAttribute("items") ?? "";
|
|
236
|
-
if (!this.hasAttribute("value") && items.length > 0 && this.valueInternal === "") {
|
|
260
|
+
if (!this.multiple && !this.hasAttribute("value") && items.length > 0 && this.valueInternal === "") {
|
|
237
261
|
this.valueInternal = items[0].value;
|
|
238
262
|
this.setAttribute("value", this.valueInternal);
|
|
239
263
|
}
|
|
240
|
-
const selectedValue = this.valueInternal;
|
|
241
264
|
this.accordionEl.setAttribute("role", "region");
|
|
242
265
|
this.accordionEl.setAttribute("aria-label", this.label);
|
|
243
266
|
if (itemsJson !== this.lastItemsJson) {
|
|
244
|
-
this.accordionEl.innerHTML = this.renderItemsMarkup(items
|
|
267
|
+
this.accordionEl.innerHTML = this.renderItemsMarkup(items);
|
|
245
268
|
this.lastItemsJson = itemsJson;
|
|
246
269
|
return;
|
|
247
270
|
}
|
|
248
271
|
this.accordionEl.querySelectorAll('[part="item"]').forEach(node => {
|
|
249
272
|
const section = node;
|
|
250
273
|
const value = section.dataset.value ?? "";
|
|
251
|
-
const isOpen = value
|
|
274
|
+
const isOpen = this.isOpen(value);
|
|
252
275
|
section.dataset.open = String(isOpen);
|
|
253
276
|
const trigger = section.querySelector('[part="trigger"]');
|
|
254
277
|
const indicator = section.querySelector('[part="indicator"]');
|
|
@@ -90,6 +90,7 @@ const codeStyles = `
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
${boeFocusVisibleStyles('[part="copy"]')}
|
|
93
|
+
${boeFocusVisibleStyles('[part="pre"]')}
|
|
93
94
|
`;
|
|
94
95
|
/**
|
|
95
96
|
* A block of code, rendered as written.
|
|
@@ -192,7 +193,7 @@ export class CodeBlock extends BaseElement {
|
|
|
192
193
|
this.shadowRoot.innerHTML = `
|
|
193
194
|
<style>${codeStyles}</style>
|
|
194
195
|
<div part="block">
|
|
195
|
-
<pre part="pre"><code part="code"><slot></slot></code></pre>
|
|
196
|
+
<pre part="pre" tabindex="0" role="region"><code part="code"><slot></slot></code></pre>
|
|
196
197
|
<button type="button" part="copy" data-copied="false"></button>
|
|
197
198
|
</div>
|
|
198
199
|
`;
|
|
@@ -221,6 +222,7 @@ export class CodeBlock extends BaseElement {
|
|
|
221
222
|
this.slotEl.textContent = "";
|
|
222
223
|
}
|
|
223
224
|
const language = this.language;
|
|
225
|
+
this.preEl.setAttribute("aria-label", language ? `${language} code` : "Source code");
|
|
224
226
|
if (language) {
|
|
225
227
|
this.codeEl.setAttribute("data-language", language);
|
|
226
228
|
this.codeEl.setAttribute("class", `language-${language}`);
|