docs(plan): Plan 3 Task A2 — DraggableBlock canonical API reconciled from 2 consumers
This commit is contained in:
@@ -7,3 +7,39 @@ The parity harness spans two PrimeVue installations that configure dark-mode dif
|
||||
> crewli-starter: `darkModeSelector: '[data-theme="dark"]'`, dark primary hardcoded `#1eafb1`, bespoke surface block.
|
||||
> apps/app: `darkModeSelector: '.dark'` (RFC-WS-FRONTEND-PRIMEVUE AD-2, matches Vuexy), dark primary `{primary.400}` Aura ramp, RFC Appendix B surface tokens.
|
||||
> **Decision (option b — normalise at the harness):** v2 components NEVER hardcode a dark selector or a semantic hex; they consume `var(--p-*)` Aura tokens only, which resolve correctly under apps/app's `.dark`. The parity harness renders the crewli-starter reference under `[data-theme="dark"]` and the v2 component under `.dark`; the human parity-check compares **rendered pixels**, not selector strings. The `#1eafb1` vs `{primary.400}` ramp delta is **accepted** (same teal family; RFC AD-2 owns the apps/app ramp) and explicitly recorded so a dark-mode parity diff is read as theme-by-design, not a component bug.
|
||||
|
||||
## DraggableBlock dual-consumer reconciliation
|
||||
|
||||
Spec §7.1 is the canonical DraggableBlock contract. Two crewli-starter consumers use fundamentally different drag models — `TimetableGrid` uses a `mousedown`→`window` `mousemove`/`mouseup` pointer approach, while `CueTimelineEditor` uses native HTML5 drag (`draggable="true"`, `dataTransfer.setData`). Both map cleanly to the §7.1 prop/emit contract as shown below.
|
||||
|
||||
```
|
||||
TimetableGrid performance block → §7.1
|
||||
artist.name → line1Left.text
|
||||
status tag (engagement) → line1Left.tag {label, severity}
|
||||
genre → line1Right.pill
|
||||
capacity/conflict warn → line1Right.tag {label:'!', severity:'danger'}
|
||||
blockTime(start,end) → line2Left
|
||||
advanceCount done/total → line2Right.progress (0..1 → 0..100)
|
||||
selected (selectedId) → selected
|
||||
drag.value!=null → dragging
|
||||
baseRowHeight 56/64/76 → density compact|regular|comfy
|
||||
|
||||
CueTimelineEditor cue block → §7.1
|
||||
cue.label → line1Left.text
|
||||
cue.kind tag → line1Left.tag {label, severity}
|
||||
cue.dest pill → line1Right.pill
|
||||
(none) → line1Right.tag
|
||||
cue.time → line2Left
|
||||
(none) → line2Right (null — no progress on cues)
|
||||
selectedCueId===cue.id → selected
|
||||
drag?.id===cue.id → dragging
|
||||
fixed 'regular' → density
|
||||
```
|
||||
|
||||
### Drag model
|
||||
|
||||
> **Drag model = PointerEvents, parent owns positioning.** `DraggableBlock` is presentational. On `pointerdown` (primary button) it calls `setPointerCapture`, tracks a 3px move threshold (TimetableGrid parity), emits `dragstart: [e: PointerEvent]` once threshold is crossed, and on `pointerup`/`lostpointercapture` emits `dragend: [delta: { x: number; y: number }]` (`clientX/Y` minus start). It performs **zero** snap/lane/px-min math. `TimetableGrid` keeps `startDragMove`'s math but is driven by `@dragstart`/`@dragend` instead of its own `mousedown`. `CueTimelineEditor` drops HTML5 drag and adopts the same emits. `click` is emitted only when no drag occurred (threshold not crossed). `vuedraggable` is not used (wrong abstraction for free-position blocks; spec §7.1).
|
||||
|
||||
### Retrofit proof
|
||||
|
||||
> Plan 3 is incomplete without a retrofit proof per consumer: `DraggableBlock.stories.ts` MUST include an `ArtistBlock` story (TimetableGrid usage expressed in the §7.1 contract) and a `CueBlock` story (CueTimelineEditor usage in the §7.1 contract). These prove the abstraction expresses both consumers without either consumer's page existing yet (Tier-4 defers the pages, not this proof).
|
||||
|
||||
Reference in New Issue
Block a user