feat(motion): unify the animation selector to one `motion` prop — (a) of §D.12

`<Cascade>` was the last component using `animation` as its preset selector — a
vestige of the coordinated `cascade-*` system retired in Plan A. Every other
animatable component (Dialog, Popover, Drawer, …) already uses `motion`. Renames
`<Cascade>`'s prop + its context field `animation` → `motion`, so the framework
has ONE universal selector name (RFC §D.12 step a). `MotionPresetName` and the
low-level `data-animation-style` attr are unchanged.

- cascade: types `animation?` → `motion?`; context `CascadeContext.animation` →
  `.motion`; component destructure + getter; item reads `ctx?.motion`.
- docs/demo: README props table + usage, index usage, panel-cascade demo.

Verified at runtime: `<Cascade motion="scale-fade">` produces identical wiring —
items carry `data-animation-style="scale-fade"`, `animation-name: scale-in,
fade-in`, structural index 0/1/2/3. `check` clean for the touched files; no
dangling `animation`-prop reference remains (only the `data-animation-style` attr).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
active-uix
dev 4 months ago
parent efb20a9c5f
commit bfac34dd4f

@ -12,7 +12,7 @@ same foundation stagger do it (see "Two ways" below).
let open = $state(true) let open = $state(true)
</script> </script>
<Cascade {open} animation="scale-fade" style="display: grid; gap: 0.75rem; --motion-stagger-each: 60ms"> <Cascade {open} motion="scale-fade" style="display: grid; gap: 0.75rem; --motion-stagger-each: 60ms">
{#each items as item (item.id)} {#each items as item (item.id)}
<Cascade.Item>{item.label}</Cascade.Item> <Cascade.Item>{item.label}</Cascade.Item>
{/each} {/each}
@ -20,7 +20,7 @@ same foundation stagger do it (see "Two ways" below).
``` ```
`--motion-stagger-each: 0` (the default) → **parallel**; `N` → **cascade**. The keyframe, `--motion-stagger-each: 0` (the default) → **parallel**; `N` → **cascade**. The keyframe,
duration, easing and reduced-motion all come from the `animation` preset. duration, easing and reduced-motion all come from the `motion` preset.
## How it works — it reuses everything ## How it works — it reuses everything
@ -45,7 +45,7 @@ down (`:nth-last-child`) so the last item leaves first.
| Prop | Type | Default | Notes | | Prop | Type | Default | Notes |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `open` | `boolean` | `true` | Drives the staggered enter (forward) / exit (reverse) via `data-state`. | | `open` | `boolean` | `true` | Drives the staggered enter (forward) / exit (reverse) via `data-state`. |
| `animation` | `MotionPresetName` | `'fade'` | The state-preset every item plays (`fade` / `scale-fade` / `slide-fade` / …). | | `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`) Plus any `HTMLAttributes<HTMLDivElement>`. Set the layout + the rhythm (`--motion-stagger-each`)
here. here.

@ -30,7 +30,7 @@
<div <div
data-cascade-item data-cascade-item
data-animation-style={ctx?.animation ?? 'fade'} data-animation-style={ctx?.motion ?? 'fade'}
data-state={ctx?.open ? 'open' : 'closed'} data-state={ctx?.open ? 'open' : 'closed'}
out:leave out:leave
{...rest} {...rest}

@ -17,14 +17,14 @@
import { setCascadeContext } from './context'; import { setCascadeContext } from './context';
import type { CascadeProps } from './types'; import type { CascadeProps } from './types';
let { open = true, animation = 'fade', children, ...rest }: CascadeProps = $props(); let { open = true, motion = 'fade', children, ...rest }: CascadeProps = $props();
setCascadeContext({ setCascadeContext({
get open() { get open() {
return open; return open;
}, },
get animation() { get motion() {
return animation; return motion;
} }
}); });
</script> </script>

@ -5,12 +5,13 @@ const KEY = Symbol('cascade');
/** /**
* What `<Cascade>` passes to its items: the container's open state + the state-preset the * What `<Cascade>` passes to its items: the container's open state + the state-preset the
* items should play. Items read it to carry `data-state` + `data-animation-style` — they * items should play (the `motion` prop value). Items read it to carry `data-state` +
* never receive a timing or a visual var, only these two semantic/structural facts. * `data-animation-style` — they never receive a timing or a visual var, only these two
* semantic/structural facts.
*/ */
export type CascadeContext = { export type CascadeContext = {
readonly open: boolean; readonly open: boolean;
readonly animation: MotionPresetName; readonly motion: MotionPresetName;
}; };
export function setCascadeContext(ctx: CascadeContext): void { export function setCascadeContext(ctx: CascadeContext): void {

@ -2,7 +2,7 @@
// //
// import { Cascade } from '$uix/eidos/components/cascade'; // import { Cascade } from '$uix/eidos/components/cascade';
// //
// <Cascade open={isOpen} animation="scale-fade" style="display: grid; gap: 0.75rem; --motion-stagger-each: 60ms"> // <Cascade open={isOpen} motion="scale-fade" style="display: grid; gap: 0.75rem; --motion-stagger-each: 60ms">
// {#each items as item (item.id)} // {#each items as item (item.id)}
// <Cascade.Item>{item.label}</Cascade.Item> // <Cascade.Item>{item.label}</Cascade.Item>
// {/each} // {/each}

@ -13,9 +13,10 @@ export type CascadeProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
/** /**
* The state-preset every item plays (its `data-animation-style`) — any registered eidos * The state-preset every item plays (its `data-animation-style`) — any registered eidos
* motion preset (`fade`, `scale-fade`, `slide-fade`, …). The keyframe / duration / * motion preset (`fade`, `scale-fade`, `slide-fade`, …). The keyframe / duration /
* reduced-motion all come from the preset. @default 'fade' * reduced-motion all come from the preset. Unified with the overlays' selector: the
* universal `motion` prop (RFC §D.12), not a separate `animation` axis. @default 'fade'
*/ */
animation?: MotionPresetName; motion?: MotionPresetName;
children?: Snippet; children?: Snippet;
}; };

@ -95,13 +95,13 @@
<p class="note"> <p class="note">
Sin recipe propio: el contenedor se marca <code>data-stagger</code> (la foundation da el Sin recipe propio: el contenedor se marca <code>data-stagger</code> (la foundation da el
índice por <code>:nth-child</code>), los items cargan un preset existente índice por <code>:nth-child</code>), los items cargan un preset existente
(<code>animation="scale-fade"</code>) + <code>data-state</code>, y el stagger sale del (<code>motion="scale-fade"</code>) + <code>data-state</code>, y el stagger sale del
preset (<code>--motion-stagger-each</code>, 0 = paralelo). Retención <code>out:</code> en el preset (<code>--motion-stagger-each</code>, 0 = paralelo). Retención <code>out:</code> en el
item. Cero <code>--cascade-*</code>. item. Cero <code>--cascade-*</code>.
</p> </p>
<Cascade <Cascade
{open} {open}
animation="scale-fade" motion="scale-fade"
style="display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: 0.75rem; padding: 1rem; border: 1px solid var(--color-border-subtle, #e5e5e5); border-radius: var(--radius-lg, 12px); min-block-size: 5rem; --motion-stagger-each: {rhythm}ms" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: 0.75rem; padding: 1rem; border: 1px solid var(--color-border-subtle, #e5e5e5); border-radius: var(--radius-lg, 12px); min-block-size: 5rem; --motion-stagger-each: {rhythm}ms"
> >
{#each cards as i (i)} {#each cards as i (i)}

Loading…
Cancel
Save

Powered by TurnKey Linux.