till download files, everything works
This commit is contained in:
1 parent
ce893a4185
commit
ed1a580b09
6050 files changed
+768543
-177
No files matched your search
+130
@@ -0,0 +1,130 @@
|
||||
import { isNone } from '../../animation/utils/is-none.mjs';
|
||||
import { getVariableValue } from './utils/css-variables-conversion.mjs';
|
||||
import { isCSSVariableToken } from './utils/is-css-variable.mjs';
|
||||
import { positionalKeys, isNumOrPxType, positionalValues } from './utils/unit-conversion.mjs';
|
||||
import { findDimensionValueType } from './value-types/dimensions.mjs';
|
||||
import { KeyframeResolver } from '../utils/KeyframesResolver.mjs';
|
||||
import { makeNoneKeyframesAnimatable } from '../html/utils/make-none-animatable.mjs';
|
||||
|
||||
class DOMKeyframesResolver extends KeyframeResolver {
|
||||
constructor(unresolvedKeyframes, onComplete, name, motionValue, element) {
|
||||
super(unresolvedKeyframes, onComplete, name, motionValue, element, true);
|
||||
}
|
||||
readKeyframes() {
|
||||
const { unresolvedKeyframes, element, name } = this;
|
||||
if (!element || !element.current)
|
||||
return;
|
||||
super.readKeyframes();
|
||||
/**
|
||||
* If any keyframe is a CSS variable, we need to find its value by sampling the element
|
||||
*/
|
||||
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
||||
let keyframe = unresolvedKeyframes[i];
|
||||
if (typeof keyframe === "string") {
|
||||
keyframe = keyframe.trim();
|
||||
if (isCSSVariableToken(keyframe)) {
|
||||
const resolved = getVariableValue(keyframe, element.current);
|
||||
if (resolved !== undefined) {
|
||||
unresolvedKeyframes[i] = resolved;
|
||||
}
|
||||
if (i === unresolvedKeyframes.length - 1) {
|
||||
this.finalKeyframe = keyframe;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
|
||||
* This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
|
||||
* have a far bigger performance impact.
|
||||
*/
|
||||
this.resolveNoneKeyframes();
|
||||
/**
|
||||
* Check to see if unit type has changed. If so schedule jobs that will
|
||||
* temporarily set styles to the destination keyframes.
|
||||
* Skip if we have more than two keyframes or this isn't a positional value.
|
||||
* TODO: We can throw if there are multiple keyframes and the value type changes.
|
||||
*/
|
||||
if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
|
||||
return;
|
||||
}
|
||||
const [origin, target] = unresolvedKeyframes;
|
||||
const originType = findDimensionValueType(origin);
|
||||
const targetType = findDimensionValueType(target);
|
||||
/**
|
||||
* Either we don't recognise these value types or we can animate between them.
|
||||
*/
|
||||
if (originType === targetType)
|
||||
return;
|
||||
/**
|
||||
* If both values are numbers or pixels, we can animate between them by
|
||||
* converting them to numbers.
|
||||
*/
|
||||
if (isNumOrPxType(originType) && isNumOrPxType(targetType)) {
|
||||
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
||||
const value = unresolvedKeyframes[i];
|
||||
if (typeof value === "string") {
|
||||
unresolvedKeyframes[i] = parseFloat(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* Else, the only way to resolve this is by measuring the element.
|
||||
*/
|
||||
this.needsMeasurement = true;
|
||||
}
|
||||
}
|
||||
resolveNoneKeyframes() {
|
||||
const { unresolvedKeyframes, name } = this;
|
||||
const noneKeyframeIndexes = [];
|
||||
for (let i = 0; i < unresolvedKeyframes.length; i++) {
|
||||
if (isNone(unresolvedKeyframes[i])) {
|
||||
noneKeyframeIndexes.push(i);
|
||||
}
|
||||
}
|
||||
if (noneKeyframeIndexes.length) {
|
||||
makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name);
|
||||
}
|
||||
}
|
||||
measureInitialState() {
|
||||
const { element, unresolvedKeyframes, name } = this;
|
||||
if (!element || !element.current)
|
||||
return;
|
||||
if (name === "height") {
|
||||
this.suspendedScrollY = window.pageYOffset;
|
||||
}
|
||||
this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
|
||||
unresolvedKeyframes[0] = this.measuredOrigin;
|
||||
// Set final key frame to measure after next render
|
||||
const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
|
||||
if (measureKeyframe !== undefined) {
|
||||
element.getValue(name, measureKeyframe).jump(measureKeyframe, false);
|
||||
}
|
||||
}
|
||||
measureEndState() {
|
||||
var _a;
|
||||
const { element, name, unresolvedKeyframes } = this;
|
||||
if (!element || !element.current)
|
||||
return;
|
||||
const value = element.getValue(name);
|
||||
value && value.jump(this.measuredOrigin, false);
|
||||
const finalKeyframeIndex = unresolvedKeyframes.length - 1;
|
||||
const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex];
|
||||
unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
|
||||
if (finalKeyframe !== null && this.finalKeyframe === undefined) {
|
||||
this.finalKeyframe = finalKeyframe;
|
||||
}
|
||||
// If we removed transform values, reapply them before the next render
|
||||
if ((_a = this.removedTransforms) === null || _a === void 0 ? void 0 : _a.length) {
|
||||
this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
|
||||
element
|
||||
.getValue(unsetTransformName)
|
||||
.set(unsetTransformValue);
|
||||
});
|
||||
}
|
||||
this.resolveNoneKeyframes();
|
||||
}
|
||||
}
|
||||
|
||||
export { DOMKeyframesResolver };
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { VisualElement } from '../VisualElement.mjs';
|
||||
import { DOMKeyframesResolver } from './DOMKeyframesResolver.mjs';
|
||||
|
||||
class DOMVisualElement extends VisualElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.KeyframeResolver = DOMKeyframesResolver;
|
||||
}
|
||||
sortInstanceNodePosition(a, b) {
|
||||
/**
|
||||
* compareDocumentPosition returns a bitmask, by using the bitwise &
|
||||
* we're returning true if 2 in that bitmask is set to true. 2 is set
|
||||
* to true if b preceeds a.
|
||||
*/
|
||||
return a.compareDocumentPosition(b) & 2 ? 1 : -1;
|
||||
}
|
||||
getBaseTargetFromProps(props, key) {
|
||||
return props.style
|
||||
? props.style[key]
|
||||
: undefined;
|
||||
}
|
||||
removeValueFromRenderState(key, { vars, style }) {
|
||||
delete vars[key];
|
||||
delete style[key];
|
||||
}
|
||||
}
|
||||
|
||||
export { DOMVisualElement };
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { Fragment } from 'react';
|
||||
import { HTMLVisualElement } from '../html/HTMLVisualElement.mjs';
|
||||
import { SVGVisualElement } from '../svg/SVGVisualElement.mjs';
|
||||
import { isSVGComponent } from './utils/is-svg-component.mjs';
|
||||
|
||||
const createDomVisualElement = (Component, options) => {
|
||||
return isSVGComponent(Component)
|
||||
? new SVGVisualElement(options)
|
||||
: new HTMLVisualElement(options, {
|
||||
allowProjection: Component !== Fragment,
|
||||
});
|
||||
};
|
||||
|
||||
export { createDomVisualElement };
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { animations } from '../../motion/features/animations.mjs';
|
||||
import { gestureAnimations } from '../../motion/features/gestures.mjs';
|
||||
import { createDomVisualElement } from './create-visual-element.mjs';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
const domAnimation = {
|
||||
renderer: createDomVisualElement,
|
||||
...animations,
|
||||
...gestureAnimations,
|
||||
};
|
||||
|
||||
export { domAnimation };
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { drag } from '../../motion/features/drag.mjs';
|
||||
import { layout } from '../../motion/features/layout.mjs';
|
||||
import { domAnimation } from './features-animation.mjs';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
const domMax = {
|
||||
...domAnimation,
|
||||
...drag,
|
||||
...layout,
|
||||
};
|
||||
|
||||
export { domMax };
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { animations } from '../../motion/features/animations.mjs';
|
||||
import { createDomVisualElement } from './create-visual-element.mjs';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
const domMin = {
|
||||
renderer: createDomVisualElement,
|
||||
...animations,
|
||||
};
|
||||
|
||||
export { domMin };
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import { resolveElements } from '../utils/resolve-element.mjs';
|
||||
|
||||
const resizeHandlers = new WeakMap();
|
||||
let observer;
|
||||
function getElementSize(target, borderBoxSize) {
|
||||
if (borderBoxSize) {
|
||||
const { inlineSize, blockSize } = borderBoxSize[0];
|
||||
return { width: inlineSize, height: blockSize };
|
||||
}
|
||||
else if (target instanceof SVGElement && "getBBox" in target) {
|
||||
return target.getBBox();
|
||||
}
|
||||
else {
|
||||
return {
|
||||
width: target.offsetWidth,
|
||||
height: target.offsetHeight,
|
||||
};
|
||||
}
|
||||
}
|
||||
function notifyTarget({ target, contentRect, borderBoxSize, }) {
|
||||
var _a;
|
||||
(_a = resizeHandlers.get(target)) === null || _a === void 0 ? void 0 : _a.forEach((handler) => {
|
||||
handler({
|
||||
target,
|
||||
contentSize: contentRect,
|
||||
get size() {
|
||||
return getElementSize(target, borderBoxSize);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
function notifyAll(entries) {
|
||||
entries.forEach(notifyTarget);
|
||||
}
|
||||
function createResizeObserver() {
|
||||
if (typeof ResizeObserver === "undefined")
|
||||
return;
|
||||
observer = new ResizeObserver(notifyAll);
|
||||
}
|
||||
function resizeElement(target, handler) {
|
||||
if (!observer)
|
||||
createResizeObserver();
|
||||
const elements = resolveElements(target);
|
||||
elements.forEach((element) => {
|
||||
let elementHandlers = resizeHandlers.get(element);
|
||||
if (!elementHandlers) {
|
||||
elementHandlers = new Set();
|
||||
resizeHandlers.set(element, elementHandlers);
|
||||
}
|
||||
elementHandlers.add(handler);
|
||||
observer === null || observer === void 0 ? void 0 : observer.observe(element);
|
||||
});
|
||||
return () => {
|
||||
elements.forEach((element) => {
|
||||
const elementHandlers = resizeHandlers.get(element);
|
||||
elementHandlers === null || elementHandlers === void 0 ? void 0 : elementHandlers.delete(handler);
|
||||
if (!(elementHandlers === null || elementHandlers === void 0 ? void 0 : elementHandlers.size)) {
|
||||
observer === null || observer === void 0 ? void 0 : observer.unobserve(element);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export { resizeElement };
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
const windowCallbacks = new Set();
|
||||
let windowResizeHandler;
|
||||
function createWindowResizeHandler() {
|
||||
windowResizeHandler = () => {
|
||||
const size = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
};
|
||||
const info = {
|
||||
target: window,
|
||||
size,
|
||||
contentSize: size,
|
||||
};
|
||||
windowCallbacks.forEach((callback) => callback(info));
|
||||
};
|
||||
window.addEventListener("resize", windowResizeHandler);
|
||||
}
|
||||
function resizeWindow(callback) {
|
||||
windowCallbacks.add(callback);
|
||||
if (!windowResizeHandler)
|
||||
createWindowResizeHandler();
|
||||
return () => {
|
||||
windowCallbacks.delete(callback);
|
||||
if (!windowCallbacks.size && windowResizeHandler) {
|
||||
windowResizeHandler = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { resizeWindow };
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { resizeElement } from './handle-element.mjs';
|
||||
import { resizeWindow } from './handle-window.mjs';
|
||||
|
||||
function resize(a, b) {
|
||||
return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b);
|
||||
}
|
||||
|
||||
export { resize };
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
import { scrollInfo } from './track.mjs';
|
||||
import { observeTimeline } from './observe.mjs';
|
||||
import { supportsScrollTimeline } from './supports.mjs';
|
||||
|
||||
function scrollTimelineFallback({ source, container, axis = "y", }) {
|
||||
// Support legacy source argument. Deprecate later.
|
||||
if (source)
|
||||
container = source;
|
||||
// ScrollTimeline records progress as a percentage CSSUnitValue
|
||||
const currentTime = { value: 0 };
|
||||
const cancel = scrollInfo((info) => {
|
||||
currentTime.value = info[axis].progress * 100;
|
||||
}, { container, axis });
|
||||
return { currentTime, cancel };
|
||||
}
|
||||
const timelineCache = new Map();
|
||||
function getTimeline({ source, container = document.documentElement, axis = "y", } = {}) {
|
||||
// Support legacy source argument. Deprecate later.
|
||||
if (source)
|
||||
container = source;
|
||||
if (!timelineCache.has(container)) {
|
||||
timelineCache.set(container, {});
|
||||
}
|
||||
const elementCache = timelineCache.get(container);
|
||||
if (!elementCache[axis]) {
|
||||
elementCache[axis] = supportsScrollTimeline()
|
||||
? new ScrollTimeline({ source: container, axis })
|
||||
: scrollTimelineFallback({ source: container, axis });
|
||||
}
|
||||
return elementCache[axis];
|
||||
}
|
||||
/**
|
||||
* If the onScroll function has two arguments, it's expecting
|
||||
* more specific information about the scroll from scrollInfo.
|
||||
*/
|
||||
function isOnScrollWithInfo(onScroll) {
|
||||
return onScroll.length === 2;
|
||||
}
|
||||
/**
|
||||
* Currently, we only support element tracking with `scrollInfo`, though in
|
||||
* the future we can also offer ViewTimeline support.
|
||||
*/
|
||||
function needsElementTracking(options) {
|
||||
return options && (options.target || options.offset);
|
||||
}
|
||||
function scrollFunction(onScroll, options) {
|
||||
if (isOnScrollWithInfo(onScroll) || needsElementTracking(options)) {
|
||||
return scrollInfo((info) => {
|
||||
onScroll(info[options.axis].progress, info);
|
||||
}, options);
|
||||
}
|
||||
else {
|
||||
return observeTimeline(onScroll, getTimeline(options));
|
||||
}
|
||||
}
|
||||
function scrollAnimation(animation, options) {
|
||||
if (needsElementTracking(options)) {
|
||||
animation.pause();
|
||||
return scrollInfo((info) => {
|
||||
animation.time = animation.duration * info[options.axis].progress;
|
||||
}, options);
|
||||
}
|
||||
else {
|
||||
const timeline = getTimeline(options);
|
||||
return animation.attachTimeline(timeline, (valueAnimation) => {
|
||||
valueAnimation.pause();
|
||||
return observeTimeline((progress) => {
|
||||
valueAnimation.time = valueAnimation.duration * progress;
|
||||
}, timeline);
|
||||
});
|
||||
}
|
||||
}
|
||||
function scroll(onScroll, { axis = "y", ...options } = {}) {
|
||||
const optionsWithDefaults = { axis, ...options };
|
||||
return typeof onScroll === "function"
|
||||
? scrollFunction(onScroll, optionsWithDefaults)
|
||||
: scrollAnimation(onScroll, optionsWithDefaults);
|
||||
}
|
||||
|
||||
export { scroll };
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import { progress } from '../../../utils/progress.mjs';
|
||||
import { velocityPerSecond } from '../../../utils/velocity-per-second.mjs';
|
||||
|
||||
/**
|
||||
* A time in milliseconds, beyond which we consider the scroll velocity to be 0.
|
||||
*/
|
||||
const maxElapsed = 50;
|
||||
const createAxisInfo = () => ({
|
||||
current: 0,
|
||||
offset: [],
|
||||
progress: 0,
|
||||
scrollLength: 0,
|
||||
targetOffset: 0,
|
||||
targetLength: 0,
|
||||
containerLength: 0,
|
||||
velocity: 0,
|
||||
});
|
||||
const createScrollInfo = () => ({
|
||||
time: 0,
|
||||
x: createAxisInfo(),
|
||||
y: createAxisInfo(),
|
||||
});
|
||||
const keys = {
|
||||
x: {
|
||||
length: "Width",
|
||||
position: "Left",
|
||||
},
|
||||
y: {
|
||||
length: "Height",
|
||||
position: "Top",
|
||||
},
|
||||
};
|
||||
function updateAxisInfo(element, axisName, info, time) {
|
||||
const axis = info[axisName];
|
||||
const { length, position } = keys[axisName];
|
||||
const prev = axis.current;
|
||||
const prevTime = info.time;
|
||||
axis.current = element[`scroll${position}`];
|
||||
axis.scrollLength = element[`scroll${length}`] - element[`client${length}`];
|
||||
axis.offset.length = 0;
|
||||
axis.offset[0] = 0;
|
||||
axis.offset[1] = axis.scrollLength;
|
||||
axis.progress = progress(0, axis.scrollLength, axis.current);
|
||||
const elapsed = time - prevTime;
|
||||
axis.velocity =
|
||||
elapsed > maxElapsed
|
||||
? 0
|
||||
: velocityPerSecond(axis.current - prev, elapsed);
|
||||
}
|
||||
function updateScrollInfo(element, info, time) {
|
||||
updateAxisInfo(element, "x", info, time);
|
||||
updateAxisInfo(element, "y", info, time);
|
||||
info.time = time;
|
||||
}
|
||||
|
||||
export { createScrollInfo, updateScrollInfo };
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { frame, cancelFrame } from '../../../frameloop/frame.mjs';
|
||||
|
||||
function observeTimeline(update, timeline) {
|
||||
let prevProgress;
|
||||
const onFrame = () => {
|
||||
const { currentTime } = timeline;
|
||||
const percentage = currentTime === null ? 0 : currentTime.value;
|
||||
const progress = percentage / 100;
|
||||
if (prevProgress !== progress) {
|
||||
update(progress);
|
||||
}
|
||||
prevProgress = progress;
|
||||
};
|
||||
frame.update(onFrame, true);
|
||||
return () => cancelFrame(onFrame);
|
||||
}
|
||||
|
||||
export { observeTimeline };
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
const namedEdges = {
|
||||
start: 0,
|
||||
center: 0.5,
|
||||
end: 1,
|
||||
};
|
||||
function resolveEdge(edge, length, inset = 0) {
|
||||
let delta = 0;
|
||||
/**
|
||||
* If we have this edge defined as a preset, replace the definition
|
||||
* with the numerical value.
|
||||
*/
|
||||
if (edge in namedEdges) {
|
||||
edge = namedEdges[edge];
|
||||
}
|
||||
/**
|
||||
* Handle unit values
|
||||
*/
|
||||
if (typeof edge === "string") {
|
||||
const asNumber = parseFloat(edge);
|
||||
if (edge.endsWith("px")) {
|
||||
delta = asNumber;
|
||||
}
|
||||
else if (edge.endsWith("%")) {
|
||||
edge = asNumber / 100;
|
||||
}
|
||||
else if (edge.endsWith("vw")) {
|
||||
delta = (asNumber / 100) * document.documentElement.clientWidth;
|
||||
}
|
||||
else if (edge.endsWith("vh")) {
|
||||
delta = (asNumber / 100) * document.documentElement.clientHeight;
|
||||
}
|
||||
else {
|
||||
edge = asNumber;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If the edge is defined as a number, handle as a progress value.
|
||||
*/
|
||||
if (typeof edge === "number") {
|
||||
delta = length * edge;
|
||||
}
|
||||
return inset + delta;
|
||||
}
|
||||
|
||||
export { namedEdges, resolveEdge };
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import { calcInset } from './inset.mjs';
|
||||
import { ScrollOffset } from './presets.mjs';
|
||||
import { resolveOffset } from './offset.mjs';
|
||||
import { interpolate } from '../../../../utils/interpolate.mjs';
|
||||
import { defaultOffset } from '../../../../utils/offsets/default.mjs';
|
||||
|
||||
const point = { x: 0, y: 0 };
|
||||
function getTargetSize(target) {
|
||||
return "getBBox" in target && target.tagName !== "svg"
|
||||
? target.getBBox()
|
||||
: { width: target.clientWidth, height: target.clientHeight };
|
||||
}
|
||||
function resolveOffsets(container, info, options) {
|
||||
const { offset: offsetDefinition = ScrollOffset.All } = options;
|
||||
const { target = container, axis = "y" } = options;
|
||||
const lengthLabel = axis === "y" ? "height" : "width";
|
||||
const inset = target !== container ? calcInset(target, container) : point;
|
||||
/**
|
||||
* Measure the target and container. If they're the same thing then we
|
||||
* use the container's scrollWidth/Height as the target, from there
|
||||
* all other calculations can remain the same.
|
||||
*/
|
||||
const targetSize = target === container
|
||||
? { width: container.scrollWidth, height: container.scrollHeight }
|
||||
: getTargetSize(target);
|
||||
const containerSize = {
|
||||
width: container.clientWidth,
|
||||
height: container.clientHeight,
|
||||
};
|
||||
/**
|
||||
* Reset the length of the resolved offset array rather than creating a new one.
|
||||
* TODO: More reusable data structures for targetSize/containerSize would also be good.
|
||||
*/
|
||||
info[axis].offset.length = 0;
|
||||
/**
|
||||
* Populate the offset array by resolving the user's offset definition into
|
||||
* a list of pixel scroll offets.
|
||||
*/
|
||||
let hasChanged = !info[axis].interpolate;
|
||||
const numOffsets = offsetDefinition.length;
|
||||
for (let i = 0; i < numOffsets; i++) {
|
||||
const offset = resolveOffset(offsetDefinition[i], containerSize[lengthLabel], targetSize[lengthLabel], inset[axis]);
|
||||
if (!hasChanged && offset !== info[axis].interpolatorOffsets[i]) {
|
||||
hasChanged = true;
|
||||
}
|
||||
info[axis].offset[i] = offset;
|
||||
}
|
||||
/**
|
||||
* If the pixel scroll offsets have changed, create a new interpolator function
|
||||
* to map scroll value into a progress.
|
||||
*/
|
||||
if (hasChanged) {
|
||||
info[axis].interpolate = interpolate(info[axis].offset, defaultOffset(offsetDefinition));
|
||||
info[axis].interpolatorOffsets = [...info[axis].offset];
|
||||
}
|
||||
info[axis].progress = info[axis].interpolate(info[axis].current);
|
||||
}
|
||||
|
||||
export { resolveOffsets };
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
function calcInset(element, container) {
|
||||
const inset = { x: 0, y: 0 };
|
||||
let current = element;
|
||||
while (current && current !== container) {
|
||||
if (current instanceof HTMLElement) {
|
||||
inset.x += current.offsetLeft;
|
||||
inset.y += current.offsetTop;
|
||||
current = current.offsetParent;
|
||||
}
|
||||
else if (current.tagName === "svg") {
|
||||
/**
|
||||
* This isn't an ideal approach to measuring the offset of <svg /> tags.
|
||||
* It would be preferable, given they behave like HTMLElements in most ways
|
||||
* to use offsetLeft/Top. But these don't exist on <svg />. Likewise we
|
||||
* can't use .getBBox() like most SVG elements as these provide the offset
|
||||
* relative to the SVG itself, which for <svg /> is usually 0x0.
|
||||
*/
|
||||
const svgBoundingBox = current.getBoundingClientRect();
|
||||
current = current.parentElement;
|
||||
const parentBoundingBox = current.getBoundingClientRect();
|
||||
inset.x += svgBoundingBox.left - parentBoundingBox.left;
|
||||
inset.y += svgBoundingBox.top - parentBoundingBox.top;
|
||||
}
|
||||
else if (current instanceof SVGGraphicsElement) {
|
||||
const { x, y } = current.getBBox();
|
||||
inset.x += x;
|
||||
inset.y += y;
|
||||
let svg = null;
|
||||
let parent = current.parentNode;
|
||||
while (!svg) {
|
||||
if (parent.tagName === "svg") {
|
||||
svg = parent;
|
||||
}
|
||||
parent = current.parentNode;
|
||||
}
|
||||
current = svg;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inset;
|
||||
}
|
||||
|
||||
export { calcInset };
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import { resolveEdge, namedEdges } from './edge.mjs';
|
||||
|
||||
const defaultOffset = [0, 0];
|
||||
function resolveOffset(offset, containerLength, targetLength, targetInset) {
|
||||
let offsetDefinition = Array.isArray(offset) ? offset : defaultOffset;
|
||||
let targetPoint = 0;
|
||||
let containerPoint = 0;
|
||||
if (typeof offset === "number") {
|
||||
/**
|
||||
* If we're provided offset: [0, 0.5, 1] then each number x should become
|
||||
* [x, x], so we default to the behaviour of mapping 0 => 0 of both target
|
||||
* and container etc.
|
||||
*/
|
||||
offsetDefinition = [offset, offset];
|
||||
}
|
||||
else if (typeof offset === "string") {
|
||||
offset = offset.trim();
|
||||
if (offset.includes(" ")) {
|
||||
offsetDefinition = offset.split(" ");
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* If we're provided a definition like "100px" then we want to apply
|
||||
* that only to the top of the target point, leaving the container at 0.
|
||||
* Whereas a named offset like "end" should be applied to both.
|
||||
*/
|
||||
offsetDefinition = [offset, namedEdges[offset] ? offset : `0`];
|
||||
}
|
||||
}
|
||||
targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset);
|
||||
containerPoint = resolveEdge(offsetDefinition[1], containerLength);
|
||||
return targetPoint - containerPoint;
|
||||
}
|
||||
|
||||
export { resolveOffset };
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
const ScrollOffset = {
|
||||
Enter: [
|
||||
[0, 1],
|
||||
[1, 1],
|
||||
],
|
||||
Exit: [
|
||||
[0, 0],
|
||||
[1, 0],
|
||||
],
|
||||
Any: [
|
||||
[1, 0],
|
||||
[0, 1],
|
||||
],
|
||||
All: [
|
||||
[0, 0],
|
||||
[1, 1],
|
||||
],
|
||||
};
|
||||
|
||||
export { ScrollOffset };
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { warnOnce } from '../../../utils/warn-once.mjs';
|
||||
import { updateScrollInfo } from './info.mjs';
|
||||
import { resolveOffsets } from './offsets/index.mjs';
|
||||
|
||||
function measure(container, target = container, info) {
|
||||
/**
|
||||
* Find inset of target within scrollable container
|
||||
*/
|
||||
info.x.targetOffset = 0;
|
||||
info.y.targetOffset = 0;
|
||||
if (target !== container) {
|
||||
let node = target;
|
||||
while (node && node !== container) {
|
||||
info.x.targetOffset += node.offsetLeft;
|
||||
info.y.targetOffset += node.offsetTop;
|
||||
node = node.offsetParent;
|
||||
}
|
||||
}
|
||||
info.x.targetLength =
|
||||
target === container ? target.scrollWidth : target.clientWidth;
|
||||
info.y.targetLength =
|
||||
target === container ? target.scrollHeight : target.clientHeight;
|
||||
info.x.containerLength = container.clientWidth;
|
||||
info.y.containerLength = container.clientHeight;
|
||||
/**
|
||||
* In development mode ensure scroll containers aren't position: static as this makes
|
||||
* it difficult to measure their relative positions.
|
||||
*/
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
if (container && target && target !== container) {
|
||||
warnOnce(getComputedStyle(container).position !== "static", "Please ensure that the container has a non-static position, like 'relative', 'fixed', or 'absolute' to ensure scroll offset is calculated correctly.");
|
||||
}
|
||||
}
|
||||
}
|
||||
function createOnScrollHandler(element, onScroll, info, options = {}) {
|
||||
return {
|
||||
measure: () => measure(element, options.target, info),
|
||||
update: (time) => {
|
||||
updateScrollInfo(element, info, time);
|
||||
if (options.offset || options.target) {
|
||||
resolveOffsets(element, info, options);
|
||||
}
|
||||
},
|
||||
notify: () => onScroll(info),
|
||||
};
|
||||
}
|
||||
|
||||
export { createOnScrollHandler };
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { memo } from '../../../utils/memo.mjs';
|
||||
|
||||
const supportsScrollTimeline = memo(() => window.ScrollTimeline !== undefined);
|
||||
|
||||
export { supportsScrollTimeline };
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
import { resize } from '../resize/index.mjs';
|
||||
import { createScrollInfo } from './info.mjs';
|
||||
import { createOnScrollHandler } from './on-scroll-handler.mjs';
|
||||
import { frame, cancelFrame, frameData } from '../../../frameloop/frame.mjs';
|
||||
|
||||
const scrollListeners = new WeakMap();
|
||||
const resizeListeners = new WeakMap();
|
||||
const onScrollHandlers = new WeakMap();
|
||||
const getEventTarget = (element) => element === document.documentElement ? window : element;
|
||||
function scrollInfo(onScroll, { container = document.documentElement, ...options } = {}) {
|
||||
let containerHandlers = onScrollHandlers.get(container);
|
||||
/**
|
||||
* Get the onScroll handlers for this container.
|
||||
* If one isn't found, create a new one.
|
||||
*/
|
||||
if (!containerHandlers) {
|
||||
containerHandlers = new Set();
|
||||
onScrollHandlers.set(container, containerHandlers);
|
||||
}
|
||||
/**
|
||||
* Create a new onScroll handler for the provided callback.
|
||||
*/
|
||||
const info = createScrollInfo();
|
||||
const containerHandler = createOnScrollHandler(container, onScroll, info, options);
|
||||
containerHandlers.add(containerHandler);
|
||||
/**
|
||||
* Check if there's a scroll event listener for this container.
|
||||
* If not, create one.
|
||||
*/
|
||||
if (!scrollListeners.has(container)) {
|
||||
const measureAll = () => {
|
||||
for (const handler of containerHandlers)
|
||||
handler.measure();
|
||||
};
|
||||
const updateAll = () => {
|
||||
for (const handler of containerHandlers) {
|
||||
handler.update(frameData.timestamp);
|
||||
}
|
||||
};
|
||||
const notifyAll = () => {
|
||||
for (const handler of containerHandlers)
|
||||
handler.notify();
|
||||
};
|
||||
const listener = () => {
|
||||
frame.read(measureAll, false, true);
|
||||
frame.read(updateAll, false, true);
|
||||
frame.update(notifyAll, false, true);
|
||||
};
|
||||
scrollListeners.set(container, listener);
|
||||
const target = getEventTarget(container);
|
||||
window.addEventListener("resize", listener, { passive: true });
|
||||
if (container !== document.documentElement) {
|
||||
resizeListeners.set(container, resize(container, listener));
|
||||
}
|
||||
target.addEventListener("scroll", listener, { passive: true });
|
||||
}
|
||||
const listener = scrollListeners.get(container);
|
||||
frame.read(listener, false, true);
|
||||
return () => {
|
||||
var _a;
|
||||
cancelFrame(listener);
|
||||
/**
|
||||
* Check if we even have any handlers for this container.
|
||||
*/
|
||||
const currentHandlers = onScrollHandlers.get(container);
|
||||
if (!currentHandlers)
|
||||
return;
|
||||
currentHandlers.delete(containerHandler);
|
||||
if (currentHandlers.size)
|
||||
return;
|
||||
/**
|
||||
* If no more handlers, remove the scroll listener too.
|
||||
*/
|
||||
const scrollListener = scrollListeners.get(container);
|
||||
scrollListeners.delete(container);
|
||||
if (scrollListener) {
|
||||
getEventTarget(container).removeEventListener("scroll", scrollListener);
|
||||
(_a = resizeListeners.get(container)) === null || _a === void 0 ? void 0 : _a();
|
||||
window.removeEventListener("resize", scrollListener);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { scrollInfo };
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { Fragment, useMemo, createElement } from 'react';
|
||||
import { useHTMLProps } from '../html/use-props.mjs';
|
||||
import { filterProps } from './utils/filter-props.mjs';
|
||||
import { isSVGComponent } from './utils/is-svg-component.mjs';
|
||||
import { useSVGProps } from '../svg/use-props.mjs';
|
||||
import { isMotionValue } from '../../value/utils/is-motion-value.mjs';
|
||||
|
||||
function createUseRender(forwardMotionProps = false) {
|
||||
const useRender = (Component, props, ref, { latestValues }, isStatic) => {
|
||||
const useVisualProps = isSVGComponent(Component)
|
||||
? useSVGProps
|
||||
: useHTMLProps;
|
||||
const visualProps = useVisualProps(props, latestValues, isStatic, Component);
|
||||
const filteredProps = filterProps(props, typeof Component === "string", forwardMotionProps);
|
||||
const elementProps = Component !== Fragment
|
||||
? { ...filteredProps, ...visualProps, ref }
|
||||
: {};
|
||||
/**
|
||||
* If component has been handed a motion value as its child,
|
||||
* memoise its initial value and render that. Subsequent updates
|
||||
* will be handled by the onChange handler
|
||||
*/
|
||||
const { children } = props;
|
||||
const renderedChildren = useMemo(() => (isMotionValue(children) ? children.get() : children), [children]);
|
||||
return createElement(Component, {
|
||||
...elementProps,
|
||||
children: renderedChildren,
|
||||
});
|
||||
};
|
||||
return useRender;
|
||||
}
|
||||
|
||||
export { createUseRender };
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Convert camelCase to dash-case properties.
|
||||
*/
|
||||
const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase();
|
||||
|
||||
export { camelToDash };
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { invariant } from '../../../utils/errors.mjs';
|
||||
import { isNumericalString } from '../../../utils/is-numerical-string.mjs';
|
||||
import { isCSSVariableToken } from './is-css-variable.mjs';
|
||||
|
||||
/**
|
||||
* Parse Framer's special CSS variable format into a CSS token and a fallback.
|
||||
*
|
||||
* ```
|
||||
* `var(--foo, #fff)` => [`--foo`, '#fff']
|
||||
* ```
|
||||
*
|
||||
* @param current
|
||||
*/
|
||||
const splitCSSVariableRegex =
|
||||
// eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
|
||||
/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
|
||||
function parseCSSVariable(current) {
|
||||
const match = splitCSSVariableRegex.exec(current);
|
||||
if (!match)
|
||||
return [,];
|
||||
const [, token1, token2, fallback] = match;
|
||||
return [`--${token1 !== null && token1 !== void 0 ? token1 : token2}`, fallback];
|
||||
}
|
||||
const maxDepth = 4;
|
||||
function getVariableValue(current, element, depth = 1) {
|
||||
invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
|
||||
const [token, fallback] = parseCSSVariable(current);
|
||||
// No CSS variable detected
|
||||
if (!token)
|
||||
return;
|
||||
// Attempt to read this CSS variable off the element
|
||||
const resolved = window.getComputedStyle(element).getPropertyValue(token);
|
||||
if (resolved) {
|
||||
const trimmed = resolved.trim();
|
||||
return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
|
||||
}
|
||||
return isCSSVariableToken(fallback)
|
||||
? getVariableValue(fallback, element, depth + 1)
|
||||
: fallback;
|
||||
}
|
||||
|
||||
export { getVariableValue, parseCSSVariable };
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import { isValidMotionProp } from '../../../motion/utils/valid-prop.mjs';
|
||||
|
||||
let shouldForward = (key) => !isValidMotionProp(key);
|
||||
function loadExternalIsValidProp(isValidProp) {
|
||||
if (!isValidProp)
|
||||
return;
|
||||
// Explicitly filter our events
|
||||
shouldForward = (key) => key.startsWith("on") ? !isValidMotionProp(key) : isValidProp(key);
|
||||
}
|
||||
/**
|
||||
* Emotion and Styled Components both allow users to pass through arbitrary props to their components
|
||||
* to dynamically generate CSS. They both use the `@emotion/is-prop-valid` package to determine which
|
||||
* of these should be passed to the underlying DOM node.
|
||||
*
|
||||
* However, when styling a Motion component `styled(motion.div)`, both packages pass through *all* props
|
||||
* as it's seen as an arbitrary component rather than a DOM node. Motion only allows arbitrary props
|
||||
* passed through the `custom` prop so it doesn't *need* the payload or computational overhead of
|
||||
* `@emotion/is-prop-valid`, however to fix this problem we need to use it.
|
||||
*
|
||||
* By making it an optionalDependency we can offer this functionality only in the situations where it's
|
||||
* actually required.
|
||||
*/
|
||||
try {
|
||||
/**
|
||||
* We attempt to import this package but require won't be defined in esm environments, in that case
|
||||
* isPropValid will have to be provided via `MotionContext`. In a 6.0.0 this should probably be removed
|
||||
* in favour of explicit injection.
|
||||
*/
|
||||
loadExternalIsValidProp(require("@emotion/is-prop-valid").default);
|
||||
}
|
||||
catch (_a) {
|
||||
// We don't need to actually do anything here - the fallback is the existing `isPropValid`.
|
||||
}
|
||||
function filterProps(props, isDom, forwardMotionProps) {
|
||||
const filteredProps = {};
|
||||
for (const key in props) {
|
||||
/**
|
||||
* values is considered a valid prop by Emotion, so if it's present
|
||||
* this will be rendered out to the DOM unless explicitly filtered.
|
||||
*
|
||||
* We check the type as it could be used with the `feColorMatrix`
|
||||
* element, which we support.
|
||||
*/
|
||||
if (key === "values" && typeof props.values === "object")
|
||||
continue;
|
||||
if (shouldForward(key) ||
|
||||
(forwardMotionProps === true && isValidMotionProp(key)) ||
|
||||
(!isDom && !isValidMotionProp(key)) ||
|
||||
// If trying to use native HTML drag events, forward drag listeners
|
||||
(props["draggable"] &&
|
||||
key.startsWith("onDrag"))) {
|
||||
filteredProps[key] =
|
||||
props[key];
|
||||
}
|
||||
}
|
||||
return filteredProps;
|
||||
}
|
||||
|
||||
export { filterProps, loadExternalIsValidProp };
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
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;
|
||||
|
||||
export { isCSSVariableName, isCSSVariableToken };
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { lowercaseSVGElements } from '../../svg/lowercase-elements.mjs';
|
||||
|
||||
function isSVGComponent(Component) {
|
||||
if (
|
||||
/**
|
||||
* If it's not a string, it's a custom React component. Currently we only support
|
||||
* HTML custom React components.
|
||||
*/
|
||||
typeof Component !== "string" ||
|
||||
/**
|
||||
* If it contains a dash, the element is a custom HTML webcomponent.
|
||||
*/
|
||||
Component.includes("-")) {
|
||||
return false;
|
||||
}
|
||||
else if (
|
||||
/**
|
||||
* If it's in our list of lowercase SVG tags, it's an SVG component
|
||||
*/
|
||||
lowercaseSVGElements.indexOf(Component) > -1 ||
|
||||
/**
|
||||
* If it contains a capital letter, it's an SVG component
|
||||
*/
|
||||
/[A-Z]/u.test(Component)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export { isSVGComponent };
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
function isSVGElement(element) {
|
||||
return element instanceof SVGElement && element.tagName !== "svg";
|
||||
}
|
||||
|
||||
export { isSVGElement };
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { invariant } from '../../../utils/errors.mjs';
|
||||
|
||||
function resolveElements(elements, scope, selectorCache) {
|
||||
var _a;
|
||||
if (typeof elements === "string") {
|
||||
let root = document;
|
||||
if (scope) {
|
||||
invariant(Boolean(scope.current), "Scope provided, but no element detected.");
|
||||
root = scope.current;
|
||||
}
|
||||
if (selectorCache) {
|
||||
(_a = selectorCache[elements]) !== null && _a !== void 0 ? _a : (selectorCache[elements] = root.querySelectorAll(elements));
|
||||
elements = selectorCache[elements];
|
||||
}
|
||||
else {
|
||||
elements = root.querySelectorAll(elements);
|
||||
}
|
||||
}
|
||||
else if (elements instanceof Element) {
|
||||
elements = [elements];
|
||||
}
|
||||
/**
|
||||
* Return an empty array
|
||||
*/
|
||||
return Array.from(elements || []);
|
||||
}
|
||||
|
||||
export { resolveElements };
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import { transformPropOrder } from '../../html/utils/transform.mjs';
|
||||
import { number } from '../../../value/types/numbers/index.mjs';
|
||||
import { px } from '../../../value/types/numbers/units.mjs';
|
||||
|
||||
const positionalKeys = new Set([
|
||||
"width",
|
||||
"height",
|
||||
"top",
|
||||
"left",
|
||||
"right",
|
||||
"bottom",
|
||||
"x",
|
||||
"y",
|
||||
"translateX",
|
||||
"translateY",
|
||||
]);
|
||||
const isNumOrPxType = (v) => v === number || v === px;
|
||||
const getPosFromMatrix = (matrix, pos) => parseFloat(matrix.split(", ")[pos]);
|
||||
const getTranslateFromMatrix = (pos2, pos3) => (_bbox, { transform }) => {
|
||||
if (transform === "none" || !transform)
|
||||
return 0;
|
||||
const matrix3d = transform.match(/^matrix3d\((.+)\)$/u);
|
||||
if (matrix3d) {
|
||||
return getPosFromMatrix(matrix3d[1], pos3);
|
||||
}
|
||||
else {
|
||||
const matrix = transform.match(/^matrix\((.+)\)$/u);
|
||||
if (matrix) {
|
||||
return getPosFromMatrix(matrix[1], pos2);
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
const transformKeys = 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 !== undefined) {
|
||||
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: getTranslateFromMatrix(4, 13),
|
||||
y: getTranslateFromMatrix(5, 14),
|
||||
};
|
||||
// Alias translate longform names
|
||||
positionalValues.translateX = positionalValues.x;
|
||||
positionalValues.translateY = positionalValues.y;
|
||||
|
||||
export { isNumOrPxType, positionalKeys, positionalValues, removeNonTranslationalTransform };
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { complex } from '../../../value/types/complex/index.mjs';
|
||||
import { filter } from '../../../value/types/complex/filter.mjs';
|
||||
import { getDefaultValueType } from './defaults.mjs';
|
||||
|
||||
function getAnimatableNone(key, value) {
|
||||
let defaultValueType = getDefaultValueType(key);
|
||||
if (defaultValueType !== filter)
|
||||
defaultValueType = complex;
|
||||
// If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
|
||||
return defaultValueType.getAnimatableNone
|
||||
? defaultValueType.getAnimatableNone(value)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
export { getAnimatableNone };
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { color } from '../../../value/types/color/index.mjs';
|
||||
import { filter } from '../../../value/types/complex/filter.mjs';
|
||||
import { numberValueTypes } from './number.mjs';
|
||||
|
||||
/**
|
||||
* A map of default value types for common values
|
||||
*/
|
||||
const defaultValueTypes = {
|
||||
...numberValueTypes,
|
||||
// Color props
|
||||
color,
|
||||
backgroundColor: color,
|
||||
outlineColor: color,
|
||||
fill: color,
|
||||
stroke: color,
|
||||
// Border props
|
||||
borderColor: color,
|
||||
borderTopColor: color,
|
||||
borderRightColor: color,
|
||||
borderBottomColor: color,
|
||||
borderLeftColor: color,
|
||||
filter,
|
||||
WebkitFilter: filter,
|
||||
};
|
||||
/**
|
||||
* Gets the default ValueType for the provided value key
|
||||
*/
|
||||
const getDefaultValueType = (key) => defaultValueTypes[key];
|
||||
|
||||
export { defaultValueTypes, getDefaultValueType };
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { number } from '../../../value/types/numbers/index.mjs';
|
||||
import { px, percent, degrees, vw, vh } from '../../../value/types/numbers/units.mjs';
|
||||
import { testValueType } from './test.mjs';
|
||||
import { auto } from './type-auto.mjs';
|
||||
|
||||
/**
|
||||
* A list of value types commonly used for dimensions
|
||||
*/
|
||||
const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
|
||||
/**
|
||||
* Tests a dimensional value against the list of dimension ValueTypes
|
||||
*/
|
||||
const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
|
||||
|
||||
export { dimensionValueTypes, findDimensionValueType };
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { color } from '../../../value/types/color/index.mjs';
|
||||
import { complex } from '../../../value/types/complex/index.mjs';
|
||||
import { dimensionValueTypes } from './dimensions.mjs';
|
||||
import { testValueType } from './test.mjs';
|
||||
|
||||
/**
|
||||
* A list of all ValueTypes
|
||||
*/
|
||||
const valueTypes = [...dimensionValueTypes, color, complex];
|
||||
/**
|
||||
* Tests a value against the list of ValueTypes
|
||||
*/
|
||||
const findValueType = (v) => valueTypes.find(testValueType(v));
|
||||
|
||||
export { findValueType };
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Provided a value and a ValueType, returns the value as that value type.
|
||||
*/
|
||||
const getValueAsType = (value, type) => {
|
||||
return type && typeof value === "number"
|
||||
? type.transform(value)
|
||||
: value;
|
||||
};
|
||||
|
||||
export { getValueAsType };
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { px } from '../../../value/types/numbers/units.mjs';
|
||||
|
||||
const browserNumberValueTypes = {
|
||||
// Border props
|
||||
borderWidth: px,
|
||||
borderTopWidth: px,
|
||||
borderRightWidth: px,
|
||||
borderBottomWidth: px,
|
||||
borderLeftWidth: px,
|
||||
borderRadius: px,
|
||||
radius: px,
|
||||
borderTopLeftRadius: px,
|
||||
borderTopRightRadius: px,
|
||||
borderBottomRightRadius: px,
|
||||
borderBottomLeftRadius: px,
|
||||
// Positioning props
|
||||
width: px,
|
||||
maxWidth: px,
|
||||
height: px,
|
||||
maxHeight: px,
|
||||
top: px,
|
||||
right: px,
|
||||
bottom: px,
|
||||
left: px,
|
||||
// Spacing props
|
||||
padding: px,
|
||||
paddingTop: px,
|
||||
paddingRight: px,
|
||||
paddingBottom: px,
|
||||
paddingLeft: px,
|
||||
margin: px,
|
||||
marginTop: px,
|
||||
marginRight: px,
|
||||
marginBottom: px,
|
||||
marginLeft: px,
|
||||
// Misc
|
||||
backgroundPositionX: px,
|
||||
backgroundPositionY: px,
|
||||
};
|
||||
|
||||
export { browserNumberValueTypes };
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { alpha } from '../../../value/types/numbers/index.mjs';
|
||||
import { px } from '../../../value/types/numbers/units.mjs';
|
||||
import { browserNumberValueTypes } from './number-browser.mjs';
|
||||
import { transformValueTypes } from './transform.mjs';
|
||||
import { int } from './type-int.mjs';
|
||||
|
||||
const numberValueTypes = {
|
||||
...browserNumberValueTypes,
|
||||
...transformValueTypes,
|
||||
zIndex: int,
|
||||
size: px,
|
||||
// SVG
|
||||
fillOpacity: alpha,
|
||||
strokeOpacity: alpha,
|
||||
numOctaves: int,
|
||||
};
|
||||
|
||||
export { numberValueTypes };
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Tests a provided value against a ValueType
|
||||
*/
|
||||
const testValueType = (v) => (type) => type.test(v);
|
||||
|
||||
export { testValueType };
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { scale, alpha } from '../../../value/types/numbers/index.mjs';
|
||||
import { degrees, px, progressPercentage } from '../../../value/types/numbers/units.mjs';
|
||||
|
||||
const transformValueTypes = {
|
||||
rotate: degrees,
|
||||
rotateX: degrees,
|
||||
rotateY: degrees,
|
||||
rotateZ: degrees,
|
||||
scale,
|
||||
scaleX: scale,
|
||||
scaleY: scale,
|
||||
scaleZ: scale,
|
||||
skew: degrees,
|
||||
skewX: degrees,
|
||||
skewY: degrees,
|
||||
distance: px,
|
||||
translateX: px,
|
||||
translateY: px,
|
||||
translateZ: px,
|
||||
x: px,
|
||||
y: px,
|
||||
z: px,
|
||||
perspective: px,
|
||||
transformPerspective: px,
|
||||
opacity: alpha,
|
||||
originX: progressPercentage,
|
||||
originY: progressPercentage,
|
||||
originZ: px,
|
||||
};
|
||||
|
||||
export { transformValueTypes };
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* ValueType for "auto"
|
||||
*/
|
||||
const auto = {
|
||||
test: (v) => v === "auto",
|
||||
parse: (v) => v,
|
||||
};
|
||||
|
||||
export { auto };
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { number } from '../../../value/types/numbers/index.mjs';
|
||||
|
||||
const int = {
|
||||
...number,
|
||||
transform: Math.round,
|
||||
};
|
||||
|
||||
export { int };
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { resolveElements } from '../utils/resolve-element.mjs';
|
||||
|
||||
const thresholds = {
|
||||
some: 0,
|
||||
all: 1,
|
||||
};
|
||||
function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount = "some" } = {}) {
|
||||
const elements = resolveElements(elementOrSelector);
|
||||
const activeIntersections = new WeakMap();
|
||||
const onIntersectionChange = (entries) => {
|
||||
entries.forEach((entry) => {
|
||||
const onEnd = activeIntersections.get(entry.target);
|
||||
/**
|
||||
* If there's no change to the intersection, we don't need to
|
||||
* do anything here.
|
||||
*/
|
||||
if (entry.isIntersecting === Boolean(onEnd))
|
||||
return;
|
||||
if (entry.isIntersecting) {
|
||||
const newOnEnd = onStart(entry);
|
||||
if (typeof newOnEnd === "function") {
|
||||
activeIntersections.set(entry.target, newOnEnd);
|
||||
}
|
||||
else {
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
}
|
||||
else if (onEnd) {
|
||||
onEnd(entry);
|
||||
activeIntersections.delete(entry.target);
|
||||
}
|
||||
});
|
||||
};
|
||||
const observer = new IntersectionObserver(onIntersectionChange, {
|
||||
root,
|
||||
rootMargin,
|
||||
threshold: typeof amount === "number" ? amount : thresholds[amount],
|
||||
});
|
||||
elements.forEach((element) => observer.observe(element));
|
||||
return () => observer.disconnect();
|
||||
}
|
||||
|
||||
export { inView };
|
||||
Reference in new issue
Block a user