solid-transition-group
Advanced tools
Comparing version 0.1.0 to 0.1.1-beta.0
import { FlowComponent } from 'solid-js'; | ||
type TransitionProps = { | ||
type TransitionEvents = { | ||
/** | ||
* Function called before the enter transition starts — before enter classes are applied. | ||
*/ | ||
onBeforeEnter?: (el: Element) => void; | ||
/** | ||
* Function called when the enter transition starts — after `enterToClass` class is applied. | ||
* Call `done` to end the transition - removes enter classes and calls `onAfterEnter`. | ||
*/ | ||
onEnter?: (el: Element, done: () => void) => void; | ||
/** | ||
* Function called after the enter transition ends — after all enter classes are removed. | ||
*/ | ||
onAfterEnter?: (el: Element) => void; | ||
/** | ||
* Function called before the exit transition starts — before exit classes are applied. | ||
*/ | ||
onBeforeExit?: (el: Element) => void; | ||
/** | ||
* Function called when the exit transition starts — after `exitToClass` class is applied. | ||
* Call `done` to end the transition - removes exit classes, calls `onAfterExit` and removes the element from the DOM. | ||
*/ | ||
onExit?: (el: Element, done: () => void) => void; | ||
/** | ||
* Function called after the exit transition ends — after all exit classes are removed. | ||
*/ | ||
onAfterExit?: (el: Element) => void; | ||
}; | ||
/** | ||
* Props for the {@link Transition} component. | ||
*/ | ||
type TransitionProps = TransitionEvents & { | ||
/** | ||
* Used to automatically generate transition CSS class names. | ||
@@ -43,31 +74,15 @@ * e.g. `name: 'fade'` will auto expand to `.fade-enter`, `.fade-enter-active`, etc. | ||
mode?: "inout" | "outin"; | ||
/** | ||
* Function called before the enter transition starts — before enter classes are applied. | ||
*/ | ||
onBeforeEnter?: (el: Element) => void; | ||
/** | ||
* Function called when the enter transition starts — after `enterToClass` class is applied. | ||
* Call `done` to end the transition - removes enter classes and calls `onAfterEnter`. | ||
*/ | ||
onEnter?: (el: Element, done: () => void) => void; | ||
/** | ||
* Function called after the enter transition ends — after all enter classes are removed. | ||
*/ | ||
onAfterEnter?: (el: Element) => void; | ||
/** | ||
* Function called before the exit transition starts — before exit classes are applied. | ||
*/ | ||
onBeforeExit?: (el: Element) => void; | ||
/** | ||
* Function called when the exit transition starts — after `exitToClass` class is applied. | ||
* Call `done` to end the transition - removes exit classes, calls `onAfterExit` and removes the element from the DOM. | ||
*/ | ||
onExit?: (el: Element, done: () => void) => void; | ||
/** | ||
* Function called after the exit transition ends — after all exit classes are removed. | ||
*/ | ||
onAfterExit?: (el: Element) => void; | ||
}; | ||
/** | ||
* 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} | ||
*/ | ||
declare const Transition: FlowComponent<TransitionProps>; | ||
/** | ||
* Props for the {@link TransitionGroup} component. | ||
*/ | ||
type TransitionGroupProps = Omit<TransitionProps, "mode"> & { | ||
@@ -80,4 +95,11 @@ /** | ||
}; | ||
/** | ||
* 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} | ||
*/ | ||
declare const TransitionGroup: FlowComponent<TransitionGroupProps>; | ||
export { Transition, TransitionGroup, TransitionGroupProps, TransitionProps }; | ||
export { Transition, TransitionEvents, TransitionGroup, TransitionGroupProps, TransitionProps }; |
@@ -1,8 +0,3 @@ | ||
// src/utils.ts | ||
// src/common.ts | ||
import { createMemo } from "solid-js"; | ||
function nextFrame(fn) { | ||
requestAnimationFrame(() => { | ||
requestAnimationFrame(fn); | ||
}); | ||
} | ||
function createClassnames(props) { | ||
@@ -22,2 +17,61 @@ return createMemo(() => { | ||
} | ||
function nextFrame(fn) { | ||
requestAnimationFrame(() => requestAnimationFrame(fn)); | ||
} | ||
function enterTransition(classnames, events, el, done) { | ||
const { enterClasses, enterActiveClasses, enterToClasses } = classnames; | ||
events.onBeforeEnter && events.onBeforeEnter(el); | ||
el.classList.add(...enterClasses); | ||
el.classList.add(...enterActiveClasses); | ||
queueMicrotask(() => { | ||
if (!el.parentNode) | ||
return done && done(); | ||
events.onEnter && events.onEnter(el, () => endTransition()); | ||
}); | ||
nextFrame(() => { | ||
el.classList.remove(...enterClasses); | ||
el.classList.add(...enterToClasses); | ||
if (!events.onEnter || events.onEnter.length < 2) { | ||
el.addEventListener("transitionend", endTransition); | ||
el.addEventListener("animationend", endTransition); | ||
} | ||
}); | ||
function endTransition(e) { | ||
if (!e || e.target === el) { | ||
el.removeEventListener("transitionend", endTransition); | ||
el.removeEventListener("animationend", endTransition); | ||
el.classList.remove(...enterActiveClasses); | ||
el.classList.remove(...enterToClasses); | ||
done && done(); | ||
events.onAfterEnter && events.onAfterEnter(el); | ||
} | ||
} | ||
} | ||
function exitTransition(classnames, events, el, done) { | ||
const { exitClasses, exitActiveClasses, exitToClasses } = classnames; | ||
if (!el.parentNode) | ||
return done && done(); | ||
events.onBeforeExit && events.onBeforeExit(el); | ||
el.classList.add(...exitClasses); | ||
el.classList.add(...exitActiveClasses); | ||
nextFrame(() => { | ||
el.classList.remove(...exitClasses); | ||
el.classList.add(...exitToClasses); | ||
}); | ||
events.onExit && events.onExit(el, () => endTransition()); | ||
if (!events.onExit || events.onExit.length < 2) { | ||
el.addEventListener("transitionend", endTransition); | ||
el.addEventListener("animationend", endTransition); | ||
} | ||
function endTransition(e) { | ||
if (!e || e.target === el) { | ||
el.removeEventListener("transitionend", endTransition); | ||
el.removeEventListener("animationend", endTransition); | ||
el.classList.remove(...exitActiveClasses); | ||
el.classList.remove(...exitToClasses); | ||
done && done(); | ||
events.onAfterExit && events.onAfterExit(el); | ||
} | ||
} | ||
} | ||
@@ -27,8 +81,7 @@ // src/Transition.ts | ||
import { resolveFirst } from "@solid-primitives/refs"; | ||
var TransitionModeMap = /* @__PURE__ */ new Map([ | ||
["inout", "in-out"], | ||
["outin", "out-in"] | ||
]); | ||
var TRANSITION_MODE_MAP = { | ||
inout: "in-out", | ||
outin: "out-in" | ||
}; | ||
var Transition = (props) => { | ||
const { onBeforeEnter, onEnter, onAfterEnter, onBeforeExit, onExit, onAfterExit } = props; | ||
const classnames = createClassnames(props); | ||
@@ -38,55 +91,9 @@ return createSwitchTransition( | ||
{ | ||
mode: TransitionModeMap.get(props.mode), | ||
mode: TRANSITION_MODE_MAP[props.mode], | ||
appear: props.appear, | ||
onEnter(el, done) { | ||
const { enterClasses, enterActiveClasses, enterToClasses } = classnames(); | ||
onBeforeEnter && onBeforeEnter(el); | ||
el.classList.add(...enterClasses); | ||
el.classList.add(...enterActiveClasses); | ||
nextFrame(() => { | ||
el.classList.remove(...enterClasses); | ||
el.classList.add(...enterToClasses); | ||
onEnter && onEnter(el, () => endTransition()); | ||
if (!onEnter || onEnter.length < 2) { | ||
el.addEventListener("transitionend", endTransition); | ||
el.addEventListener("animationend", endTransition); | ||
} | ||
}); | ||
function endTransition(e) { | ||
if (el && (!e || e.target === el)) { | ||
el.removeEventListener("transitionend", endTransition); | ||
el.removeEventListener("animationend", endTransition); | ||
el.classList.remove(...enterActiveClasses); | ||
el.classList.remove(...enterToClasses); | ||
done(); | ||
onAfterEnter && onAfterEnter(el); | ||
} | ||
} | ||
enterTransition(classnames(), props, el, done); | ||
}, | ||
onExit(el, done) { | ||
const { exitClasses, exitActiveClasses, exitToClasses } = classnames(); | ||
if (!el.parentNode) | ||
return endTransition(); | ||
onBeforeExit && onBeforeExit(el); | ||
el.classList.add(...exitClasses); | ||
el.classList.add(...exitActiveClasses); | ||
nextFrame(() => { | ||
el.classList.remove(...exitClasses); | ||
el.classList.add(...exitToClasses); | ||
}); | ||
onExit && onExit(el, () => endTransition()); | ||
if (!onExit || onExit.length < 2) { | ||
el.addEventListener("transitionend", endTransition); | ||
el.addEventListener("animationend", endTransition); | ||
} | ||
function endTransition(e) { | ||
if (!e || e.target === el) { | ||
el.removeEventListener("transitionend", endTransition); | ||
el.removeEventListener("animationend", endTransition); | ||
el.classList.remove(...exitActiveClasses); | ||
el.classList.remove(...exitToClasses); | ||
done(); | ||
onAfterExit && onAfterExit(el); | ||
} | ||
} | ||
exitTransition(classnames(), props, el, done); | ||
} | ||
@@ -103,3 +110,3 @@ } | ||
const { top, bottom, left, right, width, height } = element.getBoundingClientRect(); | ||
const parentRect = element.parentNode.getBoundingClientRect(); | ||
const parentRect = element.parentNode instanceof Element ? element.parentNode.getBoundingClientRect() : { top: 0, left: 0 }; | ||
return { | ||
@@ -115,3 +122,2 @@ top: top - parentRect.top, | ||
var TransitionGroup = (props) => { | ||
const { onBeforeEnter, onEnter, onAfterEnter, onBeforeExit, onExit, onAfterExit } = props; | ||
const classnames = createClassnames(props); | ||
@@ -122,58 +128,8 @@ const combined = createListTransition(resolveElements(() => props.children).toArray, { | ||
onChange({ added, removed, finishRemoved }) { | ||
const { | ||
enterClasses, | ||
enterActiveClasses, | ||
enterToClasses, | ||
exitClasses, | ||
exitActiveClasses, | ||
exitToClasses | ||
} = classnames(); | ||
const classes = classnames(); | ||
for (const el of added) { | ||
let endTransition2 = function(e) { | ||
if (el && (!e || e.target === el)) { | ||
el.removeEventListener("transitionend", endTransition2); | ||
el.removeEventListener("animationend", endTransition2); | ||
el.classList.remove(...enterActiveClasses); | ||
el.classList.remove(...enterToClasses); | ||
onAfterEnter && onAfterEnter(el); | ||
} | ||
}; | ||
var endTransition = endTransition2; | ||
onBeforeEnter && onBeforeEnter(el); | ||
el.classList.add(...enterClasses); | ||
el.classList.add(...enterActiveClasses); | ||
nextFrame(() => { | ||
el.classList.remove(...enterClasses); | ||
el.classList.add(...enterToClasses); | ||
onEnter && onEnter(el, () => endTransition2()); | ||
if (!onEnter || onEnter.length < 2) { | ||
el.addEventListener("transitionend", endTransition2); | ||
el.addEventListener("animationend", endTransition2); | ||
} | ||
}); | ||
enterTransition(classes, props, el); | ||
} | ||
for (const el of removed) { | ||
let endTransition2 = function(e) { | ||
if (!e || e.target === el) { | ||
el.removeEventListener("transitionend", endTransition2); | ||
el.removeEventListener("animationend", endTransition2); | ||
el.classList.remove(...exitActiveClasses); | ||
el.classList.remove(...exitToClasses); | ||
onAfterExit && onAfterExit(el); | ||
finishRemoved([el]); | ||
} | ||
}; | ||
var endTransition = endTransition2; | ||
onBeforeExit && onBeforeExit(el); | ||
el.classList.add(...exitClasses); | ||
el.classList.add(...exitActiveClasses); | ||
nextFrame(() => { | ||
el.classList.remove(...exitClasses); | ||
el.classList.add(...exitToClasses); | ||
}); | ||
onExit && onExit(el, () => endTransition2()); | ||
if (!onExit || onExit.length < 2) { | ||
el.addEventListener("transitionend", endTransition2); | ||
el.addEventListener("animationend", endTransition2); | ||
} | ||
exitTransition(classes, props, el, () => finishRemoved([el])); | ||
} | ||
@@ -183,18 +139,19 @@ } | ||
let first = !props.appear; | ||
createEffect((nodes) => { | ||
const elementInfoMap = /* @__PURE__ */ new Map(); | ||
createEffect(() => { | ||
const c = combined(); | ||
c.forEach((child) => { | ||
let n; | ||
if (!(n = nodes.get(child))) { | ||
nodes.set(child, n = { pos: getRect(child), new: !first }); | ||
} else if (n.new) { | ||
n.new = false; | ||
n.newPos = getRect(child); | ||
c.forEach((el) => { | ||
let info; | ||
if (!(info = elementInfoMap.get(el))) { | ||
elementInfoMap.set(el, info = { pos: getRect(el), new: !first }); | ||
} else if (info.new) { | ||
info.new = false; | ||
info.newPos = getRect(el); | ||
} | ||
if (n.new) { | ||
child.addEventListener( | ||
if (info.new) { | ||
el.addEventListener( | ||
"transitionend", | ||
() => { | ||
n.new = false; | ||
child.parentNode && (n.newPos = getRect(child)); | ||
info.new = false; | ||
el.parentNode && (info.newPos = getRect(el)); | ||
}, | ||
@@ -204,18 +161,18 @@ { once: true } | ||
} | ||
n.newPos && (n.pos = n.newPos); | ||
n.newPos = getRect(child); | ||
info.newPos && (info.pos = info.newPos); | ||
info.newPos = getRect(el); | ||
}); | ||
if (first) { | ||
first = false; | ||
return nodes; | ||
return; | ||
} | ||
c.forEach((child) => { | ||
const c2 = nodes.get(child); | ||
const oldPos = c2.pos; | ||
const newPos = c2.newPos; | ||
c.forEach((el) => { | ||
const info = elementInfoMap.get(el); | ||
const oldPos = info.pos; | ||
const newPos = info.newPos; | ||
const dx = oldPos.left - newPos.left; | ||
const dy = oldPos.top - newPos.top; | ||
if (dx || dy) { | ||
c2.moved = true; | ||
const s = child.style; | ||
info.moved = true; | ||
const s = el.style; | ||
s.transform = `translate(${dx}px,${dy}px)`; | ||
@@ -226,23 +183,23 @@ s.transitionDuration = "0s"; | ||
document.body.offsetHeight; | ||
c.forEach((child) => { | ||
const c2 = nodes.get(child); | ||
if (c2.moved) { | ||
c.forEach((el) => { | ||
const info = elementInfoMap.get(el); | ||
if (info.moved) { | ||
let endTransition2 = function(e) { | ||
if (e && e.target !== child || !child.parentNode) | ||
if (e && e.target !== el || !el.parentNode) | ||
return; | ||
if (!e || /transform$/.test(e.propertyName)) { | ||
child.removeEventListener( | ||
el.removeEventListener( | ||
"transitionend", | ||
endTransition2 | ||
); | ||
child.classList.remove(...moveClasses); | ||
el.classList.remove(...moveClasses); | ||
} | ||
}; | ||
var endTransition = endTransition2; | ||
c2.moved = false; | ||
const s = child.style; | ||
info.moved = false; | ||
const s = el.style; | ||
const { moveClasses } = classnames(); | ||
child.classList.add(...moveClasses); | ||
el.classList.add(...moveClasses); | ||
s.transform = s.transitionDuration = ""; | ||
child.addEventListener( | ||
el.addEventListener( | ||
"transitionend", | ||
@@ -253,4 +210,3 @@ endTransition2 | ||
}); | ||
return nodes; | ||
}, /* @__PURE__ */ new Map()); | ||
}); | ||
return combined; | ||
@@ -257,0 +213,0 @@ }; |
@@ -6,3 +6,3 @@ { | ||
"license": "MIT", | ||
"version": "0.1.0", | ||
"version": "0.1.1-beta.0", | ||
"homepage": "https://github.com/solidjs/solid-transition-group#readme", | ||
@@ -25,2 +25,5 @@ "repository": { | ||
"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", | ||
@@ -31,10 +34,14 @@ "release": "release-it", | ||
"devDependencies": { | ||
"@astrojs/solid-js": "^2.0.2", | ||
"@astrojs/solid-js": "^2.1.0", | ||
"@release-it/keep-a-changelog": "^3.1.0", | ||
"astro": "^2.0.17", | ||
"astro": "^2.1.3", | ||
"concurrently": "^7.6.0", | ||
"jsdom": "^21.1.1", | ||
"prettier": "^2.8.4", | ||
"release-it": "^15.8.0", | ||
"solid-js": "^1.6.11", | ||
"release-it": "^15.9.0", | ||
"solid-js": "^1.6.14", | ||
"tsup": "^6.6.3", | ||
"typescript": "^4.9.5" | ||
"typescript": "^4.9.5", | ||
"vite-plugin-solid": "^2.6.1", | ||
"vitest": "^0.29.3" | ||
}, | ||
@@ -41,0 +48,0 @@ "dependencies": { |
@@ -7,2 +7,6 @@ <p> | ||
[![pnpm](https://img.shields.io/badge/maintained%20with-pnpm-cc00ff.svg?style=for-the-badge&logo=pnpm)](https://pnpm.io/) | ||
[![version](https://img.shields.io/npm/v/solid-transition-group?style=for-the-badge)](https://www.npmjs.com/package/solid-transition-group) | ||
[![downloads](https://img.shields.io/npm/dw/solid-transition-group?color=blue&style=for-the-badge)](https://www.npmjs.com/package/solid-transition-group) | ||
Animation library influenced by React Transition Group and Vue Transitions for the SolidJS library. | ||
@@ -17,6 +21,7 @@ | ||
```bash | ||
# npm | ||
npm install solid-transition-group | ||
# or | ||
# yarn | ||
yarn add solid-transition-group | ||
# or | ||
# pnpm | ||
pnpm add solid-transition-group | ||
@@ -23,0 +28,0 @@ ``` |
Sorry, the diff of this file is not supported yet
99
28406
12
536