UNPKG

pose-core

Version:

Factory for Pose animation state machines

465 lines (457 loc) 20.4 kB
import { __rest, __assign, __spreadArrays } from 'tslib'; import { invariant } from '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 = __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 = __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) { 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 = __assign(__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 = __assign(__assign({}, baseTransitionProps), { key: key, value: value }); var target = resolveTarget(value, resolveProp(nextPose[key], transitionProps)); if (activeActions.has(key)) stopAction(activeActions.get(key)); var resolveTransitionProps = __assign(__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(__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 __assign(__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]; 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 = __assign(__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 = __assign(__assign({}, state.props), newProps); }, _addChild: function (childConfig, factory) { var child = factory(__assign(__assign({ initialPose: initialPose }, childConfig), { ancestorValues: __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); }; }; export default poseFactory;