motion-v
Version:
<h1 align="center"> <img width="35" height="35" alt="Motion logo" src="https://github.com/user-attachments/assets/00d6d1c3-72c4-4c2f-a664-69da13182ffc" /><br />Motion for Vue</h1>
95 lines (94 loc) • 3.69 kB
JavaScript
import { updateLazyFeatures } from "../../features/lazy-features.mjs";
import { useMotionState } from "./use-motion-state.mjs";
import { MotionComponentProps } from "./props.mjs";
import { Comment, cloneVNode, defineComponent, h, mergeProps } from "vue";
var componentMaxCache = /* @__PURE__ */ new Map();
var componentMiniCache = /* @__PURE__ */ new Map();
function renderSlotFragments(fragments) {
if (!Array.isArray(fragments)) return [fragments];
const result = [];
for (const item of fragments) if (Array.isArray(item)) result.push(...item);
else result.push(item);
return result;
}
var SELF_CLOSING_TAGS = [
"area",
"img",
"input"
];
function handlePrimitiveAndSlot(asTag, allAttrs, slots) {
if (typeof asTag === "string" && SELF_CLOSING_TAGS.includes(asTag)) return h(asTag, allAttrs);
if (asTag === "template") {
if (!slots.default) return null;
const childrens = renderSlotFragments(slots.default());
const firstNonCommentChildrenIndex = childrens.findIndex((child) => child.type !== Comment);
if (firstNonCommentChildrenIndex === -1) return childrens;
const firstNonCommentChildren = childrens[firstNonCommentChildrenIndex];
delete firstNonCommentChildren.props?.ref;
const mergedProps = firstNonCommentChildren.props ? mergeProps(allAttrs, firstNonCommentChildren.props) : allAttrs;
if (allAttrs.class && firstNonCommentChildren.props?.class) delete firstNonCommentChildren.props.class;
const cloned = cloneVNode(firstNonCommentChildren, mergedProps);
for (const prop in mergedProps) if (prop.startsWith("on")) {
cloned.props ||= {};
cloned.props[prop] = mergedProps[prop];
}
if (childrens.length === 1) return cloned;
childrens[firstNonCommentChildrenIndex] = cloned;
return childrens;
}
return null;
}
function createMotionComponent(component, options = {}) {
const isString = typeof component === "string";
const name = isString ? component : component.name || "";
const componentCache = options.renderer ? componentMaxCache : componentMiniCache;
if (isString && componentCache?.has(component)) return componentCache.get(component);
const motionComponent = defineComponent({
inheritAttrs: false,
props: {
...MotionComponentProps,
as: {
type: [String, Object],
default: component || "div"
}
},
name: name ? `motion.${name}` : "Motion",
setup(props, { slots }) {
const { getProps, getAttrs, state } = useMotionState(props, options.renderer);
function onVnodeUpdated() {
const el = state.element;
if ((!(typeof props.as === "object") || props.asChild) && el) {
const { style } = getAttrs();
if (style) for (const [key, val] of Object.entries(style)) el.style[key] = val;
}
}
return () => {
const motionProps = getProps();
const motionAttrs = getAttrs();
const asTag = props.asChild ? "template" : props.as;
const allAttrs = {
...props.forwardMotionProps ? motionProps : {},
...motionAttrs,
onVnodeUpdated
};
const primitiveOrSlotResult = handlePrimitiveAndSlot(asTag, allAttrs, slots);
if (primitiveOrSlotResult !== null) return primitiveOrSlotResult;
return h(asTag, { ...allAttrs }, slots);
};
}
});
if (isString) componentCache?.set(component, motionComponent);
return motionComponent;
}
function createMotionComponentWithFeatures(featureBundle) {
const renderer = featureBundle?.renderer;
updateLazyFeatures(featureBundle?.features || []);
return new Proxy({}, { get(_, prop) {
if (prop === "create") return (component, options) => createMotionComponent(component, {
...options,
renderer
});
return createMotionComponent(prop, { renderer });
} });
}
export { createMotionComponentWithFeatures };