@unofficialbox/box-open-elements 0.21.0 → 0.23.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 -12
- package/dist/components/actions/menu-item.js +1 -1
- package/dist/components/actions/menu.js +1 -1
- package/dist/components/collections/carousel.js +1 -1
- package/dist/components/collections/draggable-list.js +1 -1
- package/dist/components/collections/grid-view.js +1 -1
- package/dist/components/collections/pagination.js +1 -1
- package/dist/components/collections/result-blocks.d.ts +13 -1
- package/dist/components/collections/result-blocks.js +22 -10
- package/dist/components/collections/tree-grid.js +1 -1
- package/dist/components/feedback/alert.js +1 -1
- package/dist/components/feedback/chip.js +1 -1
- package/dist/components/feedback/empty-state.js +1 -1
- package/dist/components/feedback/nudge.js +1 -1
- package/dist/components/feedback/status-icon.d.ts +2 -0
- package/dist/components/feedback/status-icon.js +6 -5
- package/dist/components/feedback/toast.js +1 -1
- package/dist/components/files/drop-zone.js +1 -1
- package/dist/components/forms/category-selector.js +1 -1
- package/dist/components/forms/checkbox-group.js +1 -1
- package/dist/components/forms/checkbox.js +1 -1
- package/dist/components/forms/combobox.js +1 -1
- package/dist/components/forms/date-field.js +1 -1
- package/dist/components/forms/dropdown.js +1 -1
- package/dist/components/forms/multi-select.js +1 -1
- package/dist/components/forms/number-input.js +1 -1
- package/dist/components/forms/radio-group.js +1 -1
- package/dist/components/forms/range-slider.js +1 -1
- package/dist/components/forms/slider.js +1 -1
- package/dist/components/forms/tile-group.js +1 -1
- package/dist/components/layout/sidebar-toggle-button.js +1 -1
- package/dist/components/navigation/accordion.js +1 -1
- package/dist/components/navigation/tabs.js +1 -1
- package/dist/components/output/code-block.js +1 -1
- package/dist/components/overlays/dialog.js +1 -1
- package/dist/components/overlays/drawer.js +1 -1
- package/dist/components/overlays/guide-tooltip.js +1 -1
- package/dist/components/overlays/popover.js +1 -1
- package/dist/components/overlays/tooltip.js +1 -1
- package/dist/foundations/motion/scroll-pin.d.ts +10 -2
- package/dist/foundations/motion/scroll-pin.js +43 -7
- package/dist/foundations/motion/tokens.d.ts +1 -0
- package/dist/foundations/motion/tokens.js +1 -0
- package/dist/foundations/profiles/built-ins.js +8 -0
- package/dist/foundations/profiles/registry.d.ts +1 -0
- package/dist/foundations/profiles/registry.js +1 -0
- package/dist/foundations/profiles/types.d.ts +1 -0
- package/dist/foundations/status/index.d.ts +4 -2
- package/dist/foundations/status/index.js +10 -6
- package/dist/foundations/theming/controller.js +3 -1
- package/dist/foundations/tokens/box-defaults.d.ts +2 -0
- package/dist/foundations/tokens/box-defaults.js +32 -5
- package/dist/foundations/tokens/index.d.ts +1 -1
- package/dist/foundations/tokens/index.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/patterns/agent-chat/agent-chat.js +9 -10
- package/dist/patterns/agent-chat/controller.js +8 -4
- package/dist/patterns/agent-chat/ndjson.d.ts +1 -0
- package/dist/patterns/agent-chat/ndjson.js +20 -4
- package/dist/patterns/agent-chat/types.d.ts +7 -0
- package/dist/patterns/agent-workspace/agent-workspace.d.ts +13 -1
- package/dist/patterns/agent-workspace/agent-workspace.js +47 -15
- package/dist/patterns/agent-workspace/controller.d.ts +7 -1
- package/dist/patterns/agent-workspace/controller.js +41 -3
- package/dist/patterns/call-console/call-console.d.ts +10 -0
- package/dist/patterns/call-console/call-console.js +23 -8
- package/dist/patterns/call-console/controller.d.ts +2 -1
- package/dist/patterns/call-console/controller.js +5 -3
- package/dist/patterns/call-console/http-transport.js +13 -2
- package/dist/patterns/call-console/types.d.ts +1 -1
- package/dist/patterns/call-console/types.js +2 -2
- package/dist/patterns/comments/comment-thread.js +1 -1
- package/dist/patterns/content-explorer/adapters/action-menu.js +1 -1
- package/dist/patterns/content-explorer/adapters/list.js +1 -1
- package/dist/patterns/content-explorer/adapters/table.js +1 -1
- package/dist/patterns/content-explorer/adapters/toolbar.js +1 -1
- package/dist/patterns/content-uploader/content-uploader.js +1 -1
- package/dist/patterns/file-request/file-request-builder.js +1 -1
- package/dist/patterns/flow-builder/flow-builder.d.ts +45 -14
- package/dist/patterns/flow-builder/flow-builder.js +54 -11
- package/dist/patterns/flow-builder/model.d.ts +57 -9
- package/dist/patterns/flow-builder/model.js +20 -8
- package/dist/patterns/flow-builder/primitives.d.ts +17 -7
- package/dist/patterns/flow-builder/primitives.js +80 -16
- package/dist/patterns/governance/governance-panel.js +1 -1
- package/dist/patterns/insights/chart-panel.js +1 -1
- package/dist/patterns/insights/metric-card.js +1 -1
- package/dist/patterns/item/bulk-action-bar.js +1 -1
- package/dist/patterns/item/item-details-panel.js +1 -1
- package/dist/patterns/item/item-form.js +1 -1
- package/dist/patterns/item/preview-header.js +1 -1
- package/dist/patterns/metadata/metadata-filter-builder.js +1 -1
- package/dist/patterns/metadata/metadata-inspector.js +1 -1
- package/dist/patterns/preview/annotation-inspector.js +1 -1
- package/dist/patterns/preview/annotation-toolbar.js +1 -1
- package/dist/patterns/run/progress.d.ts +1 -1
- package/dist/patterns/run/progress.js +1 -1
- package/dist/patterns/run/run-summary.js +18 -4
- package/dist/patterns/run/run-trace.js +19 -5
- package/dist/patterns/search/filter-bar.js +1 -1
- package/dist/patterns/search/saved-view-picker.js +1 -1
- package/dist/patterns/search/search-results-header.js +1 -1
- package/dist/patterns/share/invite-collaborators-modal.js +1 -1
- package/dist/patterns/share/permission-matrix.js +1 -1
- package/dist/patterns/share/share-panel.js +1 -1
- package/dist/patterns/share/unified-share-modal.js +1 -1
- package/dist/patterns/task/review-queue-item.js +1 -1
- package/dist/patterns/task/task-assignment-panel.js +1 -1
- package/dist/patterns/undo/index.js +5 -3
- package/docs/api-guidelines.md +284 -0
- package/docs/foundations/motion.md +83 -0
- package/docs/patterns/agent-ui-guidelines.md +57 -0
- package/docs/patterns/agent-ui.md +197 -0
- package/package.json +6 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { applyRovingTabindex, handleRovingKeydown } from "../../foundations/a11y/index.js";
|
|
3
|
-
import { boeNeutralInteractiveStyles } from "../../foundations/tokens/
|
|
3
|
+
import { boeNeutralInteractiveStyles } from "../../foundations/tokens/interaction.js";
|
|
4
4
|
import { boeMotionDuration, boeMotionEasing } from "../../foundations/motion/index.js";
|
|
5
5
|
import { boePanel } from "../../foundations/geometry/index.js";
|
|
6
6
|
const DEFAULT_TAG_NAME = "box-bulk-action-bar";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
|
|
3
|
-
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/
|
|
3
|
+
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/interaction.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-item-details-panel";
|
|
5
5
|
const escapeHtml = (value) => value
|
|
6
6
|
.replaceAll("&", "&")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
|
|
3
|
-
import { boeBrandInteractiveStyles, boeFocusVisibleStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/
|
|
3
|
+
import { boeBrandInteractiveStyles, boeFocusVisibleStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/interaction.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-item-form";
|
|
5
5
|
const escapeHtml = (value) => value
|
|
6
6
|
.replaceAll("&", "&")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { boePanel } from "../../foundations/geometry/index.js";
|
|
3
|
-
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/
|
|
3
|
+
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/interaction.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-preview-header";
|
|
5
5
|
const escapeHtml = (value) => value
|
|
6
6
|
.replaceAll("&", "&")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
|
-
import { boeNeutralInteractiveStyles } from "../../foundations/tokens/
|
|
2
|
+
import { boeNeutralInteractiveStyles } from "../../foundations/tokens/interaction.js";
|
|
3
3
|
import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-metadata-filter-builder";
|
|
5
5
|
const escapeHtml = (value) => value
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
|
-
import { boeNeutralInteractiveStyles } from "../../foundations/tokens/
|
|
2
|
+
import { boeNeutralInteractiveStyles } from "../../foundations/tokens/interaction.js";
|
|
3
3
|
import { boePanel } from "../../foundations/geometry/index.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-metadata-inspector";
|
|
5
5
|
const escapeHtml = (value) => value
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
|
|
3
|
-
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/
|
|
3
|
+
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/interaction.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-annotation-inspector";
|
|
5
5
|
const escapeHtml = (value) => value
|
|
6
6
|
.replaceAll("&", "&")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { applyRovingTabindex, handleRovingKeydown } from "../../foundations/a11y/index.js";
|
|
3
3
|
import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
|
|
4
|
-
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/
|
|
4
|
+
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/interaction.js";
|
|
5
5
|
const DEFAULT_TAG_NAME = "box-annotation-toolbar";
|
|
6
6
|
const escapeHtml = (value) => value
|
|
7
7
|
.replaceAll("&", "&")
|
|
@@ -10,7 +10,7 @@ export interface RunTurn {
|
|
|
10
10
|
todos: AgentTodo[];
|
|
11
11
|
startedAt: number;
|
|
12
12
|
endedAt?: number;
|
|
13
|
-
incomplete?: boolean;
|
|
13
|
+
incomplete?: boolean | string;
|
|
14
14
|
}
|
|
15
15
|
export declare function formatElapsed(ms: number): string;
|
|
16
16
|
export declare function splitStepTitle(title: string): {
|
|
@@ -26,7 +26,7 @@ export function progress(turn, failed = false, now = Date.now(), threshold = 200
|
|
|
26
26
|
if (failed || resolved.status === "failed")
|
|
27
27
|
return { kind: "failed", label: `Stopped after ${took}` };
|
|
28
28
|
if (turn.incomplete)
|
|
29
|
-
return { kind: "warning", label:
|
|
29
|
+
return { kind: "warning", label: `${typeof turn.incomplete === "string" ? turn.incomplete : "Cut short"} after ${took}` };
|
|
30
30
|
const warnings = resolved.steps.filter(s => toStatusKind(s.status) === "warning").length;
|
|
31
31
|
return { kind: warnings ? "warning" : "done", label: `Worked for ${took}${warnings ? ` · ${warnings} warning${warnings === 1 ? "" : "s"}` : ""}` };
|
|
32
32
|
}
|
|
@@ -31,12 +31,14 @@ export class RunSummary extends BaseElement {
|
|
|
31
31
|
StatusIcon.register();
|
|
32
32
|
RunTrace.register();
|
|
33
33
|
this.shadowRoot.innerHTML = `<style>:host{display:block;font:inherit;color:var(--boe-token-text-text-secondary,#666)}:host([hidden]){display:none !important}
|
|
34
|
-
button{font:inherit;color:inherit;background:none;border:0;
|
|
34
|
+
button,[part="static-line"]{font:inherit;color:inherit;background:none;border:0;padding:.5rem 0;display:flex;gap:.5rem;align-items:center}button{cursor:pointer}
|
|
35
35
|
[part="clock"]{font-variant-numeric:tabular-nums} ul{padding:0;list-style:none} li{display:flex;gap:.5rem;margin:.5rem 0}
|
|
36
36
|
@keyframes boe-shimmer{50%{opacity:.6}} [data-running] [part="label"]{animation:boe-shimmer 1.4s ease-in-out infinite}
|
|
37
|
+
[hidden]{display:none!important}[part="chevron"]{display:inline-block;transition:transform var(--boe-profile-motion-panel,320ms) ease} :host([open]) [part="chevron"]{transform:rotate(90deg)}
|
|
37
38
|
${boeDisclosureStyles()}${boeEntranceKeyframes}${boeReducedMotionPolicy}</style>
|
|
38
|
-
<button part="trigger" aria-expanded="false" aria-controls="details"><box-status-icon></box-status-icon><span part="label"
|
|
39
|
-
<div
|
|
39
|
+
<button part="trigger" aria-expanded="false" aria-controls="details"><box-status-icon></box-status-icon><span part="label"></span><span part="clock" aria-hidden="true"></span><span part="chevron" aria-hidden="true">›</span></button>
|
|
40
|
+
<div part="static-line" hidden><box-status-icon></box-status-icon><span part="static-label"></span><span part="static-clock" aria-hidden="true"></span></div>
|
|
41
|
+
<div id="details" part="disclosure" class="boe-disclosure" inert><div><ul part="plan"></ul><box-run-trace variant="plain" exportparts="panel:trace-panel,step:trace-step,step-source:trace-step-source,step-title:trace-step-title,duration:trace-duration"></box-run-trace></div></div>`;
|
|
40
42
|
}
|
|
41
43
|
setupListeners() { this.shadowRoot.querySelector("button").addEventListener("click", () => { this.open = !this.open; }); }
|
|
42
44
|
updateLabel() {
|
|
@@ -45,15 +47,27 @@ export class RunSummary extends BaseElement {
|
|
|
45
47
|
if (label.textContent !== p.label)
|
|
46
48
|
label.textContent = p.label;
|
|
47
49
|
this.shadowRoot.querySelector('[part="clock"]').textContent = p.elapsed ?? "";
|
|
48
|
-
this.shadowRoot.querySelector("
|
|
50
|
+
this.shadowRoot.querySelector('[part="static-label"]').textContent = p.label;
|
|
51
|
+
this.shadowRoot.querySelector('[part="static-clock"]').textContent = p.elapsed ?? "";
|
|
52
|
+
this.shadowRoot.querySelectorAll("box-status-icon").forEach(icon => { icon.kind = p.kind; icon.hidden = p.kind === "done"; });
|
|
49
53
|
this.shadowRoot.querySelector("button").toggleAttribute("data-running", p.kind === "active");
|
|
50
54
|
this.shadowRoot.querySelector("button").setAttribute("aria-label", p.label);
|
|
55
|
+
const staticLabel = this.shadowRoot.querySelector('[part="static-label"]');
|
|
56
|
+
for (const target of [label, staticLabel]) {
|
|
57
|
+
if (p.kind === "active")
|
|
58
|
+
target.setAttribute("role", "status");
|
|
59
|
+
else
|
|
60
|
+
target.removeAttribute("role");
|
|
61
|
+
}
|
|
51
62
|
}
|
|
52
63
|
update() {
|
|
53
64
|
if (!this.isRendered)
|
|
54
65
|
return;
|
|
55
66
|
this.updateLabel();
|
|
56
67
|
this.syncTimer();
|
|
68
|
+
const hasDetails = this.value.steps.length > 0 || this.value.todos.length > 0;
|
|
69
|
+
this.shadowRoot.querySelector("button").hidden = !hasDetails;
|
|
70
|
+
this.shadowRoot.querySelector('[part="static-line"]').hidden = hasDetails;
|
|
57
71
|
this.shadowRoot.querySelector("button").setAttribute("aria-expanded", String(this.open));
|
|
58
72
|
const disclosure = this.shadowRoot.querySelector("#details");
|
|
59
73
|
disclosure.toggleAttribute("data-open", this.open);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatRunSummary, isRunStepRecord, resolveRunSteps, } from "./types.js";
|
|
2
|
+
import { formatElapsed, splitStepTitle } from "./progress.js";
|
|
2
3
|
import { ProgressBar } from "../../components/feedback/progress-bar.js";
|
|
3
4
|
import { BaseElement } from "../../core/index.js";
|
|
4
5
|
import { boePanel } from "../../foundations/geometry/index.js";
|
|
@@ -250,6 +251,15 @@ const elementStyles = `
|
|
|
250
251
|
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
251
252
|
}
|
|
252
253
|
|
|
254
|
+
:host([variant="plain"]) [part="panel"] { border: 0; border-radius: 0; background: none; padding: 0; }
|
|
255
|
+
:host([variant="plain"]) [part="header"],
|
|
256
|
+
:host([variant="plain"]) [part="summary"] { display: none; }
|
|
257
|
+
:host([variant="plain"]) [part="step"] { padding-block: 0.35rem; }
|
|
258
|
+
:host([variant="plain"]) [part="step"]::after { display: none; }
|
|
259
|
+
:host([variant="plain"]) [part="status"] { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
260
|
+
:host([variant="plain"]) [part="toggle"] { display: none; }
|
|
261
|
+
[part="step-source"] { font-weight: 600; color: var(--boe-token-text-text-secondary, #6f6f6f); }
|
|
262
|
+
|
|
253
263
|
.boe-sr-only {
|
|
254
264
|
position: absolute;
|
|
255
265
|
width: 1px;
|
|
@@ -281,7 +291,7 @@ const elementStyles = `
|
|
|
281
291
|
export class RunTrace extends BaseElement {
|
|
282
292
|
static tagName = DEFAULT_TAG_NAME;
|
|
283
293
|
static get observedAttributes() {
|
|
284
|
-
return ["heading", "steps"];
|
|
294
|
+
return ["heading", "steps", "variant"];
|
|
285
295
|
}
|
|
286
296
|
hostEl;
|
|
287
297
|
stepsRaw = null;
|
|
@@ -403,12 +413,15 @@ export class RunTrace extends BaseElement {
|
|
|
403
413
|
.map(entry => {
|
|
404
414
|
const { step, status, position } = entry;
|
|
405
415
|
const settled = this.previousStatuses.get(step.id) === status;
|
|
406
|
-
const
|
|
416
|
+
const start = step.startedAt ? Date.parse(step.startedAt) : NaN;
|
|
417
|
+
const finish = step.finishedAt ? Date.parse(step.finishedAt) : NaN;
|
|
418
|
+
const duration = Number.isFinite(start) && Number.isFinite(finish) ? formatElapsed(finish - start) : "";
|
|
419
|
+
const title = splitStepTitle(step.title);
|
|
407
420
|
// Slot-only detail counts: a host projecting logs for a step with no
|
|
408
421
|
// JSON description still needs the toggle that reveals them.
|
|
409
422
|
const hasSlotted = this.querySelector(`[slot="detail-${cssAttrValue(step.id)}"]`) !== null;
|
|
410
423
|
const hasDetail = Boolean(step.description) || Boolean(step.children?.length) || hasSlotted;
|
|
411
|
-
const expanded = this.expandedIds.has(step.id);
|
|
424
|
+
const expanded = this.hasAttribute("variant") && this.getAttribute("variant") === "plain" || this.expandedIds.has(step.id);
|
|
412
425
|
// The detail slot renders even without JSON detail so a host can
|
|
413
426
|
// project rich content (logs, links) for any step by id.
|
|
414
427
|
const detail = `
|
|
@@ -423,7 +436,8 @@ export class RunTrace extends BaseElement {
|
|
|
423
436
|
<span part="marker" aria-hidden="true" ${settled ? "data-settled" : ""}>${boeStatusGlyph(toStatusKind(status))}</span>
|
|
424
437
|
<div part="step-body">
|
|
425
438
|
<div part="topline">
|
|
426
|
-
|
|
439
|
+
${title.source ? `<span part="step-source">${escapeHtml(title.source)}</span><span aria-hidden="true">·</span>` : ""}
|
|
440
|
+
<span part="step-title">${escapeHtml(title.action)}</span>
|
|
427
441
|
<span part="status">${RUN_STEP_STATUS_LABEL[status]}</span>
|
|
428
442
|
<span class="boe-sr-only">, step ${String(position)} of ${String(resolution.total)}</span>
|
|
429
443
|
${duration ? `<span part="duration">${duration}</span>` : ""}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
|
-
import { boeNeutralInteractiveStyles } from "../../foundations/tokens/
|
|
2
|
+
import { boeNeutralInteractiveStyles } from "../../foundations/tokens/interaction.js";
|
|
3
3
|
import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-filter-bar";
|
|
5
5
|
const escapeHtml = (value) => value
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { applyRovingTabindex, handleRovingKeydown } from "../../foundations/a11y/index.js";
|
|
3
|
-
import { boeNeutralInteractiveStyles } from "../../foundations/tokens/
|
|
3
|
+
import { boeNeutralInteractiveStyles } from "../../foundations/tokens/interaction.js";
|
|
4
4
|
import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
|
|
5
5
|
const DEFAULT_TAG_NAME = "box-saved-view-picker";
|
|
6
6
|
const escapeHtml = (value) => value
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { boePanel } from "../../foundations/geometry/index.js";
|
|
3
|
-
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/
|
|
3
|
+
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/interaction.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-search-results-header";
|
|
5
5
|
const escapeHtml = (value) => value
|
|
6
6
|
.replaceAll("&", "&")
|
|
@@ -2,7 +2,7 @@ import { InviteCollaboratorsController } from "./invite-collaborators-controller
|
|
|
2
2
|
import { BaseElement } from "../../core/index.js";
|
|
3
3
|
import { FocusRestore, trapTabKey } from "../../foundations/a11y/index.js";
|
|
4
4
|
import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
|
|
5
|
-
import { boeBrandInteractiveStyles, boeFocusVisibleStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/
|
|
5
|
+
import { boeBrandInteractiveStyles, boeFocusVisibleStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/interaction.js";
|
|
6
6
|
const DEFAULT_TAG_NAME = "box-invite-collaborators-modal";
|
|
7
7
|
const elementStyles = `
|
|
8
8
|
:host { display: contents; color: inherit; font: inherit; }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
|
-
import { boeFocusRingShadow } from "../../foundations/tokens/
|
|
2
|
+
import { boeFocusRingShadow } from "../../foundations/tokens/interaction.js";
|
|
3
3
|
import { boeMotionDuration, boeMotionEasing } from "../../foundations/motion/index.js";
|
|
4
4
|
import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
|
|
5
5
|
const DEFAULT_TAG_NAME = "box-permission-matrix";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
|
|
3
|
-
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/
|
|
3
|
+
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/interaction.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-share-panel";
|
|
5
5
|
const escapeHtml = (value) => value
|
|
6
6
|
.replaceAll("&", "&")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { UnifiedShareController } from "./unified-share-controller.js";
|
|
2
2
|
import { BaseElement } from "../../core/index.js";
|
|
3
3
|
import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
|
|
4
|
-
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/
|
|
4
|
+
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/interaction.js";
|
|
5
5
|
const DEFAULT_TAG_NAME = "box-unified-share-modal";
|
|
6
6
|
const escapeHtml = (value) => value
|
|
7
7
|
.replaceAll("&", "&")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { boePanel } from "../../foundations/geometry/index.js";
|
|
3
|
-
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/
|
|
3
|
+
import { boeBrandInteractiveStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/interaction.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-review-queue-item";
|
|
5
5
|
const escapeHtml = (value) => value
|
|
6
6
|
.replaceAll("&", "&")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
2
|
import { boePanel } from "../../foundations/geometry/index.js";
|
|
3
|
-
import { boeBrandInteractiveStyles, boeFocusVisibleStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/
|
|
3
|
+
import { boeBrandInteractiveStyles, boeFocusVisibleStyles, boeNeutralInteractiveStyles, } from "../../foundations/tokens/interaction.js";
|
|
4
4
|
const DEFAULT_TAG_NAME = "box-task-assignment-panel";
|
|
5
5
|
const escapeHtml = (value) => value
|
|
6
6
|
.replaceAll("&", "&")
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Toast } from "../../components/feedback/toast.js";
|
|
2
|
+
import { Button } from "../../components/actions/button.js";
|
|
2
3
|
import { announce } from "../../foundations/a11y/index.js";
|
|
3
4
|
/** Remove once and return an idempotent restore preserving the original object's identity. */
|
|
4
5
|
export function removeAt(list, index) {
|
|
@@ -55,10 +56,11 @@ export function offerUndo(message, restore, options = {}) {
|
|
|
55
56
|
return { toast: null, undo() { }, dispose() { } };
|
|
56
57
|
offers.get(doc)?.dispose();
|
|
57
58
|
const toast = doc.createElement(Toast.tagName);
|
|
58
|
-
const action = doc.createElement(
|
|
59
|
-
action.
|
|
59
|
+
const action = doc.createElement(Button.tagName);
|
|
60
|
+
action.tone = "neutral";
|
|
61
|
+
action.size = "small";
|
|
62
|
+
action.label = "Undo";
|
|
60
63
|
action.slot = "action";
|
|
61
|
-
action.textContent = "Undo";
|
|
62
64
|
const mac = /Mac|iPhone|iPad/.test(doc.defaultView?.navigator.platform ?? "");
|
|
63
65
|
const shortcut = mac ? "Command-Z" : "Control-Z";
|
|
64
66
|
action.setAttribute("aria-keyshortcuts", mac ? "Meta+Z" : "Control+Z");
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Component API Guidelines
|
|
2
|
+
|
|
3
|
+
This document defines the low-opinionated API vocabulary to converge on across the component and pattern surface.
|
|
4
|
+
|
|
5
|
+
It is intentionally small. The goal is not to force every component into the same shape, but to make common concepts feel predictable. A human should be able to skim one component and guess another. An AI coding assistant should be able to infer the next likely property name, event name, and data shape without extra prompting.
|
|
6
|
+
|
|
7
|
+
## Taxonomy layers
|
|
8
|
+
|
|
9
|
+
The system has three tiers (see [taxonomy.md](./taxonomy.md)):
|
|
10
|
+
|
|
11
|
+
- `Foundations` — design decisions as data: tokens, iconography, theming
|
|
12
|
+
- `Components` — single controls or small focused surfaces
|
|
13
|
+
- `Patterns` — assembled task surfaces and orchestrated workflow shells
|
|
14
|
+
|
|
15
|
+
API guidance in this document applies to components and patterns; compositional freedom should increase as surfaces move up the stack.
|
|
16
|
+
|
|
17
|
+
## Design goals
|
|
18
|
+
|
|
19
|
+
The API should optimize for:
|
|
20
|
+
|
|
21
|
+
- predictable naming across components
|
|
22
|
+
- plain object schemas that are easy to generate and inspect
|
|
23
|
+
- event payloads that mirror component state closely
|
|
24
|
+
- clean public names at port time; compatibility aliases only when an already-published contract requires them
|
|
25
|
+
- simple escape hatches over large config surfaces
|
|
26
|
+
|
|
27
|
+
## Import contract
|
|
28
|
+
|
|
29
|
+
The package import surface should stay predictable as the catalog grows:
|
|
30
|
+
|
|
31
|
+
- `@unofficialbox/box-open-elements`
|
|
32
|
+
- concise PascalCase exports and automatic registration for the full catalog
|
|
33
|
+
- `@unofficialbox/box-open-elements/core`
|
|
34
|
+
- shared runtime (event emitter, controller base)
|
|
35
|
+
- `@unofficialbox/box-open-elements/foundations/<module>`
|
|
36
|
+
- design-token registry, token bundles, iconography
|
|
37
|
+
- `@unofficialbox/box-open-elements/<name>`
|
|
38
|
+
- optimized component entrypoint that registers only that component
|
|
39
|
+
- `@unofficialbox/box-open-elements/patterns/<area>` and `@unofficialbox/box-open-elements/patterns/<area>/<module>`
|
|
40
|
+
- workflow areas: headless controllers, contracts, and composed surfaces
|
|
41
|
+
|
|
42
|
+
Examples:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { Accordion, Avatar, Button, Switch } from "@unofficialbox/box-open-elements";
|
|
46
|
+
import { TextField } from "@unofficialbox/box-open-elements/text-field";
|
|
47
|
+
import { applyDesignTokens } from "@unofficialbox/box-open-elements/foundations/tokens";
|
|
48
|
+
import { ExplorerSelectionController } from "@unofficialbox/box-open-elements/patterns/content-explorer/selection";
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Guidance:
|
|
52
|
+
|
|
53
|
+
- component classes use concise PascalCase names without `Box`, `Element`, or
|
|
54
|
+
`Component`
|
|
55
|
+
- importing a component registers its `box-*` custom element automatically
|
|
56
|
+
- the root entrypoint is convenient but may load the full catalog; use flat
|
|
57
|
+
component entrypoints in bundle-sensitive applications
|
|
58
|
+
- every component exposes idempotent `register()` for isolated registries and
|
|
59
|
+
test realms; normal consumers should not need to call it
|
|
60
|
+
- keep module-scope registration SSR-safe
|
|
61
|
+
- reserve explicit alias exports for compatibility cases where a public name intentionally differs from the file name
|
|
62
|
+
- keep the category structure synchronized with [components/catalog.md](./components/catalog.md) and [patterns/catalog.md](./patterns/catalog.md)
|
|
63
|
+
|
|
64
|
+
## Baseline attributes
|
|
65
|
+
|
|
66
|
+
Use these names when the concept applies:
|
|
67
|
+
|
|
68
|
+
- `label` — primary accessible/display label for controls and compact components
|
|
69
|
+
- `value` — current selected or entered value for stateful components
|
|
70
|
+
- `name` — form field name for form-associated controls (`FormAssociatedElement`)
|
|
71
|
+
- `disabled` — disables interaction when the component is interactive
|
|
72
|
+
- `invalid` — marks a form control as invalid (sets `aria-invalid` and error chrome)
|
|
73
|
+
- `error-message` — validation message shown in `part="error-message"` when `invalid`
|
|
74
|
+
- `open` — visibility state for overlays and transient UI
|
|
75
|
+
- `tone` — visual semantic variant such as `primary`, `warning`, or `success`
|
|
76
|
+
- `size` — visual size when the component has a non-content-based footprint
|
|
77
|
+
- `heading` — short headline for content or status surfaces (named `heading`, not
|
|
78
|
+
`title`, to avoid colliding with the native `HTMLElement.title` tooltip property)
|
|
79
|
+
- `message` — supporting descriptive copy
|
|
80
|
+
- `description` — longer supporting copy when `message` would be ambiguous or too short
|
|
81
|
+
- `items` — structured collection data for rich hierarchical or menu-like inputs
|
|
82
|
+
- `options` — simpler choice data for flat selection controls
|
|
83
|
+
|
|
84
|
+
Form-associated controls extend `FormAssociatedElement` and submit through `ElementInternals`:
|
|
85
|
+
|
|
86
|
+
- Everyday string/boolean/number fields: `text-field`, `text-area`, `select`, `combobox`,
|
|
87
|
+
`search-field`, `checkbox`, `switch`, `radio-group`, `number-input`, `spin-button`,
|
|
88
|
+
`slider`, `date-field`, `time-field`, `dropdown`, `rating`, `color-picker`,
|
|
89
|
+
`rich-text-input`
|
|
90
|
+
- Multi-value fields submit `FormData` via `formDataFromNamedValues` (one entry per selected
|
|
91
|
+
value under `name`, or under `"value"` when `name` is empty): `checkbox-group`,
|
|
92
|
+
`multi-select`, `dual-listbox`, `tag-input`, `pill-cloud`, `pill-selector-dropdown`
|
|
93
|
+
- Range pairs submit `FormData` via `formDataFromRange` (`${name}-start` / `${name}-end`,
|
|
94
|
+
or `range-start` / `range-end` when `name` is empty): `range-slider`
|
|
95
|
+
|
|
96
|
+
Boolean controls also accept `value` (default `"on"`) as the submitted string when checked.
|
|
97
|
+
Empty multi-selections omit the control (`null`).
|
|
98
|
+
|
|
99
|
+
**Empty-name fallback keys.** When `name` is omitted, multi-value controls submit each
|
|
100
|
+
selected entry under `"value"`; range controls use `"range"` as the base for
|
|
101
|
+
`${base}-start` / `${base}-end` (that is, `range-start` and `range-end`).
|
|
102
|
+
|
|
103
|
+
## Naming preferences
|
|
104
|
+
|
|
105
|
+
- Prefer `label` for controls.
|
|
106
|
+
- Prefer `heading` plus `message` for status and empty-state surfaces.
|
|
107
|
+
- Prefer `description` for longer secondary copy.
|
|
108
|
+
- Prefer `items` for rich objects and `options` for flat choices.
|
|
109
|
+
- Prefer `value-changed` for selection/input updates.
|
|
110
|
+
- Prefer `open-changed` for overlay visibility changes.
|
|
111
|
+
- Prefer `ratio-changed` for resizable split panes (`detail: { ratio }` on `split-view`).
|
|
112
|
+
- During predecessor ports, apply the preferred vocabulary directly instead of carrying old aliases forward. For an already-published API, document the migration and add an alias only when compatibility is an explicit requirement.
|
|
113
|
+
|
|
114
|
+
## Shared event contract
|
|
115
|
+
|
|
116
|
+
Custom events should:
|
|
117
|
+
|
|
118
|
+
- bubble
|
|
119
|
+
- be composed
|
|
120
|
+
- use kebab-case names
|
|
121
|
+
- include a small `detail` payload
|
|
122
|
+
- keep the payload shape close to the component state shape
|
|
123
|
+
|
|
124
|
+
Recommended shared state-change events:
|
|
125
|
+
|
|
126
|
+
- `value-changed`
|
|
127
|
+
- `open-changed`
|
|
128
|
+
- `ratio-changed` (`detail: { ratio }` for `split-view`)
|
|
129
|
+
- `checked-changed`
|
|
130
|
+
- `selected-changed`
|
|
131
|
+
|
|
132
|
+
Recommended semantic intent events:
|
|
133
|
+
|
|
134
|
+
- `action`
|
|
135
|
+
- `confirm`
|
|
136
|
+
- `cancel`
|
|
137
|
+
- `dismiss`
|
|
138
|
+
- `search`
|
|
139
|
+
- `clear`
|
|
140
|
+
- `item-selected`
|
|
141
|
+
- `item-invoked`
|
|
142
|
+
|
|
143
|
+
Event payload guidance:
|
|
144
|
+
|
|
145
|
+
- `value-changed` should usually emit `{ value }`
|
|
146
|
+
- `open-changed` should usually emit `{ open }`
|
|
147
|
+
- `checked-changed` should usually emit `{ checked }`
|
|
148
|
+
- semantic events should include only the minimum context needed to act
|
|
149
|
+
|
|
150
|
+
Use native browser events such as `click`, `focus`, `blur`, `input`, and `change` when a custom wrapper would not add clarity.
|
|
151
|
+
|
|
152
|
+
## Shared schema contract
|
|
153
|
+
|
|
154
|
+
Prefer plain serializable objects with stable keys. Avoid clever builder APIs or multiple parallel shapes for the same concept.
|
|
155
|
+
|
|
156
|
+
Recommended `option` shape for flat choices:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
type Option = {
|
|
160
|
+
id: string;
|
|
161
|
+
label: string;
|
|
162
|
+
value?: string;
|
|
163
|
+
disabled?: boolean;
|
|
164
|
+
description?: string;
|
|
165
|
+
icon?: string;
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Recommended `item` shape for richer collections:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
type Item = {
|
|
173
|
+
id: string;
|
|
174
|
+
label?: string;
|
|
175
|
+
name?: string;
|
|
176
|
+
type?: string;
|
|
177
|
+
disabled?: boolean;
|
|
178
|
+
description?: string;
|
|
179
|
+
children?: Item[];
|
|
180
|
+
};
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Recommended event detail examples:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
type ValueChangedDetail = { value: string | string[] };
|
|
187
|
+
type OpenChangedDetail = { open: boolean };
|
|
188
|
+
type CheckedChangedDetail = { checked: boolean };
|
|
189
|
+
type ItemSelectedDetail = { id: string; value?: string; label?: string };
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
These shapes are intentionally boring. That is a feature. They are easy to author by hand and easy for AI coding assistants to infer and transform.
|
|
193
|
+
|
|
194
|
+
## Collection rendering strategies
|
|
195
|
+
|
|
196
|
+
Collection components should stay strategy-agnostic when the underlying data can grow large.
|
|
197
|
+
|
|
198
|
+
Prefer exposing stable collection state such as:
|
|
199
|
+
|
|
200
|
+
- `items`
|
|
201
|
+
- `loading`
|
|
202
|
+
- `pagination`
|
|
203
|
+
- selection state
|
|
204
|
+
- commands such as `reload()` and `loadNextPage()`
|
|
205
|
+
|
|
206
|
+
Then let consumers choose the rendering strategy that fits their scale:
|
|
207
|
+
|
|
208
|
+
- `pagination` — best default when the backend is already page-oriented and users need stable positions, shareable states, or explicit counts
|
|
209
|
+
- `infinite scroll` — best when continuous browsing matters more than explicit page boundaries
|
|
210
|
+
- `windowing` — best when the DOM cost of rendering all visible items would become too high, even if the data is already loaded
|
|
211
|
+
|
|
212
|
+
Guidance:
|
|
213
|
+
|
|
214
|
+
- Do not hard-wire collection components to only one of these strategies unless the component is explicitly specialized.
|
|
215
|
+
- Prefer headless controller state that can power all three.
|
|
216
|
+
- Treat windowing as a rendering concern, not a transport concern.
|
|
217
|
+
- Treat pagination metadata as stable shared state, even if the UI ultimately renders via infinite scroll.
|
|
218
|
+
|
|
219
|
+
## Accessibility
|
|
220
|
+
|
|
221
|
+
Accessibility semantics and keyboard behavior are part of the component contract, not optional polish. The full conventions live in [foundations/accessibility.md](./foundations/accessibility.md).
|
|
222
|
+
|
|
223
|
+
## Render lifecycle
|
|
224
|
+
|
|
225
|
+
Web Components in this package extend `BaseElement` (`@unofficialbox/box-open-elements/core`):
|
|
226
|
+
|
|
227
|
+
- build the shadow tree once in `renderTemplate()`
|
|
228
|
+
- attach listeners once in `setupListeners()`
|
|
229
|
+
- patch state in `update()` — never reassign `shadowRoot.innerHTML` on attribute/property changes
|
|
230
|
+
|
|
231
|
+
See [architecture.md](./architecture.md#web-component-render-contract) for the full contract
|
|
232
|
+
(list-container rebuilds, focused-input value guarding).
|
|
233
|
+
|
|
234
|
+
## Styling hooks
|
|
235
|
+
|
|
236
|
+
Shadow DOM components expose named internal styling targets with the `part` attribute. In docs and the docs site, refer to these as `Styling Hooks`: they let consumers style internal surfaces from outside the component without breaking encapsulation.
|
|
237
|
+
|
|
238
|
+
```html
|
|
239
|
+
<box-button></box-button>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
```css
|
|
243
|
+
box-button::part(button) {
|
|
244
|
+
border-radius: 999px;
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Recommended styling hook names stay structural and reusable:
|
|
249
|
+
|
|
250
|
+
- `label`
|
|
251
|
+
- `message`
|
|
252
|
+
- `title`
|
|
253
|
+
- `description`
|
|
254
|
+
- `trigger`
|
|
255
|
+
- `content`
|
|
256
|
+
- `item`
|
|
257
|
+
- `marker`
|
|
258
|
+
- `header-row`
|
|
259
|
+
- `cell`
|
|
260
|
+
- `empty`
|
|
261
|
+
|
|
262
|
+
Avoid overly presentational names unless the structure is unique to the component.
|
|
263
|
+
|
|
264
|
+
Components should also consume foundation tokens (`--boe-token-*`) with safe fallbacks, so they work with no design system registered and restyle automatically when one is active. Components **consume** tokens; shells and consumers **register / override** — do not invent per-component styling strategies. See [Token consumption vs shell / consumer overrides](./foundations/tokens.md#token-consumption-vs-shell--consumer-overrides).
|
|
265
|
+
|
|
266
|
+
## AI-friendly authoring rules
|
|
267
|
+
|
|
268
|
+
To keep the system easy for AI coding assistants and human developers alike:
|
|
269
|
+
|
|
270
|
+
- prefer one obvious property name for each concept
|
|
271
|
+
- prefer one obvious event name for each state transition
|
|
272
|
+
- keep data shapes serializable and easy to inspect in logs
|
|
273
|
+
- document compatibility aliases when they exist
|
|
274
|
+
- show small copy-pasteable examples in docs and demos
|
|
275
|
+
- avoid requiring imperative setup when property binding is enough
|
|
276
|
+
|
|
277
|
+
## Preview stack direction
|
|
278
|
+
|
|
279
|
+
Preview-related work stays layered:
|
|
280
|
+
|
|
281
|
+
- **Compositions** (patterns/preview) — preview headers, toolbars, sidebars, metadata panels, annotation surfaces around the rendering engine
|
|
282
|
+
- **Workflow shell** (patterns/preview) — a pluggable preview element that hosts the actual preview provider and orchestrates preview state
|
|
283
|
+
|
|
284
|
+
See [patterns/preview.md](./patterns/preview.md) for the provider-adapter contract and annotation-first priorities.
|