@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
JavaScript
'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