@profpowell/vb-project-planning 0.2.0 → 0.2.2

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 (73) hide show
  1. package/README.md +5 -2
  2. package/dist/vb-project-planning.css +70 -3
  3. package/dist/vb-project-planning.js +3333 -2565
  4. package/dist/vb-project-planning.min.css +1 -1
  5. package/dist/vb-project-planning.min.js +224 -200
  6. package/package.json +7 -4
  7. package/src/lib/board-surface.js +158 -0
  8. package/src/lib/bundle-registry.js +2 -75
  9. package/src/lib/dates.js +125 -0
  10. package/src/lib/escape.js +34 -0
  11. package/src/lib/hash.js +16 -0
  12. package/src/lib/portrait-url.js +3 -29
  13. package/src/lib/src-loader.js +78 -0
  14. package/src/utils/copy-init.js +56 -17
  15. package/src/web-components/_ux-base.js +409 -24
  16. package/src/web-components/adr-wc/README.md +18 -16
  17. package/src/web-components/adr-wc/api.json +7 -5
  18. package/src/web-components/adr-wc/logic.js +50 -136
  19. package/src/web-components/burndown-chart/api.json +8 -6
  20. package/src/web-components/burndown-chart/logic.js +75 -39
  21. package/src/web-components/empathy-map/api.json +13 -13
  22. package/src/web-components/empathy-map/logic.js +161 -168
  23. package/src/web-components/empathy-map/static.html +3 -3
  24. package/src/web-components/empathy-map/styles.js +1 -0
  25. package/src/web-components/gantt-chart/README.md +12 -6
  26. package/src/web-components/gantt-chart/api.json +23 -7
  27. package/src/web-components/gantt-chart/logic.js +456 -431
  28. package/src/web-components/gantt-chart/styles.css +9 -1
  29. package/src/web-components/impact-effort/api.json +9 -7
  30. package/src/web-components/impact-effort/logic.js +238 -209
  31. package/src/web-components/impact-effort/static.html +12 -4
  32. package/src/web-components/iron-triangle/_capacity.js +1 -19
  33. package/src/web-components/iron-triangle/_triangle-geometry.js +1 -1
  34. package/src/web-components/iron-triangle/api.json +2 -2
  35. package/src/web-components/iron-triangle/logic.js +47 -9
  36. package/src/web-components/iron-triangle/styles.css +22 -1
  37. package/src/web-components/kanban-board/api.json +8 -6
  38. package/src/web-components/kanban-board/logic.js +289 -333
  39. package/src/web-components/product-roadmap/api.json +14 -13
  40. package/src/web-components/product-roadmap/logic.js +393 -246
  41. package/src/web-components/product-roadmap/styles.css +40 -0
  42. package/src/web-components/quadrant-grid/api.json +9 -4
  43. package/src/web-components/quadrant-grid/logic.js +80 -12
  44. package/src/web-components/quadrant-grid/styles.css +7 -0
  45. package/src/web-components/quality-target/_quality-utils.js +2 -46
  46. package/src/web-components/quality-target/api.json +2 -3
  47. package/src/web-components/quality-target/logic.js +85 -13
  48. package/src/web-components/requirement-card/api.json +1 -1
  49. package/src/web-components/requirement-card/logic.js +26 -2
  50. package/src/web-components/requirement-card/static.html +1 -1
  51. package/src/web-components/review-surface/README.md +9 -5
  52. package/src/web-components/review-surface/api.json +10 -8
  53. package/src/web-components/review-surface/logic.js +349 -218
  54. package/src/web-components/risk-register/api.json +1 -6
  55. package/src/web-components/risk-register/logic.js +71 -24
  56. package/src/web-components/story-map/api.json +8 -6
  57. package/src/web-components/story-map/logic.js +280 -281
  58. package/src/web-components/story-map/static.html +15 -5
  59. package/src/web-components/traceability-matrix/api.json +10 -3
  60. package/src/web-components/traceability-matrix/logic.js +112 -63
  61. package/src/web-components/traceability-matrix/styles.css +14 -1
  62. package/src/web-components/user-journey/api.json +25 -24
  63. package/src/web-components/user-journey/logic.js +76 -127
  64. package/src/web-components/user-journey/static.html +2 -2
  65. package/src/web-components/user-persona/api.json +15 -14
  66. package/src/web-components/user-persona/logic.js +71 -117
  67. package/src/web-components/user-persona/styles.js +1 -0
  68. package/src/web-components/user-story/api.json +13 -11
  69. package/src/web-components/user-story/logic.js +164 -195
  70. package/src/web-components/user-story/styles.js +28 -1
  71. package/src/web-components/work-item/README.md +3 -3
  72. package/src/web-components/work-item/api.json +13 -11
  73. package/src/web-components/work-item/logic.js +58 -154
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * <requirement-card> — Display atom for ONE NFR ility row.
3
3
  *
