@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.
Files changed (219) hide show
  1. package/README.md +3 -3
  2. package/dist/components/collections/table.d.ts +57 -3
  3. package/dist/components/collections/table.js +339 -20
  4. package/dist/components/feedback/due-badge.d.ts +38 -0
  5. package/dist/components/feedback/due-badge.js +168 -0
  6. package/dist/components/feedback/due-types.d.ts +32 -0
  7. package/dist/components/feedback/due-types.js +91 -0
  8. package/dist/components/feedback/progress-steps.d.ts +51 -5
  9. package/dist/components/feedback/progress-steps.js +200 -22
  10. package/dist/components/feedback/stage-path.d.ts +44 -0
  11. package/dist/components/feedback/stage-path.js +235 -0
  12. package/dist/components/forms/select.d.ts +21 -0
  13. package/dist/components/forms/select.js +100 -3
  14. package/dist/components/forms/text-field.d.ts +21 -0
  15. package/dist/components/forms/text-field.js +100 -1
  16. package/dist/components/layout/nav-sidebar.d.ts +14 -0
  17. package/dist/components/layout/nav-sidebar.js +49 -0
  18. package/dist/components/layout/split-view.d.ts +14 -0
  19. package/dist/components/layout/split-view.js +80 -1
  20. package/dist/components/overlays/command-palette.d.ts +62 -0
  21. package/dist/components/overlays/command-palette.js +537 -0
  22. package/dist/components/overlays/command-types.d.ts +90 -0
  23. package/dist/components/overlays/command-types.js +259 -0
  24. package/dist/components/overlays/drawer.d.ts +18 -0
  25. package/dist/components/overlays/drawer.js +135 -9
  26. package/dist/components/overlays/shortcuts-overlay.d.ts +48 -0
  27. package/dist/components/overlays/shortcuts-overlay.js +385 -0
  28. package/dist/components/overlays/tooltip.js +1 -1
  29. package/dist/element-maps.d.ts +292 -0
  30. package/dist/element-maps.js +1 -0
  31. package/dist/entries/activity-density.d.ts +1 -0
  32. package/dist/entries/activity-density.js +1 -0
  33. package/dist/entries/agent-chat.d.ts +1 -0
  34. package/dist/entries/agent-chat.js +1 -0
  35. package/dist/entries/audit-log.d.ts +1 -0
  36. package/dist/entries/audit-log.js +1 -0
  37. package/dist/entries/command-palette.d.ts +1 -0
  38. package/dist/entries/command-palette.js +1 -0
  39. package/dist/entries/compare-view.d.ts +1 -0
  40. package/dist/entries/compare-view.js +1 -0
  41. package/dist/entries/diff-viewer.d.ts +1 -0
  42. package/dist/entries/diff-viewer.js +1 -0
  43. package/dist/entries/due-badge.d.ts +1 -0
  44. package/dist/entries/due-badge.js +1 -0
  45. package/dist/entries/form-wizard.d.ts +1 -0
  46. package/dist/entries/form-wizard.js +1 -0
  47. package/dist/entries/lineage-graph.d.ts +1 -0
  48. package/dist/entries/lineage-graph.js +1 -0
  49. package/dist/entries/notification-bell.d.ts +1 -0
  50. package/dist/entries/notification-bell.js +1 -0
  51. package/dist/entries/notification-inbox.d.ts +1 -0
  52. package/dist/entries/notification-inbox.js +1 -0
  53. package/dist/entries/provenance-strip.d.ts +1 -0
  54. package/dist/entries/provenance-strip.js +1 -0
  55. package/dist/entries/run-trace.d.ts +1 -0
  56. package/dist/entries/run-trace.js +1 -0
  57. package/dist/entries/shortcuts-overlay.d.ts +1 -0
  58. package/dist/entries/shortcuts-overlay.js +1 -0
  59. package/dist/entries/signature-ceremony.d.ts +1 -0
  60. package/dist/entries/signature-ceremony.js +1 -0
  61. package/dist/entries/stage-path.d.ts +1 -0
  62. package/dist/entries/stage-path.js +1 -0
  63. package/dist/entries/timeline.d.ts +1 -0
  64. package/dist/entries/timeline.js +1 -0
  65. package/dist/entries/version-graph.d.ts +1 -0
  66. package/dist/entries/version-graph.js +1 -0
  67. package/dist/entries/version-list.d.ts +1 -0
  68. package/dist/entries/version-list.js +1 -0
  69. package/dist/entries/wizard-summary.d.ts +1 -0
  70. package/dist/entries/wizard-summary.js +1 -0
  71. package/dist/entries/work-queue.d.ts +1 -0
  72. package/dist/entries/work-queue.js +1 -0
  73. package/dist/entries/workload-board.d.ts +1 -0
  74. package/dist/entries/workload-board.js +1 -0
  75. package/dist/index.d.ts +21 -0
  76. package/dist/index.js +21 -0
  77. package/dist/patterns/agent-chat/agent-chat.d.ts +75 -0
  78. package/dist/patterns/agent-chat/agent-chat.js +820 -0
  79. package/dist/patterns/agent-chat/controller.d.ts +27 -0
  80. package/dist/patterns/agent-chat/controller.js +182 -0
  81. package/dist/patterns/agent-chat/index.d.ts +3 -0
  82. package/dist/patterns/agent-chat/index.js +6 -0
  83. package/dist/patterns/agent-chat/types.d.ts +109 -0
  84. package/dist/patterns/agent-chat/types.js +1 -0
  85. package/dist/patterns/audit/activity-density.d.ts +68 -0
  86. package/dist/patterns/audit/activity-density.js +428 -0
  87. package/dist/patterns/audit/audit-log.d.ts +81 -0
  88. package/dist/patterns/audit/audit-log.js +794 -0
  89. package/dist/patterns/audit/index.d.ts +3 -0
  90. package/dist/patterns/audit/index.js +7 -0
  91. package/dist/patterns/audit/shared.d.ts +10 -0
  92. package/dist/patterns/audit/shared.js +50 -0
  93. package/dist/patterns/audit/types.d.ts +143 -0
  94. package/dist/patterns/audit/types.js +327 -0
  95. package/dist/patterns/content-explorer/actions/controller.js +12 -2
  96. package/dist/patterns/content-explorer/box-transport.js +117 -25
  97. package/dist/patterns/content-explorer/content-explorer.d.ts +13 -1
  98. package/dist/patterns/content-explorer/content-explorer.js +36 -3
  99. package/dist/patterns/content-explorer/contracts.d.ts +24 -1
  100. package/dist/patterns/content-explorer/contracts.js +68 -11
  101. package/dist/patterns/content-explorer/controller.d.ts +16 -1
  102. package/dist/patterns/content-explorer/controller.js +130 -4
  103. package/dist/patterns/content-explorer/navigation/controller.js +11 -9
  104. package/dist/patterns/content-explorer/types.d.ts +58 -1
  105. package/dist/patterns/content-picker/content-picker.d.ts +71 -0
  106. package/dist/patterns/content-picker/content-picker.js +747 -0
  107. package/dist/patterns/content-picker/controller.d.ts +38 -0
  108. package/dist/patterns/content-picker/controller.js +141 -0
  109. package/dist/patterns/content-picker/index.d.ts +3 -0
  110. package/dist/patterns/content-picker/index.js +6 -0
  111. package/dist/patterns/content-picker/types.d.ts +52 -0
  112. package/dist/patterns/content-picker/types.js +26 -0
  113. package/dist/patterns/content-sidebar/content-sidebar.d.ts +43 -0
  114. package/dist/patterns/content-sidebar/content-sidebar.js +293 -0
  115. package/dist/patterns/content-sidebar/index.d.ts +2 -0
  116. package/dist/patterns/content-sidebar/index.js +4 -0
  117. package/dist/patterns/content-sidebar/types.d.ts +18 -0
  118. package/dist/patterns/content-sidebar/types.js +20 -0
  119. package/dist/patterns/content-uploader/box-transport.d.ts +14 -0
  120. package/dist/patterns/content-uploader/box-transport.js +54 -0
  121. package/dist/patterns/content-uploader/content-uploader.d.ts +62 -0
  122. package/dist/patterns/content-uploader/content-uploader.js +559 -0
  123. package/dist/patterns/content-uploader/controller.d.ts +39 -0
  124. package/dist/patterns/content-uploader/controller.js +227 -0
  125. package/dist/patterns/content-uploader/index.d.ts +4 -0
  126. package/dist/patterns/content-uploader/index.js +8 -0
  127. package/dist/patterns/content-uploader/types.d.ts +120 -0
  128. package/dist/patterns/content-uploader/types.js +38 -0
  129. package/dist/patterns/diff/compare-view.d.ts +47 -0
  130. package/dist/patterns/diff/compare-view.js +276 -0
  131. package/dist/patterns/diff/diff-viewer.d.ts +42 -0
  132. package/dist/patterns/diff/diff-viewer.js +434 -0
  133. package/dist/patterns/diff/engine.d.ts +22 -0
  134. package/dist/patterns/diff/engine.js +276 -0
  135. package/dist/patterns/diff/index.d.ts +4 -0
  136. package/dist/patterns/diff/index.js +8 -0
  137. package/dist/patterns/diff/types.d.ts +69 -0
  138. package/dist/patterns/diff/types.js +24 -0
  139. package/dist/patterns/form-wizard/controller.d.ts +39 -0
  140. package/dist/patterns/form-wizard/controller.js +157 -0
  141. package/dist/patterns/form-wizard/form-wizard.d.ts +63 -0
  142. package/dist/patterns/form-wizard/form-wizard.js +468 -0
  143. package/dist/patterns/form-wizard/index.d.ts +4 -0
  144. package/dist/patterns/form-wizard/index.js +8 -0
  145. package/dist/patterns/form-wizard/types.d.ts +109 -0
  146. package/dist/patterns/form-wizard/types.js +83 -0
  147. package/dist/patterns/form-wizard/wizard-summary.d.ts +62 -0
  148. package/dist/patterns/form-wizard/wizard-summary.js +324 -0
  149. package/dist/patterns/internal/safe-href.d.ts +17 -0
  150. package/dist/patterns/internal/safe-href.js +17 -0
  151. package/dist/patterns/lineage/index.d.ts +3 -0
  152. package/dist/patterns/lineage/index.js +6 -0
  153. package/dist/patterns/lineage/lineage-graph.d.ts +30 -0
  154. package/dist/patterns/lineage/lineage-graph.js +434 -0
  155. package/dist/patterns/lineage/provenance-strip.d.ts +22 -0
  156. package/dist/patterns/lineage/provenance-strip.js +194 -0
  157. package/dist/patterns/lineage/types.d.ts +53 -0
  158. package/dist/patterns/lineage/types.js +49 -0
  159. package/dist/patterns/notifications/index.d.ts +3 -0
  160. package/dist/patterns/notifications/index.js +6 -0
  161. package/dist/patterns/notifications/notification-bell.d.ts +38 -0
  162. package/dist/patterns/notifications/notification-bell.js +198 -0
  163. package/dist/patterns/notifications/notification-inbox.d.ts +46 -0
  164. package/dist/patterns/notifications/notification-inbox.js +537 -0
  165. package/dist/patterns/notifications/types.d.ts +61 -0
  166. package/dist/patterns/notifications/types.js +108 -0
  167. package/dist/patterns/preview/annotation-inspector.d.ts +25 -6
  168. package/dist/patterns/preview/annotation-inspector.js +239 -94
  169. package/dist/patterns/preview/annotation-thread.d.ts +25 -5
  170. package/dist/patterns/preview/annotation-thread.js +233 -82
  171. package/dist/patterns/preview/annotation-toolbar.d.ts +20 -6
  172. package/dist/patterns/preview/annotation-toolbar.js +162 -96
  173. package/dist/patterns/preview/content-preview-adapter.d.ts +10 -1
  174. package/dist/patterns/preview/content-preview-adapter.js +26 -1
  175. package/dist/patterns/preview/preview-element.d.ts +15 -2
  176. package/dist/patterns/preview/preview-element.js +223 -52
  177. package/dist/patterns/preview/provider-adapter.d.ts +47 -0
  178. package/dist/patterns/preview/provider-adapter.js +17 -0
  179. package/dist/patterns/run/index.d.ts +2 -0
  180. package/dist/patterns/run/index.js +4 -0
  181. package/dist/patterns/run/run-trace.d.ts +40 -0
  182. package/dist/patterns/run/run-trace.js +441 -0
  183. package/dist/patterns/run/types.d.ts +66 -0
  184. package/dist/patterns/run/types.js +152 -0
  185. package/dist/patterns/signature/index.d.ts +2 -0
  186. package/dist/patterns/signature/index.js +4 -0
  187. package/dist/patterns/signature/signature-ceremony.d.ts +33 -0
  188. package/dist/patterns/signature/signature-ceremony.js +317 -0
  189. package/dist/patterns/signature/types.d.ts +68 -0
  190. package/dist/patterns/signature/types.js +89 -0
  191. package/dist/patterns/timeline/index.d.ts +2 -0
  192. package/dist/patterns/timeline/index.js +4 -0
  193. package/dist/patterns/timeline/timeline.d.ts +27 -0
  194. package/dist/patterns/timeline/timeline.js +430 -0
  195. package/dist/patterns/timeline/types.d.ts +36 -0
  196. package/dist/patterns/timeline/types.js +27 -0
  197. package/dist/patterns/versions/graph-layout.d.ts +43 -0
  198. package/dist/patterns/versions/graph-layout.js +159 -0
  199. package/dist/patterns/versions/index.d.ts +4 -0
  200. package/dist/patterns/versions/index.js +8 -0
  201. package/dist/patterns/versions/shared.d.ts +4 -0
  202. package/dist/patterns/versions/shared.js +13 -0
  203. package/dist/patterns/versions/types.d.ts +37 -0
  204. package/dist/patterns/versions/types.js +39 -0
  205. package/dist/patterns/versions/version-graph.d.ts +36 -0
  206. package/dist/patterns/versions/version-graph.js +438 -0
  207. package/dist/patterns/versions/version-list.d.ts +39 -0
  208. package/dist/patterns/versions/version-list.js +407 -0
  209. package/dist/patterns/work-queue/controller.d.ts +26 -0
  210. package/dist/patterns/work-queue/controller.js +150 -0
  211. package/dist/patterns/work-queue/index.d.ts +4 -0
  212. package/dist/patterns/work-queue/index.js +7 -0
  213. package/dist/patterns/work-queue/types.d.ts +128 -0
  214. package/dist/patterns/work-queue/types.js +45 -0
  215. package/dist/patterns/work-queue/work-queue.d.ts +48 -0
  216. package/dist/patterns/work-queue/work-queue.js +534 -0
  217. package/dist/patterns/work-queue/workload-board.d.ts +53 -0
  218. package/dist/patterns/work-queue/workload-board.js +608 -0
  219. package/package.json +89 -5
