All files / lib transition.js

98.13% Statements 105/107
96.96% Branches 32/33
100% Functions 3/3
98.13% Lines 105/107

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 1082x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 51x 48x 4x 4x 44x 48x 1x 1x 48x 51x 51x 51x 2x 2x 2x 2x 2x 2x 2x 2x 429x 427x 427x 2x 2x 427x 427x 427x 429x 288x 288x 139x 139x 4x 4x 135x 135x 135x 675x 675x 135x 429x 2x 2x 2x 2x 2x 2x 2x 2x 2x 482x 482x 482x 482x 482x 482x     482x 482x 482x 482x 482x 482x 429x 429x 482x 482x 482x 482x 53x 53x 53x 53x 53x 27x 27x 482x 482x 482x 482x 482x 21x 2x 21x 21x 53x 53x 482x  
import process from './util/process';
import {
  TransitionCache,
  NodeCache,
  NODE_TYPE,
  VTree,
  TransitionStateNames,
  TransitionStateName,
} from './util/types';
 
/**
 *
 * @param {TransitionStateName} stateName
 * @param {Function} callback
 * @return {void}
 */
export function addTransitionState(stateName, callback) {
  if (process.env.NODE_ENV !== 'production') {
    if (!TransitionStateNames.includes(stateName)) {
      throw new Error(`Invalid state name '${stateName}'`);
    }
 
    if (!callback) {
      throw new Error('Missing transition state callback');
    }
  }
 
  TransitionCache.get(stateName)?.add(callback);
}
 
/**
 *
 * @param {TransitionStateName=} stateName
 * @param {Function=} callback
 * @return {void}
 */
export function removeTransitionState(stateName, callback) {
  if (process.env.NODE_ENV !== 'production') {
    // Only validate the stateName if the caller provides one.
    if (stateName && !TransitionStateNames.includes(stateName)) {
      throw new Error(`Invalid state name '${stateName}'`);
    }
  }
 
  // Remove all specific transition callbacks.
  if (!callback && stateName) {
    TransitionCache.get(stateName)?.clear();
  }
  // Remove a single distinct transition callback.
  else if (stateName && callback) {
    TransitionCache.get(stateName)?.delete(callback);
  }
  // Remove all transition callbacks.
  else {
    for (let i = 0; i < TransitionStateNames.length; i++) {
      TransitionCache.get(TransitionStateNames[i])?.clear();
    }
  }
}
 
/**
 *
 * @param {TransitionStateName} setName
 * @param  {...any} args
 *
 * @return {Promise<any>[]}
 */
export function runTransitions(setName, ...args) {
  const set = TransitionCache.get(setName);
 
  /** @type {Promise<any>[]} */
  const promises = [];
 
  if (!set) {
    return promises;
  }
 
  const vTree = args[0];
  const isElement = vTree.nodeType === NODE_TYPE.ELEMENT;
 
  // Filter out text nodes and fragments from transition callbacks.
  if (!set.size || (setName !== 'textChanged' && !isElement)) {
    return promises;
  }
 
  // Run each transition callback, but only if the passed args are an
  // Element.
  set.forEach(callback => {
    const nodes = args.map(x => NodeCache.get(x) || x);
    const retVal = callback(...nodes);
 
    // Is a `thennable` object or Native Promise.
    if (typeof retVal === 'object' && retVal.then) {
      promises.push(retVal);
    }
  });
 
  // For attached and detached transitions, dig into children to ensure
  // all are run with this.
  if (setName === 'attached' || setName === 'detached') {
    vTree.childNodes.forEach((/** @type {VTree} */ childTree) => {
      promises.push(...runTransitions(setName, childTree, ...args.slice(1)));
    });
  }
 
  return promises;
}