diff --git a/.changeset/animation-groups.md b/.changeset/animation-groups.md deleted file mode 100644 index 81919e6..0000000 --- a/.changeset/animation-groups.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -"@effex/dom": minor ---- - -Add animation groups — declarative sequencing across multiple animated blocks. Solves the "chained word-by-word intro" case where each word is its own `each` and word N should only start after word N-1 finishes. - -```ts -import { $, collect, each, stagger, Animation } from "@effex/dom"; - -const App = () => - Effect.gen(function* () { - const [greeting, name, tagline] = yield* Animation.sequence(3); - return $.div( - {}, - collect( - each(greetingLetters, { - key: (l) => l.id, - render: (l) => $.span({}, $.of(l.char)), - animate: { enter: "letter-in", stagger: stagger(40), group: greeting }, - }), - each(nameLetters, { - key: (l) => l.id, - render: (l) => $.span({}, $.of(l.char)), - animate: { enter: "letter-in", stagger: stagger(40), group: name }, - }), - each(taglineLetters, { - key: (l) => l.id, - render: (l) => $.span({}, $.of(l.char)), - animate: { enter: "letter-in", stagger: stagger(40), group: tagline }, - }), - ), - ); - }); -``` - -New API: - -- `Animation.group()` — creates a group with a gate (unresolved) and a completion signal. -- `Animation.sequence(count)` — returns `count` groups wired end-to-end: group 0's gate is open immediately, group N's gate opens when group N-1's registered animations all complete. -- `Animation.parallel(count)` — returns `count` groups with all gates open (useful nested inside `sequence` for concurrent segments in a follow-up release). -- `animate.group: AnimationGroup` — new option on any animated control (`each`, `when`, `match`, ...). The animation registers with the group synchronously, awaits the gate before starting, and signals completion when finished. - -Groups finalize when their pending count returns to zero for the first time after having been non-zero. Late registrations that arrive after finalization run immediately (gate is already open) — intended semantics for one-shot intros where post-sequence additions behave like ordinary animations. diff --git a/.changeset/deprecate-animation-helpers.md b/.changeset/deprecate-animation-helpers.md deleted file mode 100644 index d590a19..0000000 --- a/.changeset/deprecate-animation-helpers.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"@effex/dom": patch ---- - -Deprecate five under-used animation helpers with `@deprecated` JSDoc tags. All continue to work — they'll be removed in a future major. - -- `staggerEased` — compose your own: `(index, total) => easingFn(index / (total - 1)) * totalDurationMs` -- `delay` — use `Effect.delay(effect, ms)` directly -- `sequence` — use `Effect.all([...], { concurrency: 1 })` directly -- `parallel` — use `Effect.all([...], { concurrency: "unbounded" })` directly -- `calculateStaggerDelay` — was only ever an internal helper; not re-exported by anything downstream - -These wrapped effect combinators without adding real value beyond a slightly shorter name; the framework should be a thin layer over Effect rather than shadow its stdlib. diff --git a/.changeset/fix-animation-serialization.md b/.changeset/fix-animation-serialization.md deleted file mode 100644 index 779c1a3..0000000 --- a/.changeset/fix-animation-serialization.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@effex/dom": patch ---- - -Fix inline animation serialization in `addSlot` / `removeSlot`. Previously each enter/exit animation was awaited via `yield*`, which serialized the `reconcile` sync loop — for a list of `[a, b, c]` added at once, item `b`'s animation would only start after item `a`'s finished. Made `stagger` configs effectively double-counted (each addSlot's stagger delay applied *after* the previous animation completed rather than concurrently). - -Now enter animations are forked into the slot's scope so multiple slots animate concurrently, and exit animations run in a fiber attached to the parent scope so `removeSlot` returns immediately (letting the reconcile loop continue) while the exit still plays before the DOM node is removed. If a slot is removed mid-enter, closing the slot scope interrupts the enter animation before exit starts. This applies to `ClientControlCtx` and both factories in `HydrationControlCtx`. - -`@effex/router` will receive an automatic patch via `updateInternalDependencies` since it depends on `@effex/dom` as a workspace dependency. diff --git a/apps/docs/CHANGELOG.md b/apps/docs/CHANGELOG.md index 182e838..ecbe667 100644 --- a/apps/docs/CHANGELOG.md +++ b/apps/docs/CHANGELOG.md @@ -1,5 +1,15 @@ # docs +## 0.0.14 + +### Patch Changes + +- Updated dependencies [b650fd8] +- Updated dependencies [12654be] +- Updated dependencies [0dd6440] + - @effex/dom@1.2.0 + - @effex/router@1.2.2 + ## 0.0.13 ### Patch Changes diff --git a/apps/docs/package.json b/apps/docs/package.json index c9c8949..1a5b9c6 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.0.13", + "version": "0.0.14", "private": true, "type": "module", "scripts": { diff --git a/packages/dom/CHANGELOG.md b/packages/dom/CHANGELOG.md index 0a79532..52e8eb3 100644 --- a/packages/dom/CHANGELOG.md +++ b/packages/dom/CHANGELOG.md @@ -1,5 +1,73 @@ # @effex/dom +## 1.2.0 + +### Minor Changes + +- b650fd8: Add animation groups — declarative sequencing across multiple animated blocks. Solves the "chained word-by-word intro" case where each word is its own `each` and word N should only start after word N-1 finishes. + + ```ts + import { $, Animation, collect, each, stagger } from "@effex/dom"; + + const App = () => + Effect.gen(function* () { + const [greeting, name, tagline] = yield* Animation.sequence(3); + return $.div( + {}, + collect( + each(greetingLetters, { + key: (l) => l.id, + render: (l) => $.span({}, $.of(l.char)), + animate: { + enter: "letter-in", + stagger: stagger(40), + group: greeting, + }, + }), + each(nameLetters, { + key: (l) => l.id, + render: (l) => $.span({}, $.of(l.char)), + animate: { enter: "letter-in", stagger: stagger(40), group: name }, + }), + each(taglineLetters, { + key: (l) => l.id, + render: (l) => $.span({}, $.of(l.char)), + animate: { + enter: "letter-in", + stagger: stagger(40), + group: tagline, + }, + }), + ), + ); + }); + ``` + + New API: + - `Animation.group()` — creates a group with a gate (unresolved) and a completion signal. + - `Animation.sequence(count)` — returns `count` groups wired end-to-end: group 0's gate is open immediately, group N's gate opens when group N-1's registered animations all complete. + - `Animation.parallel(count)` — returns `count` groups with all gates open (useful nested inside `sequence` for concurrent segments in a follow-up release). + - `animate.group: AnimationGroup` — new option on any animated control (`each`, `when`, `match`, ...). The animation registers with the group synchronously, awaits the gate before starting, and signals completion when finished. + + Groups finalize when their pending count returns to zero for the first time after having been non-zero. Late registrations that arrive after finalization run immediately (gate is already open) — intended semantics for one-shot intros where post-sequence additions behave like ordinary animations. + +### Patch Changes + +- 12654be: Deprecate five under-used animation helpers with `@deprecated` JSDoc tags. All continue to work — they'll be removed in a future major. + - `staggerEased` — compose your own: `(index, total) => easingFn(index / (total - 1)) * totalDurationMs` + - `delay` — use `Effect.delay(effect, ms)` directly + - `sequence` — use `Effect.all([...], { concurrency: 1 })` directly + - `parallel` — use `Effect.all([...], { concurrency: "unbounded" })` directly + - `calculateStaggerDelay` — was only ever an internal helper; not re-exported by anything downstream + + These wrapped effect combinators without adding real value beyond a slightly shorter name; the framework should be a thin layer over Effect rather than shadow its stdlib. + +- 0dd6440: Fix inline animation serialization in `addSlot` / `removeSlot`. Previously each enter/exit animation was awaited via `yield*`, which serialized the `reconcile` sync loop — for a list of `[a, b, c]` added at once, item `b`'s animation would only start after item `a`'s finished. Made `stagger` configs effectively double-counted (each addSlot's stagger delay applied _after_ the previous animation completed rather than concurrently). + + Now enter animations are forked into the slot's scope so multiple slots animate concurrently, and exit animations run in a fiber attached to the parent scope so `removeSlot` returns immediately (letting the reconcile loop continue) while the exit still plays before the DOM node is removed. If a slot is removed mid-enter, closing the slot scope interrupts the enter animation before exit starts. This applies to `ClientControlCtx` and both factories in `HydrationControlCtx`. + + `@effex/router` will receive an automatic patch via `updateInternalDependencies` since it depends on `@effex/dom` as a workspace dependency. + ## 1.1.1 ### Patch Changes diff --git a/packages/dom/package.json b/packages/dom/package.json index e41dc98..f742d82 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -1,6 +1,6 @@ { "name": "@effex/dom", - "version": "1.1.1", + "version": "1.2.0", "description": "DOM rendering for Effex - a reactive UI framework built on Effect.ts", "type": "module", "license": "MIT", diff --git a/packages/router/CHANGELOG.md b/packages/router/CHANGELOG.md index b763905..fde0032 100644 --- a/packages/router/CHANGELOG.md +++ b/packages/router/CHANGELOG.md @@ -1,5 +1,14 @@ # @effex/router +## 1.2.2 + +### Patch Changes + +- Updated dependencies [b650fd8] +- Updated dependencies [12654be] +- Updated dependencies [0dd6440] + - @effex/dom@1.2.0 + ## 1.2.1 ### Patch Changes diff --git a/packages/router/package.json b/packages/router/package.json index e310347..c5114e1 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@effex/router", - "version": "1.2.1", + "version": "1.2.2", "description": "Router for Effex applications", "type": "module", "license": "MIT",