pose-core
Version:
Factory for Pose animation state machines
420 lines (410 loc) • 20.9 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var tslib = require('tslib');
var heyListen = require('hey-listen');
var getPoseValues = function (_a) {
var transition = _a.transition, flip = _a.flip, delay = _a.delay, delayChildren = _a.delayChildren, staggerChildren = _a.staggerChildren, staggerDirection = _a.staggerDirection, afterChildren = _a.afterChildren, beforeChildren = _a.beforeChildren, preTransition = _a.preTransition, applyAtStart = _a.applyAtStart, applyAtEnd = _a.applyAtEnd, props = tslib.__rest(_a, ["transition", "flip", "delay", "delayChildren", "staggerChildren", "staggerDirection", "afterChildren", "beforeChildren", "preTransition", "applyAtStart", "applyAtEnd"]);
return props;
};
var selectPoses = function (_a) {
var label = _a.label, props = _a.props, values = _a.values, parentValues = _a.parentValues, ancestorValues = _a.ancestorValues, onChange = _a.onChange, passive = _a.passive, initialPose = _a.initialPose, poses = tslib.__rest(_a, ["label", "props", "values", "parentValues", "ancestorValues", "onChange", "passive", "initialPose"]);
return poses;
};
var selectAllValues = function (values, selectValue) {
var allValues = {};
values.forEach(function (value, key) { return (allValues[key] = selectValue(value)); });
return allValues;
};
var resolveProp = function (target, props) {
return typeof target === 'function' ? target(props) : target;
};
var poseDefault = function (pose, prop, defaultValue, resolveProps) {
return pose && pose[prop] !== undefined
? resolveProp(pose[prop], resolveProps)
: defaultValue;
};
var startChildAnimations = function (children, next, pose, props) {
var animations = [];
var delay = poseDefault(pose, 'delayChildren', 0, props);
var stagger = poseDefault(pose, 'staggerChildren', 0, props);
var staggerDirection = poseDefault(pose, 'staggerDirection', 1, props);
var maxStaggerDuration = (children.size - 1) * stagger;
var generateStaggerDuration = staggerDirection === 1
? function (i) { return i * stagger; }
: function (i) { return maxStaggerDuration - i * stagger; };
Array.from(children).forEach(function (child, i) {
animations.push(child.set(next, {
delay: delay + generateStaggerDuration(i)
}));
});
return animations;
};
var resolveTransition = function (transition, key, value, props, convertTransitionDefinition, getInstantTransition) {
var resolvedTransition;
if (typeof transition === 'function') {
var resolvedTransitionMap = transition(props);
resolvedTransition = resolveTransition(resolvedTransitionMap, key, value, props, convertTransitionDefinition, getInstantTransition);
}
else if (transition[key] || transition.default) {
var keyTransition = transition[key] || transition.default;
if (typeof keyTransition === 'function') {
resolvedTransition = keyTransition(props);
}
else {
resolvedTransition = keyTransition;
}
}
else {
resolvedTransition = transition;
}
return resolvedTransition === false
? getInstantTransition(value, props)
: convertTransitionDefinition(value, resolvedTransition, props);
};
var findInsertionIndex = function (poseList, priorityList, priorityIndex) {
var insertionIndex = 0;
for (var i = priorityIndex - 1; i >= 0; i--) {
var nextHighestPriorityIndex = poseList.indexOf(priorityList[i]);
if (nextHighestPriorityIndex !== -1) {
insertionIndex = nextHighestPriorityIndex + 1;
break;
}
}
return insertionIndex;
};
var applyValues = function (toApply, values, props, setValue, setValueNative) {
heyListen.invariant(typeof toApply === 'object', 'applyAtStart and applyAtEnd must be of type object');
return Object.keys(toApply).forEach(function (key) {
var valueToSet = resolveProp(toApply[key], props);
values.has(key)
? setValue(values.get(key), valueToSet)
: setValueNative(key, valueToSet, props);
});
};
var createPoseSetter = function (setterProps) {
var state = setterProps.state, poses = setterProps.poses, startAction = setterProps.startAction, stopAction = setterProps.stopAction, getInstantTransition = setterProps.getInstantTransition, addActionDelay = setterProps.addActionDelay, getTransitionProps = setterProps.getTransitionProps, resolveTarget = setterProps.resolveTarget, transformPose = setterProps.transformPose, posePriority = setterProps.posePriority, convertTransitionDefinition = setterProps.convertTransitionDefinition, setValue = setterProps.setValue, setValueNative = setterProps.setValueNative, forceRender = setterProps.forceRender;
return function (next, nextProps, propagate) {
if (nextProps === void 0) { nextProps = {}; }
if (propagate === void 0) { propagate = true; }
var children = state.children, values = state.values, props = state.props, activeActions = state.activeActions, activePoses = state.activePoses;
var _a = nextProps.delay, delay = _a === void 0 ? 0 : _a;
var hasChildren = children.size;
var baseTransitionProps = tslib.__assign(tslib.__assign({}, props), nextProps);
var nextPose = poses[next];
var getChildAnimations = function () {
return hasChildren && propagate
? startChildAnimations(children, next, nextPose, baseTransitionProps)
: [];
};
var getParentAnimations = function () {
if (!nextPose)
return [];
var applyAtStart = nextPose.applyAtStart;
if (applyAtStart) {
applyValues(applyAtStart, values, baseTransitionProps, setValue, setValueNative);
if (forceRender)
forceRender(baseTransitionProps);
}
if (transformPose)
nextPose = transformPose(nextPose, next, state);
var preTransition = nextPose.preTransition, getTransition = nextPose.transition, applyAtEnd = nextPose.applyAtEnd;
if (preTransition)
preTransition(baseTransitionProps);
var animations = Object.keys(getPoseValues(nextPose)).map(function (key) {
var valuePoses = activePoses.has(key)
? activePoses.get(key)
: (activePoses.set(key, []), activePoses.get(key));
var existingIndex = valuePoses.indexOf(next);
if (existingIndex !== -1)
valuePoses.splice(existingIndex, 1);
var priority = posePriority ? posePriority.indexOf(next) : 0;
var insertionIndex = priority <= 0
? 0
: findInsertionIndex(valuePoses, posePriority, priority);
valuePoses.splice(insertionIndex, 0, next);
return insertionIndex === 0
? new Promise(function (complete) {
var value = values.get(key);
var transitionProps = tslib.__assign(tslib.__assign({}, baseTransitionProps), { key: key,
value: value });
var target = resolveTarget(value, resolveProp(nextPose[key], transitionProps));
if (activeActions.has(key))
stopAction(activeActions.get(key));
var resolveTransitionProps = tslib.__assign(tslib.__assign({ to: target }, transitionProps), getTransitionProps(value, target, transitionProps));
var transition = resolveTransition(getTransition, key, value, resolveTransitionProps, convertTransitionDefinition, getInstantTransition);
var poseDelay = delay || resolveProp(nextPose.delay, transitionProps);
if (poseDelay) {
transition = addActionDelay(poseDelay, transition);
}
activeActions.set(key, startAction(value, transition, complete));
})
: Promise.resolve();
});
return applyAtEnd
? [
Promise.all(animations).then(function () {
applyValues(applyAtEnd, values, baseTransitionProps, setValue, setValueNative);
})
]
: animations;
};
if (nextPose && hasChildren) {
if (resolveProp(nextPose.beforeChildren, baseTransitionProps)) {
return Promise.all(getParentAnimations()).then(function () {
return Promise.all(getChildAnimations());
});
}
else if (resolveProp(nextPose.afterChildren, baseTransitionProps)) {
return Promise.all(getChildAnimations()).then(function () {
return Promise.all(getParentAnimations());
});
}
}
return Promise.all(tslib.__spreadArrays(getParentAnimations(), getChildAnimations()));
};
};
var DEFAULT_INITIAL_POSE = 'init';
var isScale = function (key) { return key.includes('scale'); };
var defaultReadValueFromSource = function (key) { return (isScale(key) ? 1 : 0); };
var readValueFromPose = function (pose, key, props) {
var valueToResolve = pose.applyAtEnd && pose.applyAtEnd[key] !== undefined
? pose.applyAtEnd[key]
: pose[key] !== undefined
? pose[key]
: pose.applyAtStart && pose.applyAtStart[key] !== undefined
? pose.applyAtStart[key]
: 0;
return resolveProp(valueToResolve, props);
};
var getPosesToSearch = function (pose) {
var posesToSearch = Array.isArray(pose) ? pose : [pose];
posesToSearch.push(DEFAULT_INITIAL_POSE);
return posesToSearch;
};
var getInitialValue = function (poses, key, initialPose, props, readValueFromSource, activePoses) {
if (readValueFromSource === void 0) { readValueFromSource = defaultReadValueFromSource; }
var posesToSearch = getPosesToSearch(initialPose);
var pose = posesToSearch.filter(Boolean).find(function (name) {
var thisPose = poses[name];
return (thisPose &&
(thisPose[key] !== undefined ||
(thisPose.applyAtStart && thisPose.applyAtStart[key] !== undefined) ||
(thisPose.applyAtEnd && thisPose.applyAtEnd[key] !== undefined)));
});
activePoses.set(key, [pose || DEFAULT_INITIAL_POSE]);
return pose
? readValueFromPose(poses[pose], key, props)
: readValueFromSource(key, props);
};
var createValues = function (values, _a) {
var userSetValues = _a.userSetValues, createValue = _a.createValue, convertValue = _a.convertValue, readValueFromSource = _a.readValueFromSource, initialPose = _a.initialPose, poses = _a.poses, activePoses = _a.activePoses, props = _a.props;
return function (key) {
if (values.has(key))
return;
var value;
if (userSetValues && userSetValues[key] !== undefined) {
value = convertValue(userSetValues[key], key, props);
}
else {
var initValue = getInitialValue(poses, key, initialPose, props, readValueFromSource, activePoses);
value = createValue(initValue, key, props);
}
values.set(key, value);
};
};
var scrapeValuesFromPose = function (values, props) { return function (key) {
var pose = props.poses[key];
Object.keys(getPoseValues(pose)).forEach(createValues(values, props));
}; };
var getAncestorValue = function (key, fromParent, ancestors) {
if (fromParent === true) {
return ancestors[0] && ancestors[0].values.get(key);
}
else {
var foundAncestor = ancestors.find(function (_a) {
var label = _a.label;
return label === fromParent;
});
return foundAncestor && foundAncestor.values.get(key);
}
};
var bindPassiveValues = function (values, _a) {
var passive = _a.passive, ancestorValues = _a.ancestorValues, createValue = _a.createValue, readValue = _a.readValue, props = _a.props;
return function (key) {
var _a = passive[key], valueKey = _a[0], passiveProps = _a[1], fromParent = _a[2];
var valueToBind = fromParent && ancestorValues.length
? getAncestorValue(valueKey, fromParent, ancestorValues)
: values.has(valueKey)
? values.get(valueKey)
: false;
if (!valueToBind)
return;
var newValue = createValue(readValue(valueToBind), key, props, {
passiveParentKey: valueKey,
passiveParent: valueToBind,
passiveProps: passiveProps
});
values.set(key, newValue);
};
};
var setNativeValues = function (_a) {
var setValueNative = _a.setValueNative, initialPose = _a.initialPose, props = _a.props, poses = _a.poses;
var valuesHaveSet = new Set();
var setValues = function (pose, propKey) {
if (pose[propKey]) {
for (var key in pose[propKey]) {
if (!valuesHaveSet.has(key)) {
valuesHaveSet.add(key);
setValueNative(key, resolveProp(pose[propKey][key], props), props);
}
}
}
};
getPosesToSearch(initialPose).forEach(function (poseKey) {
var pose = poses[poseKey];
if (pose) {
setValues(pose, 'applyAtEnd');
setValues(pose, 'applyAtStart');
}
});
};
var createValueMap = function (props) {
var poses = props.poses, passive = props.passive;
var values = new Map();
Object.keys(poses).forEach(scrapeValuesFromPose(values, props));
setNativeValues(props);
if (passive)
Object.keys(passive).forEach(bindPassiveValues(values, props));
return values;
};
var applyDefaultTransition = function (pose, key, defaultTransitions) {
return tslib.__assign(tslib.__assign({}, pose), { transition: defaultTransitions.has(key)
? defaultTransitions.get(key)
: defaultTransitions.get('default') });
};
var generateTransitions = function (poses, defaultTransitions) {
Object.keys(poses).forEach(function (key) {
var pose = poses[key];
heyListen.invariant(typeof pose === 'object', "Pose '" + key + "' is of invalid type. All poses should be objects.");
poses[key] =
pose.transition !== undefined
? pose
: applyDefaultTransition(pose, key, defaultTransitions);
});
return poses;
};
var sortByReversePriority = function (priorityOrder) { return function (a, b) {
var aP = priorityOrder.indexOf(a);
var bP = priorityOrder.indexOf(b);
if (aP === -1 && bP !== -1)
return -1;
if (aP !== -1 && bP === -1)
return 1;
return aP - bP;
}; };
var poseFactory = function (_a) {
var getDefaultProps = _a.getDefaultProps, defaultTransitions = _a.defaultTransitions, bindOnChange = _a.bindOnChange, startAction = _a.startAction, stopAction = _a.stopAction, readValue = _a.readValue, readValueFromSource = _a.readValueFromSource, resolveTarget = _a.resolveTarget, setValue = _a.setValue, setValueNative = _a.setValueNative, createValue = _a.createValue, convertValue = _a.convertValue, getInstantTransition = _a.getInstantTransition, getTransitionProps = _a.getTransitionProps, addActionDelay = _a.addActionDelay, selectValueToRead = _a.selectValueToRead, convertTransitionDefinition = _a.convertTransitionDefinition, transformPose = _a.transformPose, posePriority = _a.posePriority, forceRender = _a.forceRender, extendAPI = _a.extendAPI;
return function (config) {
var parentValues = config.parentValues, _a = config.ancestorValues, ancestorValues = _a === void 0 ? [] : _a;
if (parentValues)
ancestorValues.unshift({ values: parentValues });
var activeActions = new Map();
var activePoses = new Map();
var children = new Set();
var poses = generateTransitions(selectPoses(config), defaultTransitions);
var _b = config.props, props = _b === void 0 ? {} : _b;
if (getDefaultProps)
props = tslib.__assign(tslib.__assign({}, getDefaultProps(config)), props);
var passive = config.passive, userSetValues = config.values, _c = config.initialPose, initialPose = _c === void 0 ? DEFAULT_INITIAL_POSE : _c;
var values = createValueMap({
poses: poses,
passive: passive,
ancestorValues: ancestorValues,
readValue: readValue,
setValueNative: setValueNative,
createValue: createValue,
convertValue: convertValue,
readValueFromSource: readValueFromSource,
userSetValues: userSetValues,
initialPose: initialPose,
activePoses: activePoses,
props: props
});
var state = {
activeActions: activeActions,
activePoses: activePoses,
children: children,
props: props,
values: values
};
var onChange = config.onChange;
if (onChange)
Object.keys(onChange).forEach(bindOnChange(values, onChange));
var set = createPoseSetter({
state: state,
poses: poses,
getInstantTransition: getInstantTransition,
getTransitionProps: getTransitionProps,
convertTransitionDefinition: convertTransitionDefinition,
setValue: setValue,
setValueNative: setValueNative,
startAction: startAction,
stopAction: stopAction,
resolveTarget: resolveTarget,
addActionDelay: addActionDelay,
transformPose: transformPose,
posePriority: posePriority,
forceRender: forceRender
});
var has = function (poseName) { return !!poses[poseName]; };
var api = {
set: set,
unset: function (poseName, poseProps) {
var posesToSet = [];
activePoses.forEach(function (valuePoses) {
var poseIndex = valuePoses.indexOf(poseName);
if (poseIndex === -1)
return;
var currentPose = valuePoses[0];
valuePoses.splice(poseIndex, 1);
var nextPose = valuePoses[0];
if (nextPose === currentPose)
return;
if (posesToSet.indexOf(nextPose) === -1) {
posesToSet.push(nextPose);
}
});
var animationsToResolve = posesToSet
.sort(sortByReversePriority(posePriority))
.map(function (poseToSet) { return set(poseToSet, poseProps, false); });
children.forEach(function (child) {
return animationsToResolve.push(child.unset(poseName));
});
return Promise.all(animationsToResolve);
},
get: function (valueName) {
return valueName
? selectValueToRead(values.get(valueName))
: selectAllValues(values, selectValueToRead);
},
has: has,
setProps: function (newProps) { return (state.props = tslib.__assign(tslib.__assign({}, state.props), newProps)); },
_addChild: function (childConfig, factory) {
var child = factory(tslib.__assign(tslib.__assign({ initialPose: initialPose }, childConfig), { ancestorValues: tslib.__spreadArrays([{ label: config.label, values: values }], ancestorValues) }));
children.add(child);
return child;
},
removeChild: function (child) { return children.delete(child); },
clearChildren: function () {
children.forEach(function (child) { return child.destroy(); });
children.clear();
},
destroy: function () {
activeActions.forEach(stopAction);
children.forEach(function (child) { return child.destroy(); });
}
};
return extendAPI(api, state, config);
};
};
exports.default = poseFactory;