text: import {assign, children, css, fastdom, includes, index, isVisible, noop, offset, parent, position, Promise, Transition} from 'uikit-util';
  
  export default function (action, target, duration) {
  
      return new Promise(resolve =>
          requestAnimationFrame(() => {
  
              let nodes = children(target);
  
              // Get current state
              const currentProps = nodes.map(el => getProps(el, true));
              const targetProps = css(target, ['height', 'padding']);
  
              // Cancel previous animations
              Transition.cancel(target);
              nodes.forEach(Transition.cancel);
              reset(target);
  
              // Adding, sorting, removing nodes
              action();
  
              // Find new nodes
              nodes = nodes.concat(children(target).filter(el => !includes(nodes, el)));
  
              // Wait for update to propagate
              Promise.resolve().then(() => {
  
                  // Force update
                  fastdom.flush();
  
                  // Get new state
                  const targetPropsTo = css(target, ['height', 'padding']);
                  const [propsTo, propsFrom] = getTransitionProps(target, nodes, currentProps);
  
                  // Reset to previous state
                  nodes.forEach((el, i) => propsFrom[i] && css(el, propsFrom[i]));
                  css(target, assign({display: 'block'}, targetProps));
  
                  // Start transitions on next frame
                  requestAnimationFrame(() => {
  
                      const transitions = nodes.map((el, i) =>
                              parent(el) === target && Transition.start(el, propsTo[i], duration, 'ease')
                          ).concat(Transition.start(target, targetPropsTo, duration, 'ease'));
  
                      Promise.all(transitions).then(() => {
                          nodes.forEach((el, i) => parent(el) === target && css(el, 'display', propsTo[i].opacity === 0 ? 'none' : ''));
                          reset(target);
                      }, noop).then(resolve);
  
                  });
              });
          }));
  }
  
  function getProps(el, opacity) {
  
      const zIndex = css(el, 'zIndex');
  
      return isVisible(el)
          ? assign({
              display: '',
              opacity: opacity ? css(el, 'opacity') : '0',
              pointerEvents: 'none',
              position: 'absolute',
              zIndex: zIndex === 'auto' ? index(el) : zIndex
          }, getPositionWithMargin(el))
          : false;
  }
  
  function getTransitionProps(target, nodes, currentProps) {
  
      const propsTo = nodes.map((el, i) =>
          parent(el) && i in currentProps
              ? currentProps[i]
              ? isVisible(el)
                  ? getPositionWithMargin(el)
                  : {opacity: 0}
              : {opacity: isVisible(el) ? 1 : 0}
              : false);
  
      const propsFrom = propsTo.map((props, i) => {
  
          const from = parent(nodes[i]) === target && (currentProps[i] || getProps(nodes[i]));
  
          if (!from) {
              return false;
          }
  
          if (!props) {
              delete from.opacity;
          } else if (!('opacity' in props)) {
              const {opacity} = from;
  
              if (opacity % 1) {
                  props.opacity = 1;
              } else {
                  delete from.opacity;
              }
          }
  
          return from;
      });
  
      return [propsTo, propsFrom];
  }
  
  function reset(el) {
      css(el.children, {
          height: '',
          left: '',
          opacity: '',
          pointerEvents: '',
          position: '',
          top: '',
          marginTop: '',
          marginLeft: '',
          transform: '',
          width: '',
          zIndex: ''
      });
      css(el, {height: '', display: '', padding: ''});
  }
  
  function getPositionWithMargin(el) {
      const {height, width} = offset(el);
      const {top, left} = position(el);
      const {marginLeft, marginTop} = css(el, ['marginTop', 'marginLeft']);
  
      return {top, left, height, width, marginLeft, marginTop, transform: ''};
  }
  