main repo

This commit is contained in:
Basilosaurusrex
2025-11-24 18:09:40 +01:00
parent b636ee5e70
commit f027651f9b
34146 changed files with 4436636 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
import { transformProps, getDefaultValueType } from 'motion-dom';
import { createBox } from '../../projection/geometry/models.mjs';
import { DOMVisualElement } from '../dom/DOMVisualElement.mjs';
import { camelToDash } from '../dom/utils/camel-to-dash.mjs';
import { buildSVGAttrs } from './utils/build-attrs.mjs';
import { camelCaseAttributes } from './utils/camel-case-attrs.mjs';
import { isSVGTag } from './utils/is-svg-tag.mjs';
import { renderSVG } from './utils/render.mjs';
import { scrapeMotionValuesFromProps } from './utils/scrape-motion-values.mjs';
class SVGVisualElement extends DOMVisualElement {
constructor() {
super(...arguments);
this.type = "svg";
this.isSVGTag = false;
this.measureInstanceViewportBox = createBox;
}
getBaseTargetFromProps(props, key) {
return props[key];
}
readValueFromInstance(instance, key) {
if (transformProps.has(key)) {
const defaultType = getDefaultValueType(key);
return defaultType ? defaultType.default || 0 : 0;
}
key = !camelCaseAttributes.has(key) ? camelToDash(key) : key;
return instance.getAttribute(key);
}
scrapeMotionValuesFromProps(props, prevProps, visualElement) {
return scrapeMotionValuesFromProps(props, prevProps, visualElement);
}
build(renderState, latestValues, props) {
buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate, props.style);
}
renderInstance(instance, renderState, styleProp, projection) {
renderSVG(instance, renderState, styleProp, projection);
}
mount(instance) {
this.isSVGTag = isSVGTag(instance.tagName);
super.mount(instance);
}
}
export { SVGVisualElement };

View File

@@ -0,0 +1,33 @@
/**
* We keep these listed separately as we use the lowercase tag names as part
* of the runtime bundle to detect SVG components
*/
const lowercaseSVGElements = [
"animate",
"circle",
"defs",
"desc",
"ellipse",
"g",
"image",
"line",
"filter",
"marker",
"mask",
"metadata",
"path",
"pattern",
"polygon",
"polyline",
"rect",
"stop",
"switch",
"symbol",
"svg",
"text",
"tspan",
"use",
"view",
];
export { lowercaseSVGElements };

View File

@@ -0,0 +1,25 @@
"use client";
import { useMemo } from 'react';
import { copyRawValuesOnly } from '../html/use-props.mjs';
import { buildSVGAttrs } from './utils/build-attrs.mjs';
import { createSvgRenderState } from './utils/create-render-state.mjs';
import { isSVGTag } from './utils/is-svg-tag.mjs';
function useSVGProps(props, visualState, _isStatic, Component) {
const visualProps = useMemo(() => {
const state = createSvgRenderState();
buildSVGAttrs(state, visualState, isSVGTag(Component), props.transformTemplate, props.style);
return {
...state.attrs,
style: { ...state.style },
};
}, [visualState]);
if (props.style) {
const rawStyles = {};
copyRawValuesOnly(rawStyles, props.style, props);
visualProps.style = { ...rawStyles, ...visualProps.style };
}
return visualProps;
}
export { useSVGProps };

View File

@@ -0,0 +1,11 @@
"use client";
import { makeUseVisualState } from '../../motion/utils/use-visual-state.mjs';
import { createSvgRenderState } from './utils/create-render-state.mjs';
import { scrapeMotionValuesFromProps } from './utils/scrape-motion-values.mjs';
const useSVGVisualState = /*@__PURE__*/ makeUseVisualState({
scrapeMotionValuesFromProps: scrapeMotionValuesFromProps,
createRenderState: createSvgRenderState,
});
export { useSVGVisualState };

View File