@@ -0,0 +1,428 @@
1
+ import { computeActivityDensity, formatUtcDay, formatUtcMonth, resolveAuditDay, } from "./types.js";
2
+ import { escapeHtml, resolveReferenceTime } from "./shared.js";
3
+ import { isTimelineEventRecord } from "../timeline/types.js";
4
+ import { BaseElement } from "../../core/index.js";
5
+ import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
6
+ const DEFAULT_TAG_NAME = "box-activity-density";
7
+ const WEEKDAY_LABELS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
8
+ const elementStyles = `
9
+ [hidden] {
10
+ display: none !important;
11
+ }
12
+
13
+ :host {
14
+ display: block;
15
+ color: inherit;
16
+ font: inherit;
17
+ }
18
+
19
+ [part="panel"] {
20
+ display: grid;
21
+ gap: ${boePanel.gap};
22
+ padding: ${boePanel.padding};
23
+ border: 1px solid color-mix(in srgb, var(--boe-token-stroke-stroke, #e8e8e8) 82%, transparent);
24
+ border-radius: ${boePanel.radius};
25
+ background: ${boePanel.background};
26
+ }
27
+
28
+ [part="title"] {
29
+ margin: 0;
30
+ font: inherit;
31
+ font-size: 1.1rem;
32
+ font-weight: 700;
33
+ color: var(--boe-token-text-text, #1f1e1b);
34
+ }
35
+
36
+ [part="scroller"] {
37
+ overflow-x: auto;
38
+ }
39
+
40
+ [part="grid"] {
41
+ border-collapse: separate;
42
+ border-spacing: 3px;
43
+ }
44
+
45
+ [part="caption"] {
46
+ caption-side: top;
47
+ text-align: start;
48
+ padding-block-end: 0.35rem;
49
+ font-size: 0.8rem;
50
+ color: var(--boe-token-text-text-secondary, #6f6f6f);
51
+ }
52
+
53
+ [part="weekday"],
54
+ [part="month"] {
55
+ font-size: 0.66rem;
56
+ font-weight: 600;
57
+ color: var(--boe-token-text-text-secondary, #6f6f6f);
58
+ text-align: end;
59
+ padding-inline-end: 0.25rem;
60
+ white-space: nowrap;
61
+ }
62
+
63
+ [part="month"] {
64
+ text-align: start;
65
+ padding: 0;
66
+ }
67
+
68
+ [part="cell"] {
69
+ inline-size: 0.85rem;
70
+ block-size: 0.85rem;
71
+ padding: 0;
72
+ border-radius: ${boeRadius.size};
73
+ background: var(--boe-token-surface-surface-hover, #f4f4f4);
74
+ }
75
+
76
+ [part="cell"][data-level="1"] {
77
+ background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 28%, transparent);
78
+ }
79
+
80
+ [part="cell"][data-level="2"] {
81
+ background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 50%, transparent);
82
+ }
83
+
84
+ [part="cell"][data-level="3"] {
85
+ background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 74%, transparent);
86
+ }
87
+
88
+ [part="cell"][data-level="4"] {
89
+ background: var(--boe-token-surface-surface-brand, #0061d5);
90
+ }
91
+
92
+ [part="cell-button"] {
93
+ appearance: none;
94
+ display: block;
95
+ inline-size: 100%;
96
+ block-size: 100%;
97
+ min-block-size: 0.85rem;
98
+ padding: 0;
99
+ border: none;
100
+ border-radius: inherit;
101
+ background: none;
102
+ cursor: pointer;
103
+ }
104
+
105
+ [part="cell-button"]:focus-visible {
106
+ outline: 2px solid var(--boe-token-surface-surface-brand, #0061d5);
107
+ outline-offset: 2px;
108
+ }
109
+
110
+ [part="legend"] {
111
+ display: flex;
112
+ align-items: center;
113
+ gap: 0.3rem;
114
+ font-size: 0.72rem;
115
+ color: var(--boe-token-text-text-secondary, #6f6f6f);
116
+ }
117
+
118
+ [part="legend-swatch"] {
119
+ inline-size: 0.7rem;
120
+ block-size: 0.7rem;
121
+ border-radius: ${boeRadius.size};
122
+ background: var(--boe-token-surface-surface-hover, #f4f4f4);
123
+ }
124
+
125
+ [part="legend-swatch"][data-level="1"] {
126
+ background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 28%, transparent);
127
+ }
128
+
129
+ [part="legend-swatch"][data-level="2"] {
130
+ background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 50%, transparent);
131
+ }
132
+
133
+ [part="legend-swatch"][data-level="3"] {
134
+ background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 74%, transparent);
135
+ }
136
+
137
+ [part="legend-swatch"][data-level="4"] {
138
+ background: var(--boe-token-surface-surface-brand, #0061d5);
139
+ }
140
+ `;
141
+ /**
142
+ * The managerial companion to `box-audit-log`: a calendar heatmap of event
143
+ * volume per UTC day over a trailing window, for spotting throughput and
144
+ * quiet periods at a glance.
145
+ *
146
+ * Days with activity are buttons in a roving-tabindex grid (arrow keys move
147
+ * by day and by week, Home/End jump to the ends of the window), each labelled
148
+ * with its own count and date so the surface is understandable without the
149
+ * colour. Selecting one emits `day-selected` with that day's events — the
150
+ * drill-down into the log.
151
+ */
152
+ export class ActivityDensityStrip extends BaseElement {
153
+ static tagName = DEFAULT_TAG_NAME;
154
+ static get observedAttributes() {
155
+ return ["events", "heading", "reference-time", "weeks"];
156
+ }
157
+ titleEl;
158
+ gridEl;
159
+ captionEl;
160
+ monthsRowEl;
161
+ rowsEl;
162
+ signature = "";
163
+ /** Roving focus position, `week:weekday`. Null until the first render. */
164
+ activeKey = null;
165
+ renderedWeeks = 0;
166
+ /** Raw `events` attribute the parse cache was built from. */
167
+ eventsRaw = null;
168
+ eventsCache = [];
169
+ get heading() {
170
+ return this.getAttribute("heading") ?? "Activity density";
171
+ }
172
+ set heading(value) {
173
+ this.setAttribute("heading", value);
174
+ }
175
+ /**
176
+ * Memoized on the raw attribute for the same reason as `box-audit-log`:
177
+ * `density`, `eventsOn`, and `update` each read it, and an arrow keypress
178
+ * must not re-parse the payload. Returns a copy.
179
+ */
180
+ get events() {
181
+ const raw = this.getAttribute("events");
182
+ if (!raw) {
183
+ return [];
184
+ }
185
+ if (raw !== this.eventsRaw) {
186
+ this.eventsRaw = raw;
187
+ try {
188
+ const parsed = JSON.parse(raw);
189
+ this.eventsCache =
190
+ Array.isArray(parsed) && parsed.every(isTimelineEventRecord)
191
+ ? parsed
192
+ : [];
193
+ }
194
+ catch {
195
+ this.eventsCache = [];
196
+ }
197
+ }
198
+ return [...this.eventsCache];
199
+ }
200
+ set events(value) {
201
+ if (value.length) {
202
+ this.setAttribute("events", JSON.stringify(value));
203
+ return;
204
+ }
205
+ this.removeAttribute("events");
206
+ }
207
+ get weeks() {
208
+ const raw = Number.parseInt(this.getAttribute("weeks") ?? "", 10);
209
+ return Number.isFinite(raw) ? raw : 12;
210
+ }
211
+ set weeks(value) {
212
+ this.setAttribute("weeks", String(value));
213
+ }
214
+ get referenceTime() {
215
+ return this.getAttribute("reference-time") ?? "";
216
+ }
217
+ set referenceTime(value) {
218
+ this.setAttribute("reference-time", value);
219
+ }
220
+ /** The computed window — cells, totals, and the busiest day's count. */
221
+ get density() {
222
+ return computeActivityDensity(this.events, {
223
+ now: resolveReferenceTime(this.getAttribute("reference-time")),
224
+ weeks: this.weeks,
225
+ });
226
+ }
227
+ /** Every event on the given UTC day, in input order. */
228
+ eventsOn(date) {
229
+ return this.events.filter(event => resolveAuditDay(event) === date);
230
+ }
231
+ emitDaySelected(date, count) {
232
+ this.dispatchEvent(new CustomEvent("day-selected", {
233
+ bubbles: true,
234
+ composed: true,
235
+ detail: { date, count, events: this.eventsOn(date) },
236
+ }));
237
+ }
238
+ buttons() {
239
+ return Array.from(this.gridEl.querySelectorAll('[part="cell-button"]'));
240
+ }
241
+ /**
242
+ * Buttons oldest → newest. DOM order is weekday-major (the grid renders one
243
+ * row per weekday), so it is *not* chronological — anything that means
244
+ * "first" or "last day in the window" has to sort by date.
245
+ */
246
+ buttonsByDate() {
247
+ return this.buttons().sort((left, right) => (left.getAttribute("data-date") ?? "").localeCompare(right.getAttribute("data-date") ?? ""));
248
+ }
249
+ buttonAt(week, weekday) {
250
+ return this.gridEl.querySelector(`[part="cell-button"][data-week="${String(week)}"][data-weekday="${String(weekday)}"]`);
251
+ }
252
+ /** Walk in one grid direction until a day with activity is found. */
253
+ step(week, weekday, deltaWeek, deltaWeekday) {
254
+ // The rendered column count, not a recomputed window: a keypress must not
255
+ // re-parse the whole event payload.
256
+ const total = this.renderedWeeks;
257
+ let nextWeek = week + deltaWeek;
258
+ let nextWeekday = weekday + deltaWeekday;
259
+ while (nextWeek >= 0 && nextWeek < total && nextWeekday >= 0 && nextWeekday < 7) {
260
+ const button = this.buttonAt(nextWeek, nextWeekday);
261
+ if (button) {
262
+ return button;
263
+ }
264
+ nextWeek += deltaWeek;
265
+ nextWeekday += deltaWeekday;
266
+ }
267
+ return null;
268
+ }
269
+ focusButton(button) {
270
+ this.activeKey = `${button.getAttribute("data-week") ?? ""}:${button.getAttribute("data-weekday") ?? ""}`;
271
+ for (const candidate of this.buttons()) {
272
+ candidate.tabIndex = candidate === button ? 0 : -1;
273
+ }
274
+ button.focus();
275
+ }
276
+ /**
277
+ * A day with activity is a labelled button — that label is the accessible
278
+ * contract, since colour alone carries no meaning. A quiet day is an inert
279
+ * cell with a hover title: nothing to drill into, and no extra tab stop.
280
+ */
281
+ cellHtml(cell) {
282
+ const date = escapeHtml(cell.date);
283
+ const label = escapeHtml(`${String(cell.count)} ${cell.count === 1 ? "event" : "events"} on ${formatUtcDay(cell.date)}`);
284
+ const body = cell.count > 0
285
+ ? `<button type="button" part="cell-button" tabindex="-1" data-date="${date}" data-week="${String(cell.week)}" data-weekday="${String(cell.weekday)}" data-count="${String(cell.count)}" aria-label="${label}"></button>`
286
+ : "";
287
+ return `<td part="cell" data-level="${String(cell.level)}" data-date="${date}" data-count="${String(cell.count)}" title="${label}">${body}</td>`;
288
+ }
289
+ rebuild(density) {
290
+ this.renderedWeeks = density.weeks;
291
+ const byWeekday = new Map();
292
+ for (const cell of density.cells) {
293
+ const row = byWeekday.get(cell.weekday) ?? [];
294
+ row.push(cell);
295
+ byWeekday.set(cell.weekday, row);
296
+ }
297
+ // Column headers carry the month, printed once where it changes — the
298
+ // same sparse labelling a git contribution graph uses.
299
+ const firstRow = byWeekday.get(0) ?? [];
300
+ let lastMonth = "";
301
+ const monthCells = firstRow.map(cell => {
302
+ const month = cell.date.slice(0, 7);
303
+ const label = month === lastMonth ? "" : formatUtcMonth(cell.date);
304
+ lastMonth = month;
305
+ return `<th scope="col" part="month">${label}</th>`;
306
+ });
307
+ // Only the header row and the body are re-minted — the caption is part of
308
+ // the stable shell, so rebuilding the grid never drops it.
309
+ this.monthsRowEl.innerHTML = `<td></td>${monthCells.join("")}`;
310
+ this.rowsEl.innerHTML = WEEKDAY_LABELS.map((label, weekday) => {
311
+ const cells = (byWeekday.get(weekday) ?? []).map(cell => this.cellHtml(cell)).join("");
312
+ return `<tr><th scope="row" part="weekday">${label}</th>${cells}</tr>`;
313
+ }).join("");
314
+ }
315
+ renderTemplate() {
316
+ if (!this.shadowRoot) {
317
+ return;
318
+ }
319
+ this.shadowRoot.innerHTML = `
320
+ <style>${elementStyles}</style>
321
+ <section part="panel">
322
+ <h2 part="title"></h2>
323
+ <div part="scroller">
324
+ <table part="grid">
325
+ <caption part="caption"></caption>
326
+ <thead><tr part="months-row"></tr></thead>
327
+ <tbody part="rows"></tbody>
328
+ </table>
329
+ </div>
330
+ <div part="legend">
331
+ <span>Less</span>
332
+ ${[0, 1, 2, 3, 4]
333
+ .map(level => `<span part="legend-swatch" data-level="${String(level)}"></span>`)
334
+ .join("")}
335
+ <span>More</span>
336
+ </div>
337
+ </section>
338
+ `;
339
+ this.titleEl = this.shadowRoot.querySelector('[part="title"]');
340
+ this.gridEl = this.shadowRoot.querySelector('[part="grid"]');
341
+ this.captionEl = this.shadowRoot.querySelector('[part="caption"]');
342
+ this.monthsRowEl = this.shadowRoot.querySelector('[part="months-row"]');
343
+ this.rowsEl = this.shadowRoot.querySelector('[part="rows"]');
344
+ }
345
+ setupListeners() {
346
+ this.gridEl.addEventListener("click", event => {
347
+ const button = event.target.closest('[part="cell-button"]');
348
+ if (!button || !this.gridEl.contains(button)) {
349
+ return;
350
+ }
351
+ this.focusButton(button);
352
+ this.emitDaySelected(button.getAttribute("data-date") ?? "", Number(button.getAttribute("data-count") ?? "0"));
353
+ });
354
+ this.gridEl.addEventListener("keydown", event => {
355
+ const button = event.target.closest('[part="cell-button"]');
356
+ if (!button || !this.gridEl.contains(button)) {
357
+ return;
358
+ }
359
+ const week = Number(button.getAttribute("data-week") ?? "0");
360
+ const weekday = Number(button.getAttribute("data-weekday") ?? "0");
361
+ // Home/End mean the ends of the *window*, not the ends of the DOM.
362
+ const byDate = this.buttonsByDate();
363
+ let next = null;
364
+ switch (event.key) {
365
+ case "ArrowLeft":
366
+ next = this.step(week, weekday, -1, 0);
367
+ break;
368
+ case "ArrowRight":
369
+ next = this.step(week, weekday, 1, 0);
370
+ break;
371
+ case "ArrowUp":
372
+ next = this.step(week, weekday, 0, -1);
373
+ break;
374
+ case "ArrowDown":
375
+ next = this.step(week, weekday, 0, 1);
376
+ break;
377
+ case "Home":
378
+ next = byDate[0] ?? null;
379
+ break;
380
+ case "End":
381
+ next = byDate[byDate.length - 1] ?? null;
382
+ break;
383
+ default:
384
+ return;
385
+ }
386
+ if (next) {
387
+ event.preventDefault();
388
+ this.focusButton(next);
389
+ }
390
+ });
391
+ }
392
+ update() {
393
+ if (!this.gridEl) {
394
+ return;
395
+ }
396
+ this.titleEl.textContent = this.heading;
397
+ const density = this.density;
398
+ const signature = JSON.stringify([
399
+ this.getAttribute("events") ?? "",
400
+ this.weeks,
401
+ this.referenceTime,
402
+ ]);
403
+ if (signature !== this.signature) {
404
+ this.signature = signature;
405
+ this.rebuild(density);
406
+ // Cells are re-minted, so the previous roving position may be gone.
407
+ this.activeKey = null;
408
+ }
409
+ this.captionEl.textContent = `${String(density.total)} ${density.total === 1 ? "event" : "events"} from ${formatUtcDay(density.start)} to ${formatUtcDay(density.end)}`;
410
+ this.gridEl.setAttribute("aria-label", this.heading);
411
+ const all = this.buttons();
412
+ const byDate = this.buttonsByDate();
413
+ const active = (this.activeKey
414
+ ? all.find(button => `${button.getAttribute("data-week") ?? ""}:${button.getAttribute("data-weekday") ?? ""}` ===
415
+ this.activeKey)
416
+ : undefined) ??
417
+ // Default the tab stop to the most recent day with activity — the last
418
+ // by date, not the last in the weekday-major DOM order.
419
+ byDate[byDate.length - 1];
420
+ for (const button of all) {
421
+ button.tabIndex = button === active ? 0 : -1;
422
+ }
423
+ if (active) {
424
+ this.activeKey = `${active.getAttribute("data-week") ?? ""}:${active.getAttribute("data-weekday") ?? ""}`;
425
+ }
426
+ }
427
+ }
428
+ ActivityDensityStrip.register();
@@ -0,0 +1,81 @@
1
+ import type { AuditEvent, AuditFacets, AuditGroupBy } from "./types.js";
2
+ import { BaseElement } from "../../core/index.js";
3
+ /**
4
+ * The aggregation, faceting, and export layer over the `box-timeline` event
5
+ * contract (opportunity 3 of `plans/component-opportunities.md`): the same
6
+ * records the flat feed renders, grouped into collapsible day/actor/action
7
+ * sections with counts, narrowed by facets and a date range, drillable to one
8
+ * workflow run by correlation id, and exportable as CSV.
9
+ *
10
+ * Aggregation is client-side and pure. Server-side paging and row
11
+ * virtualization for production-scale logs are tracked depth limitations.
12
+ */
13
+ export declare class AuditLog extends BaseElement {
14
+ static readonly tagName: string;
15
+ static get observedAttributes(): string[];
16
+ private toolbarEl;
17
+ private groupByEl;
18
+ private actorSelectEl;
19
+ private actionSelectEl;
20
+ private fromInputEl;
21
+ private toInputEl;
22
+ private clearFiltersEl;
23
+ private exportEl;
24
+ private drilldownEl;
25
+ private drilldownIdEl;
26
+ private summaryEl;
27
+ private groupsEl;
28
+ private emptyEl;
29
+ private titleEl;
30
+ /** Section keys the reader has collapsed. Absent means expanded. */
31
+ private collapsedGroups;
32
+ private contentSignature;
33
+ private optionsSignature;
34
+ /** Raw `events` attribute the parse cache was built from. */
35
+ private eventsRaw;
36
+ private eventsCache;
37
+ get heading(): string;
38
+ set heading(value: string);
39
+ /**
40
+ * Audit records; JSON payloads are validated per record before rendering.
41
+ *
42
+ * Memoized on the raw attribute so a single keystroke in the date field
43
+ * does not re-parse and re-validate the whole payload once per reader
44
+ * (`update`, `visibleEvents`, and each click-handler branch all read it).
45
+ * The cache invalidates itself when the attribute changes; the returned
46
+ * array is a copy, so a caller cannot mutate the log out from under it.
47
+ */
48
+ get events(): AuditEvent[];
49
+ set events(value: AuditEvent[]);
50
+ get groupBy(): AuditGroupBy;
51
+ set groupBy(value: AuditGroupBy);
52
+ get facets(): AuditFacets;
53
+ /** Replaces the whole selection — an omitted or empty facet is cleared. */
54
+ set facets(value: AuditFacets);
55
+ get exportable(): boolean;
56
+ set exportable(value: boolean);
57
+ get referenceTime(): string;
58
+ set referenceTime(value: string);
59
+ /** The events currently on screen: the full set narrowed by the facets. */
60
+ get visibleEvents(): AuditEvent[];
61
+ toggleGroup(key: string, expanded?: boolean): void;
62
+ expandAll(): void;
63
+ collapseAll(): void;
64
+ /**
65
+ * Export what is on screen, not the whole set — an export that silently
66
+ * widens past the reader's filters is a compliance hazard. Returns the CSV
67
+ * and emits `export-requested` so the host owns delivery (download, mail,
68
+ * archive API).
69
+ */
70
+ exportCsv(): string;
71
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
72
+ private now;
73
+ private currentGroups;
74
+ private patchFacets;
75
+ private eventHtml;
76
+ private groupHtml;
77
+ private renderOptions;
78
+ protected renderTemplate(): void;
79
+ protected setupListeners(): void;
80
+ protected update(): void;
81
+ }