@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,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node roles in a provenance chain. Open vocabulary — `clause`, `template`,
|
|
3
|
+
* and `contract` are the CLM conventions, but hosts add their own kinds
|
|
4
|
+
* without a schema change.
|
|
5
|
+
*/
|
|
6
|
+
export type LineageKind = string;
|
|
7
|
+
/** How far a node's text drifted from the parent it derives from. */
|
|
8
|
+
export type LineageDeviation = "none" | "minor" | "major";
|
|
9
|
+
/**
|
|
10
|
+
* One typed derivation link. Unlike the versions pattern (where a parent is
|
|
11
|
+
* just an id), lineage links carry the comparison payload: how severely the
|
|
12
|
+
* child deviates from that parent, and an optional one-line summary.
|
|
13
|
+
*/
|
|
14
|
+
export interface LineageParentLink {
|
|
15
|
+
id: string;
|
|
16
|
+
deviation?: LineageDeviation;
|
|
17
|
+
/** One-line description of the drift, e.g. "liability cap reworded". */
|
|
18
|
+
note?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* One node in the provenance DAG: a library clause, a template that embeds
|
|
22
|
+
* it, or an executed contract containing the final text. `parents` carries
|
|
23
|
+
* the derivation topology; a root (the source clause) has none.
|
|
24
|
+
*/
|
|
25
|
+
export interface LineageNode {
|
|
26
|
+
id: string;
|
|
27
|
+
/** Short display name: "Clause 4.2 v5", "Template 2026", "MSA_Acme §4.2". */
|
|
28
|
+
label: string;
|
|
29
|
+
kind?: LineageKind;
|
|
30
|
+
parents?: LineageParentLink[];
|
|
31
|
+
/** Reference to the underlying record, for hosts to resolve on selection. */
|
|
32
|
+
entityRef?: {
|
|
33
|
+
id: string;
|
|
34
|
+
label?: string;
|
|
35
|
+
};
|
|
36
|
+
actor?: {
|
|
37
|
+
name: string;
|
|
38
|
+
initials?: string;
|
|
39
|
+
};
|
|
40
|
+
/** ISO timestamp; display only — `parents` defines the topology. */
|
|
41
|
+
timestamp?: string;
|
|
42
|
+
note?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Detail payload for `edge-selected`; feeds the diff viewer's base/target pair. */
|
|
45
|
+
export interface LineageEdgeDetail {
|
|
46
|
+
parent: LineageNode;
|
|
47
|
+
child: LineageNode;
|
|
48
|
+
deviation: LineageDeviation;
|
|
49
|
+
note?: string;
|
|
50
|
+
}
|
|
51
|
+
export declare const resolveLineageDeviation: (value: string | null | undefined) => LineageDeviation;
|
|
52
|
+
/** Attribute payloads are author input — validate every record, not just the array. */
|
|
53
|
+
export declare const isLineageNodeRecord: (value: unknown) => value is LineageNode;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const DEVIATIONS = new Set(["none", "minor", "major"]);
|
|
2
|
+
export const resolveLineageDeviation = (value) => (value && DEVIATIONS.has(value) ? value : "none");
|
|
3
|
+
/** Attribute payloads are author input — validate every record, not just the array. */
|
|
4
|
+
export const isLineageNodeRecord = (value) => {
|
|
5
|
+
if (typeof value !== "object" || value === null) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
const node = value;
|
|
9
|
+
if (typeof node.id !== "string" || node.id.length === 0 || typeof node.label !== "string") {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
for (const field of ["kind", "timestamp", "note"]) {
|
|
13
|
+
if (node[field] !== undefined && typeof node[field] !== "string") {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (node.parents !== undefined) {
|
|
18
|
+
if (!Array.isArray(node.parents)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
for (const entry of node.parents) {
|
|
22
|
+
const link = entry;
|
|
23
|
+
if (typeof link !== "object" ||
|
|
24
|
+
link === null ||
|
|
25
|
+
typeof link.id !== "string" ||
|
|
26
|
+
link.id.length === 0 ||
|
|
27
|
+
(link.deviation !== undefined && typeof link.deviation !== "string") ||
|
|
28
|
+
(link.note !== undefined && typeof link.note !== "string")) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (node.entityRef !== undefined) {
|
|
34
|
+
const ref = node.entityRef;
|
|
35
|
+
if (typeof ref !== "object" || ref === null || typeof ref.id !== "string") {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (node.actor !== undefined) {
|
|
40
|
+
const actor = node.actor;
|
|
41
|
+
if (typeof actor !== "object" || actor === null || typeof actor.name !== "string") {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (actor.initials !== undefined && typeof actor.initials !== "string") {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { NotificationItem } from "./types.js";
|
|
2
|
+
import { BaseElement } from "../../core/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* The bell: an unread count over a trigger, for the app chrome.
|
|
5
|
+
*
|
|
6
|
+
* The count is the accessible name, not decoration — "Notifications, 3
|
|
7
|
+
* unread" — because a red dot alone tells a screen-reader user nothing. Past
|
|
8
|
+
* `max` the badge reads "9+" while the label still states the true count,
|
|
9
|
+
* since the abbreviation is a layout concession and the number is the fact.
|
|
10
|
+
*/
|
|
11
|
+
export declare class NotificationBell extends BaseElement {
|
|
12
|
+
static readonly tagName: string;
|
|
13
|
+
static get observedAttributes(): string[];
|
|
14
|
+
private triggerEl;
|
|
15
|
+
private countEl;
|
|
16
|
+
private notificationsRaw;
|
|
17
|
+
private notificationsCache;
|
|
18
|
+
get label(): string;
|
|
19
|
+
set label(value: string);
|
|
20
|
+
/** Largest number rendered before the badge abbreviates, e.g. `9+`. */
|
|
21
|
+
get max(): number;
|
|
22
|
+
set max(value: number);
|
|
23
|
+
get expanded(): boolean;
|
|
24
|
+
set expanded(value: boolean);
|
|
25
|
+
/** Optional records; supply these or `unread-count`, not both. */
|
|
26
|
+
get notifications(): NotificationItem[];
|
|
27
|
+
set notifications(value: NotificationItem[]);
|
|
28
|
+
/**
|
|
29
|
+
* Unread total. Derived from `notifications` when they are supplied;
|
|
30
|
+
* otherwise read from the attribute, so a host that keeps the list on the
|
|
31
|
+
* server can still drive the badge with one number.
|
|
32
|
+
*/
|
|
33
|
+
get unreadCount(): number;
|
|
34
|
+
set unreadCount(value: number);
|
|
35
|
+
protected renderTemplate(): void;
|
|
36
|
+
protected setupListeners(): void;
|
|
37
|
+
protected update(): void;
|
|
38
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { countUnreadNotifications, isNotificationItemRecord } from "./types.js";
|
|
2
|
+
import { BaseElement } from "../../core/index.js";
|
|
3
|
+
import { boeRadius } from "../../foundations/geometry/index.js";
|
|
4
|
+
import { boeMotionDuration, boeMotionEasing } from "../../foundations/motion/index.js";
|
|
5
|
+
const DEFAULT_TAG_NAME = "box-notification-bell";
|
|
6
|
+
const escapeHtml = (value) => value
|
|
7
|
+
.replaceAll("&", "&")
|
|
8
|
+
.replaceAll("<", "<")
|
|
9
|
+
.replaceAll(">", ">")
|
|
10
|
+
.replaceAll('"', """)
|
|
11
|
+
.replaceAll("'", "'");
|
|
12
|
+
const elementStyles = `
|
|
13
|
+
[hidden] {
|
|
14
|
+
display: none !important;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
:host {
|
|
18
|
+
display: inline-block;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
[part="trigger"] {
|
|
22
|
+
position: relative;
|
|
23
|
+
appearance: none;
|
|
24
|
+
display: inline-flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
inline-size: 2.25rem;
|
|
28
|
+
block-size: 2.25rem;
|
|
29
|
+
padding: 0;
|
|
30
|
+
border: 1px solid transparent;
|
|
31
|
+
border-radius: ${boeRadius.control};
|
|
32
|
+
background: none;
|
|
33
|
+
color: var(--boe-token-text-text, #1f1e1b);
|
|
34
|
+
font: inherit;
|
|
35
|
+
font-size: 1.05rem;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
transition: background ${boeMotionDuration.interactive} ${boeMotionEasing.standard};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
[part="trigger"]:hover {
|
|
41
|
+
background: var(--boe-token-surface-surface-hover, #f4f4f4);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
[part="trigger"]:focus-visible {
|
|
45
|
+
outline: none;
|
|
46
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 18%, transparent);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[part="trigger"][aria-expanded="true"] {
|
|
50
|
+
background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 12%, transparent);
|
|
51
|
+
border-color: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 30%, transparent);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
[part="count"] {
|
|
55
|
+
position: absolute;
|
|
56
|
+
inset-block-start: 0;
|
|
57
|
+
inset-inline-end: 0;
|
|
58
|
+
transform: translate(35%, -35%);
|
|
59
|
+
min-inline-size: 1.1rem;
|
|
60
|
+
padding: 0 0.25rem;
|
|
61
|
+
border-radius: 999px;
|
|
62
|
+
background: var(--boe-token-surface-status-surface-error, #ed3757);
|
|
63
|
+
color: var(--boe-token-text-text-on-brand, #ffffff);
|
|
64
|
+
font-size: 0.68rem;
|
|
65
|
+
font-weight: 700;
|
|
66
|
+
line-height: 1.1rem;
|
|
67
|
+
text-align: center;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
[part="count"][data-dot="true"] {
|
|
71
|
+
min-inline-size: 0.5rem;
|
|
72
|
+
block-size: 0.5rem;
|
|
73
|
+
padding: 0;
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
/**
|
|
77
|
+
* The bell: an unread count over a trigger, for the app chrome.
|
|
78
|
+
*
|
|
79
|
+
* The count is the accessible name, not decoration — "Notifications, 3
|
|
80
|
+
* unread" — because a red dot alone tells a screen-reader user nothing. Past
|
|
81
|
+
* `max` the badge reads "9+" while the label still states the true count,
|
|
82
|
+
* since the abbreviation is a layout concession and the number is the fact.
|
|
83
|
+
*/
|
|
84
|
+
export class NotificationBell extends BaseElement {
|
|
85
|
+
static tagName = DEFAULT_TAG_NAME;
|
|
86
|
+
static get observedAttributes() {
|
|
87
|
+
return ["expanded", "label", "max", "notifications", "unread-count"];
|
|
88
|
+
}
|
|
89
|
+
triggerEl;
|
|
90
|
+
countEl;
|
|
91
|
+
notificationsRaw = null;
|
|
92
|
+
notificationsCache = [];
|
|
93
|
+
get label() {
|
|
94
|
+
return this.getAttribute("label") ?? "Notifications";
|
|
95
|
+
}
|
|
96
|
+
set label(value) {
|
|
97
|
+
this.setAttribute("label", value);
|
|
98
|
+
}
|
|
99
|
+
/** Largest number rendered before the badge abbreviates, e.g. `9+`. */
|
|
100
|
+
get max() {
|
|
101
|
+
const raw = Number.parseInt(this.getAttribute("max") ?? "", 10);
|
|
102
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 99;
|
|
103
|
+
}
|
|
104
|
+
set max(value) {
|
|
105
|
+
this.setAttribute("max", String(value));
|
|
106
|
+
}
|
|
107
|
+
get expanded() {
|
|
108
|
+
return this.hasAttribute("expanded");
|
|
109
|
+
}
|
|
110
|
+
set expanded(value) {
|
|
111
|
+
this.toggleAttribute("expanded", value);
|
|
112
|
+
}
|
|
113
|
+
/** Optional records; supply these or `unread-count`, not both. */
|
|
114
|
+
get notifications() {
|
|
115
|
+
const raw = this.getAttribute("notifications");
|
|
116
|
+
if (!raw) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
if (raw !== this.notificationsRaw) {
|
|
120
|
+
this.notificationsRaw = raw;
|
|
121
|
+
try {
|
|
122
|
+
const parsed = JSON.parse(raw);
|
|
123
|
+
this.notificationsCache =
|
|
124
|
+
Array.isArray(parsed) && parsed.every(isNotificationItemRecord)
|
|
125
|
+
? parsed
|
|
126
|
+
: [];
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
this.notificationsCache = [];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return [...this.notificationsCache];
|
|
133
|
+
}
|
|
134
|
+
set notifications(value) {
|
|
135
|
+
if (value.length) {
|
|
136
|
+
this.setAttribute("notifications", JSON.stringify(value));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
this.removeAttribute("notifications");
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Unread total. Derived from `notifications` when they are supplied;
|
|
143
|
+
* otherwise read from the attribute, so a host that keeps the list on the
|
|
144
|
+
* server can still drive the badge with one number.
|
|
145
|
+
*/
|
|
146
|
+
get unreadCount() {
|
|
147
|
+
if (this.hasAttribute("notifications")) {
|
|
148
|
+
return countUnreadNotifications(this.notifications);
|
|
149
|
+
}
|
|
150
|
+
const raw = Number.parseInt(this.getAttribute("unread-count") ?? "", 10);
|
|
151
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 0;
|
|
152
|
+
}
|
|
153
|
+
set unreadCount(value) {
|
|
154
|
+
this.setAttribute("unread-count", String(Math.max(0, Math.trunc(value))));
|
|
155
|
+
}
|
|
156
|
+
renderTemplate() {
|
|
157
|
+
if (!this.shadowRoot) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
this.shadowRoot.innerHTML = `
|
|
161
|
+
<style>${elementStyles}</style>
|
|
162
|
+
<button type="button" part="trigger" aria-expanded="false" aria-haspopup="dialog">
|
|
163
|
+
<span part="icon" aria-hidden="true">🔔</span>
|
|
164
|
+
<span part="count" aria-hidden="true" hidden></span>
|
|
165
|
+
</button>
|
|
166
|
+
`;
|
|
167
|
+
this.triggerEl = this.shadowRoot.querySelector('[part="trigger"]');
|
|
168
|
+
this.countEl = this.shadowRoot.querySelector('[part="count"]');
|
|
169
|
+
}
|
|
170
|
+
setupListeners() {
|
|
171
|
+
this.triggerEl.addEventListener("click", () => {
|
|
172
|
+
const expanded = !this.expanded;
|
|
173
|
+
this.expanded = expanded;
|
|
174
|
+
this.dispatchEvent(new CustomEvent("toggle", {
|
|
175
|
+
bubbles: true,
|
|
176
|
+
composed: true,
|
|
177
|
+
detail: { expanded, unreadCount: this.unreadCount },
|
|
178
|
+
}));
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
update() {
|
|
182
|
+
if (!this.triggerEl) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const unread = this.unreadCount;
|
|
186
|
+
const max = this.max;
|
|
187
|
+
this.countEl.hidden = unread === 0;
|
|
188
|
+
this.countEl.textContent = unread > max ? `${String(max)}+` : String(unread);
|
|
189
|
+
// A zero-width badge would be invisible; a dot is the honest minimum.
|
|
190
|
+
this.countEl.setAttribute("data-dot", String(this.countEl.textContent === ""));
|
|
191
|
+
// The abbreviated badge is a layout concession — the label keeps the
|
|
192
|
+
// real number so assistive tech is never told "9+" when it is 240.
|
|
193
|
+
const suffix = unread === 0 ? "no unread" : `${String(unread)} unread`;
|
|
194
|
+
this.triggerEl.setAttribute("aria-label", `${escapeHtml(this.label)}, ${suffix}`);
|
|
195
|
+
this.triggerEl.setAttribute("aria-expanded", String(this.expanded));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
NotificationBell.register();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { NotificationFilter, NotificationItem } from "./types.js";
|
|
2
|
+
import { BaseElement } from "../../core/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* The triage inbox: approvals waiting, SLA breaches, mentions — grouped by
|
|
5
|
+
* type, filterable to unread, with per-row and bulk intents.
|
|
6
|
+
*
|
|
7
|
+
* Toasts are the wrong surface for this: they are transient and unordered,
|
|
8
|
+
* while triage needs a list you can come back to. Sections lead with the most
|
|
9
|
+
* unread so what needs attention rises to the top.
|
|
10
|
+
*
|
|
11
|
+
* Mutations are intents, not state changes. The element never marks anything
|
|
12
|
+
* read on its own — the host owns the write and feeds back a new list, so the
|
|
13
|
+
* inbox can never disagree with the server about what has been seen.
|
|
14
|
+
*/
|
|
15
|
+
export declare class NotificationInbox extends BaseElement {
|
|
16
|
+
static readonly tagName: string;
|
|
17
|
+
static get observedAttributes(): string[];
|
|
18
|
+
private panelEl;
|
|
19
|
+
private titleEl;
|
|
20
|
+
private unreadPillEl;
|
|
21
|
+
private markAllEl;
|
|
22
|
+
private filtersEl;
|
|
23
|
+
private groupsEl;
|
|
24
|
+
private emptyEl;
|
|
25
|
+
private signature;
|
|
26
|
+
private notificationsRaw;
|
|
27
|
+
private notificationsCache;
|
|
28
|
+
get heading(): string;
|
|
29
|
+
set heading(value: string);
|
|
30
|
+
get filter(): NotificationFilter;
|
|
31
|
+
set filter(value: NotificationFilter);
|
|
32
|
+
/** Notification records; JSON payloads are validated per record. */
|
|
33
|
+
get notifications(): NotificationItem[];
|
|
34
|
+
set notifications(value: NotificationItem[]);
|
|
35
|
+
/** Optional `{ type: heading }` map; unmapped types are humanized. */
|
|
36
|
+
get typeLabels(): Record<string, string>;
|
|
37
|
+
set typeLabels(value: Record<string, string>);
|
|
38
|
+
get unreadCount(): number;
|
|
39
|
+
/** The records currently on screen, after the filter. */
|
|
40
|
+
get visibleNotifications(): NotificationItem[];
|
|
41
|
+
private emitIntent;
|
|
42
|
+
private itemHtml;
|
|
43
|
+
protected renderTemplate(): void;
|
|
44
|
+
protected setupListeners(): void;
|
|
45
|
+
protected update(): void;
|
|
46
|
+
}
|