@@ -0,0 +1,57 @@
import { buildHTMLStyles } from '../../html/utils/build-styles.mjs';
import { buildSVGPath } from './path.mjs';
/**
* Build SVG visual attributes, like cx and style.transform
*/
function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0,
// This is object creation, which we try to avoid per-frame.
...latest }, isSVGTag, transformTemplate, styleProp) {
buildHTMLStyles(state, latest, transformTemplate);
/**
* For svg tags we just want to make sure viewBox is animatable and treat all the styles
* as normal HTML tags.
*/
if (isSVGTag) {
if (state.style.viewBox) {
state.attrs.viewBox = state.style.viewBox;
}
return;
}
state.attrs = state.style;
state.style = {};
const { attrs, style } = state;
/**
* However, we apply transforms as CSS transforms.
* So if we detect a transform, transformOrigin we take it from attrs and copy it into style.
*/
if (attrs.transform) {
style.transform = attrs.transform;
delete attrs.transform;
}
if (style.transform || attrs.transformOrigin) {
style.transformOrigin = attrs.transformOrigin ?? "50% 50%";
delete attrs.transformOrigin;
}
if (style.transform) {
/**
* SVG's element transform-origin uses its own median as a reference.
* Therefore, transformBox becomes a fill-box
*/
style.transformBox = styleProp?.transformBox ?? "fill-box";
delete attrs.transformBox;
}
// Render attrX/attrY/attrScale as attributes
if (attrX !== undefined)
attrs.x = attrX;
if (attrY !== undefined)
attrs.y = attrY;
if (attrScale !== undefined)
attrs.scale = attrScale;
// Build SVG path if one has been defined
if (pathLength !== undefined) {
buildSVGPath(attrs, pathLength, pathSpacing, pathOffset, false);
}
}
export { buildSVGAttrs };

View File

@@ -0,0 +1,30 @@
/**
* A set of attribute names that are always read/written as camel case.
*/
const camelCaseAttributes = new Set([
"baseFrequency",
"diffuseConstant",
"kernelMatrix",
"kernelUnitLength",
"keySplines",
"keyTimes",
"limitingConeAngle",
"markerHeight",
"markerWidth",
"numOctaves",
"targetX",
"targetY",
"surfaceScale",
"specularConstant",
"specularExponent",
"stdDeviation",
"tableValues",
"viewBox",
"gradientTransform",
"pathLength",
"startOffset",
"textLength",
"lengthAdjust",
]);
export { camelCaseAttributes };

View File

@@ -0,0 +1,8 @@
import { createHtmlRenderState } from '../../html/utils/create-render-state.mjs';
const createSvgRenderState = () => ({
...createHtmlRenderState(),
attrs: {},
});
export { createSvgRenderState };

View File

@@ -0,0 +1,3 @@
const isSVGTag = (tag) => typeof tag === "string" && tag.toLowerCase() === "svg";
export { isSVGTag };

View File

@@ -0,0 +1,32 @@
import { px } from 'motion-dom';
const dashKeys = {
offset: "stroke-dashoffset",
array: "stroke-dasharray",
};
const camelKeys = {
offset: "strokeDashoffset",
array: "strokeDasharray",
};
/**
* Build SVG path properties. Uses the path's measured length to convert
* our custom pathLength, pathSpacing and pathOffset into stroke-dashoffset
* and stroke-dasharray attributes.
*
* This function is mutative to reduce per-frame GC.
*/
function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true) {
// Normalise path length by setting SVG attribute pathLength to 1
attrs.pathLength = 1;
// We use dash case when setting attributes directly to the DOM node and camel case
// when defining props on a React component.
const keys = useDashCase ? dashKeys : camelKeys;
// Build the dash offset
attrs[keys.offset] = px.transform(-offset);
// Build the dash array
const pathLength = px.transform(length);
const pathSpacing = px.transform(spacing);
attrs[keys.array] = `${pathLength} ${pathSpacing}`;
}
export { buildSVGPath };

View File

@@ -0,0 +1,12 @@
import { camelToDash } from '../../dom/utils/camel-to-dash.mjs';
import { renderHTML } from '../../html/utils/render.mjs';
import { camelCaseAttributes } from './camel-case-attrs.mjs';
function renderSVG(element, renderState, _styleProp, projection) {
renderHTML(element, renderState, undefined, projection);
for (const key in renderState.attrs) {
element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]);
}
}
export { renderSVG };

View File

@@ -0,0 +1,18 @@
import { isMotionValue, transformPropOrder } from 'motion-dom';
import { scrapeMotionValuesFromProps as scrapeMotionValuesFromProps$1 } from '../../html/utils/scrape-motion-values.mjs';
function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
for (const key in props) {
if (isMotionValue(props[key]) ||
isMotionValue(prevProps[key])) {
const targetKey = transformPropOrder.indexOf(key) !== -1
? "attr" + key.charAt(0).toUpperCase() + key.substring(1)
: key;
newValues[targetKey] = props[key];
}
}
return newValues;
}
export { scrapeMotionValuesFromProps };