@unofficialbox/box-open-elements 0.5.0 → 0.6.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 +3 -3
- package/dist/components/collections/table.d.ts +57 -3
- package/dist/components/collections/table.js +339 -20
- 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/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 +81 -0
- package/dist/patterns/audit/audit-log.js +794 -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/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,33 @@
|
|
|
1
|
+
import type { CeremonyResolution, Signatory, SignatureMode } from "./types.js";
|
|
2
|
+
import { BaseElement } from "../../core/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Party-oriented signing progress: who signs, in what order, and — the part
|
|
5
|
+
* that matters — who can act *right now*.
|
|
6
|
+
*
|
|
7
|
+
* The rules live in `resolveCeremony`, which is pure, so a host can drive its
|
|
8
|
+
* own surface or its reminder emails from the same function rather than
|
|
9
|
+
* reimplementing the ordering logic and drifting from what the UI shows.
|
|
10
|
+
*
|
|
11
|
+
* Read-only by design. Signing happens in the signature provider's own flow,
|
|
12
|
+
* so this states position rather than offering a button that would have to
|
|
13
|
+
* duplicate that flow's authority.
|
|
14
|
+
*/
|
|
15
|
+
export declare class SignatureCeremony extends BaseElement {
|
|
16
|
+
static readonly tagName: string;
|
|
17
|
+
static get observedAttributes(): string[];
|
|
18
|
+
private hostEl;
|
|
19
|
+
private signatoriesRaw;
|
|
20
|
+
private signatoriesCache;
|
|
21
|
+
get heading(): string;
|
|
22
|
+
set heading(value: string);
|
|
23
|
+
/** `sequential` (default) routes one party at a time; `parallel` sends to all. */
|
|
24
|
+
get mode(): SignatureMode;
|
|
25
|
+
set mode(value: SignatureMode);
|
|
26
|
+
get signatories(): Signatory[];
|
|
27
|
+
set signatories(value: Signatory[]);
|
|
28
|
+
/** The resolved ceremony — same object the render uses. */
|
|
29
|
+
get resolution(): CeremonyResolution;
|
|
30
|
+
protected renderTemplate(): void;
|
|
31
|
+
protected setupListeners(): void;
|
|
32
|
+
protected update(): void;
|
|
33
|
+
}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { formatCeremonySummary, isSignatoryRecord, resolveCeremony, } from "./types.js";
|
|
2
|
+
import { formatUtcDay } from "../audit/types.js";
|
|
3
|
+
import { BaseElement } from "../../core/index.js";
|
|
4
|
+
import { boePanel } from "../../foundations/geometry/index.js";
|
|
5
|
+
const DEFAULT_TAG_NAME = "box-signature-ceremony";
|
|
6
|
+
const escapeHtml = (value) => value
|
|
7
|
+
.replaceAll("&", "&")
|
|
8
|
+
.replaceAll("<", "<")
|
|
9
|
+
.replaceAll(">", ">")
|
|
10
|
+
.replaceAll('"', """)
|
|
11
|
+
.replaceAll("'", "'");
|
|
12
|
+
const initialsOf = (name) => name
|
|
13
|
+
.split(/\s+/)
|
|
14
|
+
.filter(Boolean)
|
|
15
|
+
.slice(0, 2)
|
|
16
|
+
.map(part => part[0].toUpperCase())
|
|
17
|
+
.join("");
|
|
18
|
+
/**
|
|
19
|
+
* `formatUtcDay` takes a date-only string or a `Date`; handing it a full ISO
|
|
20
|
+
* timestamp makes it concatenate a second time component and fall back to
|
|
21
|
+
* echoing the raw input. Signing timestamps carry a time, so parse first.
|
|
22
|
+
* Returns an empty string for an unparseable value, so callers can fall back.
|
|
23
|
+
*/
|
|
24
|
+
const signedOnDay = (iso) => formatUtcDay(new Date(iso));
|
|
25
|
+
const STATE_LABEL = {
|
|
26
|
+
signed: "Signed",
|
|
27
|
+
declined: "Declined",
|
|
28
|
+
awaiting: "Awaiting signature",
|
|
29
|
+
waiting: "Not yet their turn",
|
|
30
|
+
};
|
|
31
|
+
const elementStyles = `
|
|
32
|
+
:host {
|
|
33
|
+
display: block;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[part="card"] {
|
|
37
|
+
border: ${boePanel.border};
|
|
38
|
+
border-radius: ${boePanel.radius};
|
|
39
|
+
background: ${boePanel.background};
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
[part="header"] {
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: baseline;
|
|
46
|
+
gap: 0.6rem;
|
|
47
|
+
padding: 0.8rem 1rem;
|
|
48
|
+
border-block-end: 1px solid color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 70%, transparent);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
[part="heading"] {
|
|
52
|
+
margin: 0;
|
|
53
|
+
font: inherit;
|
|
54
|
+
font-size: 0.95rem;
|
|
55
|
+
font-weight: 700;
|
|
56
|
+
color: var(--boe-token-text-text, #1f1e1b);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[part="summary"] {
|
|
60
|
+
margin-inline-start: auto;
|
|
61
|
+
font-size: 0.78rem;
|
|
62
|
+
font-weight: 600;
|
|
63
|
+
padding: 0.14rem 0.5rem;
|
|
64
|
+
border-radius: 999px;
|
|
65
|
+
border: 1px solid transparent;
|
|
66
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
67
|
+
background: color-mix(in srgb, var(--boe-token-surface-surface-hover, #f4f4f4) 80%, transparent);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
[part="summary"][data-status="completed"] {
|
|
71
|
+
background: color-mix(in srgb, var(--boe-token-surface-status-surface-success, #26a27b) 14%, transparent);
|
|
72
|
+
color: color-mix(in srgb, var(--boe-token-surface-status-surface-success, #26a27b) 74%, black 26%);
|
|
73
|
+
border-color: color-mix(in srgb, var(--boe-token-surface-status-surface-success, #26a27b) 32%, transparent);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
[part="summary"][data-status="declined"] {
|
|
77
|
+
background: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 14%, transparent);
|
|
78
|
+
color: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 76%, black 24%);
|
|
79
|
+
border-color: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 32%, transparent);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
[part="parties"] {
|
|
83
|
+
margin: 0;
|
|
84
|
+
padding: 0;
|
|
85
|
+
list-style: none;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
[part="party"] {
|
|
89
|
+
display: grid;
|
|
90
|
+
grid-template-columns: auto auto 1fr auto;
|
|
91
|
+
align-items: center;
|
|
92
|
+
gap: 0.7rem;
|
|
93
|
+
padding: 0.65rem 1rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
[part="party"] + [part="party"] {
|
|
97
|
+
border-block-start: 1px solid color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 45%, transparent);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
[part="order"] {
|
|
101
|
+
inline-size: 1.35rem;
|
|
102
|
+
block-size: 1.35rem;
|
|
103
|
+
display: grid;
|
|
104
|
+
place-items: center;
|
|
105
|
+
border-radius: 999px;
|
|
106
|
+
font-size: 0.7rem;
|
|
107
|
+
font-weight: 700;
|
|
108
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
109
|
+
background: var(--boe-token-surface-surface-hover, #f4f4f4);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
[part="party"][data-state="signed"] [part="order"] {
|
|
113
|
+
background: color-mix(in srgb, var(--boe-token-surface-status-surface-success, #26a27b) 18%, transparent);
|
|
114
|
+
color: color-mix(in srgb, var(--boe-token-surface-status-surface-success, #26a27b) 74%, black 26%);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
[part="party"][data-state="declined"] [part="order"] {
|
|
118
|
+
background: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 16%, transparent);
|
|
119
|
+
color: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 76%, black 24%);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
[part="party"][data-state="awaiting"] [part="order"] {
|
|
123
|
+
background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 16%, transparent);
|
|
124
|
+
color: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 80%, black 20%);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
[part="avatar"] {
|
|
128
|
+
inline-size: 1.7rem;
|
|
129
|
+
block-size: 1.7rem;
|
|
130
|
+
display: grid;
|
|
131
|
+
place-items: center;
|
|
132
|
+
border-radius: 999px;
|
|
133
|
+
font-size: 0.68rem;
|
|
134
|
+
font-weight: 700;
|
|
135
|
+
color: var(--boe-token-text-text, #1f1e1b);
|
|
136
|
+
background: var(--boe-token-surface-surface-hover, #f4f4f4);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
[part="identity"] {
|
|
140
|
+
min-inline-size: 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
[part="name"] {
|
|
144
|
+
display: block;
|
|
145
|
+
font-size: 0.88rem;
|
|
146
|
+
color: var(--boe-token-text-text, #1f1e1b);
|
|
147
|
+
overflow-wrap: anywhere;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
[part="role"] {
|
|
151
|
+
display: block;
|
|
152
|
+
font-size: 0.76rem;
|
|
153
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
[part="state"] {
|
|
157
|
+
text-align: end;
|
|
158
|
+
font-size: 0.76rem;
|
|
159
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
160
|
+
white-space: nowrap;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
[part="party"][data-state="awaiting"] [part="state"] {
|
|
164
|
+
font-weight: 700;
|
|
165
|
+
color: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 80%, black 20%);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
[part="party"][data-state="declined"] [part="state"] {
|
|
169
|
+
font-weight: 700;
|
|
170
|
+
color: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 76%, black 24%);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
[part="reason"] {
|
|
174
|
+
grid-column: 3 / -1;
|
|
175
|
+
font-size: 0.76rem;
|
|
176
|
+
font-style: italic;
|
|
177
|
+
color: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 70%, black 30%);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
[part="empty"] {
|
|
181
|
+
padding: 1.2rem 1rem;
|
|
182
|
+
text-align: center;
|
|
183
|
+
color: var(--boe-token-text-text-secondary, #6f6f6f);
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
/**
|
|
187
|
+
* Party-oriented signing progress: who signs, in what order, and — the part
|
|
188
|
+
* that matters — who can act *right now*.
|
|
189
|
+
*
|
|
190
|
+
* The rules live in `resolveCeremony`, which is pure, so a host can drive its
|
|
191
|
+
* own surface or its reminder emails from the same function rather than
|
|
192
|
+
* reimplementing the ordering logic and drifting from what the UI shows.
|
|
193
|
+
*
|
|
194
|
+
* Read-only by design. Signing happens in the signature provider's own flow,
|
|
195
|
+
* so this states position rather than offering a button that would have to
|
|
196
|
+
* duplicate that flow's authority.
|
|
197
|
+
*/
|
|
198
|
+
export class SignatureCeremony extends BaseElement {
|
|
199
|
+
static tagName = DEFAULT_TAG_NAME;
|
|
200
|
+
static get observedAttributes() {
|
|
201
|
+
return ["heading", "mode", "signatories"];
|
|
202
|
+
}
|
|
203
|
+
hostEl;
|
|
204
|
+
signatoriesRaw = null;
|
|
205
|
+
signatoriesCache = [];
|
|
206
|
+
get heading() {
|
|
207
|
+
return this.getAttribute("heading") ?? "Signatures";
|
|
208
|
+
}
|
|
209
|
+
set heading(value) {
|
|
210
|
+
this.setAttribute("heading", value);
|
|
211
|
+
}
|
|
212
|
+
/** `sequential` (default) routes one party at a time; `parallel` sends to all. */
|
|
213
|
+
get mode() {
|
|
214
|
+
return this.getAttribute("mode") === "parallel" ? "parallel" : "sequential";
|
|
215
|
+
}
|
|
216
|
+
set mode(value) {
|
|
217
|
+
this.setAttribute("mode", value);
|
|
218
|
+
}
|
|
219
|
+
get signatories() {
|
|
220
|
+
const raw = this.getAttribute("signatories");
|
|
221
|
+
if (!raw) {
|
|
222
|
+
return [...this.signatoriesCache];
|
|
223
|
+
}
|
|
224
|
+
if (raw !== this.signatoriesRaw) {
|
|
225
|
+
this.signatoriesRaw = raw;
|
|
226
|
+
try {
|
|
227
|
+
const parsed = JSON.parse(raw);
|
|
228
|
+
this.signatoriesCache =
|
|
229
|
+
Array.isArray(parsed) && parsed.every(isSignatoryRecord)
|
|
230
|
+
? parsed
|
|
231
|
+
: [];
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
this.signatoriesCache = [];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return [...this.signatoriesCache];
|
|
238
|
+
}
|
|
239
|
+
set signatories(value) {
|
|
240
|
+
if (value.length) {
|
|
241
|
+
this.setAttribute("signatories", JSON.stringify(value));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
// Clear the cache too: the no-attribute branch of the getter serves from
|
|
245
|
+
// it, and a stale cache would make an emptied roster un-clearable.
|
|
246
|
+
this.signatoriesCache = [];
|
|
247
|
+
this.signatoriesRaw = null;
|
|
248
|
+
this.removeAttribute("signatories");
|
|
249
|
+
}
|
|
250
|
+
/** The resolved ceremony — same object the render uses. */
|
|
251
|
+
get resolution() {
|
|
252
|
+
return resolveCeremony(this.signatories, this.mode);
|
|
253
|
+
}
|
|
254
|
+
renderTemplate() {
|
|
255
|
+
if (!this.shadowRoot) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
this.shadowRoot.innerHTML = `<style>${elementStyles}</style><div part="host"></div>`;
|
|
259
|
+
this.hostEl = this.shadowRoot.querySelector('[part="host"]');
|
|
260
|
+
}
|
|
261
|
+
setupListeners() {
|
|
262
|
+
// Read-only: signing is the provider's flow, not this component's.
|
|
263
|
+
}
|
|
264
|
+
update() {
|
|
265
|
+
if (!this.hostEl) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const resolution = this.resolution;
|
|
269
|
+
const rows = resolution.statuses
|
|
270
|
+
.map(entry => {
|
|
271
|
+
const { signatory, state, order } = entry;
|
|
272
|
+
// An unparseable timestamp falls back to the bare state rather than
|
|
273
|
+
// rendering "Signed " with nothing after it.
|
|
274
|
+
const signedDay = signatory.signedAt ? signedOnDay(signatory.signedAt) : "";
|
|
275
|
+
const declinedDay = signatory.declinedAt ? signedOnDay(signatory.declinedAt) : "";
|
|
276
|
+
// "Not yet their turn" would be a lie once someone has refused: their
|
|
277
|
+
// turn is not coming. A stopped ceremony says so.
|
|
278
|
+
const waitingLabel = resolution.status === "declined" ? "Ceremony stopped" : STATE_LABEL.waiting;
|
|
279
|
+
const detail = state === "signed" && signedDay
|
|
280
|
+
? `Signed ${signedDay}`
|
|
281
|
+
: state === "declined" && declinedDay
|
|
282
|
+
? `Declined ${declinedDay}`
|
|
283
|
+
: state === "waiting"
|
|
284
|
+
? waitingLabel
|
|
285
|
+
: STATE_LABEL[state];
|
|
286
|
+
const reason = state === "declined" && signatory.declineReason
|
|
287
|
+
? `<span part="reason">${escapeHtml(signatory.declineReason)}</span>`
|
|
288
|
+
: "";
|
|
289
|
+
return `
|
|
290
|
+
<li part="party" data-state="${state}" data-signatory-id="${escapeHtml(signatory.id)}">
|
|
291
|
+
<span part="order" aria-hidden="true">${String(order)}</span>
|
|
292
|
+
<span part="avatar" aria-hidden="true">${escapeHtml(initialsOf(signatory.name))}</span>
|
|
293
|
+
<span part="identity">
|
|
294
|
+
<span part="name">${escapeHtml(signatory.name)}</span>
|
|
295
|
+
${signatory.role ? `<span part="role">${escapeHtml(signatory.role)}</span>` : ""}
|
|
296
|
+
</span>
|
|
297
|
+
<span part="state">${escapeHtml(detail)}</span>
|
|
298
|
+
${reason}
|
|
299
|
+
</li>
|
|
300
|
+
`;
|
|
301
|
+
})
|
|
302
|
+
.join("");
|
|
303
|
+
const summary = formatCeremonySummary(resolution);
|
|
304
|
+
this.hostEl.innerHTML = `
|
|
305
|
+
<section part="card" aria-label="${escapeHtml(this.heading)}">
|
|
306
|
+
<div part="header">
|
|
307
|
+
<h3 part="heading">${escapeHtml(this.heading)}</h3>
|
|
308
|
+
<span part="summary" data-status="${resolution.status}">${escapeHtml(summary)}</span>
|
|
309
|
+
</div>
|
|
310
|
+
${resolution.total
|
|
311
|
+
? `<ol part="parties">${rows}</ol>`
|
|
312
|
+
: `<div part="empty">No signatories yet.</div>`}
|
|
313
|
+
</section>
|
|
314
|
+
`;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
SignatureCeremony.register();
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signature ceremony model and the pure engine that resolves it.
|
|
3
|
+
*
|
|
4
|
+
* DOM-free and deterministic, so the rules that decide *who may sign now* are
|
|
5
|
+
* testable on their own and a host can drive its own signing surface — or its
|
|
6
|
+
* server-side reminders — from the same function.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* `sequential` routes the document one party at a time, in order.
|
|
10
|
+
* `parallel` sends it to everyone at once.
|
|
11
|
+
*/
|
|
12
|
+
export type SignatureMode = "sequential" | "parallel";
|
|
13
|
+
/**
|
|
14
|
+
* `awaiting` means this party can act **now**. `waiting` means their turn has
|
|
15
|
+
* not come — or the ceremony has stopped and will never reach them. The
|
|
16
|
+
* distinction is the whole point of the component: only `awaiting` should ever
|
|
17
|
+
* carry a sign-now affordance.
|
|
18
|
+
*/
|
|
19
|
+
export type SignatoryState = "signed" | "declined" | "awaiting" | "waiting";
|
|
20
|
+
export type CeremonyStatus = "completed" | "in-progress" | "declined";
|
|
21
|
+
export interface Signatory {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
/** Capacity they sign in — "Counterparty", "General Counsel". */
|
|
25
|
+
role?: string;
|
|
26
|
+
email?: string;
|
|
27
|
+
/** ISO timestamp. Present means signed. */
|
|
28
|
+
signedAt?: string;
|
|
29
|
+
/** ISO timestamp. Present means refused, which stops the ceremony. */
|
|
30
|
+
declinedAt?: string;
|
|
31
|
+
declineReason?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface SignatoryStatus {
|
|
34
|
+
signatory: Signatory;
|
|
35
|
+
state: SignatoryState;
|
|
36
|
+
/** 1-based: the signing position in sequential mode, display order otherwise. */
|
|
37
|
+
order: number;
|
|
38
|
+
}
|
|
39
|
+
export interface CeremonyResolution {
|
|
40
|
+
statuses: SignatoryStatus[];
|
|
41
|
+
status: CeremonyStatus;
|
|
42
|
+
signedCount: number;
|
|
43
|
+
total: number;
|
|
44
|
+
/** Parties who may act right now. Empty once the ceremony is done or stopped. */
|
|
45
|
+
awaiting: Signatory[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve every party's state from the signing order and what has happened.
|
|
49
|
+
*
|
|
50
|
+
* Three rules, in priority order:
|
|
51
|
+
*
|
|
52
|
+
* 1. **A decline stops the ceremony.** Nobody who has not already signed
|
|
53
|
+
* becomes `awaiting` — not even in parallel mode, where they otherwise
|
|
54
|
+
* could all act. This is the rule worth being strict about: a declined
|
|
55
|
+
* document is dead until the host revives it, and showing someone a
|
|
56
|
+
* sign-now affordance for it wastes their time and produces a signature
|
|
57
|
+
* against a document the counterparty has already refused.
|
|
58
|
+
* 2. **Sequential grants exactly one turn.** The first unsigned party is
|
|
59
|
+
* `awaiting`; everyone behind them is `waiting`. A later party can never
|
|
60
|
+
* appear actionable before an earlier one has signed.
|
|
61
|
+
* 3. **Parallel grants every turn at once** — all unsigned parties are
|
|
62
|
+
* `awaiting`.
|
|
63
|
+
*/
|
|
64
|
+
export declare const resolveCeremony: (signatories: readonly Signatory[], mode?: SignatureMode) => CeremonyResolution;
|
|
65
|
+
/** Human phrasing for the ceremony as a whole. */
|
|
66
|
+
export declare const formatCeremonySummary: (resolution: CeremonyResolution) => string;
|
|
67
|
+
/** Attribute payloads are author input — validate every record. */
|
|
68
|
+
export declare const isSignatoryRecord: (value: unknown) => value is Signatory;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signature ceremony model and the pure engine that resolves it.
|
|
3
|
+
*
|
|
4
|
+
* DOM-free and deterministic, so the rules that decide *who may sign now* are
|
|
5
|
+
* testable on their own and a host can drive its own signing surface — or its
|
|
6
|
+
* server-side reminders — from the same function.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Resolve every party's state from the signing order and what has happened.
|
|
10
|
+
*
|
|
11
|
+
* Three rules, in priority order:
|
|
12
|
+
*
|
|
13
|
+
* 1. **A decline stops the ceremony.** Nobody who has not already signed
|
|
14
|
+
* becomes `awaiting` — not even in parallel mode, where they otherwise
|
|
15
|
+
* could all act. This is the rule worth being strict about: a declined
|
|
16
|
+
* document is dead until the host revives it, and showing someone a
|
|
17
|
+
* sign-now affordance for it wastes their time and produces a signature
|
|
18
|
+
* against a document the counterparty has already refused.
|
|
19
|
+
* 2. **Sequential grants exactly one turn.** The first unsigned party is
|
|
20
|
+
* `awaiting`; everyone behind them is `waiting`. A later party can never
|
|
21
|
+
* appear actionable before an earlier one has signed.
|
|
22
|
+
* 3. **Parallel grants every turn at once** — all unsigned parties are
|
|
23
|
+
* `awaiting`.
|
|
24
|
+
*/
|
|
25
|
+
export const resolveCeremony = (signatories, mode = "sequential") => {
|
|
26
|
+
const declined = signatories.some(signatory => signatory.declinedAt);
|
|
27
|
+
const signedCount = signatories.filter(signatory => signatory.signedAt && !signatory.declinedAt).length;
|
|
28
|
+
let turnTaken = false;
|
|
29
|
+
const statuses = signatories.map((signatory, index) => {
|
|
30
|
+
const order = index + 1;
|
|
31
|
+
if (signatory.declinedAt) {
|
|
32
|
+
return { signatory, state: "declined", order };
|
|
33
|
+
}
|
|
34
|
+
if (signatory.signedAt) {
|
|
35
|
+
return { signatory, state: "signed", order };
|
|
36
|
+
}
|
|
37
|
+
// Rule 1: a stopped ceremony grants no turns at all.
|
|
38
|
+
if (declined) {
|
|
39
|
+
return { signatory, state: "waiting", order };
|
|
40
|
+
}
|
|
41
|
+
if (mode === "parallel") {
|
|
42
|
+
return { signatory, state: "awaiting", order };
|
|
43
|
+
}
|
|
44
|
+
// Rule 2: sequential hands out a single turn, to the first unsigned party.
|
|
45
|
+
if (turnTaken) {
|
|
46
|
+
return { signatory, state: "waiting", order };
|
|
47
|
+
}
|
|
48
|
+
turnTaken = true;
|
|
49
|
+
return { signatory, state: "awaiting", order };
|
|
50
|
+
});
|
|
51
|
+
const status = declined
|
|
52
|
+
? "declined"
|
|
53
|
+
: signatories.length > 0 && signedCount === signatories.length
|
|
54
|
+
? "completed"
|
|
55
|
+
: "in-progress";
|
|
56
|
+
return {
|
|
57
|
+
statuses,
|
|
58
|
+
status,
|
|
59
|
+
signedCount,
|
|
60
|
+
total: signatories.length,
|
|
61
|
+
awaiting: statuses
|
|
62
|
+
.filter(entry => entry.state === "awaiting")
|
|
63
|
+
.map(entry => entry.signatory),
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
/** Human phrasing for the ceremony as a whole. */
|
|
67
|
+
export const formatCeremonySummary = (resolution) => {
|
|
68
|
+
if (resolution.total === 0) {
|
|
69
|
+
return "No signatories";
|
|
70
|
+
}
|
|
71
|
+
if (resolution.status === "declined") {
|
|
72
|
+
return "Declined";
|
|
73
|
+
}
|
|
74
|
+
if (resolution.status === "completed") {
|
|
75
|
+
return "Fully executed";
|
|
76
|
+
}
|
|
77
|
+
return `${String(resolution.signedCount)} of ${String(resolution.total)} signed`;
|
|
78
|
+
};
|
|
79
|
+
/** Attribute payloads are author input — validate every record. */
|
|
80
|
+
export const isSignatoryRecord = (value) => {
|
|
81
|
+
if (typeof value !== "object" || value === null) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const signatory = value;
|
|
85
|
+
return (typeof signatory.id === "string" &&
|
|
86
|
+
signatory.id.length > 0 &&
|
|
87
|
+
typeof signatory.name === "string" &&
|
|
88
|
+
signatory.name.length > 0);
|
|
89
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TimelineEvent } from "./types.js";
|
|
2
|
+
import { BaseElement } from "../../core/index.js";
|
|
3
|
+
export declare class Timeline extends BaseElement {
|
|
4
|
+
static readonly tagName: string;
|
|
5
|
+
static get observedAttributes(): string[];
|
|
6
|
+
private titleEl;
|
|
7
|
+
private eventsEl;
|
|
8
|
+
private emptyEl;
|
|
9
|
+
private loadMoreRegionEl;
|
|
10
|
+
private composerEl;
|
|
11
|
+
private composerInputEl;
|
|
12
|
+
private eventsSignature;
|
|
13
|
+
get heading(): string;
|
|
14
|
+
set heading(value: string);
|
|
15
|
+
/** Append-only event list; JSON records validated before rendering. */
|
|
16
|
+
get events(): TimelineEvent[];
|
|
17
|
+
set events(value: TimelineEvent[]);
|
|
18
|
+
get composable(): boolean;
|
|
19
|
+
set composable(value: boolean);
|
|
20
|
+
get hasMore(): boolean;
|
|
21
|
+
set hasMore(value: boolean);
|
|
22
|
+
private emitEntrySubmitted;
|
|
23
|
+
private rebuildEvents;
|
|
24
|
+
protected renderTemplate(): void;
|
|
25
|
+
protected setupListeners(): void;
|
|
26
|
+
protected update(): void;
|
|
27
|
+
}
|