solid-transition-group
Advanced tools
Comparing version 0.2.3 to 0.3.0
@@ -1,4 +0,18 @@ | ||
import { FlowComponent } from 'solid-js'; | ||
type TransitionEvents = { | ||
import { type FlowComponent } from "solid-js"; | ||
declare function createClassnames(props: TransitionProps & TransitionGroupProps): import("solid-js").Accessor<{ | ||
enterActive: string[]; | ||
enter: string[]; | ||
enterTo: string[]; | ||
exitActive: string[]; | ||
exit: string[]; | ||
exitTo: string[]; | ||
move: string[]; | ||
}>; | ||
/** | ||
* @private | ||
* | ||
* Run an exit transition on an element - common for both Transition and TransitionGroup | ||
*/ | ||
export declare function exitTransition(classes: ReturnType<ReturnType<typeof createClassnames>>, events: TransitionEvents, el: Element, done?: VoidFunction): void; | ||
export type TransitionEvents = { | ||
/** | ||
@@ -47,3 +61,3 @@ * Function called before the enter transition starts. | ||
*/ | ||
type TransitionProps = TransitionEvents & { | ||
export type TransitionProps = TransitionEvents & { | ||
/** | ||
@@ -103,8 +117,7 @@ * Used to automatically generate transition CSS class names. | ||
*/ | ||
declare const Transition: FlowComponent<TransitionProps>; | ||
export declare const Transition: FlowComponent<TransitionProps>; | ||
/** | ||
* Props for the {@link TransitionGroup} component. | ||
*/ | ||
type TransitionGroupProps = Omit<TransitionProps, "mode"> & { | ||
export type TransitionGroupProps = Omit<TransitionProps, "mode"> & { | ||
/** | ||
@@ -123,4 +136,3 @@ * CSS class applied to the moving elements for the entire duration of the move transition. | ||
*/ | ||
declare const TransitionGroup: FlowComponent<TransitionGroupProps>; | ||
export { Transition, TransitionEvents, TransitionGroup, TransitionGroupProps, TransitionProps }; | ||
export declare const TransitionGroup: FlowComponent<TransitionGroupProps>; | ||
export {}; |
@@ -1,155 +0,183 @@ | ||
// src/common.ts | ||
import { createMemo } from "solid-js"; | ||
import { createSwitchTransition, createListTransition } from "@solid-primitives/transition-group"; | ||
import { resolveFirst, resolveElements } from "@solid-primitives/refs"; | ||
function createClassnames(props) { | ||
return createMemo(() => { | ||
const name = props.name || "s"; | ||
return { | ||
enterActive: (props.enterActiveClass || name + "-enter-active").split(" "), | ||
enter: (props.enterClass || name + "-enter").split(" "), | ||
enterTo: (props.enterToClass || name + "-enter-to").split(" "), | ||
exitActive: (props.exitActiveClass || name + "-exit-active").split(" "), | ||
exit: (props.exitClass || name + "-exit").split(" "), | ||
exitTo: (props.exitToClass || name + "-exit-to").split(" "), | ||
move: (props.moveClass || name + "-move").split(" ") | ||
}; | ||
}); | ||
return createMemo(() => { | ||
const name = props.name || "s"; | ||
return { | ||
enterActive: (props.enterActiveClass || name + "-enter-active").split(" "), | ||
enter: (props.enterClass || name + "-enter").split(" "), | ||
enterTo: (props.enterToClass || name + "-enter-to").split(" "), | ||
exitActive: (props.exitActiveClass || name + "-exit-active").split(" "), | ||
exit: (props.exitClass || name + "-exit").split(" "), | ||
exitTo: (props.exitToClass || name + "-exit-to").split(" "), | ||
move: (props.moveClass || name + "-move").split(" "), | ||
}; | ||
}); | ||
} | ||
// https://github.com/solidjs-community/solid-transition-group/issues/12 | ||
// for the css transition be triggered properly on firefox | ||
// we need to wait for two frames before changeing classes | ||
function nextFrame(fn) { | ||
requestAnimationFrame(() => requestAnimationFrame(fn)); | ||
requestAnimationFrame(() => requestAnimationFrame(fn)); | ||
} | ||
/** | ||
* Run an enter transition on an element - common for both Transition and TransitionGroup | ||
*/ | ||
function enterTransition(classes, events, el, done) { | ||
const { onBeforeEnter, onEnter, onAfterEnter } = events; | ||
onBeforeEnter?.(el); | ||
el.classList.add(...classes.enter); | ||
el.classList.add(...classes.enterActive); | ||
queueMicrotask(() => { | ||
if (!el.parentNode) | ||
return done?.(); | ||
onEnter?.(el, () => endTransition()); | ||
}); | ||
nextFrame(() => { | ||
el.classList.remove(...classes.enter); | ||
el.classList.add(...classes.enterTo); | ||
if (!onEnter || onEnter.length < 2) { | ||
el.addEventListener("transitionend", endTransition); | ||
el.addEventListener("animationend", endTransition); | ||
const { onBeforeEnter, onEnter, onAfterEnter } = events; | ||
// before the elements are added to the DOM | ||
onBeforeEnter?.(el); | ||
el.classList.add(...classes.enter); | ||
el.classList.add(...classes.enterActive); | ||
// after the microtask the elements will be added to the DOM | ||
// and onEnter will be called in the same frame | ||
queueMicrotask(() => { | ||
// Don't animate element if it's not in the DOM | ||
// This can happen when elements are changed under Suspense | ||
if (!el.parentNode) | ||
return done?.(); | ||
onEnter?.(el, () => endTransition()); | ||
}); | ||
nextFrame(() => { | ||
el.classList.remove(...classes.enter); | ||
el.classList.add(...classes.enterTo); | ||
if (!onEnter || onEnter.length < 2) { | ||
el.addEventListener("transitionend", endTransition); | ||
el.addEventListener("animationend", endTransition); | ||
} | ||
}); | ||
function endTransition(e) { | ||
if (!e || e.target === el) { | ||
done?.(); // starts exit transition in "in-out" mode | ||
el.removeEventListener("transitionend", endTransition); | ||
el.removeEventListener("animationend", endTransition); | ||
el.classList.remove(...classes.enterActive); | ||
el.classList.remove(...classes.enterTo); | ||
onAfterEnter?.(el); | ||
} | ||
} | ||
}); | ||
function endTransition(e) { | ||
if (!e || e.target === el) { | ||
done?.(); | ||
el.removeEventListener("transitionend", endTransition); | ||
el.removeEventListener("animationend", endTransition); | ||
el.classList.remove(...classes.enterActive); | ||
el.classList.remove(...classes.enterTo); | ||
onAfterEnter?.(el); | ||
} | ||
} | ||
} | ||
function exitTransition(classes, events, el, done) { | ||
const { onBeforeExit, onExit, onAfterExit } = events; | ||
if (!el.parentNode) | ||
return done?.(); | ||
onBeforeExit?.(el); | ||
el.classList.add(...classes.exit); | ||
el.classList.add(...classes.exitActive); | ||
onExit?.(el, () => endTransition()); | ||
nextFrame(() => { | ||
el.classList.remove(...classes.exit); | ||
el.classList.add(...classes.exitTo); | ||
if (!onExit || onExit.length < 2) { | ||
el.addEventListener("transitionend", endTransition); | ||
el.addEventListener("animationend", endTransition); | ||
/** | ||
* @private | ||
* | ||
* Run an exit transition on an element - common for both Transition and TransitionGroup | ||
*/ | ||
export function exitTransition(classes, events, el, done) { | ||
const { onBeforeExit, onExit, onAfterExit } = events; | ||
// Don't animate element if it's not in the DOM | ||
// This can happen when elements are changed under Suspense | ||
if (!el.parentNode) | ||
return done?.(); | ||
onBeforeExit?.(el); | ||
el.classList.add(...classes.exit); | ||
el.classList.add(...classes.exitActive); | ||
onExit?.(el, () => endTransition()); | ||
nextFrame(() => { | ||
el.classList.remove(...classes.exit); | ||
el.classList.add(...classes.exitTo); | ||
if (!onExit || onExit.length < 2) { | ||
el.addEventListener("transitionend", endTransition); | ||
el.addEventListener("animationend", endTransition); | ||
} | ||
}); | ||
function endTransition(e) { | ||
if (!e || e.target === el) { | ||
// calling done() will remove element from the DOM, | ||
// but also trigger onChange callback in <TransitionGroup>. | ||
// Which is why the classes need to removed afterwards, | ||
// so that removing them won't change el styles when for the move transition | ||
done?.(); | ||
el.removeEventListener("transitionend", endTransition); | ||
el.removeEventListener("animationend", endTransition); | ||
el.classList.remove(...classes.exitActive); | ||
el.classList.remove(...classes.exitTo); | ||
onAfterExit?.(el); | ||
} | ||
} | ||
}); | ||
function endTransition(e) { | ||
if (!e || e.target === el) { | ||
done?.(); | ||
el.removeEventListener("transitionend", endTransition); | ||
el.removeEventListener("animationend", endTransition); | ||
el.classList.remove(...classes.exitActive); | ||
el.classList.remove(...classes.exitTo); | ||
onAfterExit?.(el); | ||
} | ||
} | ||
} | ||
// src/Transition.ts | ||
import { createSwitchTransition } from "@solid-primitives/transition-group"; | ||
import { resolveFirst } from "@solid-primitives/refs"; | ||
var TRANSITION_MODE_MAP = { | ||
inout: "in-out", | ||
outin: "out-in" | ||
const TRANSITION_MODE_MAP = { | ||
inout: "in-out", | ||
outin: "out-in", | ||
}; | ||
var Transition = (props) => { | ||
const classnames = createClassnames(props); | ||
return createSwitchTransition( | ||
resolveFirst(() => props.children), | ||
{ | ||
mode: TRANSITION_MODE_MAP[props.mode], | ||
appear: props.appear, | ||
onEnter(el, done) { | ||
enterTransition(classnames(), props, el, done); | ||
}, | ||
onExit(el, done) { | ||
exitTransition(classnames(), props, el, done); | ||
} | ||
} | ||
); | ||
/** | ||
* The `<Transition>` component lets you apply enter and leave animations on element passed to `props.children`. | ||
* | ||
* It only supports transitioning a single element at a time. | ||
* | ||
* @param props {@link TransitionProps} | ||
*/ | ||
export const Transition = props => { | ||
const classnames = createClassnames(props); | ||
return createSwitchTransition(resolveFirst(() => props.children), { | ||
mode: TRANSITION_MODE_MAP[props.mode], | ||
appear: props.appear, | ||
onEnter(el, done) { | ||
enterTransition(classnames(), props, el, done); | ||
}, | ||
onExit(el, done) { | ||
exitTransition(classnames(), props, el, done); | ||
}, | ||
}); | ||
}; | ||
// src/TransitionGroup.ts | ||
import { createListTransition } from "@solid-primitives/transition-group"; | ||
import { resolveElements } from "@solid-primitives/refs"; | ||
var TransitionGroup = (props) => { | ||
const classnames = createClassnames(props); | ||
return createListTransition(resolveElements(() => props.children).toArray, { | ||
appear: props.appear, | ||
exitMethod: "keep-index", | ||
onChange({ added, removed, finishRemoved, list }) { | ||
const classes = classnames(); | ||
for (const el of added) { | ||
enterTransition(classes, props, el); | ||
} | ||
const toMove = []; | ||
for (const el of list) { | ||
if (el.isConnected && (el instanceof HTMLElement || el instanceof SVGElement)) { | ||
toMove.push({ el, rect: el.getBoundingClientRect() }); | ||
} | ||
} | ||
queueMicrotask(() => { | ||
const moved = []; | ||
for (const { el, rect } of toMove) { | ||
if (el.isConnected) { | ||
const newRect = el.getBoundingClientRect(), dX = rect.left - newRect.left, dY = rect.top - newRect.top; | ||
if (dX || dY) { | ||
el.style.transform = `translate(${dX}px, ${dY}px)`; | ||
el.style.transitionDuration = "0s"; | ||
moved.push(el); | ||
/** | ||
* The `<TransitionGroup>` component lets you apply enter and leave animations on elements passed to `props.children`. | ||
* | ||
* It supports transitioning multiple elements at a time and moving elements around. | ||
* | ||
* @param props {@link TransitionGroupProps} | ||
*/ | ||
export const TransitionGroup = props => { | ||
const classnames = createClassnames(props); | ||
return createListTransition(resolveElements(() => props.children).toArray, { | ||
appear: props.appear, | ||
exitMethod: "keep-index", | ||
onChange({ added, removed, finishRemoved, list }) { | ||
const classes = classnames(); | ||
// ENTER | ||
for (const el of added) { | ||
enterTransition(classes, props, el); | ||
} | ||
} | ||
} | ||
document.body.offsetHeight; | ||
for (const el of moved) { | ||
let endTransition2 = function(e) { | ||
if (e.target === el || /transform$/.test(e.propertyName)) { | ||
el.removeEventListener("transitionend", endTransition2); | ||
el.classList.remove(...classes.move); | ||
// MOVE | ||
const toMove = []; | ||
// get rects of elements before the changes to the DOM | ||
for (const el of list) { | ||
if (el.isConnected && (el instanceof HTMLElement || el instanceof SVGElement)) { | ||
toMove.push({ el, rect: el.getBoundingClientRect() }); | ||
} | ||
} | ||
}; | ||
var endTransition = endTransition2; | ||
el.classList.add(...classes.move); | ||
el.style.transform = el.style.transitionDuration = ""; | ||
el.addEventListener("transitionend", endTransition2); | ||
} | ||
}); | ||
for (const el of removed) { | ||
exitTransition(classes, props, el, () => finishRemoved([el])); | ||
} | ||
} | ||
}); | ||
// wait for th new list to be rendered | ||
queueMicrotask(() => { | ||
const moved = []; | ||
for (const { el, rect } of toMove) { | ||
if (el.isConnected) { | ||
const newRect = el.getBoundingClientRect(), dX = rect.left - newRect.left, dY = rect.top - newRect.top; | ||
if (dX || dY) { | ||
// set els to their old position before transition | ||
el.style.transform = `translate(${dX}px, ${dY}px)`; | ||
el.style.transitionDuration = "0s"; | ||
moved.push(el); | ||
} | ||
} | ||
} | ||
document.body.offsetHeight; // force reflow | ||
for (const el of moved) { | ||
el.classList.add(...classes.move); | ||
// clear transition - els will move to their new position | ||
el.style.transform = el.style.transitionDuration = ""; | ||
function endTransition(e) { | ||
if (e.target === el || /transform$/.test(e.propertyName)) { | ||
el.removeEventListener("transitionend", endTransition); | ||
el.classList.remove(...classes.move); | ||
} | ||
} | ||
el.addEventListener("transitionend", endTransition); | ||
} | ||
}); | ||
// EXIT | ||
for (const el of removed) { | ||
exitTransition(classes, props, el, () => finishRemoved([el])); | ||
} | ||
}, | ||
}); | ||
}; | ||
export { | ||
Transition, | ||
TransitionGroup | ||
}; |
@@ -6,3 +6,3 @@ { | ||
"license": "MIT", | ||
"version": "0.2.3", | ||
"version": "0.3.0", | ||
"homepage": "https://github.com/solidjs/solid-transition-group#readme", | ||
@@ -16,37 +16,29 @@ "repository": { | ||
"type": "module", | ||
"main": "dist/index.cjs", | ||
"module": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"exports": { | ||
"import": { | ||
"types": "./dist/index.d.ts", | ||
"default": "./dist/index.js" | ||
} | ||
}, | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"dev": "astro dev --root dev", | ||
"build:site": "astro build --root dev", | ||
"build": "tsup", | ||
"test:client": "vitest", | ||
"test:ssr": "pnpm run test:client --mode ssr", | ||
"test": "concurrently pnpm:test:*", | ||
"format": "prettier -w **/*.{js,ts,json,css,tsx,jsx} --ignore-path .gitignore", | ||
"release": "pnpm build && release-it" | ||
}, | ||
"devDependencies": { | ||
"@astrojs/solid-js": "^2.2.0", | ||
"@astrojs/tailwind": "^4.0.0", | ||
"@release-it/keep-a-changelog": "^4.0.0", | ||
"astro": "^2.10.1", | ||
"concurrently": "^8.2.0", | ||
"jsdom": "^22.1.0", | ||
"prettier": "^3.0.1", | ||
"release-it": "^16.1.3", | ||
"solid-js": "^1.7.9", | ||
"concurrently": "^9.1.2", | ||
"jsdom": "^26.0.0", | ||
"prettier": "^3.4.2", | ||
"solid-js": "^1.9.4", | ||
"tailwindcss": "^3.3.3", | ||
"tsup": "^7.2.0", | ||
"typescript": "^5.1.6", | ||
"vite-plugin-solid": "^2.7.0", | ||
"vitest": "^0.34.1" | ||
"typescript": "^5.7.3", | ||
"vite-plugin-solid": "^2.11.0", | ||
"vitest": "^2.1.8" | ||
}, | ||
"dependencies": { | ||
"@solid-primitives/refs": "^1.0.5", | ||
"@solid-primitives/transition-group": "^1.0.2" | ||
"@solid-primitives/refs": "^1.1.0", | ||
"@solid-primitives/transition-group": "^1.1.0" | ||
}, | ||
@@ -56,7 +48,15 @@ "peerDependencies": { | ||
}, | ||
"packageManager": "pnpm@8.6.0", | ||
"engines": { | ||
"node": ">=18.0.0", | ||
"pnpm": ">=8.6.0" | ||
"node": ">=20.0.0", | ||
"pnpm": ">=9.0.0" | ||
}, | ||
"scripts": { | ||
"dev": "astro dev", | ||
"build:site": "astro build", | ||
"build": "tsc -b tsconfig.build.json", | ||
"test:client": "vitest", | ||
"test:ssr": "pnpm run test:client --mode ssr", | ||
"test": "concurrently pnpm:test:*", | ||
"format": "prettier -w **/*.{js,ts,json,css,tsx,jsx} --ignore-path .gitignore" | ||
} | ||
} | ||
} |
@@ -146,3 +146,3 @@ <p> | ||
Source code: https://github.com/solidjs-community/solid-transition-group/blob/main/dev/Test.tsx | ||
Source code: https://github.com/solidjs-community/solid-transition-group/blob/main/dev/src/pages/kitchen-sink.tsx | ||
@@ -149,0 +149,0 @@ ## FAQ |
Sorry, the diff of this file is not supported yet
11
25846
5
318