UNPKG

@appletosolutions/reactbits

Version:

A comprehensive collection of beautiful, performant React animation components including bounce effects, click sparks, star borders, scroll-triggered animations, and fade transitions.

1,388 lines (1,312 loc) 7.07 MB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var React = require('react'); var gsap$5 = require('gsap'); var ScrollTrigger$1 = require('gsap/ScrollTrigger'); var ReactDOM = require('react-dom/client'); var reactDom = require('react-dom'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React); var ReactDOM__namespace = /*#__PURE__*/_interopNamespaceDefault(ReactDOM); /*! * SplitText 3.13.0 * https://gsap.com * * @license Copyright 2025, GreenSock. All rights reserved. Subject to the terms at https://gsap.com/standard-license. * @author: Jack Doyle */ let gsap$4, _fonts, _coreInitted$4, _initIfNecessary = () => _coreInitted$4 || SplitText$1.register(window.gsap), _charSegmenter = typeof Intl !== "undefined" ? new Intl.Segmenter() : 0, _toArray$2 = (r) => typeof r === "string" ? _toArray$2(document.querySelectorAll(r)) : "length" in r ? Array.from(r) : [r], _elements = (targets) => _toArray$2(targets).filter((e) => e instanceof HTMLElement), _emptyArray = [], _context$2 = function() { }, _spacesRegEx = /\s+/g, _emojiSafeRegEx = new RegExp("\\p{RI}\\p{RI}|\\p{Emoji}(\\p{EMod}|\\u{FE0F}\\u{20E3}?|[\\u{E0020}-\\u{E007E}]+\\u{E007F})?(\\u{200D}\\p{Emoji}(\\p{EMod}|\\u{FE0F}\\u{20E3}?|[\\u{E0020}-\\u{E007E}]+\\u{E007F})?)*|.", "gu"), _emptyBounds = { left: 0, top: 0, width: 0, height: 0 }, _stretchToFitSpecialChars = (collection, specialCharsRegEx) => { if (specialCharsRegEx) { let charsFound = new Set(collection.join("").match(specialCharsRegEx) || _emptyArray), i = collection.length, slots, word, char, combined; if (charsFound.size) { while (--i > -1) { word = collection[i]; for (char of charsFound) { if (char.startsWith(word) && char.length > word.length) { slots = 0; combined = word; while (char.startsWith(combined += collection[i + ++slots]) && combined.length < char.length) { } if (slots && combined.length === char.length) { collection[i] = char; collection.splice(i + 1, slots); break; } } } } } } return collection; }, _disallowInline = (element) => window.getComputedStyle(element).display === "inline" && (element.style.display = "inline-block"), _insertNodeBefore = (newChild, parent, existingChild) => parent.insertBefore(typeof newChild === "string" ? document.createTextNode(newChild) : newChild, existingChild), _getWrapper = (type, config, collection) => { let className = config[type + "sClass"] || "", { tag = "div", aria = "auto", propIndex = false } = config, display = type === "line" ? "block" : "inline-block", incrementClass = className.indexOf("++") > -1, wrapper = (text) => { let el = document.createElement(tag), i = collection.length + 1; className && (el.className = className + (incrementClass ? " " + className + i : "")); propIndex && el.style.setProperty("--" + type, i + ""); aria !== "none" && el.setAttribute("aria-hidden", "true"); if (tag !== "span") { el.style.position = "relative"; el.style.display = display; } el.textContent = text; collection.push(el); return el; }; incrementClass && (className = className.replace("++", "")); wrapper.collection = collection; return wrapper; }, _getLineWrapper = (element, nodes, config, collection) => { let lineWrapper = _getWrapper("line", config, collection), textAlign = window.getComputedStyle(element).textAlign || "left"; return (startIndex, endIndex) => { let newLine = lineWrapper(""); newLine.style.textAlign = textAlign; element.insertBefore(newLine, nodes[startIndex]); for (; startIndex < endIndex; startIndex++) { newLine.appendChild(nodes[startIndex]); } newLine.normalize(); }; }, _splitWordsAndCharsRecursively = (element, config, wordWrapper, charWrapper, prepForCharsOnly, deepSlice, ignore, charSplitRegEx, specialCharsRegEx, isNested) => { var _a; let nodes = Array.from(element.childNodes), i = 0, { wordDelimiter, reduceWhiteSpace = true, prepareText } = config, elementBounds = element.getBoundingClientRect(), lastBounds = elementBounds, isPreformatted = !reduceWhiteSpace && window.getComputedStyle(element).whiteSpace.substring(0, 3) === "pre", ignoredPreviousSibling = 0, wordsCollection = wordWrapper.collection, wordDelimIsNotSpace, wordDelimString, wordDelimSplitter, curNode, words, curWordEl, startsWithSpace, endsWithSpace, j, bounds, curWordChars, clonedNode, curSubNode, tempSubNode, curTextContent, wordText, lastWordText, k; if (typeof wordDelimiter === "object") { wordDelimSplitter = wordDelimiter.delimiter || wordDelimiter; wordDelimString = wordDelimiter.replaceWith || ""; } else { wordDelimString = wordDelimiter === "" ? "" : wordDelimiter || " "; } wordDelimIsNotSpace = wordDelimString !== " "; for (; i < nodes.length; i++) { curNode = nodes[i]; if (curNode.nodeType === 3) { curTextContent = curNode.textContent || ""; if (reduceWhiteSpace) { curTextContent = curTextContent.replace(_spacesRegEx, " "); } else if (isPreformatted) { curTextContent = curTextContent.replace(/\n/g, wordDelimString + "\n"); } prepareText && (curTextContent = prepareText(curTextContent, element)); curNode.textContent = curTextContent; words = wordDelimString || wordDelimSplitter ? curTextContent.split(wordDelimSplitter || wordDelimString) : curTextContent.match(charSplitRegEx) || _emptyArray; lastWordText = words[words.length - 1]; endsWithSpace = wordDelimIsNotSpace ? lastWordText.slice(-1) === " " : !lastWordText; lastWordText || words.pop(); lastBounds = elementBounds; startsWithSpace = wordDelimIsNotSpace ? words[0].charAt(0) === " " : !words[0]; startsWithSpace && _insertNodeBefore(" ", element, curNode); words[0] || words.shift(); _stretchToFitSpecialChars(words, specialCharsRegEx); deepSlice && isNested || (curNode.textContent = ""); for (j = 1; j <= words.length; j++) { wordText = words[j - 1]; if (!reduceWhiteSpace && isPreformatted && wordText.charAt(0) === "\n") { (_a = curNode.previousSibling) == null ? void 0 : _a.remove(); _insertNodeBefore(document.createElement("br"), element, curNode); wordText = wordText.slice(1); } if (!reduceWhiteSpace && wordText === "") { _insertNodeBefore(wordDelimString, element, curNode); } else if (wordText === " ") { element.insertBefore(document.createTextNode(" "), curNode); } else { wordDelimIsNotSpace && wordText.charAt(0) === " " && _insertNodeBefore(" ", element, curNode); if (ignoredPreviousSibling && j === 1 && !startsWithSpace && wordsCollection.indexOf(ignoredPreviousSibling.parentNode) > -1) { curWordEl = wordsCollection[wordsCollection.length - 1]; curWordEl.appendChild(document.createTextNode(charWrapper ? "" : wordText)); } else { curWordEl = wordWrapper(charWrapper ? "" : wordText); _insertNodeBefore(curWordEl, element, curNode); ignoredPreviousSibling && j === 1 && !startsWithSpace && curWordEl.insertBefore(ignoredPreviousSibling, curWordEl.firstChild); } if (charWrapper) { curWordChars = _charSegmenter ? _stretchToFitSpecialChars([..._charSegmenter.segment(wordText)].map((s) => s.segment), specialCharsRegEx) : wordText.match(charSplitRegEx) || _emptyArray; for (k = 0; k < curWordChars.length; k++) { curWordEl.appendChild(curWordChars[k] === " " ? document.createTextNode(" ") : charWrapper(curWordChars[k])); } } if (deepSlice && isNested) { curTextContent = curNode.textContent = curTextContent.substring(wordText.length + 1, curTextContent.length); bounds = curWordEl.getBoundingClientRect(); if (bounds.top > lastBounds.top && bounds.left <= lastBounds.left) { clonedNode = element.cloneNode(); curSubNode = element.childNodes[0]; while (curSubNode && curSubNode !== curWordEl) { tempSubNode = curSubNode; curSubNode = curSubNode.nextSibling; clonedNode.appendChild(tempSubNode); } element.parentNode.insertBefore(clonedNode, element); prepForCharsOnly && _disallowInline(clonedNode); } lastBounds = bounds; } if (j < words.length || endsWithSpace) { _insertNodeBefore(j >= words.length ? " " : wordDelimIsNotSpace && wordText.slice(-1) === " " ? " " + wordDelimString : wordDelimString, element, curNode); } } } element.removeChild(curNode); ignoredPreviousSibling = 0; } else if (curNode.nodeType === 1) { if (ignore && ignore.indexOf(curNode) > -1) { wordsCollection.indexOf(curNode.previousSibling) > -1 && wordsCollection[wordsCollection.length - 1].appendChild(curNode); ignoredPreviousSibling = curNode; } else { _splitWordsAndCharsRecursively(curNode, config, wordWrapper, charWrapper, prepForCharsOnly, deepSlice, ignore, charSplitRegEx, specialCharsRegEx, true); ignoredPreviousSibling = 0; } prepForCharsOnly && _disallowInline(curNode); } } }; const _SplitText = class _SplitText { constructor(elements, config) { this.isSplit = false; _initIfNecessary(); this.elements = _elements(elements); this.chars = []; this.words = []; this.lines = []; this.masks = []; this.vars = config; this._split = () => this.isSplit && this.split(this.vars); let orig = [], timerId, checkWidths = () => { let i = orig.length, o; while (i--) { o = orig[i]; let w = o.element.offsetWidth; if (w !== o.width) { o.width = w; this._split(); return; } } }; this._data = { orig, obs: typeof ResizeObserver !== "undefined" && new ResizeObserver(() => { clearTimeout(timerId); timerId = setTimeout(checkWidths, 200); }) }; _context$2(this); this.split(config); } split(config) { this.isSplit && this.revert(); this.vars = config = config || this.vars || {}; let { type = "chars,words,lines", aria = "auto", deepSlice = true, smartWrap, onSplit, autoSplit = false, specialChars, mask } = this.vars, splitLines = type.indexOf("lines") > -1, splitCharacters = type.indexOf("chars") > -1, splitWords = type.indexOf("words") > -1, onlySplitCharacters = splitCharacters && !splitWords && !splitLines, specialCharsRegEx = specialChars && ("push" in specialChars ? new RegExp("(?:" + specialChars.join("|") + ")", "gu") : specialChars), finalCharSplitRegEx = specialCharsRegEx ? new RegExp(specialCharsRegEx.source + "|" + _emojiSafeRegEx.source, "gu") : _emojiSafeRegEx, ignore = !!config.ignore && _elements(config.ignore), { orig, animTime, obs } = this._data, onSplitResult; if (splitCharacters || splitWords || splitLines) { this.elements.forEach((element, index) => { orig[index] = { element, html: element.innerHTML, ariaL: element.getAttribute("aria-label"), ariaH: element.getAttribute("aria-hidden") }; aria === "auto" ? element.setAttribute("aria-label", (element.textContent || "").trim()) : aria === "hidden" && element.setAttribute("aria-hidden", "true"); let chars = [], words = [], lines = [], charWrapper = splitCharacters ? _getWrapper("char", config, chars) : null, wordWrapper = _getWrapper("word", config, words), i, curWord, smartWrapSpan, nextSibling; _splitWordsAndCharsRecursively(element, config, wordWrapper, charWrapper, onlySplitCharacters, deepSlice && (splitLines || onlySplitCharacters), ignore, finalCharSplitRegEx, specialCharsRegEx, false); if (splitLines) { let nodes = _toArray$2(element.childNodes), wrapLine = _getLineWrapper(element, nodes, config, lines), curNode, toRemove = [], lineStartIndex = 0, allBounds = nodes.map((n) => n.nodeType === 1 ? n.getBoundingClientRect() : _emptyBounds), lastBounds = _emptyBounds; for (i = 0; i < nodes.length; i++) { curNode = nodes[i]; if (curNode.nodeType === 1) { if (curNode.nodeName === "BR") { toRemove.push(curNode); wrapLine(lineStartIndex, i + 1); lineStartIndex = i + 1; lastBounds = allBounds[lineStartIndex]; } else { if (i && allBounds[i].top > lastBounds.top && allBounds[i].left <= lastBounds.left) { wrapLine(lineStartIndex, i); lineStartIndex = i; } lastBounds = allBounds[i]; } } } lineStartIndex < i && wrapLine(lineStartIndex, i); toRemove.forEach((el) => { var _a; return (_a = el.parentNode) == null ? void 0 : _a.removeChild(el); }); } if (!splitWords) { for (i = 0; i < words.length; i++) { curWord = words[i]; if (splitCharacters || !curWord.nextSibling || curWord.nextSibling.nodeType !== 3) { if (smartWrap && !splitLines) { smartWrapSpan = document.createElement("span"); smartWrapSpan.style.whiteSpace = "nowrap"; while (curWord.firstChild) { smartWrapSpan.appendChild(curWord.firstChild); } curWord.replaceWith(smartWrapSpan); } else { curWord.replaceWith(...curWord.childNodes); } } else { nextSibling = curWord.nextSibling; if (nextSibling && nextSibling.nodeType === 3) { nextSibling.textContent = (curWord.textContent || "") + (nextSibling.textContent || ""); curWord.remove(); } } } words.length = 0; element.normalize(); } this.lines.push(...lines); this.words.push(...words); this.chars.push(...chars); }); mask && this[mask] && this.masks.push(...this[mask].map((el) => { let maskEl = el.cloneNode(); el.replaceWith(maskEl); maskEl.appendChild(el); el.className && (maskEl.className = el.className.replace(/(\b\w+\b)/g, "$1-mask")); maskEl.style.overflow = "clip"; return maskEl; })); } this.isSplit = true; _fonts && (autoSplit ? _fonts.addEventListener("loadingdone", this._split) : _fonts.status === "loading" && console.warn("SplitText called before fonts loaded")); if ((onSplitResult = onSplit && onSplit(this)) && onSplitResult.totalTime) { this._data.anim = animTime ? onSplitResult.totalTime(animTime) : onSplitResult; } splitLines && autoSplit && this.elements.forEach((element, index) => { orig[index].width = element.offsetWidth; obs && obs.observe(element); }); return this; } revert() { var _a, _b; let { orig, anim, obs } = this._data; obs && obs.disconnect(); orig.forEach(({ element, html, ariaL, ariaH }) => { element.innerHTML = html; ariaL ? element.setAttribute("aria-label", ariaL) : element.removeAttribute("aria-label"); ariaH ? element.setAttribute("aria-hidden", ariaH) : element.removeAttribute("aria-hidden"); }); this.chars.length = this.words.length = this.lines.length = orig.length = this.masks.length = 0; this.isSplit = false; _fonts == null ? void 0 : _fonts.removeEventListener("loadingdone", this._split); if (anim) { this._data.animTime = anim.totalTime(); anim.revert(); } (_b = (_a = this.vars).onRevert) == null ? void 0 : _b.call(_a, this); return this; } static create(elements, config) { return new _SplitText(elements, config); } static register(core) { gsap$4 = gsap$4 || core || window.gsap; if (gsap$4) { _toArray$2 = gsap$4.utils.toArray; _context$2 = gsap$4.core.context || _context$2; } if (!_coreInitted$4 && window.innerWidth > 0) { _fonts = document.fonts; _coreInitted$4 = true; } } }; _SplitText.version = "3.13.0"; let SplitText$1 = _SplitText; gsap$5.gsap.registerPlugin(ScrollTrigger$1.ScrollTrigger, SplitText$1); const SplitText = ({ text, className = "", delay = 100, duration = 0.6, ease = "power3.out", splitType = "chars", from = { opacity: 0, y: 40 }, to = { opacity: 1, y: 0 }, threshold = 0.1, rootMargin = "-100px", textAlign = "center", onLetterAnimationComplete, }) => { const ref = React.useRef(null); React.useEffect(() => { const el = ref.current; if (!el) return; const absoluteLines = splitType === "lines"; if (absoluteLines) el.style.position = "relative"; const splitter = new SplitText$1(el, { type: splitType, absolute: absoluteLines, linesClass: "split-line", }); let targets; switch (splitType) { case "lines": targets = splitter.lines; break; case "words": targets = splitter.words; break; case "words, chars": targets = [...splitter.words, ...splitter.chars]; break; default: targets = splitter.chars; } targets.forEach((t) => { t.style.willChange = "transform, opacity"; }); const startPct = (1 - threshold) * 100; const m = /^(-?\d+)px$/.exec(rootMargin); const raw = m ? parseInt(m[1], 10) : 0; const sign = raw < 0 ? `-=${Math.abs(raw)}px` : `+=${raw}px`; const start = `top ${startPct}%${sign}`; const tl = gsap$5.gsap.timeline({ scrollTrigger: { trigger: el, start, toggleActions: "play none none none", once: true, }, smoothChildTiming: true, onComplete: onLetterAnimationComplete, }); tl.set(targets, { ...from, immediateRender: false, force3D: true }); tl.to(targets, { ...to, duration, ease, stagger: delay / 1000, force3D: true, }); return () => { tl.kill(); ScrollTrigger$1.ScrollTrigger.getAll().forEach((t) => t.kill()); gsap$5.gsap.killTweensOf(targets); splitter.revert(); }; }, [ text, delay, duration, ease, splitType, from, to, threshold, rootMargin, onLetterAnimationComplete, ]); return (jsxRuntime.jsx("p", { ref: ref, className: `split-parent ${className}`, style: { textAlign, overflow: "hidden", display: "inline-block", whiteSpace: "normal", wordWrap: "break-word", }, children: text })); }; const LayoutGroupContext = React.createContext({}); /** * Creates a constant value over the lifecycle of a component. * * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer * a guarantee that it won't re-run for performance reasons later on. By using `useConstant` * you can ensure that initialisers don't execute twice or more. */ function useConstant(init) { const ref = React.useRef(null); if (ref.current === null) { ref.current = init(); } return ref.current; } const isBrowser$2 = typeof window !== "undefined"; const useIsomorphicLayoutEffect$1 = isBrowser$2 ? React.useLayoutEffect : React.useEffect; /** * @public */ const PresenceContext = /* @__PURE__ */ React.createContext(null); function addUniqueItem(arr, item) { if (arr.indexOf(item) === -1) arr.push(item); } function removeItem(arr, item) { const index = arr.indexOf(item); if (index > -1) arr.splice(index, 1); } const clamp$4 = (min, max, v) => { if (v > max) return max; if (v < min) return min; return v; }; let warning = () => { }; let invariant$1 = () => { }; if (process.env.NODE_ENV !== "production") { warning = (check, message) => { if (!check && typeof console !== "undefined") { console.warn(message); } }; invariant$1 = (check, message) => { if (!check) { throw new Error(message); } }; } const MotionGlobalConfig = {}; /** * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1" */ const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v); function isObject$4(value) { return typeof value === "object" && value !== null; } /** * Check if the value is a zero value string like "0px" or "0%" */ const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v); /*#__NO_SIDE_EFFECTS__*/ function memo$3(callback) { let result; return () => { if (result === undefined) result = callback(); return result; }; } /*#__NO_SIDE_EFFECTS__*/ const noop$2 = (any) => any; /** * Pipe * Compose other transformers to run linearily * pipe(min(20), max(40)) * @param {...functions} transformers * @return {function} */ const combineFunctions = (a, b) => (v) => b(a(v)); const pipe$1 = (...transformers) => transformers.reduce(combineFunctions); /* Progress within given range Given a lower limit and an upper limit, we return the progress (expressed as a number 0-1) represented by the given value, and limit that progress to within 0-1. @param [number]: Lower limit @param [number]: Upper limit @param [number]: Value to find progress within given range @return [number]: Progress of value within range as expressed 0-1 */ /*#__NO_SIDE_EFFECTS__*/ const progress = (from, to, value) => { const toFromDifference = to - from; return toFromDifference === 0 ? 1 : (value - from) / toFromDifference; }; class SubscriptionManager { constructor() { this.subscriptions = []; } add(handler) { addUniqueItem(this.subscriptions, handler); return () => removeItem(this.subscriptions, handler); } notify(a, b, c) { const numSubscriptions = this.subscriptions.length; if (!numSubscriptions) return; if (numSubscriptions === 1) { /** * If there's only a single handler we can just call it without invoking a loop. */ this.subscriptions[0](a, b, c); } else { for (let i = 0; i < numSubscriptions; i++) { /** * Check whether the handler exists before firing as it's possible * the subscriptions were modified during this loop running. */ const handler = this.subscriptions[i]; handler && handler(a, b, c); } } } getSize() { return this.subscriptions.length; } clear() { this.subscriptions.length = 0; } } /** * Converts seconds to milliseconds * * @param seconds - Time in seconds. * @return milliseconds - Converted time in milliseconds. */ /*#__NO_SIDE_EFFECTS__*/ const secondsToMilliseconds = (seconds) => seconds * 1000; /*#__NO_SIDE_EFFECTS__*/ const millisecondsToSeconds = (milliseconds) => milliseconds / 1000; /* Convert velocity into velocity per second @param [number]: Unit per frame @param [number]: Frame duration in ms */ function velocityPerSecond(velocity, frameDuration) { return frameDuration ? velocity * (1000 / frameDuration) : 0; } const warned = new Set(); function warnOnce$1(condition, message, element) { if (condition || warned.has(message)) return; console.warn(message); warned.add(message); } const wrap$4 = (min, max, v) => { const rangeSize = max - min; return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min; }; /* Bezier function generator This has been modified from Gaëtan Renaudeau's BezierEasing https://github.com/gre/bezier-easing/blob/master/src/index.js https://github.com/gre/bezier-easing/blob/master/LICENSE I've removed the newtonRaphsonIterate algo because in benchmarking it wasn't noticiably faster than binarySubdivision, indeed removing it usually improved times, depending on the curve. I also removed the lookup table, as for the added bundle size and loop we're only cutting ~4 or so subdivision iterations. I bumped the max iterations up to 12 to compensate and this still tended to be faster for no perceivable loss in accuracy. Usage const easeOut = cubicBezier(.17,.67,.83,.67); const x = easeOut(0.5); // returns 0.627... */ // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) * t; const subdivisionPrecision = 0.0000001; const subdivisionMaxIterations = 12; function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) { let currentX; let currentT; let i = 0; do { currentT = lowerBound + (upperBound - lowerBound) / 2.0; currentX = calcBezier(currentT, mX1, mX2) - x; if (currentX > 0.0) { upperBound = currentT; } else { lowerBound = currentT; } } while (Math.abs(currentX) > subdivisionPrecision && ++i < subdivisionMaxIterations); return currentT; } function cubicBezier(mX1, mY1, mX2, mY2) { // If this is a linear gradient, return linear easing if (mX1 === mY1 && mX2 === mY2) return noop$2; const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2); // If animation is at start/end, return t without easing return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2); } // Accepts an easing function and returns a new one that outputs mirrored values for // the second half of the animation. Turns easeIn into easeInOut. const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2; // Accepts an easing function and returns a new one that outputs reversed values. // Turns easeIn into easeOut. const reverseEasing = (easing) => (p) => 1 - easing(1 - p); const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99); const backIn = /*@__PURE__*/ reverseEasing(backOut); const backInOut = /*@__PURE__*/ mirrorEasing(backIn); const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1))); const circIn = (p) => 1 - Math.sin(Math.acos(p)); const circOut = reverseEasing(circIn); const circInOut = mirrorEasing(circIn); const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1); const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1); const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1); const isEasingArray = (ease) => { return Array.isArray(ease) && typeof ease[0] !== "number"; }; function getEasingForSegment(easing, i) { return isEasingArray(easing) ? easing[wrap$4(0, easing.length, i)] : easing; } const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number"; const easingLookup = { linear: noop$2, easeIn, easeInOut, easeOut, circIn, circInOut, circOut, backIn, backInOut, backOut, anticipate, }; const isValidEasing = (easing) => { return typeof easing === "string"; }; const easingDefinitionToFunction = (definition) => { if (isBezierDefinition(definition)) { // If cubic bezier definition, create bezier curve invariant$1(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`); const [x1, y1, x2, y2] = definition; return cubicBezier(x1, y1, x2, y2); } else if (isValidEasing(definition)) { // Else lookup from table invariant$1(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`); return easingLookup[definition]; } return definition; }; const stepsOrder = [ "setup", // Compute "read", // Read "resolveKeyframes", // Write/Read/Write/Read "preUpdate", // Compute "update", // Compute "preRender", // Compute "render", // Write "postRender", // Compute ]; function createRenderStep(runNextFrame, stepName) { /** * We create and reuse two queues, one to queue jobs for the current frame * and one for the next. We reuse to avoid triggering GC after x frames. */ let thisFrame = new Set(); let nextFrame = new Set(); /** * Track whether we're currently processing jobs in this step. This way * we can decide whether to schedule new jobs for this frame or next. */ let isProcessing = false; let flushNextFrame = false; /** * A set of processes which were marked keepAlive when scheduled. */ const toKeepAlive = new WeakSet(); let latestFrameData = { delta: 0.0, timestamp: 0.0, isProcessing: false, }; function triggerCallback(callback) { if (toKeepAlive.has(callback)) { step.schedule(callback); runNextFrame(); } callback(latestFrameData); } const step = { /** * Schedule a process to run on the next frame. */ schedule: (callback, keepAlive = false, immediate = false) => { const addToCurrentFrame = immediate && isProcessing; const queue = addToCurrentFrame ? thisFrame : nextFrame; if (keepAlive) toKeepAlive.add(callback); if (!queue.has(callback)) queue.add(callback); return callback; }, /** * Cancel the provided callback from running on the next frame. */ cancel: (callback) => { nextFrame.delete(callback); toKeepAlive.delete(callback); }, /** * Execute all schedule callbacks. */ process: (frameData) => { latestFrameData = frameData; /** * If we're already processing we've probably been triggered by a flushSync * inside an existing process. Instead of executing, mark flushNextFrame * as true and ensure we flush the following frame at the end of this one. */ if (isProcessing) { flushNextFrame = true; return; } isProcessing = true; [thisFrame, nextFrame] = [nextFrame, thisFrame]; // Execute this frame thisFrame.forEach(triggerCallback); // Clear the frame so no callbacks remain. This is to avoid // memory leaks should this render step not run for a while. thisFrame.clear(); isProcessing = false; if (flushNextFrame) { flushNextFrame = false; step.process(frameData); } }, }; return step; } const maxElapsed$1 = 40; function createRenderBatcher(scheduleNextBatch, allowKeepAlive) { let runNextFrame = false; let useDefaultElapsed = true; const state = { delta: 0.0, timestamp: 0.0, isProcessing: false, }; const flagRunNextFrame = () => (runNextFrame = true); const steps = stepsOrder.reduce((acc, key) => { acc[key] = createRenderStep(flagRunNextFrame); return acc; }, {}); const { setup, read, resolveKeyframes, preUpdate, update, preRender, render, postRender, } = steps; const processBatch = () => { const timestamp = MotionGlobalConfig.useManualTiming ? state.timestamp : performance.now(); runNextFrame = false; if (!MotionGlobalConfig.useManualTiming) { state.delta = useDefaultElapsed ? 1000 / 60 : Math.max(Math.min(timestamp - state.timestamp, maxElapsed$1), 1); } state.timestamp = timestamp; state.isProcessing = true; // Unrolled render loop for better per-frame performance setup.process(state); read.process(state); resolveKeyframes.process(state); preUpdate.process(state); update.process(state); preRender.process(state); render.process(state); postRender.process(state); state.isProcessing = false; if (runNextFrame && allowKeepAlive) { useDefaultElapsed = false; scheduleNextBatch(processBatch); } }; const wake = () => { runNextFrame = true; useDefaultElapsed = true; if (!state.isProcessing) { scheduleNextBatch(processBatch); } }; const schedule = stepsOrder.reduce((acc, key) => { const step = steps[key]; acc[key] = (process, keepAlive = false, immediate = false) => { if (!runNextFrame) wake(); return step.schedule(process, keepAlive, immediate); }; return acc; }, {}); const cancel = (process) => { for (let i = 0; i < stepsOrder.length; i++) { steps[stepsOrder[i]].cancel(process); } }; return { schedule, cancel, state, steps }; } const { schedule: frame$1, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop$2, true); let now$1; function clearTime() { now$1 = undefined; } /** * An eventloop-synchronous alternative to performance.now(). * * Ensures that time measurements remain consistent within a synchronous context. * Usually calling performance.now() twice within the same synchronous context * will return different values which isn't useful for animations when we're usually * trying to sync animations to the same frame. */ const time = { now: () => { if (now$1 === undefined) { time.set(frameData.isProcessing || MotionGlobalConfig.useManualTiming ? frameData.timestamp : performance.now()); } return now$1; }, set: (newTime) => { now$1 = newTime; queueMicrotask(clearTime); }, }; const checkStringStartsWith = (token) => (key) => typeof key === "string" && key.startsWith(token); const isCSSVariableName = /*@__PURE__*/ checkStringStartsWith("--"); const startsAsVariableToken = /*@__PURE__*/ checkStringStartsWith("var(--"); const isCSSVariableToken = (value) => { const startsWithToken = startsAsVariableToken(value); if (!startsWithToken) return false; // Ensure any comments are stripped from the value as this can harm performance of the regex. return singleCssVariableRegex.test(value.split("/*")[0].trim()); }; const singleCssVariableRegex = /var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu; const number = { test: (v) => typeof v === "number", parse: parseFloat, transform: (v) => v, }; const alpha = { ...number, transform: (v) => clamp$4(0, 1, v), }; const scale$6 = { ...number, default: 1, }; // If this number is a decimal, make it just five decimal places // to avoid exponents const sanitize$1 = (v) => Math.round(v * 100000) / 100000; const floatRegex = /-?(?:\d+(?:\.\d+)?|\.\d+)/gu; function isNullish(v) { return v == null; } const singleColorRegex = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu; /** * Returns true if the provided string is a color, ie rgba(0,0,0,0) or #000, * but false if a number or multiple colors */ const isColorString = (type, testProp) => (v) => { return Boolean((typeof v === "string" && singleColorRegex.test(v) && v.startsWith(type)) || (testProp && !isNullish(v) && Object.prototype.hasOwnProperty.call(v, testProp))); }; const splitColor = (aName, bName, cName) => (v) => { if (typeof v !== "string") return v; const [a, b, c, alpha] = v.match(floatRegex); return { [aName]: parseFloat(a), [bName]: parseFloat(b), [cName]: parseFloat(c), alpha: alpha !== undefined ? parseFloat(alpha) : 1, }; }; const clampRgbUnit = (v) => clamp$4(0, 255, v); const rgbUnit = { ...number, transform: (v) => Math.round(clampRgbUnit(v)), }; const rgba = { test: /*@__PURE__*/ isColorString("rgb", "red"), parse: /*@__PURE__*/ splitColor("red", "green", "blue"), transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" + rgbUnit.transform(red) + ", " + rgbUnit.transform(green) + ", " + rgbUnit.transform(blue) + ", " + sanitize$1(alpha.transform(alpha$1)) + ")", }; function parseHex(v) { let r = ""; let g = ""; let b = ""; let a = ""; // If we have 6 characters, ie #FF0000 if (v.length > 5) { r = v.substring(1, 3); g = v.substring(3, 5); b = v.substring(5, 7); a = v.substring(7, 9); // Or we have 3 characters, ie #F00 } else { r = v.substring(1, 2); g = v.substring(2, 3); b = v.substring(3, 4); a = v.substring(4, 5); r += r; g += g; b += b; a += a; } return { red: parseInt(r, 16), green: parseInt(g, 16), blue: parseInt(b, 16), alpha: a ? parseInt(a, 16) / 255 : 1, }; } const hex = { test: /*@__PURE__*/ isColorString("#"), parse: parseHex, transform: rgba.transform, }; /*#__NO_SIDE_EFFECTS__*/ const createUnitType = (unit) => ({ test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1, parse: parseFloat, transform: (v) => `${v}${unit}`, }); const degrees = /*@__PURE__*/ createUnitType("deg"); const percent = /*@__PURE__*/ createUnitType("%"); const px$1 = /*@__PURE__*/ createUnitType("px"); const vh = /*@__PURE__*/ createUnitType("vh"); const vw = /*@__PURE__*/ createUnitType("vw"); const progressPercentage = /*@__PURE__*/ (() => ({ ...percent, parse: (v) => percent.parse(v) / 100, transform: (v) => percent.transform(v * 100), }))(); const hsla = { test: /*@__PURE__*/ isColorString("hsl", "hue"), parse: /*@__PURE__*/ splitColor("hue", "saturation", "lightness"), transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => { return ("hsla(" + Math.round(hue) + ", " + percent.transform(sanitize$1(saturation)) + ", " + percent.transform(sanitize$1(lightness)) + ", " + sanitize$1(alpha.transform(alpha$1)) + ")"); }, }; const color$1 = { test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v), parse: (v) => { if (rgba.test(v)) { return rgba.parse(v); } else if (hsla.test(v)) { return hsla.parse(v); } else { return hex.parse(v); } }, transform: (v) => { return typeof v === "string" ? v : v.hasOwnProperty("red") ? rgba.transform(v) : hsla.transform(v); }, getAnimatableNone: (v) => { const parsed = color$1.parse(v); parsed.alpha = 0; return color$1.transform(parsed); }, }; const colorRegex = /(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu; function test(v) { return (isNaN(v) && typeof v === "string" && (v.match(floatRegex)?.length || 0) + (v.match(colorRegex)?.length || 0) > 0); } const NUMBER_TOKEN = "number"; const COLOR_TOKEN = "color"; const VAR_TOKEN = "var"; const VAR_FUNCTION_TOKEN = "var("; const SPLIT_TOKEN = "${}"; // this regex consists of the `singleCssVariableRegex|rgbHSLValueRegex|digitRegex` const complexRegex = /var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu; function analyseComplexValue(value) { const originalValue = value.toString(); const values = []; const indexes = { color: [], number: [], var: [], }; const types = []; let i = 0; const tokenised = originalValue.replace(complexRegex, (parsedValue) => { if (color$1.test(parsedValue)) { indexes.color.push(i); types.push(COLOR_TOKEN); values.push(color$1.parse(parsedValue)); } else if (parsedValue.startsWith(VAR_FUNCTION_TOKEN)) { indexes.var.push(i); types.push(VAR_TOKEN); values.push(parsedValue); } else { indexes.number.push(i); types.push(NUMBER_TOKEN); values.push(parseFloat(parsedValue)); } ++i; return SPLIT_TOKEN; }); const split = tokenised.split(SPLIT_TOKEN); return { values, split, indexes, types }; } function parseComplexValue(v) { return analyseComplexValue(v).values; } function createTransformer(source) { const { split, types } = analyseComplexValue(source); const numSections = split.length; return (v) => { let output = ""; for (let i = 0; i < numSections; i++) { output += split[i]; if (v[i] !== undefined) { const type = types[i]; if (type === NUMBER_TOKEN) { output += sanitize$1(v[i]); } else if (type === COLOR_TOKEN) { output += color$1.transform(v[i]); } else { output += v[i]; } } } return output; }; } const convertNumbersToZero = (v) => typeof v === "number" ? 0 : color$1.test(v) ? color$1.getAnimatableNone(v) : v; function getAnimatableNone$1(v) { const parsed = parseComplexValue(v); const transformer = createTransformer(v); return transformer(parsed.map(convertNumbersToZero)); } const complex = { test, parse: parseComplexValue, createTransformer, getAnimatableNone: getAnimatableNone$1, }; // Adapted from https://gist.github.com/mjackson/5311256 function hueToRgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; } function hslaToRgba({ hue, saturation, lightness, alpha }) { hue /= 360; saturation /= 100; lightness /= 100; let red = 0; let green = 0; let blue = 0; if (!saturation) { red = green = blue = lightness; } else { const q = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation; const p = 2 * lightness - q; red = hueToRgb(p, q, hue + 1 / 3); green = hueToRgb(p, q, hue); blue = hueToRgb(p, q, hue - 1 / 3); } return { red: Math.round(red * 255), green: Math.round(green * 255), blue: Math.round(blue * 255), alpha, }; } function mixImmediate(a, b) { return (p) => (p > 0 ? b : a); } /* Value in range from progress Given a lower limit and an upper limit, we return the value within that range as expressed by progress (usually a number from 0 to 1) So progress = 0.5 would change from -------- to to from ---- to E.g. from = 10, to = 20, progress = 0.5 => 15 @param [number]: Lower limit of range @param [number]: Upper limit of range @param [number]: The progress between lower and upper limits expressed 0-1 @return [number]: Value as calculated from progress within range (not limited within range) */ const mixNumber$1 = (from, to, progress) => { return from + (to - from) * progress; }; // Linear color space blending // Explained https://www.youtube.com/watch?v=LKnqECcg6Gw // Demonstrated http://codepen.io/osublake/pen/xGVVaN const mixLinearColor = (from, to, v) => { const fromExpo = from * from; const expo = v * (to * to - fromExpo) + fromExpo; return expo < 0 ? 0 : Math.sqrt(expo); }; const colorTypes = [hex, rgba, hsla]; const getColorType = (v) => colorTypes.find((type) => type.test(v)); function asRGBA(color) { const type = getColorType(color); warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`); if (!Boolean(type)) return false; let model = type.parse(color); if (type === hsla) { // TODO Remove this cast - needed since Motion's stricter typing model = hslaToRgba(model); } return model; } const mixColor = (from, to) => { const fromRGBA = asRGBA(from); const toRGBA = asRGBA(to); if (!fromRGBA || !toRGBA) { return mixImmediate(from, to); } const blended = { ...fromRGBA }; return (v) => { blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v); blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v); blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v); blended.alpha = mixNumber$1(fromRGBA.alpha, toRGBA.alpha, v); return rgba.transform(blended); }; }; const invisibleValues = new Set(["none", "hidden"]); /** * Returns a function that, when provided a progress value between 0 and 1, * will return the "none" or "hidden" string only when the progress is that of * the origin or target. */ function mixVisibility(origin, target) { if (invisibleValues.has(origin)) { return (p) => (p <= 0 ? origin : target); } else { return (p) => (p >= 1 ? target : origin); } } function mixNumber(a, b) { return (p) => mixNumber$1(a, b, p); } function getMixer(a) { if (typeof a === "number") { return mixNumber; } else if (typeof a === "string") { return isCSSVariableToken(a) ? mixImmediate : color$1.test(a) ? mixColor : mixComplex; } else if (Array.isArray(a)) { return mixArray; } else if (typeof a === "object") { return color$1.test(a) ? mixColor : mixObject; } return mixImmediate; } function mixArray(a, b) { const output = [...a]; const numValues = output.length; const blendValue = a.map((v, i) => getMixer(v)(v, b[i])); return (p) => { for (let i = 0; i < numValues; i++) { output[i] = blendValue[i](p); } return output; }; } function mixObject(a, b) { const output = { ...a, ...b }; const blendValue = {}; for (const key in output) { if (a[key] !== undefined && b[key] !== undefined) { blendValue[key] = getMixer(a[key])(a[key], b[key]); } } return (v) => { for (const key in blendValue) { output[key] = blendValue[key](v); } return output; }; } function matchOrder(origin, target) { const orderedOrigin = []; const pointers = { color: 0, var: 0, number: 0 }; for (let i = 0; i < target.values.length; i++) { const type = target.types[i]; const originIndex = origin.indexes[type][pointers[type]]; const originValue = origin.values[originIndex] ?? 0; orderedOrigin[i] = originValue; pointers[type]++; } return orderedOrigin; } const mixComplex = (origin, target) => { const template = complex.createTransformer(target); const originStats = analyseComplexValue(origin); const targetStats = analyseComplexValue(target); const canInterpolate = originStats.indexes.var.length === targetStats.indexes.var.length && originStats.indexes.color.length === targetStats.indexes.color.length && originStats.indexes.number.length >= targetStats.indexes.number.length; if (canInterpolate) { if ((invisibleValues.has(origin) && !targetStats.values.length) || (invisibleValues.has(target) && !originStats.values.length)) { return mixVisibility(origin, target); } return pipe$1(mixArray(matchOrder(originStats, targetStats), targetStats.values), template); } else { warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`); return mixImmediate(origin, target); } }; function mix(from, to, p) { if (typeof from === "number" && typeof to === "number" && typeof p === "number") { return mixNumber$1(from, to, p); } const mixer = getMixer(from); return mixer(from, to); } const frameloopDriver = (update) => { const passTimestamp = ({ ti