4
- * Display-only. The prioritization decision lives in <nfr-compass>;
4
+ * Display-only. The prioritization decision lives in <quality-target>;
5
5
  * this component just renders one row's worth of state — name +
6
6
  * category badge + priority pill + rationale + (optional) conflict flag —
7
7
  * for use on status dashboards, ADR appendices, or anywhere a project
8
- * wants to surface a single ility outside the compass.
8
+ * wants to surface a single ility outside the target.
9
9
  *
10
10
  * Slots:
11
11
  * name — ility label (e.g. "Performance")
@@ -24,6 +24,8 @@
24
24
  * requirement-card:click — bubbles a structured detail when the card
25
25
  * is activated (mouse, keyboard, or touch);
26
26
  * parent components can wire navigation.
27
+ * Inside an <a>/<button> wrapper, the
28
+ * wrapper's own keyboard activation counts.
27
29
  *
28
30
  * Pattern reference: matches the shadow-DOM + slot conventions from
29
31
  * <score-card> (also a single-row dashboard atom).
@@ -58,6 +60,9 @@ const TEMPLATE = `
58
60
  `;
59
61
 
60
62
  class RequirementCard extends VBElement {
63
+ /** True when this element added role="button" itself (so it may remove it). */
64
+ #ownsRole = false;
65
+
61
66
  static get observedAttributes() {
62
67
  return ['data-priority', 'data-conflict', 'tabindex'];
63
68
  }
@@ -82,6 +87,15 @@ class RequirementCard extends VBElement {
82
87
  this.#syncInteractive();
83
88
 
84
89
  this.listen(this, 'click', (e) => this.#onActivate(e));
90
+ // Keyboard activation of a wrapping <a>/<button> fires `click` on the
91
+ // wrapper itself, which never reaches the card's own listener. Clicks
92
+ // that started inside the card are already handled above.
93
+ const wrapper = this.closest('a[href], button');
94
+ if (wrapper) {
95
+ this.listen(wrapper, 'click', (e) => {
96
+ if (!this.contains(/** @type {Node} */ (e.target))) this.#onActivate(e);
97
+ });
98
+ }
85
99
  this.listen(this, 'keydown', (e) => {
86
100
  const ke = /** @type {KeyboardEvent} */ (e);
87
101
  if ((ke.key === 'Enter' || ke.key === ' ') && this.#interactive) {
@@ -146,6 +160,16 @@ class RequirementCard extends VBElement {
146
160
 
147
161
  #syncInteractive() {
148
162
  this.setState('interactive', !!this.#interactive);
163
+ // A focusable card that is not inside a native control needs a role
164
+ // so assistive tech announces it as activatable. Author roles win.
165
+ const needsRole = this.hasAttribute('tabindex') && !this.closest('a[href], button');
166
+ if (needsRole && !this.hasAttribute('role')) {
167
+ this.setAttribute('role', 'button');
168
+ this.#ownsRole = true;
169
+ } else if (!needsRole && this.#ownsRole) {
170
+ this.removeAttribute('role');
171
+ this.#ownsRole = false;
172
+ }
149
173
  }
150
174
 
151
175
  #onActivate(event) {
@@ -8,7 +8,7 @@
8
8
  <body>
9
9
  <!--
10
10
  Display-only card for ONE NFR ility row. The prioritization
11
- decision lives in <nfr-compass>; this is the visual atom.
11
+ decision lives in <quality-target>; this is the visual atom.
12
12
  Pre-upgrade, the card renders as a plain article — no JS needed
13
13
  for it to be readable.
14
14
  -->
@@ -92,13 +92,15 @@ With JavaScript:
92
92
 
93
93
  | Event | Detail | Description |
94
94
  |-------|--------|-------------|
95
- | `review-surface:ready` | `{ pinCount }` | After render |
95
+ | `review-surface:upgraded` | — | Once, on first connect |
96
+ | `review-surface:ready` | `{ pinCount }` | Once, after the initial pins render |
96
97
  | `review-surface:add` | `{ pin }` | Pin created |
97
98
  | `review-surface:update` | `{ pin, changes }` | Pin modified |
98
99
  | `review-surface:remove` | `{ pin }` | Pin deleted |
99
100
  | `review-surface:resolve` | `{ pin }` | Pin resolved |
100
101
  | `review-surface:select` | `{ pin }` | Popover opened |
101
102
  | `review-surface:mode` | `{ mode }` | Mode toggled |
103
+ | `review-surface:pins-changed` | `{ pins, source }` | After `.pins` assignment |
102
104
 
103
105
  ## Adapters
104
106
 
@@ -121,22 +123,24 @@ Set via property:
121
123
  ```js
122
124
  document.querySelector('review-surface').adapter = myCustomAdapter;
123
125
  ```
124
- Must implement: `load()`, `save(pin)`, `update(id, changes)`, `remove(id)`.
126
+ Must implement: `load()`, `save(pin)`, `update(id, changes, pin)`, `remove(id)`.
127
+ `update` receives the component's full pin as a third argument so it can
128
+ upsert pins it did not create (loaded via `src` or `.pins`).
125
129
 
126
130
  ## Failure Modes
127
131
 
128
132
  - **No JavaScript:** Child content renders normally; pins are not shown.
129
133
  - **No CSS:** Shadow DOM styles are self-contained; layout degrades gracefully.
130
- - **Keyboard Only:** Tab navigates pins; Enter opens popover; Escape closes.
134
+ - **Keyboard Only:** Tab navigates pins; Enter opens popover (focus moves into it); Escape closes and returns focus to the pin.
131
135
  - **Screen Reader:** Pin markers announced as "Pin N: [text preview]";
132
136
  live region announces create/delete/resolve actions.
133
137
 
134
138
  ## Accessibility
135
139
 
136
- - Pin markers are `<button>` with `aria-label`, `aria-expanded`, `aria-haspopup="dialog"`
140
+ - Pin markers are `<button>` with `aria-label`, `aria-expanded`, `aria-haspopup="dialog"`, inside a `role="group"` overlay labelled "Review pins"
137
141
  - Popover uses `role="dialog"` with `aria-labelledby`
138
142
  - Toolbar uses `role="toolbar"` with `aria-label`
139
- - `aria-live="polite"` region announces pin actions
143
+ - One persistent `aria-live="polite"` region announces pin actions
140
144
  - All transitions respect `prefers-reduced-motion: reduce`
141
145
 
142
146
  ## CSS Tokens
@@ -5,7 +5,7 @@
5
5
  "description": "Pin-based annotation overlay for design review — wraps any content and adds interactive comment pins with pluggable persistence",
6
6
  "htmlvalidate": { "flow": true, "permittedContent": ["@flow"] },
7
7
  "attributes": [
8
- { "name": "src", "kind": "host-api", "purpose": "config", "type": "string", "description": "URL to JSON file containing pin data" },
8
+ { "name": "src", "kind": "host-api", "purpose": "config", "type": "string", "description": "URL to JSON file containing pin data (an array, or { pins }). A newer value aborts an older in-flight request; removing it, or assigning .pins / importPins(), aborts a pending request (removing it mid-load falls back to the adapter)" },
9
9
  { "name": "editable", "kind": "host-api", "purpose": "config", "type": "boolean", "description": "Enables annotate mode toolbar and pin creation" },
10
10
  { "name": "adapter", "kind": "host-api", "purpose": "config", "type": "string", "description": "Persistence backend: memory | local | rest" },
11
11
  { "name": "endpoint", "kind": "host-api", "purpose": "config", "type": "string", "description": "REST endpoint URL (when adapter=rest)" },
@@ -19,12 +19,14 @@
19
19
  { "name": "(default)", "description": "The content to annotate (any HTML)" }
20
20
  ],
21
21
  "events": [
22
- { "name": "review-surface:ready", "detail": "{ pinCount }", "description": "Fired after initial render" },
23
- { "name": "review-surface:add", "detail": "{ pin }", "description": "Fired when a pin is created" },
24
- { "name": "review-surface:update", "detail": "{ pin, changes }", "description": "Fired when a pin is modified" },
25
- { "name": "review-surface:remove", "detail": "{ pin }", "description": "Fired when a pin is deleted" },
26
- { "name": "review-surface:resolve", "detail": "{ pin }", "description": "Fired when a pin is marked resolved" },
27
- { "name": "review-surface:select", "detail": "{ pin }", "description": "Fired when a pin popover is opened" },
28
- { "name": "review-surface:mode", "detail": "{ mode }", "description": "Fired when view/annotate mode toggles" }
22
+ { "name": "review-surface:upgraded", "detail": "—", "description": "Fired once, on first connect, after the adapter is resolved and before the initial pin load. Not re-fired when the element is moved." },
23
+ { "name": "review-surface:ready", "detail": "{ pinCount }", "description": "Fired once, after the initial pins (from src, the adapter, or a pre-connect .pins assignment) have loaded and rendered. Not re-fired on later renders." },
24
+ { "name": "review-surface:add", "detail": "{ pin }", "description": "Fired when a pin is created (annotate-mode click or addPin())" },
25
+ { "name": "review-surface:update", "detail": "{ pin, changes }", "description": "Fired when a pin is modified (reply added, text set, re-opened)" },
26
+ { "name": "review-surface:remove", "detail": "{ pin }", "description": "Fired when a pin is deleted" },
27
+ { "name": "review-surface:resolve", "detail": "{ pin }", "description": "Fired when a pin is marked resolved" },
28
+ { "name": "review-surface:select", "detail": "{ pin }", "description": "Fired when a pin popover is opened" },
29
+ { "name": "review-surface:mode", "detail": "{ mode: 'view' | 'annotate' }", "description": "Fired when view/annotate mode toggles" },
30
+ { "name": "review-surface:pins-changed", "detail": "{ pins, source: 'property' }", "description": "Fired after a .pins assignment (not for adapter loads or addPin())" }
29
31
  ]
30
32
  }