@profpowell/vb-project-planning 0.1.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 (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/dist/vb-project-planning.css +1603 -0
  4. package/dist/vb-project-planning.js +11138 -0
  5. package/dist/vb-project-planning.min.css +1 -0
  6. package/dist/vb-project-planning.min.js +3312 -0
  7. package/package.json +80 -0
  8. package/src/index.css +14 -0
  9. package/src/index.js +26 -0
  10. package/src/lib/bundle-registry.js +156 -0
  11. package/src/lib/diff-by-key.js +86 -0
  12. package/src/lib/portrait-url.js +87 -0
  13. package/src/lib/vb-collection.js +122 -0
  14. package/src/lib/vb-element.js +96 -0
  15. package/src/lib/vb-store.js +200 -0
  16. package/src/lib/vb-view-transition.js +44 -0
  17. package/src/utils/copy-init.js +287 -0
  18. package/src/web-components/_ux-base.js +106 -0
  19. package/src/web-components/adr-wc/README.md +77 -0
  20. package/src/web-components/adr-wc/api.json +26 -0
  21. package/src/web-components/adr-wc/logic.js +376 -0
  22. package/src/web-components/adr-wc/static.html +17 -0
  23. package/src/web-components/adr-wc/styles.js +338 -0
  24. package/src/web-components/burndown-chart/api.json +74 -0
  25. package/src/web-components/burndown-chart/logic.js +198 -0
  26. package/src/web-components/burndown-chart/static.html +31 -0
  27. package/src/web-components/burndown-chart/styles.css +59 -0
  28. package/src/web-components/capacity-plan/_capacity-utils.js +56 -0
  29. package/src/web-components/capacity-plan/api.json +20 -0
  30. package/src/web-components/capacity-plan/logic.js +222 -0
  31. package/src/web-components/capacity-plan/static.html +24 -0
  32. package/src/web-components/capacity-plan/styles.css +84 -0
  33. package/src/web-components/empathy-map/api.json +31 -0
  34. package/src/web-components/empathy-map/logic.js +502 -0
  35. package/src/web-components/empathy-map/static.html +32 -0
  36. package/src/web-components/empathy-map/styles.js +534 -0
  37. package/src/web-components/gantt-chart/README.md +91 -0
  38. package/src/web-components/gantt-chart/api.json +20 -0
  39. package/src/web-components/gantt-chart/logic.js +812 -0
  40. package/src/web-components/gantt-chart/static.html +60 -0
  41. package/src/web-components/gantt-chart/styles.css +391 -0
  42. package/src/web-components/impact-effort/api.json +21 -0
  43. package/src/web-components/impact-effort/logic.js +392 -0
  44. package/src/web-components/impact-effort/static.html +23 -0
  45. package/src/web-components/impact-effort/styles.css +193 -0
  46. package/src/web-components/iron-triangle/_capacity.js +83 -0
  47. package/src/web-components/iron-triangle/_triangle-geometry.js +322 -0
  48. package/src/web-components/iron-triangle/api.json +26 -0
  49. package/src/web-components/iron-triangle/logic.js +474 -0
  50. package/src/web-components/iron-triangle/static.html +28 -0
  51. package/src/web-components/iron-triangle/styles.css +158 -0
  52. package/src/web-components/kanban-board/api.json +24 -0
  53. package/src/web-components/kanban-board/logic.js +698 -0
  54. package/src/web-components/kanban-board/static.html +20 -0
  55. package/src/web-components/kanban-board/styles.css +242 -0
  56. package/src/web-components/product-roadmap/api.json +118 -0
  57. package/src/web-components/product-roadmap/logic.js +460 -0
  58. package/src/web-components/product-roadmap/static.html +44 -0
  59. package/src/web-components/product-roadmap/styles.css +158 -0
  60. package/src/web-components/quadrant-grid/api.json +125 -0
  61. package/src/web-components/quadrant-grid/logic.js +397 -0
  62. package/src/web-components/quadrant-grid/static.html +49 -0
  63. package/src/web-components/quadrant-grid/styles.css +184 -0
  64. package/src/web-components/quality-target/_quality-utils.js +460 -0
  65. package/src/web-components/quality-target/api.json +30 -0
  66. package/src/web-components/quality-target/logic.js +516 -0
  67. package/src/web-components/quality-target/static.html +22 -0
  68. package/src/web-components/quality-target/styles.css +203 -0
  69. package/src/web-components/requirement-card/api.json +25 -0
  70. package/src/web-components/requirement-card/logic.js +166 -0
  71. package/src/web-components/requirement-card/static.html +36 -0
  72. package/src/web-components/requirement-card/styles.js +114 -0
  73. package/src/web-components/review-surface/README.md +178 -0
  74. package/src/web-components/review-surface/api.json +30 -0
  75. package/src/web-components/review-surface/logic.js +807 -0
  76. package/src/web-components/review-surface/static.html +33 -0
  77. package/src/web-components/review-surface/styles.js +459 -0
  78. package/src/web-components/risk-register/api.json +47 -0
  79. package/src/web-components/risk-register/logic.js +180 -0
  80. package/src/web-components/risk-register/static.html +46 -0
  81. package/src/web-components/risk-register/styles.css +37 -0
  82. package/src/web-components/story-map/api.json +22 -0
  83. package/src/web-components/story-map/logic.js +445 -0
  84. package/src/web-components/story-map/static.html +35 -0
  85. package/src/web-components/story-map/styles.css +169 -0
  86. package/src/web-components/traceability-matrix/api.json +84 -0
  87. package/src/web-components/traceability-matrix/logic.js +243 -0
  88. package/src/web-components/traceability-matrix/static.html +41 -0
  89. package/src/web-components/traceability-matrix/styles.css +94 -0
  90. package/src/web-components/user-journey/api.json +84 -0
  91. package/src/web-components/user-journey/logic.js +368 -0
  92. package/src/web-components/user-journey/static.html +14 -0
  93. package/src/web-components/user-journey/styles.js +317 -0
  94. package/src/web-components/user-persona/api.json +35 -0
  95. package/src/web-components/user-persona/logic.js +502 -0
  96. package/src/web-components/user-persona/static.html +20 -0
  97. package/src/web-components/user-persona/styles.js +299 -0
  98. package/src/web-components/user-story/api.json +35 -0
  99. package/src/web-components/user-story/logic.js +567 -0
  100. package/src/web-components/user-story/static.html +27 -0
  101. package/src/web-components/user-story/styles.js +332 -0
  102. package/src/web-components/work-item/README.md +85 -0
  103. package/src/web-components/work-item/api.json +30 -0
  104. package/src/web-components/work-item/logic.js +414 -0
  105. package/src/web-components/work-item/static.html +24 -0
  106. package/src/web-components/work-item/styles.js +376 -0
@@ -0,0 +1,33 @@
1
+ <!--
2
+ review-surface — static (zero-JS) form
3
+ Without JS, child content renders normally.
4
+ Pin data can be listed as a fallback <ol>.
5
+ -->
6
+ <review-surface
7
+ editable
8
+ adapter="local"
9
+ storage-key="demo-review"
10
+ author="Sarah Chen">
11
+
12
+ <img src="/comps/landing-page-v3.png"
13
+ alt="Landing page design comp"
14
+ width="800" height="600" />
15
+
16
+ <ol slot="pins-fallback">
17
+ <li>
18
+ <strong>Sarah Chen</strong> (42%, 18%):
19
+ Header alignment is off — needs 8px left padding
20
+ </li>
21
+ <li>
22
+ <strong>Tom Rivera</strong> (67%, 55%):
23
+ CTA button colour doesn't match the brand guide
24
+ <ul>
25
+ <li><strong>Sarah Chen</strong>: Updated in v2 — please re-check</li>
26
+ </ul>
27
+ </li>
28
+ <li>
29
+ <strong>Sarah Chen</strong> (15%, 82%):
30
+ Footer links need to match the nav order
31
+ </li>
32
+ </ol>
33
+ </review-surface>
@@ -0,0 +1,459 @@
1
+ /**
2
+ * Shadow DOM styles for <review-surface>
3
+ *
4
+ * Uses 3-tier custom property pattern:
5
+ * --review-surface-* (author override)
6
+ * --color-* (VB theme token)
7
+ * #fallback (hardcoded default)
8
+ */
9
+ const styles = `
10
+ :host {
11
+ display: block;
12
+ position: relative;
13
+ font-family: var(--_font-sans);
14
+ line-height: 1.5;
15
+ color: var(--_text);
16
+ container-type: inline-size;
17
+
18
+ --_bg: var(--review-surface-bg, var(--color-surface, #ffffff));
19
+ --_card: var(--review-surface-card, var(--color-surface-raised, #f8f9fa));
20
+ --_border: var(--review-surface-border, var(--color-border, #e0e0e0));
21
+ --_text: var(--review-surface-text, var(--color-text, #1a1a1a));
22
+ --_muted: var(--review-surface-muted, var(--color-text-muted, #666666));
23
+ --_accent: var(--review-surface-accent, var(--color-interactive, #0066cc));
24
+ --_pin-bg: var(--review-surface-pin-bg, var(--color-interactive, #0066cc));
25
+ --_pin-text: var(--review-surface-pin-text, #ffffff);
26
+ --_pin-size: var(--review-surface-pin-size, 28px);
27
+ --_resolved: var(--review-surface-resolved, var(--color-success, #16a34a));
28
+ --_radius: var(--review-surface-radius, var(--radius-xl, 1rem));
29
+ --_radius-m: var(--review-surface-radius-m, var(--radius-m, 0.5rem));
30
+ --_radius-full: var(--review-surface-radius-full, var(--radius-full, 9999px));
31
+ --_font-sans: var(--review-surface-font, var(--font-sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif));
32
+ --_font-xs: var(--review-surface-font-xs, var(--font-size-xs, 0.75rem));
33
+ --_font-sm: var(--review-surface-font-sm, var(--font-size-sm, 0.875rem));
34
+ --_font-md: var(--review-surface-font-md, var(--font-size-md, 1rem));
35
+ --_space-2xs: var(--review-surface-space-2xs, var(--size-2xs, 0.25rem));
36
+ --_space-xs: var(--review-surface-space-xs, var(--size-xs, 0.5rem));
37
+ --_space-s: var(--review-surface-space-s, var(--size-s, 0.75rem));
38
+ --_space-m: var(--review-surface-space-m, var(--size-m, 1rem));
39
+ --_space-l: var(--review-surface-space-l, var(--size-l, 1.5rem));
40
+ --_duration: var(--review-surface-duration, var(--duration-normal, 200ms));
41
+ --_duration-fast: var(--review-surface-duration-fast, var(--duration-fast, 100ms));
42
+ --_ease: var(--review-surface-ease, var(--ease-default, ease));
43
+ }
44
+
45
+ *, *::before, *::after { box-sizing: border-box; margin: 0; }
46
+
47
+ /* ── Container ───────────────────────────────────── */
48
+ .rs-container {
49
+ position: relative;
50
+ display: grid;
51
+ }
52
+
53
+ .rs-container > ::slotted(*),
54
+ .rs-container > .rs-overlay {
55
+ grid-area: 1 / 1;
56
+ }
57
+
58
+ /* ── Overlay ─────────────────────────────────────── */
59
+ .rs-overlay {
60
+ position: relative;
61
+ z-index: 10;
62
+ pointer-events: none;
63
+ cursor: default;
64
+ }
65
+
66
+ :host([data-annotating]) .rs-overlay {
67
+ pointer-events: auto;
68
+ cursor: crosshair;
69
+ }
70
+
71
+ /* ── Pin markers ─────────────────────────────────── */
72
+ .rs-pin {
73
+ position: absolute;
74
+ transform: translate(-50%, -50%);
75
+ width: var(--_pin-size);
76
+ height: var(--_pin-size);
77
+ border-radius: var(--_radius-full);
78
+ background: var(--_pin-bg);
79
+ color: var(--_pin-text);
80
+ border: 2px solid var(--_pin-text);
81
+ font-size: var(--_font-xs);
82
+ font-weight: 700;
83
+ font-family: inherit;
84
+ display: inline-flex;
85
+ align-items: center;
86
+ justify-content: center;
87
+ cursor: pointer;
88
+ pointer-events: auto;
89
+ padding: 0;
90
+ line-height: 1;
91
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
92
+ transition: transform var(--_duration-fast) var(--_ease),
93
+ box-shadow var(--_duration-fast) var(--_ease);
94
+ z-index: 11;
95
+ }
96
+
97
+ .rs-pin:hover {
98
+ transform: translate(-50%, -50%) scale(1.15);
99
+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.25);
100
+ }
101
+
102
+ .rs-pin:focus-visible {
103
+ outline: 2px solid var(--_accent);
104
+ outline-offset: 2px;
105
+ }
106
+
107
+ .rs-pin[data-resolved] {
108
+ background: var(--_resolved);
109
+ }
110
+
111
+ .rs-pin[data-active] {
112
+ transform: translate(-50%, -50%) scale(1.2);
113
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--_pin-bg) 30%, transparent);
114
+ }
115
+
116
+ /* ── Popover ─────────────────────────────────────── */
117
+ .rs-popover {
118
+ position: absolute;
119
+ z-index: 20;
120
+ width: 300px;
121
+ max-height: 400px;
122
+ overflow-y: auto;
123
+ background: var(--_bg);
124
+ border: 1px solid var(--_border);
125
+ border-radius: var(--_radius-m);
126
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
127
+ padding: 0;
128
+ opacity: 0;
129
+ transform: translateY(4px);
130
+ transition: opacity var(--_duration) var(--_ease), transform var(--_duration) var(--_ease);
131
+ pointer-events: none;
132
+ }
133
+
134
+ .rs-popover[data-open] {
135
+ opacity: 1;
136
+ transform: translateY(0);
137
+ pointer-events: auto;
138
+ }
139
+
140
+ .rs-popover__header {
141
+ display: flex;
142
+ align-items: center;
143
+ justify-content: space-between;
144
+ padding: 10px 12px 8px;
145
+ border-block-end: 1px solid var(--_border);
146
+ }
147
+
148
+ .rs-popover__title {
149
+ font-size: var(--_font-sm);
150
+ font-weight: 700;
151
+ color: var(--_text);
152
+ }
153
+
154
+ .rs-popover__actions {
155
+ display: flex;
156
+ gap: 4px;
157
+ }
158
+
159
+ .rs-popover__btn {
160
+ display: inline-flex;
161
+ align-items: center;
162
+ justify-content: center;
163
+ width: 26px;
164
+ height: 26px;
165
+ border: none;
166
+ border-radius: 4px;
167
+ background: transparent;
168
+ color: var(--_muted);
169
+ cursor: pointer;
170
+ padding: 0;
171
+ transition: background var(--_duration-fast) var(--_ease), color var(--_duration-fast) var(--_ease);
172
+ }
173
+
174
+ .rs-popover__btn svg {
175
+ width: 14px;
176
+ height: 14px;
177
+ }
178
+
179
+ .rs-popover__btn:hover {
180
+ background: var(--_border);
181
+ color: var(--_text);
182
+ }
183
+
184
+ .rs-popover__btn--resolve {
185
+ color: var(--_resolved);
186
+ }
187
+
188
+ .rs-popover__btn--resolve:hover {
189
+ background: color-mix(in srgb, var(--_resolved) 15%, transparent);
190
+ }
191
+
192
+ .rs-popover__btn--delete {
193
+ color: var(--color-error-text, var(--color-error, #dc2626));
194
+ }
195
+
196
+ .rs-popover__btn--delete:hover {
197
+ background: color-mix(in oklch, var(--color-error, #dc2626) 8%, var(--_card));
198
+ color: var(--color-error-text, var(--color-error, #dc2626));
199
+ }
200
+
201
+ /* ── Comment thread ──────────────────────────────── */
202
+ .rs-comment {
203
+ padding: 10px 12px;
204
+ }
205
+
206
+ .rs-comment__meta {
207
+ display: flex;
208
+ align-items: center;
209
+ gap: 6px;
210
+ margin-block-end: 4px;
211
+ }
212
+
213
+ .rs-comment__avatar {
214
+ width: 22px;
215
+ height: 22px;
216
+ border-radius: var(--_radius-full);
217
+ font-size: 10px;
218
+ font-weight: 700;
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: center;
222
+ color: var(--color-text-inverted, #fff);
223
+ flex-shrink: 0;
224
+ }
225
+
226
+ .rs-comment__author {
227
+ font-size: var(--_font-xs);
228
+ font-weight: 600;
229
+ color: var(--_text);
230
+ }
231
+
232
+ .rs-comment__time {
233
+ font-size: 11px;
234
+ color: var(--_muted);
235
+ }
236
+
237
+ .rs-comment__text {
238
+ font-size: var(--_font-sm);
239
+ color: var(--_text);
240
+ line-height: 1.5;
241
+ white-space: pre-wrap;
242
+ word-break: break-word;
243
+ }
244
+
245
+ /* Replies */
246
+ .rs-replies {
247
+ border-block-start: 1px solid var(--_border);
248
+ }
249
+
250
+ .rs-reply {
251
+ padding: 8px 12px;
252
+ border-block-end: 1px solid color-mix(in srgb, var(--_border) 50%, transparent);
253
+ }
254
+
255
+ .rs-reply:last-child {
256
+ border-block-end: none;
257
+ }
258
+
259
+ /* ── Input area ──────────────────────────────────── */
260
+ .rs-input {
261
+ display: flex;
262
+ gap: 6px;
263
+ padding: 8px 12px;
264
+ border-block-start: 1px solid var(--_border);
265
+ background: var(--_card);
266
+ border-radius: 0 0 var(--_radius-m) var(--_radius-m);
267
+ }
268
+
269
+ .rs-input__field {
270
+ flex: 1;
271
+ min-height: 32px;
272
+ max-height: 80px;
273
+ padding: 6px 10px;
274
+ border: 1px solid var(--_border);
275
+ border-radius: 6px;
276
+ font-family: inherit;
277
+ font-size: var(--_font-xs);
278
+ line-height: 1.4;
279
+ color: var(--_text);
280
+ background: var(--_bg);
281
+ resize: none;
282
+ }
283
+
284
+ .rs-input__field:focus {
285
+ outline: 2px solid var(--_accent);
286
+ outline-offset: -1px;
287
+ border-color: var(--_accent);
288
+ }
289
+
290
+ .rs-input__field::placeholder {
291
+ color: var(--_muted);
292
+ }
293
+
294
+ .rs-input__submit {
295
+ align-self: flex-end;
296
+ display: inline-flex;
297
+ align-items: center;
298
+ justify-content: center;
299
+ width: 32px;
300
+ height: 32px;
301
+ border: none;
302
+ border-radius: 6px;
303
+ background: var(--_accent);
304
+ color: var(--color-text-inverted, #fff);
305
+ cursor: pointer;
306
+ padding: 0;
307
+ flex-shrink: 0;
308
+ transition: background var(--_duration-fast) var(--_ease);
309
+ }
310
+
311
+ .rs-input__submit svg {
312
+ width: 14px;
313
+ height: 14px;
314
+ }
315
+
316
+ .rs-input__submit:hover {
317
+ background: color-mix(in srgb, var(--_accent) 85%, #000);
318
+ }
319
+
320
+ .rs-input__submit:disabled {
321
+ opacity: 0.4;
322
+ cursor: not-allowed;
323
+ }
324
+
325
+ /* ── Toolbar ─────────────────────────────────────── */
326
+ .rs-toolbar {
327
+ display: flex;
328
+ align-items: center;
329
+ gap: var(--_space-xs);
330
+ padding: var(--_space-xs) var(--_space-s);
331
+ background: var(--_card);
332
+ border: 1px solid var(--_border);
333
+ border-radius: var(--_radius-m);
334
+ margin-block-start: var(--_space-xs);
335
+ }
336
+
337
+ .rs-toolbar__btn {
338
+ display: inline-flex;
339
+ align-items: center;
340
+ gap: 5px;
341
+ padding: 4px 10px;
342
+ border: 1px solid var(--_border);
343
+ border-radius: 6px;
344
+ background: var(--_bg);
345
+ color: var(--_text);
346
+ font-family: inherit;
347
+ font-size: var(--_font-xs);
348
+ font-weight: 600;
349
+ cursor: pointer;
350
+ line-height: 1;
351
+ transition: background var(--_duration-fast) var(--_ease),
352
+ border-color var(--_duration-fast) var(--_ease);
353
+ }
354
+
355
+ .rs-toolbar__btn svg {
356
+ width: 14px;
357
+ height: 14px;
358
+ }
359
+
360
+ .rs-toolbar__btn:hover {
361
+ background: var(--_border);
362
+ }
363
+
364
+ .rs-toolbar__btn[aria-pressed="true"] {
365
+ background: var(--_accent);
366
+ color: var(--color-text-inverted, #fff);
367
+ border-color: var(--_accent);
368
+ }
369
+
370
+ .rs-toolbar__count {
371
+ font-size: var(--_font-xs);
372
+ color: var(--_muted);
373
+ margin-inline-start: auto;
374
+ }
375
+
376
+ /* ── Resolved badge ──────────────────────────────── */
377
+ .rs-resolved-badge {
378
+ display: inline-flex;
379
+ align-items: center;
380
+ gap: 4px;
381
+ font-size: 11px;
382
+ font-weight: 600;
383
+ color: var(--_resolved);
384
+ padding: 2px 8px;
385
+ border-radius: var(--_radius-full);
386
+ background: color-mix(in srgb, var(--_resolved) 12%, transparent);
387
+ }
388
+
389
+ .rs-resolved-badge svg {
390
+ width: 12px;
391
+ height: 12px;
392
+ }
393
+
394
+ /* ── Live region ─────────────────────────────────── */
395
+ .rs-live {
396
+ position: absolute;
397
+ width: 1px;
398
+ height: 1px;
399
+ overflow: hidden;
400
+ clip: rect(0, 0, 0, 0);
401
+ white-space: nowrap;
402
+ }
403
+
404
+ /* ── Compact ─────────────────────────────────────── */
405
+ :host([compact]) {
406
+ --_pin-size: 22px;
407
+ }
408
+
409
+ :host([compact]) .rs-popover {
410
+ width: 250px;
411
+ max-height: 300px;
412
+ }
413
+
414
+ :host([compact]) .rs-toolbar {
415
+ padding: 3px var(--_space-xs);
416
+ }
417
+
418
+ :host([compact]) .rs-toolbar__btn {
419
+ font-size: 11px;
420
+ padding: 3px 8px;
421
+ }
422
+
423
+ /* ── Responsive ──────────────────────────────────── */
424
+ @container (max-width: 400px) {
425
+ .rs-popover {
426
+ width: 240px;
427
+ }
428
+
429
+ .rs-toolbar {
430
+ flex-wrap: wrap;
431
+ }
432
+ }
433
+
434
+ /* ── Motion ──────────────────────────────────────── */
435
+ @media (prefers-reduced-motion: reduce) {
436
+ .rs-pin,
437
+ .rs-popover,
438
+ .rs-toolbar__btn,
439
+ .rs-popover__btn,
440
+ .rs-input__submit {
441
+ transition: none;
442
+ }
443
+ }
444
+
445
+ /* ── Print ───────────────────────────────────────── */
446
+ @media print {
447
+ .rs-overlay,
448
+ .rs-toolbar,
449
+ .rs-popover {
450
+ display: none;
451
+ }
452
+ }
453
+
454
+ /* ── Utility ─────────────────────────────────────── */
455
+ .state-msg { padding: var(--_space-l); font-size: var(--_font-sm); color: var(--_muted); font-style: italic; }
456
+ .state-msg--error { color: var(--color-error-text, var(--color-error, #dc2626)); }
457
+ `;
458
+
459
+ export { styles };
@@ -0,0 +1,47 @@
1
+ {
2
+ "$schema": "../../../schemas/api.schema.json",
3
+ "element": "risk-register",
4
+ "type": "web-component",
5
+ "description": "Composing preset that renders a sortable risk table + a likelihood \u00d7 impact quadrant grid from one source",
6
+ "attributes": [
7
+ {
8
+ "name": "src",
9
+ "kind": "host-api",
10
+ "purpose": "config",
11
+ "type": "string",
12
+ "description": "URL to JSON [{ id, title, likelihood, impact, owner, mitigation }]"
13
+ },
14
+ {
15
+ "name": "label",
16
+ "kind": "host-api",
17
+ "purpose": "config",
18
+ "type": "string",
19
+ "description": "Optional heading shown above the layout"
20
+ }
21
+ ],
22
+ "structure": [
23
+ {
24
+ "element": "<template>",
25
+ "description": "Inline data source: <tr> rows with cells [Title, Likelihood, Impact, Owner, Mitigation]"
26
+ }
27
+ ],
28
+ "childAttributes": [],
29
+ "events": [
30
+ {
31
+ "name": "risk-register:ready",
32
+ "detail": "{ count }",
33
+ "description": "Fired after the table + grid render"
34
+ },
35
+ {
36
+ "name": "risk-register:change",
37
+ "detail": "{ row, field, value }",
38
+ "description": "Reserved: emitted on inline edits (future)"
39
+ }
40
+ ],
41
+ "htmlvalidate": {
42
+ "flow": true,
43
+ "permittedContent": [
44
+ "@flow"
45
+ ]
46
+ }
47
+ }