@unofficialbox/box-open-elements 0.5.0 → 0.7.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 +27 -73
- package/dist/components/collections/table.d.ts +113 -3
- package/dist/components/collections/table.js +540 -37
- package/dist/components/feedback/due-badge.d.ts +38 -0
- package/dist/components/feedback/due-badge.js +168 -0
- package/dist/components/feedback/due-types.d.ts +32 -0
- package/dist/components/feedback/due-types.js +91 -0
- package/dist/components/feedback/progress-steps.d.ts +51 -5
- package/dist/components/feedback/progress-steps.js +200 -22
- package/dist/components/feedback/stage-path.d.ts +44 -0
- package/dist/components/feedback/stage-path.js +235 -0
- package/dist/components/forms/select.d.ts +21 -0
- package/dist/components/forms/select.js +100 -3
- package/dist/components/forms/text-field.d.ts +21 -0
- package/dist/components/forms/text-field.js +100 -1
- package/dist/components/layout/nav-sidebar.d.ts +14 -0
- package/dist/components/layout/nav-sidebar.js +49 -0
- package/dist/components/layout/split-view.d.ts +14 -0
- package/dist/components/layout/split-view.js +80 -1
- package/dist/components/overlays/command-palette.d.ts +62 -0
- package/dist/components/overlays/command-palette.js +537 -0
- package/dist/components/overlays/command-types.d.ts +90 -0
- package/dist/components/overlays/command-types.js +259 -0
- package/dist/components/overlays/drawer.d.ts +18 -0
- package/dist/components/overlays/drawer.js +135 -9
- package/dist/components/overlays/shortcuts-overlay.d.ts +48 -0
- package/dist/components/overlays/shortcuts-overlay.js +385 -0
- package/dist/components/overlays/tooltip.js +1 -1
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +2 -0
- package/dist/core/offset-window.d.ts +70 -0
- package/dist/core/offset-window.js +134 -0
- package/dist/core/virtualize.d.ts +71 -0
- package/dist/core/virtualize.js +67 -0
- package/dist/element-maps.d.ts +292 -0
- package/dist/element-maps.js +1 -0
- package/dist/entries/activity-density.d.ts +1 -0
- package/dist/entries/activity-density.js +1 -0
- package/dist/entries/agent-chat.d.ts +1 -0
- package/dist/entries/agent-chat.js +1 -0
- package/dist/entries/audit-log.d.ts +1 -0
- package/dist/entries/audit-log.js +1 -0
- package/dist/entries/command-palette.d.ts +1 -0
- package/dist/entries/command-palette.js +1 -0
- package/dist/entries/compare-view.d.ts +1 -0
- package/dist/entries/compare-view.js +1 -0
- package/dist/entries/diff-viewer.d.ts +1 -0
- package/dist/entries/diff-viewer.js +1 -0
- package/dist/entries/due-badge.d.ts +1 -0
- package/dist/entries/due-badge.js +1 -0
- package/dist/entries/form-wizard.d.ts +1 -0
- package/dist/entries/form-wizard.js +1 -0
- package/dist/entries/lineage-graph.d.ts +1 -0
- package/dist/entries/lineage-graph.js +1 -0
- package/dist/entries/notification-bell.d.ts +1 -0
- package/dist/entries/notification-bell.js +1 -0
- package/dist/entries/notification-inbox.d.ts +1 -0
- package/dist/entries/notification-inbox.js +1 -0
- package/dist/entries/provenance-strip.d.ts +1 -0
- package/dist/entries/provenance-strip.js +1 -0
- package/dist/entries/run-trace.d.ts +1 -0
- package/dist/entries/run-trace.js +1 -0
- package/dist/entries/shortcuts-overlay.d.ts +1 -0
- package/dist/entries/shortcuts-overlay.js +1 -0
- package/dist/entries/signature-ceremony.d.ts +1 -0
- package/dist/entries/signature-ceremony.js +1 -0
- package/dist/entries/stage-path.d.ts +1 -0
- package/dist/entries/stage-path.js +1 -0
- package/dist/entries/timeline.d.ts +1 -0
- package/dist/entries/timeline.js +1 -0
- package/dist/entries/version-graph.d.ts +1 -0
- package/dist/entries/version-graph.js +1 -0
- package/dist/entries/version-list.d.ts +1 -0
- package/dist/entries/version-list.js +1 -0
- package/dist/entries/wizard-summary.d.ts +1 -0
- package/dist/entries/wizard-summary.js +1 -0
- package/dist/entries/work-queue.d.ts +1 -0
- package/dist/entries/work-queue.js +1 -0
- package/dist/entries/workload-board.d.ts +1 -0
- package/dist/entries/workload-board.js +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +21 -0
- package/dist/patterns/agent-chat/agent-chat.d.ts +75 -0
- package/dist/patterns/agent-chat/agent-chat.js +820 -0
- package/dist/patterns/agent-chat/controller.d.ts +27 -0
- package/dist/patterns/agent-chat/controller.js +182 -0
- package/dist/patterns/agent-chat/index.d.ts +3 -0
- package/dist/patterns/agent-chat/index.js +6 -0
- package/dist/patterns/agent-chat/types.d.ts +109 -0
- package/dist/patterns/agent-chat/types.js +1 -0
- package/dist/patterns/audit/activity-density.d.ts +68 -0
- package/dist/patterns/audit/activity-density.js +428 -0
- package/dist/patterns/audit/audit-log.d.ts +182 -0
- package/dist/patterns/audit/audit-log.js +1082 -0
- package/dist/patterns/audit/index.d.ts +3 -0
- package/dist/patterns/audit/index.js +7 -0
- package/dist/patterns/audit/shared.d.ts +10 -0
- package/dist/patterns/audit/shared.js +50 -0
- package/dist/patterns/audit/types.d.ts +143 -0
- package/dist/patterns/audit/types.js +327 -0
- package/dist/patterns/audit/window.d.ts +116 -0
- package/dist/patterns/audit/window.js +132 -0
- package/dist/patterns/content-explorer/actions/controller.js +12 -2
- package/dist/patterns/content-explorer/box-transport.js +117 -25
- package/dist/patterns/content-explorer/content-explorer.d.ts +13 -1
- package/dist/patterns/content-explorer/content-explorer.js +36 -3
- package/dist/patterns/content-explorer/contracts.d.ts +24 -1
- package/dist/patterns/content-explorer/contracts.js +68 -11
- package/dist/patterns/content-explorer/controller.d.ts +16 -1
- package/dist/patterns/content-explorer/controller.js +130 -4
- package/dist/patterns/content-explorer/navigation/controller.js +11 -9
- package/dist/patterns/content-explorer/types.d.ts +58 -1
- package/dist/patterns/content-picker/content-picker.d.ts +71 -0
- package/dist/patterns/content-picker/content-picker.js +747 -0
- package/dist/patterns/content-picker/controller.d.ts +38 -0
- package/dist/patterns/content-picker/controller.js +141 -0
- package/dist/patterns/content-picker/index.d.ts +3 -0
- package/dist/patterns/content-picker/index.js +6 -0
- package/dist/patterns/content-picker/types.d.ts +52 -0
- package/dist/patterns/content-picker/types.js +26 -0
- package/dist/patterns/content-sidebar/content-sidebar.d.ts +43 -0
- package/dist/patterns/content-sidebar/content-sidebar.js +293 -0
- package/dist/patterns/content-sidebar/index.d.ts +2 -0
- package/dist/patterns/content-sidebar/index.js +4 -0
- package/dist/patterns/content-sidebar/types.d.ts +18 -0
- package/dist/patterns/content-sidebar/types.js +20 -0
- package/dist/patterns/content-uploader/box-transport.d.ts +14 -0
- package/dist/patterns/content-uploader/box-transport.js +54 -0
- package/dist/patterns/content-uploader/content-uploader.d.ts +62 -0
- package/dist/patterns/content-uploader/content-uploader.js +559 -0
- package/dist/patterns/content-uploader/controller.d.ts +39 -0
- package/dist/patterns/content-uploader/controller.js +227 -0
- package/dist/patterns/content-uploader/index.d.ts +4 -0
- package/dist/patterns/content-uploader/index.js +8 -0
- package/dist/patterns/content-uploader/types.d.ts +120 -0
- package/dist/patterns/content-uploader/types.js +38 -0
- package/dist/patterns/diff/compare-view.d.ts +47 -0
- package/dist/patterns/diff/compare-view.js +276 -0
- package/dist/patterns/diff/diff-viewer.d.ts +42 -0
- package/dist/patterns/diff/diff-viewer.js +434 -0
- package/dist/patterns/diff/engine.d.ts +22 -0
- package/dist/patterns/diff/engine.js +276 -0
- package/dist/patterns/diff/index.d.ts +4 -0
- package/dist/patterns/diff/index.js +8 -0
- package/dist/patterns/diff/types.d.ts +69 -0
- package/dist/patterns/diff/types.js +24 -0
- package/dist/patterns/form-wizard/controller.d.ts +39 -0
- package/dist/patterns/form-wizard/controller.js +157 -0
- package/dist/patterns/form-wizard/form-wizard.d.ts +63 -0
- package/dist/patterns/form-wizard/form-wizard.js +468 -0
- package/dist/patterns/form-wizard/index.d.ts +4 -0
- package/dist/patterns/form-wizard/index.js +8 -0
- package/dist/patterns/form-wizard/types.d.ts +109 -0
- package/dist/patterns/form-wizard/types.js +83 -0
- package/dist/patterns/form-wizard/wizard-summary.d.ts +62 -0
- package/dist/patterns/form-wizard/wizard-summary.js +324 -0
- package/dist/patterns/internal/safe-href.d.ts +17 -0
- package/dist/patterns/internal/safe-href.js +17 -0
- package/dist/patterns/lineage/index.d.ts +3 -0
- package/dist/patterns/lineage/index.js +6 -0
- package/dist/patterns/lineage/lineage-graph.d.ts +30 -0
- package/dist/patterns/lineage/lineage-graph.js +434 -0
- package/dist/patterns/lineage/provenance-strip.d.ts +22 -0
- package/dist/patterns/lineage/provenance-strip.js +194 -0
- package/dist/patterns/lineage/types.d.ts +53 -0
- package/dist/patterns/lineage/types.js +49 -0
- package/dist/patterns/notifications/index.d.ts +3 -0
- package/dist/patterns/notifications/index.js +6 -0
- package/dist/patterns/notifications/notification-bell.d.ts +38 -0
- package/dist/patterns/notifications/notification-bell.js +198 -0
- package/dist/patterns/notifications/notification-inbox.d.ts +46 -0
- package/dist/patterns/notifications/notification-inbox.js +537 -0
- package/dist/patterns/notifications/types.d.ts +61 -0
- package/dist/patterns/notifications/types.js +108 -0
- package/dist/patterns/preview/annotation-inspector.d.ts +25 -6
- package/dist/patterns/preview/annotation-inspector.js +239 -94
- package/dist/patterns/preview/annotation-thread.d.ts +25 -5
- package/dist/patterns/preview/annotation-thread.js +233 -82
- package/dist/patterns/preview/annotation-toolbar.d.ts +20 -6
- package/dist/patterns/preview/annotation-toolbar.js +162 -96
- package/dist/patterns/preview/content-preview-adapter.d.ts +10 -1
- package/dist/patterns/preview/content-preview-adapter.js +26 -1
- package/dist/patterns/preview/preview-element.d.ts +15 -2
- package/dist/patterns/preview/preview-element.js +223 -52
- package/dist/patterns/preview/provider-adapter.d.ts +47 -0
- package/dist/patterns/preview/provider-adapter.js +17 -0
- package/dist/patterns/run/index.d.ts +2 -0
- package/dist/patterns/run/index.js +4 -0
- package/dist/patterns/run/run-trace.d.ts +40 -0
- package/dist/patterns/run/run-trace.js +441 -0
- package/dist/patterns/run/types.d.ts +66 -0
- package/dist/patterns/run/types.js +152 -0
- package/dist/patterns/signature/index.d.ts +2 -0
- package/dist/patterns/signature/index.js +4 -0
- package/dist/patterns/signature/signature-ceremony.d.ts +33 -0
- package/dist/patterns/signature/signature-ceremony.js +317 -0
- package/dist/patterns/signature/types.d.ts +68 -0
- package/dist/patterns/signature/types.js +89 -0
- package/dist/patterns/timeline/index.d.ts +2 -0
- package/dist/patterns/timeline/index.js +4 -0
- package/dist/patterns/timeline/timeline.d.ts +27 -0
- package/dist/patterns/timeline/timeline.js +430 -0
- package/dist/patterns/timeline/types.d.ts +36 -0
- package/dist/patterns/timeline/types.js +27 -0
- package/dist/patterns/versions/graph-layout.d.ts +43 -0
- package/dist/patterns/versions/graph-layout.js +159 -0
- package/dist/patterns/versions/index.d.ts +4 -0
- package/dist/patterns/versions/index.js +8 -0
- package/dist/patterns/versions/shared.d.ts +4 -0
- package/dist/patterns/versions/shared.js +13 -0
- package/dist/patterns/versions/types.d.ts +37 -0
- package/dist/patterns/versions/types.js +39 -0
- package/dist/patterns/versions/version-graph.d.ts +36 -0
- package/dist/patterns/versions/version-graph.js +438 -0
- package/dist/patterns/versions/version-list.d.ts +39 -0
- package/dist/patterns/versions/version-list.js +407 -0
- package/dist/patterns/work-queue/controller.d.ts +26 -0
- package/dist/patterns/work-queue/controller.js +150 -0
- package/dist/patterns/work-queue/index.d.ts +4 -0
- package/dist/patterns/work-queue/index.js +7 -0
- package/dist/patterns/work-queue/types.d.ts +128 -0
- package/dist/patterns/work-queue/types.js +45 -0
- package/dist/patterns/work-queue/work-queue.d.ts +48 -0
- package/dist/patterns/work-queue/work-queue.js +534 -0
- package/dist/patterns/work-queue/workload-board.d.ts +53 -0
- package/dist/patterns/work-queue/workload-board.js +608 -0
- package/package.json +89 -5
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification model and the pure grouping/counting engine behind
|
|
3
|
+
* `box-notification-inbox`. DOM-free, so a host can drive its own inbox —
|
|
4
|
+
* or a bell in a different shell — from the same functions.
|
|
5
|
+
*/
|
|
6
|
+
const TONES = new Set(["neutral", "brand", "success", "warning", "error"]);
|
|
7
|
+
export const resolveNotificationTone = (value) => value && TONES.has(value) ? value : "neutral";
|
|
8
|
+
export const resolveNotificationFilter = (value) => value === "unread" ? "unread" : "all";
|
|
9
|
+
/** Unread count — what the bell shows, and the only number that must be exact. */
|
|
10
|
+
export const countUnreadNotifications = (items) => items.reduce((total, item) => total + (item.read ? 0 : 1), 0);
|
|
11
|
+
const instant = (item) => {
|
|
12
|
+
if (!item.timestamp) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const value = new Date(item.timestamp).getTime();
|
|
16
|
+
return Number.isNaN(value) ? null : value;
|
|
17
|
+
};
|
|
18
|
+
/** Newest first; undated items sink without reordering among themselves. */
|
|
19
|
+
const byNewestFirst = (left, right) => {
|
|
20
|
+
const a = instant(left);
|
|
21
|
+
const b = instant(right);
|
|
22
|
+
if (a === b) {
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
if (a === null) {
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
if (b === null) {
|
|
29
|
+
return -1;
|
|
30
|
+
}
|
|
31
|
+
return b - a;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Turn a `type` slug into a section heading: `sla-breach` → `Sla breach`. A
|
|
35
|
+
* host wanting different wording supplies its own labels through `typeLabels`.
|
|
36
|
+
*/
|
|
37
|
+
const humanize = (value) => {
|
|
38
|
+
const spaced = value.replaceAll(/[-_]+/g, " ").trim();
|
|
39
|
+
return spaced ? spaced.charAt(0).toUpperCase() + spaced.slice(1) : value;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Group notifications into sections by type.
|
|
43
|
+
*
|
|
44
|
+
* Sections lead with the most unread, then the most items, then the label —
|
|
45
|
+
* so what needs attention rises, and the order never depends on input order.
|
|
46
|
+
* Items inside a section run newest first, with unread ahead of read at the
|
|
47
|
+
* same instant: this is a triage surface, not a feed.
|
|
48
|
+
*/
|
|
49
|
+
export const groupNotifications = (items, options = {}) => {
|
|
50
|
+
const filter = options.filter ?? "all";
|
|
51
|
+
const labels = options.typeLabels ?? {};
|
|
52
|
+
const visible = filter === "unread" ? items.filter(item => !item.read) : items;
|
|
53
|
+
const groups = new Map();
|
|
54
|
+
for (const item of visible) {
|
|
55
|
+
let group = groups.get(item.type);
|
|
56
|
+
if (!group) {
|
|
57
|
+
group = { key: item.type, label: labels[item.type] ?? humanize(item.type), items: [], unread: 0 };
|
|
58
|
+
groups.set(item.type, group);
|
|
59
|
+
}
|
|
60
|
+
group.items.push(item);
|
|
61
|
+
if (!item.read) {
|
|
62
|
+
group.unread += 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const group of groups.values()) {
|
|
66
|
+
group.items.sort((left, right) => {
|
|
67
|
+
if (Boolean(left.read) !== Boolean(right.read)) {
|
|
68
|
+
return left.read ? 1 : -1;
|
|
69
|
+
}
|
|
70
|
+
return byNewestFirst(left, right);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return [...groups.values()].sort((left, right) => {
|
|
74
|
+
if (left.unread !== right.unread) {
|
|
75
|
+
return right.unread - left.unread;
|
|
76
|
+
}
|
|
77
|
+
if (left.items.length !== right.items.length) {
|
|
78
|
+
return right.items.length - left.items.length;
|
|
79
|
+
}
|
|
80
|
+
return left.label < right.label ? -1 : left.label > right.label ? 1 : 0;
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
/** Attribute payloads are author input — validate every record. */
|
|
84
|
+
export const isNotificationItemRecord = (value) => {
|
|
85
|
+
if (typeof value !== "object" || value === null) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const item = value;
|
|
89
|
+
if (typeof item.id !== "string" || item.id.length === 0) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (typeof item.title !== "string" || item.title.length === 0) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (typeof item.type !== "string" || item.type.length === 0) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (item.entityRef !== undefined) {
|
|
99
|
+
const entity = item.entityRef;
|
|
100
|
+
if (typeof entity !== "object" ||
|
|
101
|
+
entity === null ||
|
|
102
|
+
typeof entity.id !== "string" ||
|
|
103
|
+
typeof entity.label !== "string") {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
};
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
|
-
type AnnotationInspectorAction = {
|
|
2
|
+
export type AnnotationInspectorAction = {
|
|
3
3
|
id: string;
|
|
4
4
|
label: string;
|
|
5
5
|
tone?: string;
|
|
6
6
|
};
|
|
7
|
-
type AnnotationInspectorReply = {
|
|
7
|
+
export type AnnotationInspectorReply = {
|
|
8
8
|
author: string;
|
|
9
9
|
body: string;
|
|
10
|
+
createdAt?: string;
|
|
11
|
+
id?: string;
|
|
10
12
|
initials?: string;
|
|
11
13
|
};
|
|
12
|
-
type AnnotationInspectorAnnotation = {
|
|
14
|
+
export type AnnotationInspectorAnnotation = {
|
|
13
15
|
author: string;
|
|
14
16
|
body: string;
|
|
15
17
|
color?: string;
|
|
@@ -22,23 +24,40 @@ type AnnotationInspectorAnnotation = {
|
|
|
22
24
|
subject?: string;
|
|
23
25
|
toolLabel?: string;
|
|
24
26
|
};
|
|
27
|
+
export type AnnotationInspectorReplySubmittedDetail = {
|
|
28
|
+
annotationId: string | null;
|
|
29
|
+
body: string;
|
|
30
|
+
};
|
|
25
31
|
export declare class AnnotationInspector extends BaseElement {
|
|
26
32
|
static readonly tagName: string;
|
|
27
33
|
static get observedAttributes(): string[];
|
|
34
|
+
private titleEl;
|
|
35
|
+
private messageEl;
|
|
36
|
+
private detailHostEl;
|
|
37
|
+
private actionsEl;
|
|
38
|
+
private composerEl;
|
|
39
|
+
private composerInputEl;
|
|
40
|
+
private annotationSignature;
|
|
41
|
+
private actionsSignature;
|
|
28
42
|
get actions(): AnnotationInspectorAction[];
|
|
29
43
|
set actions(value: AnnotationInspectorAction[]);
|
|
30
44
|
get annotation(): AnnotationInspectorAnnotation | null;
|
|
31
45
|
set annotation(value: AnnotationInspectorAnnotation | null);
|
|
46
|
+
get composable(): boolean;
|
|
47
|
+
set composable(value: boolean);
|
|
32
48
|
get message(): string;
|
|
33
49
|
set message(value: string);
|
|
34
50
|
get heading(): string;
|
|
35
51
|
set heading(value: string);
|
|
36
|
-
connectedCallback(): void;
|
|
37
|
-
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
38
52
|
private parseJsonAttribute;
|
|
39
53
|
private emitAction;
|
|
40
54
|
private emitReplySelected;
|
|
55
|
+
private submitComposer;
|
|
56
|
+
private actionsKey;
|
|
57
|
+
private rebuildDetail;
|
|
58
|
+
private rebuildActions;
|
|
59
|
+
private patchActionLabels;
|
|
41
60
|
protected renderTemplate(): void;
|
|
61
|
+
protected setupListeners(): void;
|
|
42
62
|
protected update(): void;
|
|
43
63
|
}
|
|
44
|
-
export {};
|
|
@@ -8,7 +8,14 @@ const escapeHtml = (value) => value
|
|
|
8
8
|
.replaceAll(">", ">")
|
|
9
9
|
.replaceAll('"', """)
|
|
10
10
|
.replaceAll("'", "'");
|
|
11
|
+
const isSafeColorValue = (value) => /^#[0-9a-fA-F]{3,8}$/.test(value) || /^[a-zA-Z]+$/.test(value);
|
|
11
12
|
const elementStyles = `
|
|
13
|
+
/* Author display rules on parts would otherwise defeat the UA's
|
|
14
|
+
[hidden] rule — state toggling relies on the hidden attribute. */
|
|
15
|
+
[hidden] {
|
|
16
|
+
display: none !important;
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
:host {
|
|
13
20
|
display: block;
|
|
14
21
|
color: inherit;
|
|
@@ -42,6 +49,11 @@ const elementStyles = `
|
|
|
42
49
|
line-height: 1.5;
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
[part="detail-host"] {
|
|
53
|
+
display: grid;
|
|
54
|
+
gap: ${boePanel.gap};
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
[part="annotation"] {
|
|
46
58
|
display: grid;
|
|
47
59
|
gap: ${boePanel.gap};
|
|
@@ -160,6 +172,11 @@ const elementStyles = `
|
|
|
160
172
|
line-height: 1.45;
|
|
161
173
|
}
|
|
162
174
|
|
|
175
|
+
[part="reply-time"] {
|
|
176
|
+
color: color-mix(in srgb, var(--boe-token-text-text-secondary, #6f6f6f) 86%, transparent);
|
|
177
|
+
font-size: 0.78rem;
|
|
178
|
+
}
|
|
179
|
+
|
|
163
180
|
[part="actions"] {
|
|
164
181
|
display: flex;
|
|
165
182
|
flex-wrap: wrap;
|
|
@@ -185,9 +202,48 @@ const elementStyles = `
|
|
|
185
202
|
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
186
203
|
}
|
|
187
204
|
|
|
205
|
+
[part="composer"] {
|
|
206
|
+
display: grid;
|
|
207
|
+
gap: 0.5rem;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
[part="composer-label"] {
|
|
211
|
+
font-size: 0.74rem;
|
|
212
|
+
font-weight: 700;
|
|
213
|
+
letter-spacing: 0.08em;
|
|
214
|
+
text-transform: uppercase;
|
|
215
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
[part="composer-input"] {
|
|
219
|
+
min-height: 4rem;
|
|
220
|
+
padding: 0.55rem;
|
|
221
|
+
border: 1px solid color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 60%, transparent);
|
|
222
|
+
border-radius: ${boePanel.radius};
|
|
223
|
+
background: var(--boe-token-surface-surface, #ffffff);
|
|
224
|
+
color: inherit;
|
|
225
|
+
font: inherit;
|
|
226
|
+
line-height: 1.5;
|
|
227
|
+
resize: vertical;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
[part="composer-submit"] {
|
|
231
|
+
appearance: none;
|
|
232
|
+
justify-self: start;
|
|
233
|
+
min-height: 2rem;
|
|
234
|
+
padding: 0.4rem 0.7rem;
|
|
235
|
+
border: 1px solid transparent;
|
|
236
|
+
border-radius: 999px;
|
|
237
|
+
background: var(--boe-token-surface-surface-brand, #0061d5);
|
|
238
|
+
color: var(--boe-token-text-text-on-brand, #ffffff);
|
|
239
|
+
font: inherit;
|
|
240
|
+
cursor: pointer;
|
|
241
|
+
}
|
|
242
|
+
|
|
188
243
|
${boeNeutralInteractiveStyles('[part="reply"]')}
|
|
189
244
|
${boeNeutralInteractiveStyles('[part="action"]')}
|
|
190
245
|
${boeBrandInteractiveStyles('[part="action"][data-tone="primary"]')}
|
|
246
|
+
${boeBrandInteractiveStyles('[part="composer-submit"]')}
|
|
191
247
|
|
|
192
248
|
[part="action"][data-tone="primary"] {
|
|
193
249
|
border-color: transparent;
|
|
@@ -198,8 +254,16 @@ const elementStyles = `
|
|
|
198
254
|
export class AnnotationInspector extends BaseElement {
|
|
199
255
|
static tagName = DEFAULT_TAG_NAME;
|
|
200
256
|
static get observedAttributes() {
|
|
201
|
-
return ["actions", "annotation", "heading", "message"];
|
|
257
|
+
return ["actions", "annotation", "composable", "heading", "message"];
|
|
202
258
|
}
|
|
259
|
+
titleEl;
|
|
260
|
+
messageEl;
|
|
261
|
+
detailHostEl;
|
|
262
|
+
actionsEl;
|
|
263
|
+
composerEl;
|
|
264
|
+
composerInputEl;
|
|
265
|
+
annotationSignature = null;
|
|
266
|
+
actionsSignature = "";
|
|
203
267
|
get actions() {
|
|
204
268
|
return this.parseJsonAttribute("actions", []);
|
|
205
269
|
}
|
|
@@ -216,6 +280,12 @@ export class AnnotationInspector extends BaseElement {
|
|
|
216
280
|
}
|
|
217
281
|
this.setAttribute("annotation", JSON.stringify(value));
|
|
218
282
|
}
|
|
283
|
+
get composable() {
|
|
284
|
+
return this.hasAttribute("composable");
|
|
285
|
+
}
|
|
286
|
+
set composable(value) {
|
|
287
|
+
this.toggleAttribute("composable", value);
|
|
288
|
+
}
|
|
219
289
|
get message() {
|
|
220
290
|
return this.getAttribute("message") ?? "";
|
|
221
291
|
}
|
|
@@ -232,12 +302,6 @@ export class AnnotationInspector extends BaseElement {
|
|
|
232
302
|
set heading(value) {
|
|
233
303
|
this.setAttribute("heading", value);
|
|
234
304
|
}
|
|
235
|
-
connectedCallback() {
|
|
236
|
-
super.connectedCallback();
|
|
237
|
-
}
|
|
238
|
-
attributeChangedCallback(name, oldValue, newValue) {
|
|
239
|
-
super.attributeChangedCallback(name, oldValue, newValue);
|
|
240
|
-
}
|
|
241
305
|
parseJsonAttribute(name, fallback) {
|
|
242
306
|
const raw = this.getAttribute(name);
|
|
243
307
|
if (!raw) {
|
|
@@ -264,111 +328,192 @@ export class AnnotationInspector extends BaseElement {
|
|
|
264
328
|
detail: { ...reply, index, annotationId: this.annotation?.id ?? null },
|
|
265
329
|
}));
|
|
266
330
|
}
|
|
267
|
-
|
|
268
|
-
|
|
331
|
+
submitComposer() {
|
|
332
|
+
const body = this.composerInputEl.value.trim();
|
|
333
|
+
if (!this.composable || !body) {
|
|
269
334
|
return;
|
|
270
335
|
}
|
|
271
|
-
this.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
336
|
+
this.dispatchEvent(new CustomEvent("reply-submitted", {
|
|
337
|
+
bubbles: true,
|
|
338
|
+
composed: true,
|
|
339
|
+
detail: {
|
|
340
|
+
annotationId: this.annotation?.id ?? null,
|
|
341
|
+
body,
|
|
342
|
+
},
|
|
343
|
+
}));
|
|
344
|
+
this.composerInputEl.value = "";
|
|
275
345
|
}
|
|
276
|
-
|
|
277
|
-
|
|
346
|
+
actionsKey() {
|
|
347
|
+
return JSON.stringify(this.actions.map(action => action.id));
|
|
348
|
+
}
|
|
349
|
+
rebuildDetail() {
|
|
350
|
+
const annotation = this.annotation;
|
|
351
|
+
if (!annotation) {
|
|
352
|
+
this.detailHostEl.innerHTML = `<div part="empty">No annotation selected.</div>`;
|
|
278
353
|
return;
|
|
279
354
|
}
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
const actionsMarkup = this.actions.length
|
|
283
|
-
? `
|
|
284
|
-
<div part="actions">
|
|
285
|
-
${this.actions
|
|
286
|
-
.map(action => `
|
|
287
|
-
<button
|
|
288
|
-
type="button"
|
|
289
|
-
part="action"
|
|
290
|
-
data-action-id="${escapeHtml(action.id)}"
|
|
291
|
-
data-tone="${escapeHtml(action.tone ?? "neutral")}"
|
|
292
|
-
>
|
|
293
|
-
${escapeHtml(action.label)}
|
|
294
|
-
</button>
|
|
295
|
-
`)
|
|
296
|
-
.join("")}
|
|
297
|
-
</div>
|
|
298
|
-
`
|
|
355
|
+
const colorChipMarkup = annotation.color && isSafeColorValue(annotation.color)
|
|
356
|
+
? `<span part="color-chip" style="--annotation-color:${escapeHtml(annotation.color)};"></span>`
|
|
299
357
|
: "";
|
|
300
|
-
|
|
358
|
+
this.detailHostEl.innerHTML = `
|
|
359
|
+
<section part="annotation">
|
|
360
|
+
<div part="annotation-header">
|
|
361
|
+
<div part="avatar">${escapeHtml(annotation.initials ?? annotation.author.slice(0, 2).toUpperCase())}</div>
|
|
362
|
+
<div part="author-copy">
|
|
363
|
+
<div part="author">${escapeHtml(annotation.author)}</div>
|
|
364
|
+
<div part="meta">
|
|
365
|
+
${annotation.toolLabel ? `<span part="tool">${escapeHtml(annotation.toolLabel)}</span>` : ""}
|
|
366
|
+
${annotation.status ? `<span part="status">${escapeHtml(annotation.status)}</span>` : ""}
|
|
367
|
+
${annotation.pageLabel ? `<span part="page">${escapeHtml(annotation.pageLabel)}</span>` : ""}
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
${colorChipMarkup}
|
|
371
|
+
</div>
|
|
372
|
+
${annotation.subject ? `<div part="subject">${escapeHtml(annotation.subject)}</div>` : ""}
|
|
373
|
+
<div part="body">${escapeHtml(annotation.body)}</div>
|
|
374
|
+
${annotation.createdAt ? `<div part="timestamp">${escapeHtml(annotation.createdAt)}</div>` : ""}
|
|
375
|
+
${annotation.replies?.length
|
|
301
376
|
? `
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
377
|
+
<div part="replies">
|
|
378
|
+
<div part="section-title">Replies</div>
|
|
379
|
+
<div part="reply-list">
|
|
380
|
+
${annotation.replies
|
|
381
|
+
.map((reply, index) => `
|
|
382
|
+
<button type="button" part="reply" data-reply-index="${String(index)}">
|
|
383
|
+
<span part="reply-avatar">${escapeHtml(reply.initials ?? reply.author.slice(0, 2).toUpperCase())}</span>
|
|
384
|
+
<span part="reply-copy">
|
|
385
|
+
<span part="reply-author">${escapeHtml(reply.author)}</span>
|
|
386
|
+
<span part="reply-body">${escapeHtml(reply.body)}</span>
|
|
387
|
+
${reply.createdAt ? `<span part="reply-time">${escapeHtml(reply.createdAt)}</span>` : ""}
|
|
388
|
+
</span>
|
|
389
|
+
</button>
|
|
390
|
+
`)
|
|
391
|
+
.join("")}
|
|
311
392
|
</div>
|
|
312
393
|
</div>
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
394
|
+
`
|
|
395
|
+
: ""}
|
|
396
|
+
</section>
|
|
397
|
+
`;
|
|
398
|
+
}
|
|
399
|
+
rebuildActions() {
|
|
400
|
+
this.actionsEl.innerHTML = this.actions
|
|
401
|
+
.map(action => `
|
|
402
|
+
<button
|
|
403
|
+
type="button"
|
|
404
|
+
part="action"
|
|
405
|
+
data-action-id="${escapeHtml(action.id)}"
|
|
406
|
+
data-tone="${escapeHtml(action.tone ?? "neutral")}"
|
|
407
|
+
>
|
|
408
|
+
${escapeHtml(action.label)}
|
|
409
|
+
</button>
|
|
410
|
+
`)
|
|
411
|
+
.join("");
|
|
412
|
+
}
|
|
413
|
+
patchActionLabels() {
|
|
414
|
+
// Action IDs are arbitrary strings (quotes, newlines, …) — match on the
|
|
415
|
+
// dataset instead of interpolating them into a CSS selector.
|
|
416
|
+
const buttons = Array.from(this.actionsEl.querySelectorAll('[part="action"]'));
|
|
417
|
+
this.actions.forEach(action => {
|
|
418
|
+
const button = buttons.find(candidate => candidate.dataset.actionId === action.id);
|
|
419
|
+
if (!button) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
button.textContent = action.label;
|
|
423
|
+
button.dataset.tone = action.tone ?? "neutral";
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
renderTemplate() {
|
|
427
|
+
if (!this.shadowRoot) {
|
|
343
428
|
return;
|
|
344
429
|
}
|
|
345
|
-
|
|
430
|
+
this.shadowRoot.innerHTML = `
|
|
431
|
+
<style>${elementStyles}</style>
|
|
346
432
|
<article part="panel">
|
|
347
433
|
<header part="header">
|
|
348
|
-
<h2 part="title"
|
|
349
|
-
|
|
434
|
+
<h2 part="title"></h2>
|
|
435
|
+
<div part="message" hidden></div>
|
|
350
436
|
</header>
|
|
351
|
-
|
|
352
|
-
|
|
437
|
+
<div part="detail-host"></div>
|
|
438
|
+
<div part="actions" hidden></div>
|
|
439
|
+
<div part="composer" hidden>
|
|
440
|
+
<label part="composer-label" for="annotation-inspector-composer-input">Reply</label>
|
|
441
|
+
<textarea
|
|
442
|
+
part="composer-input"
|
|
443
|
+
id="annotation-inspector-composer-input"
|
|
444
|
+
placeholder="Add a reply"
|
|
445
|
+
></textarea>
|
|
446
|
+
<button type="button" part="composer-submit">Reply</button>
|
|
447
|
+
</div>
|
|
353
448
|
</article>
|
|
354
449
|
`;
|
|
355
|
-
this.shadowRoot.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
450
|
+
this.titleEl = this.shadowRoot.querySelector('[part="title"]');
|
|
451
|
+
this.messageEl = this.shadowRoot.querySelector('[part="message"]');
|
|
452
|
+
this.detailHostEl = this.shadowRoot.querySelector('[part="detail-host"]');
|
|
453
|
+
this.actionsEl = this.shadowRoot.querySelector('[part="actions"]');
|
|
454
|
+
this.composerEl = this.shadowRoot.querySelector('[part="composer"]');
|
|
455
|
+
this.composerInputEl = this.shadowRoot.querySelector('[part="composer-input"]');
|
|
456
|
+
}
|
|
457
|
+
setupListeners() {
|
|
458
|
+
this.actionsEl.addEventListener("click", event => {
|
|
459
|
+
const button = event.target.closest('[part="action"]');
|
|
460
|
+
if (!button || !this.actionsEl.contains(button)) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const actionId = button.getAttribute("data-action-id");
|
|
464
|
+
if (actionId) {
|
|
465
|
+
this.emitAction(actionId);
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
this.detailHostEl.addEventListener("click", event => {
|
|
469
|
+
const button = event.target.closest('[part="reply"]');
|
|
470
|
+
if (!button || !this.detailHostEl.contains(button)) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const index = Number(button.getAttribute("data-reply-index"));
|
|
474
|
+
const reply = this.annotation?.replies?.[index];
|
|
475
|
+
if (reply) {
|
|
476
|
+
this.emitReplySelected(reply, index);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
this.composerEl.addEventListener("click", event => {
|
|
480
|
+
const button = event.target.closest('[part="composer-submit"]');
|
|
481
|
+
if (button) {
|
|
482
|
+
this.submitComposer();
|
|
483
|
+
}
|
|
362
484
|
});
|
|
363
|
-
this.
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
this.emitReplySelected(reply, index);
|
|
369
|
-
}
|
|
370
|
-
});
|
|
485
|
+
this.composerInputEl.addEventListener("keydown", event => {
|
|
486
|
+
if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
|
|
487
|
+
event.preventDefault();
|
|
488
|
+
this.submitComposer();
|
|
489
|
+
}
|
|
371
490
|
});
|
|
372
491
|
}
|
|
492
|
+
update() {
|
|
493
|
+
if (!this.detailHostEl) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
this.titleEl.textContent = this.heading;
|
|
497
|
+
this.messageEl.hidden = !this.message;
|
|
498
|
+
this.messageEl.textContent = this.message;
|
|
499
|
+
const nextAnnotation = this.getAttribute("annotation");
|
|
500
|
+
if (nextAnnotation !== this.annotationSignature || this.detailHostEl.childElementCount === 0) {
|
|
501
|
+
this.annotationSignature = nextAnnotation;
|
|
502
|
+
this.rebuildDetail();
|
|
503
|
+
}
|
|
504
|
+
const actions = this.actions;
|
|
505
|
+
this.actionsEl.hidden = actions.length === 0;
|
|
506
|
+
const nextActions = this.actionsKey();
|
|
507
|
+
if (nextActions !== this.actionsSignature || this.actionsEl.childElementCount === 0) {
|
|
508
|
+
this.actionsSignature = nextActions;
|
|
509
|
+
this.rebuildActions();
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
this.patchActionLabels();
|
|
513
|
+
}
|
|
514
|
+
const composable = this.composable;
|
|
515
|
+
this.composerEl.hidden = !composable;
|
|
516
|
+
this.composerInputEl.disabled = !composable;
|
|
517
|
+
}
|
|
373
518
|
}
|
|
374
519
|
AnnotationInspector.register();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { BaseElement } from "../../core/index.js";
|
|
2
|
-
type AnnotationThreadAction = {
|
|
2
|
+
export type AnnotationThreadAction = {
|
|
3
3
|
id: string;
|
|
4
4
|
label: string;
|
|
5
5
|
tone?: string;
|
|
6
6
|
};
|
|
7
|
-
type AnnotationThreadEntry = {
|
|
7
|
+
export type AnnotationThreadEntry = {
|
|
8
8
|
author: string;
|
|
9
9
|
body: string;
|
|
10
10
|
createdAt?: string;
|
|
@@ -13,11 +13,26 @@ type AnnotationThreadEntry = {
|
|
|
13
13
|
status?: string;
|
|
14
14
|
toolLabel?: string;
|
|
15
15
|
};
|
|
16
|
+
export type AnnotationThreadEntrySubmittedDetail = {
|
|
17
|
+
body: string;
|
|
18
|
+
inReplyToId: string | null;
|
|
19
|
+
};
|
|
16
20
|
export declare class AnnotationThread extends BaseElement {
|
|
17
21
|
static readonly tagName: string;
|
|
18
22
|
static get observedAttributes(): string[];
|
|
23
|
+
private titleEl;
|
|
24
|
+
private messageEl;
|
|
25
|
+
private entriesEl;
|
|
26
|
+
private emptyEl;
|
|
27
|
+
private actionsEl;
|
|
28
|
+
private composerEl;
|
|
29
|
+
private composerInputEl;
|
|
30
|
+
private entriesSignature;
|
|
31
|
+
private actionsSignature;
|
|
19
32
|
get actions(): AnnotationThreadAction[];
|
|
20
33
|
set actions(value: AnnotationThreadAction[]);
|
|
34
|
+
get composable(): boolean;
|
|
35
|
+
set composable(value: boolean);
|
|
21
36
|
get entries(): AnnotationThreadEntry[];
|
|
22
37
|
set entries(value: AnnotationThreadEntry[]);
|
|
23
38
|
get message(): string;
|
|
@@ -26,12 +41,17 @@ export declare class AnnotationThread extends BaseElement {
|
|
|
26
41
|
set selectedEntryId(value: string);
|
|
27
42
|
get heading(): string;
|
|
28
43
|
set heading(value: string);
|
|
29
|
-
connectedCallback(): void;
|
|
30
|
-
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
31
44
|
private parseJsonAttribute;
|
|
32
45
|
private emitAction;
|
|
33
46
|
private emitEntrySelected;
|
|
47
|
+
private submitComposer;
|
|
48
|
+
private entriesKey;
|
|
49
|
+
private actionsKey;
|
|
50
|
+
private rebuildEntries;
|
|
51
|
+
private patchEntrySelection;
|
|
52
|
+
private rebuildActions;
|
|
53
|
+
private patchActionLabels;
|
|
34
54
|
protected renderTemplate(): void;
|
|
55
|
+
protected setupListeners(): void;
|
|
35
56
|
protected update(): void;
|
|
36
57
|
}
|
|
37
|
-
export {};
|