vue-circular-gauge
Version:
Circular gauge component for Vue.js with customizable colors, animations, and thresholds
1,638 lines • 288 kB
JavaScript
import { inject, provide, watch, computed, ref, useAttrs, getCurrentInstance, onBeforeMount, onMounted, onBeforeUnmount, onUnmounted, onBeforeUpdate, onUpdated, defineComponent, h, Comment, mergeProps, cloneVNode, createElementBlock, openBlock, unref, createElementVNode, createBlock, createCommentVNode, toDisplayString } from "vue";
const calculatePrimaryStrokeDasharray = (strokePercent, offsetFactor, gapPercent, circumference) => {
const percentToPx = circumference / 100;
if (offsetFactor > 0 && strokePercent > 100 - gapPercent * 2 * offsetFactor) {
const subtract = -strokePercent + 100;
return `${Math.max(strokePercent * percentToPx - subtract * percentToPx, 0)} ${circumference}`;
} else {
const subtract = gapPercent * 2 * offsetFactor;
return `${Math.max(strokePercent * percentToPx - subtract * percentToPx, 0)} ${circumference}`;
}
};
const calculateSecondaryStrokeDasharray = (strokePercent, offsetFactorSecondary, gapPercent, circumference) => {
const percentToPx = circumference / 100;
if (offsetFactorSecondary < 1 && strokePercent < gapPercent * 2 * offsetFactorSecondary) {
const subtract = strokePercent;
return `${Math.max((100 - strokePercent) * percentToPx - subtract * percentToPx, 0)} ${circumference}`;
} else {
const subtract = gapPercent * 2 * offsetFactorSecondary;
return `${Math.max((100 - strokePercent) * percentToPx - subtract * percentToPx, 0)} ${circumference}`;
}
};
const calculatePrimaryTransform = (strokePercent, offsetFactor, gapPercent, isAscendingVariant) => {
const percentToDegree = 360 / 100;
if (offsetFactor > 0 && strokePercent > 100 - gapPercent * 2 * offsetFactor) {
const add = 0.5 * (-strokePercent + 100);
const rotateDegValue = -90 + add * percentToDegree;
return isAscendingVariant ? `rotate(${rotateDegValue}deg)` : `rotate(${-1 * rotateDegValue}deg) scaleX(-1)`;
} else {
const add = gapPercent * offsetFactor;
const rotateDegValue = -90 + add * percentToDegree;
return isAscendingVariant ? `rotate(${rotateDegValue}deg)` : `rotate(${-1 * rotateDegValue}deg) scaleX(-1)`;
}
};
const calculateSecondaryTransform = (strokePercent, offsetFactorSecondary, gapPercent, isAscendingVariant) => {
const percentToDegree = 360 / 100;
if (offsetFactorSecondary < 1 && strokePercent < gapPercent * 2 * offsetFactorSecondary) {
const subtract = 0.5 * strokePercent;
const rotateDegValue = -90 + subtract * percentToDegree;
return isAscendingVariant ? `rotate(${rotateDegValue}deg) scaleY(-1)` : `rotate(${-1 * rotateDegValue}deg) scaleY(-1) scaleX(-1)`;
} else {
const subtract = gapPercent * offsetFactorSecondary;
const rotateDegValue = 360 - 90 - subtract * percentToDegree;
return isAscendingVariant ? `rotate(${rotateDegValue}deg) scaleY(-1)` : `rotate(${-1 * rotateDegValue}deg) scaleY(-1) scaleX(-1)`;
}
};
const checkCustomColorRange = (color2, strokePercent) => {
const colorKeys = Object.keys(color2).sort((a, b) => Number(a) - Number(b));
let stroke = "";
for (let i = 0; i < colorKeys.length; i++) {
const currentKey = Number(colorKeys[i]);
const nextKey = Number(colorKeys[i + 1]);
if (strokePercent >= currentKey && (strokePercent < nextKey || !nextKey)) {
stroke = color2[currentKey];
break;
}
}
return stroke;
};
const calculatePrimaryStroke = (primary, strokePercent) => {
if (!primary) {
return strokePercent <= 25 ? "hsl(358 75% 59%)" : strokePercent <= 50 ? "hsl(39 100% 57%)" : strokePercent <= 75 ? "hsl(212 100% 48%)" : "hsl(131 41% 46%)";
} else if (typeof primary === "string") {
return primary;
} else if (typeof primary === "object") {
return checkCustomColorRange(primary, strokePercent);
}
};
const calculateSecondaryStroke = (secondary, strokePercent) => {
if (!secondary) {
return "hsl(0 0% 92%)";
} else if (typeof secondary === "string") {
return secondary;
} else if (typeof secondary === "object") {
return checkCustomColorRange(secondary, 100 - strokePercent);
}
};
const calculatePrimaryOpacity = (offsetFactor, strokePercent, gapPercent, offsetFactorSecondary) => {
if (offsetFactor > 0 && strokePercent < gapPercent * 2 * offsetFactor && strokePercent < gapPercent * 2 * offsetFactorSecondary) {
return 0;
} else return 1;
};
const calculateSecondaryOpacity = (offsetFactor, strokePercent, gapPercent, offsetFactorSecondary) => {
if (offsetFactor === 0 && strokePercent > 100 - gapPercent * 2 || offsetFactor > 0 && strokePercent > 100 - gapPercent * 2 * offsetFactor && strokePercent > 100 - gapPercent * 2 * offsetFactorSecondary) {
return 0;
} else return 1;
};
const sizeConfig = {
xs: {
size: 32
},
sm: {
size: 48
},
md: {
size: 72
},
lg: {
size: 96
},
xl: {
size: 120
},
"2xl": {
size: 144
}
};
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;
return singleCssVariableRegex.test(value.split("/*")[0].trim());
};
const singleCssVariableRegex = /var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu;
const scaleCorrectors = {};
function addScaleCorrector(correctors) {
for (const key in correctors) {
scaleCorrectors[key] = correctors[key];
if (isCSSVariableName(key)) {
scaleCorrectors[key].isCSSVariable = true;
}
}
}
function createContext(providerComponentName, contextName) {
const symbolDescription = typeof providerComponentName === "string" && true ? `${providerComponentName}Context` : contextName;
const injectionKey = Symbol(symbolDescription);
const injectContext = (fallback) => {
const context = inject(injectionKey, fallback);
if (context)
return context;
if (context === null)
return context;
throw new Error(
`Injection \`${injectionKey.toString()}\` not found. Component must be used within ${Array.isArray(providerComponentName) ? `one of the following components: ${providerComponentName.join(
", "
)}` : `\`${providerComponentName}\``}`
);
};
const provideContext = (contextValue) => {
provide(injectionKey, contextValue);
return contextValue;
};
return [injectContext, provideContext];
}
const [injectMotion, provideMotion] = createContext("Motion");
const [injectLayoutGroup, provideLayoutGroup] = createContext("LayoutGroup");
function isAnimationControls$1(v) {
return v !== null && typeof v === "object" && typeof v.start === "function";
}
class Feature {
constructor(state) {
this.state = state;
}
beforeMount() {
}
mount() {
}
unmount() {
}
update() {
}
beforeUpdate() {
}
beforeUnmount() {
}
}
var warning$1 = function() {
};
var invariant$1 = function() {
};
if (process.env.NODE_ENV !== "production") {
warning$1 = function(check, message) {
if (!check && typeof console !== "undefined") {
console.warn(message);
}
};
invariant$1 = function(check, message) {
if (!check) {
throw new Error(message);
}
};
}
function resolveVariant$1(definition, variants, custom) {
if (Array.isArray(definition)) {
return definition.reduce((acc, item) => {
const resolvedVariant = resolveVariant$1(item, variants, custom);
return resolvedVariant ? { ...acc, ...resolvedVariant } : acc;
}, {});
} else if (typeof definition === "object") {
return definition;
} else if (definition && variants) {
const variant = variants[definition];
return typeof variant === "function" ? variant(custom) : variant;
}
}
function hasChanged(a, b) {
if (typeof a !== typeof b)
return true;
if (Array.isArray(a) && Array.isArray(b))
return !shallowCompare(a, b);
return a !== b;
}
function shallowCompare(next, prev) {
const prevLength = prev.length;
if (prevLength !== next.length)
return false;
for (let i = 0; i < prevLength; i++) {
if (prev[i] !== next[i])
return false;
}
return true;
}
function isCssVar(name) {
return name == null ? void 0 : name.startsWith("--");
}
const noopReturn = (v) => v;
function isNumber$1(value) {
return typeof value === "number";
}
const svgElements = [
"animate",
"circle",
"defs",
"desc",
"ellipse",
"g",
"image",
"line",
"filter",
"marker",
"mask",
"metadata",
"path",
"pattern",
"polygon",
"polyline",
"rect",
"stop",
"svg",
"switch",
"symbol",
"text",
"tspan",
"use",
"view",
"clipPath",
"feBlend",
"feColorMatrix",
"feComponentTransfer",
"feComposite",
"feConvolveMatrix",
"feDiffuseLighting",
"feDisplacementMap",
"feDistantLight",
"feDropShadow",
"feFlood",
"feFuncA",
"feFuncB",
"feFuncG",
"feFuncR",
"feGaussianBlur",
"feImage",
"feMerge",
"feMergeNode",
"feMorphology",
"feOffset",
"fePointLight",
"feSpecularLighting",
"feSpotLight",
"feTile",
"feTurbulence",
"foreignObject",
"linearGradient",
"radialGradient",
"textPath"
];
const svgElementSet = new Set(svgElements);
function isSVGElement$1(as) {
return svgElementSet.has(as);
}
function isAnimateChanged(oldOptions, newOptions) {
const oldAnimate = oldOptions.animate;
const newAnimate = newOptions.animate;
if (oldAnimate === newAnimate)
return false;
if (!oldAnimate || !newAnimate) {
return true;
}
if (typeof oldAnimate === "object" || typeof newAnimate === "object") {
const oldKeys = Object.keys(oldAnimate);
const newKeys = Object.keys(newAnimate);
if (oldKeys.length !== newKeys.length)
return true;
return oldKeys.some((key) => {
if (key === "transition")
return false;
const oldVal = oldAnimate[key];
const newVal = newAnimate[key];
return oldVal !== newVal;
});
}
return oldAnimate !== newAnimate;
}
class FeatureManager {
constructor(state) {
this.features = [];
const { features = [], lazyMotionContext } = state.options;
const allFeatures = features.concat(lazyMotionContext.features.value);
this.features = allFeatures.map((Feature2) => new Feature2(state));
const featureInstances = this.features;
watch(lazyMotionContext.features, (features2) => {
features2.forEach((feature) => {
if (!allFeatures.includes(feature)) {
allFeatures.push(feature);
const featureInstance = new feature(state);
featureInstances.push(featureInstance);
if (state.isMounted()) {
featureInstance.beforeMount();
featureInstance.mount();
}
}
});
}, {
flush: "pre"
});
}
mount() {
this.features.forEach((feature) => feature.mount());
}
beforeMount() {
this.features.forEach((feature) => {
var _a;
return (_a = feature.beforeMount) == null ? void 0 : _a.call(feature);
});
}
unmount() {
this.features.forEach((feature) => feature.unmount());
}
update() {
this.features.forEach((feature) => {
var _a;
return (_a = feature.update) == null ? void 0 : _a.call(feature);
});
}
beforeUpdate() {
this.features.forEach((feature) => feature.beforeUpdate());
}
beforeUnmount() {
this.features.forEach((feature) => feature.beforeUnmount());
}
}
const doneCallbacks = /* @__PURE__ */ new WeakMap();
const [injectAnimatePresence, provideAnimatePresence] = createContext("AnimatePresenceContext");
function isVariantLabels(value) {
return typeof value === "string" || value === false || Array.isArray(value);
}
const noop = /* @__NO_SIDE_EFFECTS__ */ (any) => any;
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) {
let thisFrame = /* @__PURE__ */ new Set();
let nextFrame = /* @__PURE__ */ new Set();
let isProcessing = false;
let flushNextFrame = false;
const toKeepAlive = /* @__PURE__ */ new WeakSet();
let latestFrameData = {
delta: 0,
timestamp: 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: (frameData2) => {
latestFrameData = frameData2;
if (isProcessing) {
flushNextFrame = true;
return;
}
isProcessing = true;
[thisFrame, nextFrame] = [nextFrame, thisFrame];
thisFrame.forEach(triggerCallback);
thisFrame.clear();
isProcessing = false;
if (flushNextFrame) {
flushNextFrame = false;
step.process(frameData2);
}
}
};
return step;
}
const MotionGlobalConfig = {};
const maxElapsed = 40;
function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
let runNextFrame = false;
let useDefaultElapsed = true;
const state = {
delta: 0,
timestamp: 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 ? 1e3 / 60 : Math.max(Math.min(timestamp - state.timestamp, maxElapsed), 1);
}
state.timestamp = timestamp;
state.isProcessing = true;
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] = (process2, keepAlive = false, immediate = false) => {
if (!runNextFrame)
wake();
return step.schedule(process2, keepAlive, immediate);
};
return acc;
}, {});
const cancel = (process2) => {
for (let i = 0; i < stepsOrder.length; i++) {
steps[stepsOrder[i]].cancel(process2);
}
};
return { schedule, cancel, state, steps };
}
const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
const mountedStates = /* @__PURE__ */ new WeakMap();
let id$1 = 0;
const mountedLayoutIds = /* @__PURE__ */ new Set();
class MotionState {
constructor(options, parent) {
var _a;
this.element = null;
this.isSafeToRemove = false;
this.isVShow = false;
this.children = /* @__PURE__ */ new Set();
this.activeStates = {
initial: true,
animate: true
};
this.currentProcess = null;
this._context = null;
this.animateUpdates = noop;
this.id = `motion-state-${id$1++}`;
this.options = options;
this.parent = parent;
(_a = parent == null ? void 0 : parent.children) == null ? void 0 : _a.add(this);
this.depth = (parent == null ? void 0 : parent.depth) + 1 || 0;
const initial = options.initial === void 0 && options.variants ? this.context.initial : options.initial;
const initialVariantSource = initial === false ? ["initial", "animate"] : ["initial"];
this.initTarget(initialVariantSource);
this.featureManager = new FeatureManager(this);
this.type = isSVGElement$1(this.options.as) ? "svg" : "html";
}
// Get animation context, falling back to parent context for inheritance
get context() {
if (!this._context) {
const handler = {
get: (target, prop) => {
var _a;
return isVariantLabels(this.options[prop]) ? this.options[prop] : (_a = this.parent) == null ? void 0 : _a.context[prop];
}
};
this._context = new Proxy({}, handler);
}
return this._context;
}
// Initialize animation target values
initTarget(initialVariantSource) {
this.baseTarget = initialVariantSource.reduce((acc, variant) => {
return {
...acc,
...resolveVariant$1(this.options[variant] || this.context[variant], this.options.variants)
};
}, {});
this.target = {};
}
// Update visual element with new options
updateOptions(options) {
var _a;
this.options = options;
(_a = this.visualElement) == null ? void 0 : _a.update({
...this.options,
whileTap: this.options.whilePress,
reducedMotionConfig: this.options.motionConfig.reduceMotion
}, {
isPresent: !doneCallbacks.has(this.element)
});
}
// Called before mounting, executes in parent-to-child order
beforeMount() {
this.featureManager.beforeMount();
}
// Mount motion state to DOM element, handles parent-child relationships
mount(element, options, notAnimate = false) {
var _a;
invariant$1(
Boolean(element),
"Animation state must be mounted with valid Element"
);
this.element = element;
this.updateOptions(options);
this.featureManager.mount();
if (!notAnimate && this.options.animate) {
(_a = this.startAnimation) == null ? void 0 : _a.call(this);
}
if (this.options.layoutId) {
mountedLayoutIds.add(this.options.layoutId);
frame.render(() => {
mountedLayoutIds.clear();
});
}
}
clearAnimation() {
var _a, _b;
this.currentProcess && cancelFrame(this.currentProcess);
this.currentProcess = null;
(_b = (_a = this.visualElement) == null ? void 0 : _a.variantChildren) == null ? void 0 : _b.forEach((child) => {
child.state.clearAnimation();
});
}
// update trigger animation
startAnimation() {
this.clearAnimation();
this.currentProcess = frame.render(() => {
this.currentProcess = null;
this.animateUpdates();
});
}
// Called before unmounting, executes in child-to-parent order
beforeUnmount() {
this.featureManager.beforeUnmount();
}
// Unmount motion state and optionally unmount children
// Handles unmounting in the correct order based on component tree
unmount(unMountChildren = false) {
const shouldDelay = this.options.layoutId && !mountedLayoutIds.has(this.options.layoutId);
const unmount = () => {
const unmountState = () => {
var _a, _b, _c;
if (unMountChildren) {
Array.from(this.children).reverse().forEach(this.unmountChild);
}
(_b = (_a = this.parent) == null ? void 0 : _a.children) == null ? void 0 : _b.delete(this);
mountedStates.delete(this.element);
this.featureManager.unmount();
(_c = this.visualElement) == null ? void 0 : _c.unmount();
this.clearAnimation();
};
shouldDelay ? Promise.resolve().then(unmountState) : unmountState();
};
unmount();
}
unmountChild(child) {
child.unmount(true);
}
// Called before updating, executes in parent-to-child order
beforeUpdate() {
this.featureManager.beforeUpdate();
}
// Update motion state with new options
update(options) {
const hasAnimateChange = isAnimateChanged(this.options, options);
this.updateOptions(options);
this.featureManager.update();
if (hasAnimateChange) {
this.startAnimation();
}
}
// Set animation state active status and propagate to children
setActive(name, isActive, isAnimate = true) {
var _a;
if (!this.element || this.activeStates[name] === isActive)
return;
this.activeStates[name] = isActive;
(_a = this.visualElement.variantChildren) == null ? void 0 : _a.forEach((child) => {
child.state.setActive(name, isActive, false);
});
if (isAnimate) {
this.animateUpdates({
isFallback: !isActive && name !== "exit" && this.visualElement.isControllingVariants,
isExit: name === "exit" && this.activeStates.exit
});
}
}
isMounted() {
return Boolean(this.element);
}
// Called before layout updates to prepare for changes
willUpdate(label) {
var _a;
if (this.options.layout || this.options.layoutId) {
(_a = this.visualElement.projection) == null ? void 0 : _a.willUpdate();
}
}
}
const visualElementStore = /* @__PURE__ */ new WeakMap();
function motionEvent(name, target, isExit) {
return new CustomEvent(name, { detail: { target, isExit } });
}
const rotation = {
syntax: "<angle>",
initialValue: "0deg",
toDefaultUnit: (v) => `${v}deg`
};
const baseTransformProperties = {
translate: {
syntax: "<length-percentage>",
initialValue: "0px",
toDefaultUnit: (v) => `${v}px`
},
rotate: rotation,
scale: {
syntax: "<number>",
initialValue: 1,
toDefaultUnit: noopReturn
},
skew: rotation
};
const order = ["translate", "scale", "rotate", "skew"];
const axes = ["", "X", "Y", "Z"];
const transformDefinitions = /* @__PURE__ */ new Map();
const transforms = ["transformPerspective", "x", "y", "z", "translateX", "translateY", "translateZ", "scale", "scaleX", "scaleY", "rotate", "rotateX", "rotateY", "rotateZ", "skew", "skewX", "skewY"];
order.forEach((name) => {
axes.forEach((axis) => {
transforms.push(name + axis);
transformDefinitions.set(
name + axis,
baseTransformProperties[name]
);
});
});
const transformLookup = new Set(transforms);
const isTransform = (name) => transformLookup.has(name);
const transformAlias = {
x: "translateX",
y: "translateY",
z: "translateZ"
};
function compareTransformOrder([a], [b]) {
return transforms.indexOf(a) - transforms.indexOf(b);
}
function transformListToString(template, [name, value]) {
return `${template} ${name}(${value})`;
}
function buildTransformTemplate(transforms2) {
return transforms2.sort(compareTransformOrder).reduce(transformListToString, "").trim();
}
const transformResetValue = {
translate: [0, 0],
rotate: 0,
scale: 1,
skew: 0,
x: 0,
y: 0,
z: 0
};
function createUnitType$1(unit) {
return {
test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1,
parse: parseFloat,
transform: (v) => `${v}${unit}`
};
}
const percent$1 = /* @__PURE__ */ createUnitType$1("%");
const px$1 = /* @__PURE__ */ createUnitType$1("px");
function isMotionValue$1(value) {
return Boolean(value && value.getVelocity);
}
const style = {
get: (element, name) => {
let value = isCssVar(name) ? element.style.getPropertyValue(name) : getComputedStyle(element)[name];
if (!value && value !== "0") {
const definition = transformDefinitions.get(name);
if (definition)
value = definition.initialValue;
}
return value;
},
set: (element, name, value) => {
if (isCssVar(name)) {
element.style.setProperty(name, value);
} else {
element.style[name] = value;
}
}
};
function createStyles(keyframes2) {
var _a;
const initialKeyframes = {};
const transforms2 = [];
for (let key in keyframes2) {
let value = keyframes2[key];
value = isMotionValue$1(value) ? value.get() : value;
if (isTransform(key)) {
if (key in transformAlias) {
key = transformAlias[key];
}
}
let initialKeyframe = Array.isArray(value) ? value[0] : value;
const definition = transformDefinitions.get(key);
if (definition) {
initialKeyframe = isNumber$1(value) ? (_a = definition.toDefaultUnit) == null ? void 0 : _a.call(definition, value) : value;
transforms2.push([key, initialKeyframe]);
} else {
initialKeyframes[key] = initialKeyframe;
}
}
if (transforms2.length) {
initialKeyframes.transform = buildTransformTemplate(transforms2);
}
if (Object.keys(initialKeyframes).length === 0) {
return null;
}
return initialKeyframes;
}
const SVG_STYLE_TO_ATTRIBUTES = {
"fill": true,
"stroke": true,
"opacity": true,
"stroke-width": true,
"fill-opacity": true,
"stroke-opacity": true,
"stroke-linecap": true,
"stroke-linejoin": true,
"stroke-dasharray": true,
"stroke-dashoffset": true,
"cx": true,
"cy": true,
"r": true,
"d": true,
"x1": true,
"y1": true,
"x2": true,
"y2": true,
"points": true,
"path-length": true,
"viewBox": true,
"width": true,
"height": true,
"preserve-aspect-ratio": true,
"clip-path": true,
"filter": true,
"mask": true,
"stop-color": true,
"stop-opacity": true,
"gradient-transform": true,
"gradient-units": true,
"spread-method": true,
"marker-end": true,
"marker-mid": true,
"marker-start": true,
"text-anchor": true,
"dominant-baseline": true,
"font-family": true,
"font-size": true,
"font-weight": true,
"letter-spacing": true,
"vector-effect": true
};
function camelToKebab(str) {
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
}
function buildSVGPath$1(attrs, length, spacing = 1, offset = 0) {
attrs.pathLength = 1;
delete attrs["path-length"];
attrs["stroke-dashoffset"] = px$1.transform(-offset);
const pathLength = px$1.transform(length);
const pathSpacing = px$1.transform(spacing);
attrs["stroke-dasharray"] = `${pathLength} ${pathSpacing}`;
}
function convertSvgStyleToAttributes(keyframes2) {
const attrs = {};
const styleProps = {};
for (const key in keyframes2) {
const kebabKey = camelToKebab(key);
if (kebabKey in SVG_STYLE_TO_ATTRIBUTES) {
const value = keyframes2[key];
attrs[kebabKey] = isMotionValue$1(value) ? value.get() : value;
} else {
styleProps[key] = keyframes2[key];
}
}
if (attrs["path-length"] !== void 0) {
buildSVGPath$1(attrs, attrs["path-length"], attrs["path-spacing"], attrs["path-offset"]);
}
return {
attrs,
style: styleProps
};
}
typeof WorkerGlobalScope !== "undefined" && globalThis instanceof WorkerGlobalScope;
const isDef = (val) => typeof val !== "undefined";
function convertBoundingBoxToBox$1({ top, left, right, bottom }) {
return {
x: { min: left, max: right },
y: { min: top, max: bottom }
};
}
function transformBoxPoints$1(point, transformPoint2) {
if (!transformPoint2)
return point;
const topLeft = transformPoint2({ x: point.left, y: point.top });
const bottomRight = transformPoint2({ x: point.right, y: point.bottom });
return {
top: topLeft.y,
left: topLeft.x,
bottom: bottomRight.y,
right: bottomRight.x
};
}
function measureViewportBox$1(instance, transformPoint2) {
return convertBoundingBoxToBox$1(transformBoxPoints$1(instance.getBoundingClientRect(), transformPoint2));
}
const isMotionValue = (value) => Boolean(value && value.getVelocity);
const featureProps = {
animation: [
"animate",
"variants",
"whileHover",
"whileTap",
"exit",
"whileInView",
"whileFocus",
"whileDrag"
],
exit: ["exit"],
drag: ["drag", "dragControls"],
focus: ["whileFocus"],
hover: ["whileHover", "onHoverStart", "onHoverEnd"],
tap: ["whileTap", "onTap", "onTapStart", "onTapCancel"],
pan: ["onPan", "onPanStart", "onPanSessionStart", "onPanEnd"],
inView: ["whileInView", "onViewportEnter", "onViewportLeave"],
layout: ["layout", "layoutId"]
};
const featureDefinitions = {};
for (const key in featureProps) {
featureDefinitions[key] = {
isEnabled: (props) => featureProps[key].some((name) => !!props[name])
};
}
const createAxisDelta = () => ({
translate: 0,
scale: 1,
origin: 0,
originPoint: 0
});
const createDelta = () => ({
x: createAxisDelta(),
y: createAxisDelta()
});
const createAxis$1 = () => ({ min: 0, max: 0 });
const createBox$1 = () => ({
x: createAxis$1(),
y: createAxis$1()
});
const isBrowser = typeof window !== "undefined";
const prefersReducedMotion = { current: null };
const hasReducedMotionListener = { current: false };
function initPrefersReducedMotion() {
hasReducedMotionListener.current = true;
if (!isBrowser)
return;
if (window.matchMedia) {
const motionMediaQuery = window.matchMedia("(prefers-reduced-motion)");
const setReducedMotionPreferences = () => prefersReducedMotion.current = motionMediaQuery.matches;
motionMediaQuery.addListener(setReducedMotionPreferences);
setReducedMotionPreferences();
} else {
prefersReducedMotion.current = false;
}
}
function isAnimationControls(v) {
return v !== null && typeof v === "object" && typeof v.start === "function";
}
function isVariantLabel(v) {
return typeof v === "string" || Array.isArray(v);
}
const variantPriorityOrder = [
"animate",
"whileInView",
"whileFocus",
"whileHover",
"whileTap",
"whileDrag",
"exit"
];
const variantProps = ["initial", ...variantPriorityOrder];
function isControllingVariants(props) {
return isAnimationControls(props.animate) || variantProps.some((name) => isVariantLabel(props[name]));
}
function isVariantNode(props) {
return Boolean(isControllingVariants(props) || props.variants);
}
let now;
function clearTime() {
now = void 0;
}
const time = {
now: () => {
if (now === void 0) {
time.set(frameData.isProcessing || MotionGlobalConfig.useManualTiming ? frameData.timestamp : performance.now());
}
return now;
},
set: (newTime) => {
now = newTime;
queueMicrotask(clearTime);
}
};
const warned = /* @__PURE__ */ new Set();
function warnOnce(condition, message, element) {
if (condition || warned.has(message))
return;
console.warn(message);
warned.add(message);
}
function addUniqueItem(arr, item) {
if (arr.indexOf(item) === -1)
arr.push(item);
}
function removeItem(arr, item) {
const index2 = arr.indexOf(item);
if (index2 > -1)
arr.splice(index2, 1);
}
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) {
this.subscriptions[0](a, b, c);
} else {
for (let i = 0; i < numSubscriptions; i++) {
const handler = this.subscriptions[i];
handler && handler(a, b, c);
}
}
}
getSize() {
return this.subscriptions.length;
}
clear() {
this.subscriptions.length = 0;
}
}
function velocityPerSecond(velocity, frameDuration) {
return frameDuration ? velocity * (1e3 / frameDuration) : 0;
}
const MAX_VELOCITY_DELTA = 30;
const isFloat = (value) => {
return !isNaN(parseFloat(value));
};
class MotionValue {
/**
* @param init - The initiating value
* @param config - Optional configuration options
*
* - `transformer`: A function to transform incoming values with.
*/
constructor(init, options = {}) {
this.version = "__VERSION__";
this.canTrackVelocity = null;
this.events = {};
this.updateAndNotify = (v, render = true) => {
var _a, _b;
const currentTime = time.now();
if (this.updatedAt !== currentTime) {
this.setPrevFrameValue();
}
this.prev = this.current;
this.setCurrent(v);
if (this.current !== this.prev) {
(_a = this.events.change) == null ? void 0 : _a.notify(this.current);
if (this.dependents) {
for (const dependent of this.dependents) {
dependent.dirty();
}
}
}
if (render) {
(_b = this.events.renderRequest) == null ? void 0 : _b.notify(this.current);
}
};
this.hasAnimated = false;
this.setCurrent(init);
this.owner = options.owner;
}
setCurrent(current) {
this.current = current;
this.updatedAt = time.now();
if (this.canTrackVelocity === null && current !== void 0) {
this.canTrackVelocity = isFloat(this.current);
}
}
setPrevFrameValue(prevFrameValue = this.current) {
this.prevFrameValue = prevFrameValue;
this.prevUpdatedAt = this.updatedAt;
}
/**
* Adds a function that will be notified when the `MotionValue` is updated.
*
* It returns a function that, when called, will cancel the subscription.
*
* When calling `onChange` inside a React component, it should be wrapped with the
* `useEffect` hook. As it returns an unsubscribe function, this should be returned
* from the `useEffect` function to ensure you don't add duplicate subscribers..
*
* ```jsx
* export const MyComponent = () => {
* const x = useMotionValue(0)
* const y = useMotionValue(0)
* const opacity = useMotionValue(1)
*
* useEffect(() => {
* function updateOpacity() {
* const maxXY = Math.max(x.get(), y.get())
* const newOpacity = transform(maxXY, [0, 100], [1, 0])
* opacity.set(newOpacity)
* }
*
* const unsubscribeX = x.on("change", updateOpacity)
* const unsubscribeY = y.on("change", updateOpacity)
*
* return () => {
* unsubscribeX()
* unsubscribeY()
* }
* }, [])
*
* return <motion.div style={{ x }} />
* }
* ```
*
* @param subscriber - A function that receives the latest value.
* @returns A function that, when called, will cancel this subscription.
*
* @deprecated
*/
onChange(subscription) {
if (process.env.NODE_ENV !== "production") {
warnOnce(false, `value.onChange(callback) is deprecated. Switch to value.on("change", callback).`);
}
return this.on("change", subscription);
}
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = new SubscriptionManager();
}
const unsubscribe = this.events[eventName].add(callback);
if (eventName === "change") {
return () => {
unsubscribe();
frame.read(() => {
if (!this.events.change.getSize()) {
this.stop();
}
});
};
}
return unsubscribe;
}
clearListeners() {
for (const eventManagers in this.events) {
this.events[eventManagers].clear();
}
}
/**
* Attaches a passive effect to the `MotionValue`.
*/
attach(passiveEffect, stopPassiveEffect) {
this.passiveEffect = passiveEffect;
this.stopPassiveEffect = stopPassiveEffect;
}
/**
* Sets the state of the `MotionValue`.
*
* @remarks
*
* ```jsx
* const x = useMotionValue(0)
* x.set(10)
* ```
*
* @param latest - Latest value to set.
* @param render - Whether to notify render subscribers. Defaults to `true`
*
* @public
*/
set(v, render = true) {
if (!render || !this.passiveEffect) {
this.updateAndNotify(v, render);
} else {
this.passiveEffect(v, this.updateAndNotify);
}
}
setWithVelocity(prev, current, delta) {
this.set(current);
this.prev = void 0;
this.prevFrameValue = prev;
this.prevUpdatedAt = this.updatedAt - delta;
}
/**
* Set the state of the `MotionValue`, stopping any active animations,
* effects, and resets velocity to `0`.
*/
jump(v, endAnimation = true) {
this.updateAndNotify(v);
this.prev = v;
this.prevUpdatedAt = this.prevFrameValue = void 0;
endAnimation && this.stop();
if (this.stopPassiveEffect)
this.stopPassiveEffect();
}
dirty() {
var _a;
(_a = this.events.change) == null ? void 0 : _a.notify(this.current);
}
addDependent(dependent) {
if (!this.dependents) {
this.dependents = /* @__PURE__ */ new Set();
}
this.dependents.add(dependent);
}
removeDependent(dependent) {
if (this.dependents) {
this.dependents.delete(dependent);
}
}
/**
* Returns the latest state of `MotionValue`
*
* @returns - The latest state of `MotionValue`
*
* @public
*/
get() {
return this.current;
}
/**
* @public
*/
getPrevious() {
return this.prev;
}
/**
* Returns the latest velocity of `MotionValue`
*
* @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
*
* @public
*/
getVelocity() {
const currentTime = time.now();
if (!this.canTrackVelocity || this.prevFrameValue === void 0 || currentTime - this.updatedAt > MAX_VELOCITY_DELTA) {
return 0;
}
const delta = Math.min(this.updatedAt - this.prevUpdatedAt, MAX_VELOCITY_DELTA);
return velocityPerSecond(parseFloat(this.current) - parseFloat(this.prevFrameValue), delta);
}
/**
* Registers a new animation to control this `MotionValue`. Only one
* animation can drive a `MotionValue` at one time.
*
* ```jsx
* value.start()
* ```
*
* @param animation - A function that starts the provided animation
*/
start(startAnimation) {
this.stop();
return new Promise((resolve) => {
this.hasAnimated = true;
this.animation = startAnimation(resolve);
if (this.events.animationStart) {
this.events.animationStart.notify();
}
}).then(() => {
if (this.events.animationComplete) {
this.events.animationComplete.notify();
}
this.clearAnimation();
});
}
/**
* Stop the currently active animation.
*
* @public
*/
stop() {
if (this.animation) {
this.animation.stop();
if (this.events.animationCancel) {
this.events.animationCancel.notify();
}
}
this.clearAnimation();
}
/**
* Returns `true` if this value is currently animating.
*
* @public
*/
isAnimating() {
return !!this.animation;
}
clearAnimation() {
delete this.animation;
}
/**
* Destroy and clean up subscribers to this `MotionValue`.
*
* The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically
* handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually
* created a `MotionValue` via the `motionValue` function.
*
* @public
*/
destroy() {
var _a, _b;
(_a = this.dependents) == null ? void 0 : _a.clear();
(_b = this.events.destroy) == null ? void 0 : _b.notify();
this.clearListeners();
this.stop();
if (this.stopPassiveEffect) {
this.stopPassiveEffect();
}
}
}
function motionValue(init, options) {
return new MotionValue(init, options);
}
function updateMotionValuesFromProps(element, next, prev) {
for (const key in next) {
const nextValue = next[key];
const prevValue = prev[key];
if (isMotionValue(nextValue)) {
element.addValue(key, nextValue);
} else if (isMotionValue(prevValue)) {
element.addValue(key, motionValue(nextValue, { owner: element }));
} else if (prevValue !== nextValue) {
if (element.hasValue(key)) {
const existingValue = element.getValue(key);
if (existingValue.liveStyle === true) {
existingValue.jump(nextValue);
} else if (!existingValue.hasAnimated) {
existingValue.set(nextValue);
}
} else {
const latestValue = element.getStaticValue(key);
element.addValue(key, motionValue(latestValue !== void 0 ? latestValue : nextValue, { owner: element }));
}
}
}
for (const key in prev) {
if (next[key] === void 0)
element.removeValue(key);
}
return next;
}
function getValueState(visualElement) {
const state = [{}, {}];
visualElement == null ? void 0 : visualElement.values.forEach((value, key) => {
state[0][key] = value.get();
state[1][key] = value.getVelocity();
});
return state;
}
function resolveVariantFromProps(props, definition, custom, visualElement) {
if (typeof definition === "function") {
const [current, velocity] = getValueState(visualElement);
definition = definition(custom !== void 0 ? custom : props.custom, current, velocity);
}
if (typeof definition === "string") {
definition = props.variants && props.variants[definition];
}
if (typeof definition === "function") {
const [current, velocity] = getValueState(visualElement);
definition = definition(custom !== void 0 ? custom : props.custom, current, velocity);
}
return definition;
}
function fillWildcards(keyframes2) {
for (let i = 1; i < keyframes2.length; i++) {
keyframes2[i] ?? (keyframes2[i] = keyframes2[i - 1]);
}
}
const radToDeg = (rad) => rad * 180 / Math.PI;
const rotate = (v) => {
const angle = radToDeg(Math.atan2(v[1], v[0]));
return rebaseAngle(angle);
};
const matrix2dParsers = {
x: 4,
y: 5,
translateX: 4,
translateY: 5,
scaleX: 0,
scaleY: 3,
scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
rotate,
rotateZ: rotate,
skewX: (v) => radToDeg(Math.atan(v[1])),
skewY: (v) => radToDeg(Math.atan(v[2])),
skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2
};
const rebaseAngle = (angle) => {
angle = angle % 360;
if (angle < 0)
angle += 360;
return angle;
};
const rotateZ = rotate;
const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]);
const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]);
const matrix3dParsers = {
x: 12,
y: 13,
z: 14,
translateX: 12,
translateY: 13,
translateZ: 14,
scaleX,
scaleY,
scale: (v) => (scaleX(v) + scaleY(v)) / 2,
rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
rotateZ,
rotate: rotateZ,
skewX: (v) => radToDeg(Math.atan(v[4])),
skewY: (v) => radToDeg(Math.atan(v[1])),
skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2
};
function defaultTransformValue(name) {
return name.includes("scale") ? 1 : 0;
}
function parseValueFromTransform(transform, name) {
if (!transform || transform === "none") {
return defaultTransformValue(name);
}
const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);
let parsers;
let match;
if (matrix3dMatch) {
parsers = matrix3dParsers;
match = matrix3dMatch;
} else {
const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
parsers = matrix2dParsers;
match = matrix2dMatch;
}
if (!match) {
return defaultTransformValue(name);
}
const valueParser = parsers[name];
const values = match[1].split(",").map(convertTransformToNumber);
return typeof valueParser === "function" ? valueParser(values) : values[valueParser];
}
const readTransformValue = (instance, name) => {
const { transform = "none" } = getComputedStyle(instance);
return parseValueFromTransform(transform, name);
};
function convertTransformToNumber(value) {
return parseFloat(value.trim());
}
const transformPropOrder = [
"transformPerspective",
"x",
"y",
"z",
"translateX",
"translateY",
"translateZ",
"scale",
"scaleX",
"scaleY",
"rotate",
"rotateX",
"rotateY",
"rotateZ",
"skew",
"skewX",
"skewY"
];
const transformProps = /* @__PURE__ */ (() => new Set(transformPropOrder))();
const clamp$1 = (min, max, v) => {
if (v > max)
return max;
if (v < min)
return min;
return v;
};
const number = {
test: (v) => typeof v === "number",
parse: parseFloat,
transform: (v) => v
};
const alpha = {
...number,
transform: (v) => clamp$1(0, 1, v)
};
const scale = {
...number,
default: 1
};
const createUnitType = /* @__NO_SIDE_EFFECTS__ */ (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 = /* @__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 isNumOrPxType = (v) => v === number || v === px;
const transformKeys = /* @__PURE__ */ new Set(["x", "y", "z"]);
const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
function removeNonTranslationalTransform(visualElement) {
const removedTransforms = [];
nonTranslationalTransformKeys.forEach((key) => {
const value = visualElement.getValue(key);
if (value !== void 0) {
removedTransforms.push([key, value.get()]);
value.set(key.startsWith("scale") ? 1 : 0);
}
});
return removedTransforms;
}
const positionalValues = {
// Dimensions
width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
top: (_bbox, { top }) => parseFloat(top),
left: (_bbox, { left }) => parseFloat(left),
bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
// Transform
x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"),
y: (_bbox, { transform }) => parseValueFromTransform(transform, "y")
};
positionalValues.translateX = positionalValues.x;
positionalValues.translateY = positionalValues.y;
const toResolve = /* @__PURE__ */ new Set();
let isScheduled = false;
let anyNeedsMeasurement = false;
let isForced = false;
function measureAllKeyframes() {
if (anyNeedsMeasurement) {
const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement);
const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element));
const transformsToRestore = /* @__PURE__ */ new Map();
elementsToMeasure.forEach((element) => {
const removedTransforms = removeNonTranslationalTransform(element);
if (!removedTransforms.length)
return;
transformsToRestore.set(element, removedTransforms);
element.render();
});
resolversToMeasure.forEach((resolver) => resolver.measureInitialState());
elementsToMeasure.forEach((element) => {
element.render();
const restore = transformsToRestore.get(element);
if (restore) {
restore.forEach(([key, value]) => {
var _a;
(_a = element.getValue(key)) == null ? void 0 : _a.set(value);
});
}
});
resolversToMeasure.forEach((resolver) => resolver.measureEndState());
resolversToMeasure.forEach((resolver) => {
if (resolver.suspendedScrollY !== void 0) {
window.scrollTo(0, resolver.suspendedScrollY);
}
});
}
anyNeedsMeasurement = false;
isScheduled = false;
toResolve.forEach((resolver) => resolver.complete(isForced));
toResolve.clear();
}
function readAllKeyframes() {
toResolve.forEach((resolver) => {
resolver.readKeyframes();
if (resolver.needsMeasurement) {
anyNeedsMeasurement = true;
}
});
}
function flushKeyframeResolvers() {
isForced = true;
readAllKeyframes();
measureAllKeyframes();
isForced = false;
}
class KeyframeResolver {
constructor(unresolvedKeyframes, onComplete, name, motionValue2, element, isAsync = false) {
this.state = "pending";
this.isAsync = false;
this.needsMeasurement = false;
this.unresolvedKeyframes = [...unresolvedKeyframes];
this.onComplete = onComplete;
this.name = name;
this.motionValue = motionValue2;
this.element = element;
this.isAsync = isAsync;
}
scheduleResolve() {
this.state = "scheduled";
if (this.isAsync) {
toResolve.add(this);
if (!isScheduled) {
isScheduled = true;
frame.read(readAllKeyframes);
frame.resolveKeyframes(measureAllKeyframes);
}
} else {
this.readKeyframes();
this.complete();
}
}
readKeyframes() {
const { unresolvedKeyframes, name, element, motionValue: motionValue2 } = this;
if (unresolvedKeyframes[0] === null) {
const currentValue = motionValue2 == null ? void 0 : motionValue2.get();
const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
if (currentValue !== void 0) {
unresolvedKeyframes[0] = currentValue;
} else if (element && name) {
const valueAsRead = element.readValue(name, finalKeyframe);
if (valueAsRead !== void 0 && valueAsRead !== null) {
unresolvedKeyframes[0] = valueAsRead;
}
}
if (unresolvedKeyframes[0] === void 0) {
unresolvedKeyframes[0] = finalKeyframe;
}
if (motionValue2 && currentValue === void 0) {
motionValue2.set(unresolvedKeyframes[0]);
}
}
fillWildcards(unresolvedKeyframes);
}
setFinalKeyframe() {
}
measureInitialState() {
}
renderEndStyles() {
}
measureEndState