You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/components/cascade/README.md

104 lines
5.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Cascade
A thin **explicit** orchestrator that staggers its children's enter/exit. It owns **no recipe
and no timing of its own** — it rides the foundation stagger + the existing state-presets. It
is the *explicit* / content-animation way to sequence a set of elements (you designed them to
stagger), as opposed to the *event-driven* path where a component's own `emerge` firma + the
same foundation stagger do it (see "Two ways" below).
```svelte
<script>
import { Cascade } from '$uix/eidos/components/cascade'
let open = $state(true)
</script>
<Cascade {open} motion="scale-fade" style="display: grid; gap: 0.75rem; --motion-stagger-each: 60ms">
{#each items as item (item.id)}
<Cascade.Item>{item.label}</Cascade.Item>
{/each}
</Cascade>
```
`--motion-stagger-each: 0` (the default) → **parallel**; `N` → **cascade**. The keyframe,
duration, easing and reduced-motion all come from the `motion` preset.
## How it works — it reuses everything
Nothing here is a new system. The cascade is the *existing declared motion* plus one
foundation rule:
| Piece | Where it's declared / derived |
| --- | --- |
| the per-item **animation** (keyframe, duration, ease, reduced-motion) | a **state-preset** declared in `EidosConfig.motion` (`fade` / `scale-fade` / …), generated by eidos to `generated/base.css` and registered with `uix.motion` |
| the **stagger delay** (`index × --motion-stagger-each`, default 0 = parallel) | already in **every preset's** enter/exit rule (`lib/render-css.ts`) |
| the **structural index** (`--motion-stagger-index` / `-rev`) | the one new foundation rule: `[data-stagger] > *:nth-child / :nth-last-child` (generated by `lib/render-css.ts`) — **nobody writes it**, soma never writes a visual var |
`<Cascade>` just **wires** those: it marks the container `[data-stagger]`, reflects `open` as
`data-state`, and passes `open` + the chosen preset to its items (context), so each item
carries `data-animation-style` + `data-state`. Enter counts up (`:nth-child`); exit counts
down (`:nth-last-child`) so the last item leaves first.
## Props
### `<Cascade>`
| Prop | Type | Default | Notes |
| --- | --- | --- | --- |
| `open` | `boolean` | `true` | Drives the staggered enter (forward) / exit (reverse) via `data-state`. |
| `motion` | `MotionPresetName` | `'fade'` | The state-preset every item plays (`fade` / `scale-fade` / `slide-fade` / …). Unified with the overlays' selector (RFC §D.12). |
Plus any `HTMLAttributes<HTMLDivElement>`. Set the layout + the rhythm (`--motion-stagger-each`)
here.
### `<Cascade.Item>`
A participant. On removal it flips to `data-state='closed'` (the existing **exit** preset
plays, with the reverse structural index), is retained (`out:`) for the eidos-declared exit
duration, then unmounts. Accepts any `HTMLAttributes<HTMLDivElement>`.
## Ownership
| | Owns | Never touches |
| --- | --- | --- |
| **soma** (the wrappers) | **state** (`open` → `data-state`, propagated to items) + **lifecycle** (`out:` retention) | any `--motion-*` / visual variable — it reads the eidos duration via the ActiveDom `getComputedStyle`, writes nothing |
| **eidos** | **all the visual** — the preset (keyframe/duration/ease/reduced-motion) + the foundation stagger index | the semantics / state |
## Two ways to stagger children
1. **Explicit** — `<Cascade>` (this component). You want these elements to stagger; there is no
semantic event. The content/intrinsic animation domain.
2. **Event-driven** — a real component (a menu, a list) declares its own `emerge` firma (the
container flourish + sound) **and** marks its item container `[data-stagger]`; its items
carry a preset (`data-animation-style`). Same foundation stagger, no `<Cascade>` wrapper —
the appearance flows from the declared event. The container-driven preset rule
`[data-stagger][data-state='open'] > [data-animation-style]` fires the children's enter off
the *container's* `data-state` (which soma already writes via the morfo `commits`), so the
items carry **no per-item `data-state`** — a `menuitem` isn't open/closed; the menu is.
(The firma is *generic by event*: "children of an emerging container present together, by
structural order"; the concrete timing — parallel/cascade, ms, keyframe — is the
per-component realization.)
**First concrete consumer: `<DropdownMenu>`** — items fade in staggered on open
(`src/uix/eidos/components/dropdown-menu/`). See its README → *Motion*. The container-driven
rule is **enter-only**; the coordinated *exit* cascade (retain the container until its
children finish) is the deferred soma `PresenceGroup` work.
## Known limitation — bulk removal
When **several** items are removed at once (e.g. a slider jumping 10 → 3), the leaving nodes
unmount one-by-one as each finishes, so the grid reflows during the cascade and the remaining
items' `:nth-last-child` index re-evaluates. In practice it reads fine, but the glitch-free
version wants the **container to retain the leaving set as a unit** (one reflow at the end) —
a soma **lifecycle** concern (what the retired `pending` did, without touching the visual).
Dragging the slider down step-by-step (a sequence of lone removals) is always clean.
## Decisions
- **No recipe, no `--cascade-*` namespace, no bespoke keyframe.** An earlier prototype forked
all of that — it reinvented the existing `--motion-stagger-*` + state-preset system. This
replaces it by reusing them; the only new code is the foundation structural-index writer.
- **`Cascade` is the EXPLICIT domain.** Event-driven children appearance rides the firma +
the same foundation stagger, declared in the component's own morfo — not this wrapper.
- **Index from `:nth-child`, capped 1–24** (the foundation writer). Beyond the cap items share
the last index until `sibling-index()` is broadly supported.

Powered by TurnKey Linux.