Skip to content

Commit 3c3f699

Browse files
committed
feat(cards): add click/keydown decision flowchart
1 parent fbd3971 commit 3c3f699

1 file changed

Lines changed: 33 additions & 0 deletions

File tree

CONTRIBUTOR-DOCS/03_project-planning/03_components/card/migration-plan.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,39 @@ Both modes need the same two supporting pieces, whichever (or both) are enabled:
207207
- **Elevate nested interactive targets** — actions, in-content links, etc. need `position: relative` plus a higher stacking order than the stretched pseudo-element/click-catching surface, in `card-template.css`, so native hit-testing routes their own clicks to them rather than the card surface. This is a CSS-only concern, independent of the JS filtering below.
208208
- **Filter clicks on nested interactive targets** — implemented in `CardBase`'s click handler via `event.composedPath()`, checking each node's `tabIndex` IDL property (not the `tabindex` attribute or a tag-name list): natively-interactive elements (`button`, `input`, `select`, `textarea`, `a[href]`) report `tabIndex >= 0` with no explicit attribute needed, `tabindex="-1"` and disabled controls correctly report as non-interactive, and — because `composedPath()` already traverses into other custom elements' shadow roots for composed events like `click` — an internal `<button>` inside e.g. `<swc-button>` slotted into `actions` is inspected directly, regardless of which shadow tree it belongs to. No tag-name enumeration, and no `stopPropagation()` wiring on wrapper elements in `renderCardTemplate()`, is needed. The `actions` slot is additionally excluded unconditionally (checking whether its `<slot>` element appears in the composed path) as defense in depth, since it's contractually for interactive content regardless of whether a given control correctly reflects focusability — mirroring 1st-gen's own belt-and-suspenders approach (a hard `stopPropagationOnHref` boundary for actions, plus a softer anchor-detection heuristic for everything else).
209209

210+
<details>
211+
<summary>Click/keydown decision flowchart</summary>
212+
213+
(`handleSurfaceClick`/`handleSelectableKeydown` in `Card.base.ts`)
214+
215+
```mermaid
216+
flowchart TD
217+
A["Pointer click on card (bubbles to host)"] --> C
218+
B["Enter / Space keydown (listener only attached when selectable)"] --> C
219+
220+
C{"titleAsLink or\nselectable set?"}
221+
C -->|No| Z1["No-op"]
222+
C -->|Yes| D{"Click path includes the actions slot?"}
223+
224+
D -->|Yes| Z2["No-op — actions slot is\nexcluded unconditionally"]
225+
D -->|No| E{"Any node before the host has tabIndex >= 0?\n(checks across shadow-root boundaries too)"}
226+
227+
E -->|Yes| Z3["No-op — hit a nested interactive target"]
228+
E -->|No| F{"titleAsLink set?"}
229+
230+
F -->|Yes| G["Find the title slot's linked anchor (direct or nested, requires href) and call .click()"]
231+
F -->|No| H
232+
G --> H{"selectable set?"}
233+
234+
H -->|Yes| I["Dispatch swc-card-click\n(bubbles, composed)"]
235+
H -->|No| J["Done"]
236+
I --> J
237+
```
238+
239+
</details>
240+
241+
Both branches (`titleAsLink`'s proxy-click and `selectable`'s event dispatch) run independently once a click clears the filtering step — a card with both attributes set does both for the same click.
242+
210243
**Where this lives:** the `title-as-link`/`selectable` property declarations and the click-filtering/proxy logic are all behavior with no rendering — they belong in `CardBase`'s `SHARED API`/`IMPLEMENTATION` sections. Resolving the assigned title-link element needs **no** change to `renderCardTemplate()`: the existing `<slot name="title">` and `<slot name="actions">` markup is already directly queryable via `renderRoot.querySelector('slot[name="..."]')`, and `HTMLSlotElement.assignedElements()` returns the actual light-DOM element(s) assigned to it — that's the standard API for reading slotted content from script.
211244

212245
**Status:** implemented in `Card.base.ts``titleAsLink`/`selectable` properties, `tabindex` management, the click-vs-interactive-target filter, and the click-proxy/event dispatch. Only `selectable`'s dispatched event name remains open (see [Blockers Q1](#blockers-and-open-questions)).

0 commit comments

Comments
 (0)