@solid-primitives/transition-group
Advanced tools
Comparing version 0.0.1-beta.1 to 0.0.1-beta.2
@@ -1,2 +0,2 @@ | ||
import { Accessor } from 'solid-js'; | ||
import { Accessor } from "solid-js"; | ||
@@ -6,6 +6,6 @@ type TransitionMode = "out-in" | "in-out" | "parallel"; | ||
type SwitchTransitionOptions<T> = { | ||
onEnter?: OnTransition<T>; | ||
onExit?: OnTransition<T>; | ||
mode?: TransitionMode; | ||
appear?: boolean; | ||
onEnter?: OnTransition<T>; | ||
onExit?: OnTransition<T>; | ||
mode?: TransitionMode; | ||
appear?: boolean; | ||
}; | ||
@@ -46,12 +46,15 @@ /** | ||
*/ | ||
declare function createSwitchTransition<T>(source: Accessor<T>, options: SwitchTransitionOptions<NonNullable<T>>): Accessor<NonNullable<T>[]>; | ||
declare function createSwitchTransition<T>( | ||
source: Accessor<T>, | ||
options: SwitchTransitionOptions<NonNullable<T>>, | ||
): Accessor<NonNullable<T>[]>; | ||
type OnListChange<T> = (payload: { | ||
added: T[]; | ||
removed: T[]; | ||
moved: T[]; | ||
finishRemoved: (els: T[]) => void; | ||
added: T[]; | ||
removed: T[]; | ||
moved: T[]; | ||
finishRemoved: (els: T[]) => void; | ||
}) => void; | ||
type ListTransitionOptions<T> = { | ||
onChange: OnListChange<T>; | ||
appear?: boolean; | ||
onChange: OnListChange<T>; | ||
appear?: boolean; | ||
}; | ||
@@ -90,4 +93,15 @@ /** | ||
*/ | ||
declare function createListTransition<T>(source: Accessor<readonly NonNullable<T>[]>, options: ListTransitionOptions<NonNullable<T>>): Accessor<NonNullable<T>[]>; | ||
declare function createListTransition<T>( | ||
source: Accessor<readonly NonNullable<T>[]>, | ||
options: ListTransitionOptions<NonNullable<T>>, | ||
): Accessor<NonNullable<T>[]>; | ||
export { ListTransitionOptions, OnListChange, OnTransition, SwitchTransitionOptions, TransitionMode, createListTransition, createSwitchTransition }; | ||
export { | ||
ListTransitionOptions, | ||
OnListChange, | ||
OnTransition, | ||
SwitchTransitionOptions, | ||
TransitionMode, | ||
createListTransition, | ||
createSwitchTransition, | ||
}; |
@@ -1,6 +0,5 @@ | ||
import { untrack, createSignal, createEffect, $TRACK, batch } from 'solid-js'; | ||
import { untrack, createSignal, createEffect, $TRACK, batch } from "solid-js"; | ||
// src/index.ts | ||
var noop = () => { | ||
}; | ||
var noop = () => {}; | ||
var noopTransition = (el, done) => done(); | ||
@@ -16,4 +15,3 @@ function createSwitchTransition(source, options) { | ||
function exitTransition(el, after) { | ||
if (!el) | ||
return after && after(); | ||
if (!el) return after && after(); | ||
isExiting = true; | ||
@@ -23,3 +21,3 @@ onExit(el, () => { | ||
isExiting = false; | ||
setReturned((p) => p.filter((e) => e !== el)); | ||
setReturned(p => p.filter(e => e !== el)); | ||
after && after(); | ||
@@ -31,23 +29,21 @@ }); | ||
const el = next; | ||
if (!el) | ||
return after && after(); | ||
if (!el) return after && after(); | ||
next = void 0; | ||
setReturned((p) => [el, ...p]); | ||
setReturned(p => [el, ...p]); | ||
onEnter(el, after ?? noop); | ||
} | ||
const transition = options.mode === "out-in" ? ( | ||
// exit -> enter | ||
(prev) => isExiting || exitTransition(prev, enterTransition) | ||
) : options.mode === "in-out" ? ( | ||
// enter -> exit | ||
(prev) => enterTransition(() => exitTransition(prev)) | ||
) : ( | ||
// exit & enter | ||
(prev) => { | ||
enterTransition(); | ||
exitTransition(prev); | ||
} | ||
); | ||
const transition = | ||
options.mode === "out-in" | ||
? // exit -> enter | ||
prev => isExiting || exitTransition(prev, enterTransition) | ||
: options.mode === "in-out" | ||
? // enter -> exit | ||
prev => enterTransition(() => exitTransition(prev)) | ||
: // exit & enter | ||
prev => { | ||
enterTransition(); | ||
exitTransition(prev); | ||
}; | ||
createEffect( | ||
(prev) => { | ||
prev => { | ||
const el = source(); | ||
@@ -67,3 +63,3 @@ if (appear) { | ||
// or will animate the transition if the source is different from the initial value | ||
appear ? void 0 : initSource | ||
appear ? void 0 : initSource, | ||
); | ||
@@ -80,5 +76,4 @@ return returned; | ||
function finishRemoved(els) { | ||
setReturned((p) => p.filter((e) => !els.includes(e))); | ||
for (const el of els) | ||
exiting.delete(el); | ||
setReturned(p => p.filter(e => !els.includes(e))); | ||
for (const el of els) exiting.delete(el); | ||
} | ||
@@ -92,4 +87,4 @@ createEffect(() => { | ||
} | ||
untrack( | ||
() => setReturned((prev) => { | ||
untrack(() => | ||
setReturned(prev => { | ||
const nextSet = new Set(list); | ||
@@ -113,11 +108,9 @@ const next = list.slice(); | ||
} | ||
if (sameOrder && el !== next[i]) | ||
sameOrder = false; | ||
if (sameOrder && el !== next[i]) sameOrder = false; | ||
} | ||
if (!added.length && !removed.length && sameOrder) | ||
return prev; | ||
if (!added.length && !removed.length && sameOrder) return prev; | ||
onChange({ added, removed, moved, finishRemoved }); | ||
prevSet = nextSet; | ||
return next; | ||
}) | ||
}), | ||
); | ||
@@ -124,0 +117,0 @@ }); |
@@ -1,2 +0,2 @@ | ||
import { untrack } from 'solid-js'; | ||
import { untrack } from "solid-js"; | ||
@@ -3,0 +3,0 @@ // src/index.ts |
{ | ||
"name": "@solid-primitives/transition-group", | ||
"version": "0.0.1-beta.1", | ||
"version": "0.0.1-beta.2", | ||
"description": "Reactive primitives for implementing transition effects in SolidJS", | ||
@@ -5,0 +5,0 @@ "author": "Damian Tarnawski <gthetarnav@gmail.com>", |
@@ -44,4 +44,7 @@ <p> | ||
- `onEnter` - a function to be called when a new element is entering. It receives the element and a callback to be called when the transition is done. | ||
- `onExit` - a function to be called when an exiting element is leaving. It receives the element and a callback to be called when the transition is done. | ||
- `mode` - transition mode. Defaults to `"parallel"`. Other options are `"out-in"` and `"in-out"`. | ||
- `appear` - whether to run the transition on the initial element. Defaults to `false`. | ||
@@ -61,4 +64,4 @@ | ||
// the enter callback is called before the element is inserted into the DOM | ||
// so run the animation in the next animation frame | ||
requestAnimationFrame(() => { | ||
// so run the animation in the next animation frame / microtask | ||
queueMicrotask(() => { | ||
/*...*/ | ||
@@ -76,2 +79,31 @@ }); | ||
### Resolving JSX | ||
Usually the source will be a JSX element, and you will want to resolve it to a DOM element before passing it to `createSwitchTransition`. It leaves the resolving to you, so you can do it in any way you want. | ||
For example, you can `children` helper from `solid-js`, to get the first found HTML element. | ||
```ts | ||
import { children } from "solid-js"; | ||
import { createSwitchTransition } from "@solid-primitives/transition-group"; | ||
const resolved = children(() => props.children); | ||
const filtered = createMemo(() => resolved.asArray().find(el => el instanceof HTMLElement)); | ||
return createSwitchTransition(filtered, { | ||
/*...*/ | ||
}); | ||
``` | ||
Or use a `resolveFirst` helper from `@solid-primitives/refs` | ||
```ts | ||
import { resolveFirst } from "@solid-primitives/refs"; | ||
import { createSwitchTransition } from "@solid-primitives/transition-group"; | ||
const resolved = resolveFirst(() => props.children); | ||
return createSwitchTransition(resolved, { | ||
/*...*/ | ||
}); | ||
``` | ||
## `createListTransition` | ||
@@ -94,6 +126,13 @@ | ||
- `onChange` - a function to be called when the list changes. It receives the list of added elements, removed elements, and moved elements. It also receives a callback to be called when the removed elements are finished animating (they can be removed from the DOM). | ||
- `appear` - whether to run the transition on the initial elements. Defaults to `false`. | ||
If enabled, the initial elements will still be included in the initial render (for SSR), but the transition fill happen when the first client-side effect is run. So to avoid the initial elements to be visible, you can set the initial element's style to `display: none` and set it to `display: block` in the `onEnter` callback. | ||
If enabled, the initial elements will still be included in the initial render (for SSR), but the transition fill happen when the first client-side effect is run. So to avoid the initial elements to be visible, you can set the initial element's style to `display: none` and set it to `display: block` in the `onChange` callback. | ||
- `exitMethod` - This controls how the elements exit. | ||
- `"remove"` removes the element immediately. | ||
- `"move-to-end"` (default) will move elements which have exited to the end of the array. | ||
- `"keep-index"` will splice them in at their previous index. | ||
Returns a signal with an array of the current elements and exiting previous elements. | ||
@@ -107,6 +146,6 @@ | ||
const rendered = createListTransition(els, { | ||
onChange({ added, removed, moved, finishRemoved }) { | ||
onChange({ list, added, removed, unchanged, finishRemoved }) { | ||
// the callback is called before the added elements are inserted into the DOM | ||
// so run the animation in the next animation frame | ||
requestAnimationFrame(() => { | ||
// so run the animation in the next animation frame / microtask | ||
queueMicrotask(() => { | ||
/*...*/ | ||
@@ -124,2 +163,19 @@ }); | ||
### Resolving JSX | ||
Usually the source will be a JSX Element, and you will want to resolve it to a list of DOM elements before passing it to `createListTransition`. It leaves the resolving to you, so you can do it in any way you want. | ||
For example, you can `children` helper from `solid-js`, and filter out non-HTML elements: | ||
```ts | ||
import { children } from "solid-js"; | ||
import { createListTransition } from "@solid-primitives/transition-group"; | ||
const resolved = children(() => props.children); | ||
const filtered = createMemo(() => resolved.asArray().filter(el => el instanceof HTMLElement)); | ||
return createListTransition(filtered, { | ||
/*...*/ | ||
}); | ||
``` | ||
## Demo | ||
@@ -126,0 +182,0 @@ |
23909
387
188