# @interactjs/reflow/index.ts

    import Interactable from '@interactjs/core/Interactable'
    import { ActionProps, Interaction } from '@interactjs/core/Interaction'
    import Scope from '@interactjs/core/scope'
    import { arr, extend, is, pointer as pointerUtils, rect as rectUtils, win } from '@interactjs/utils/index'

    declare module '@interactjs/core/Interactable' {
      interface Interactable {
        reflow: (action: ActionProps) => ReturnType<typeof reflow>
      }
    }

    declare module '@interactjs/core/Interaction' {
      interface Interaction {
        _reflowPromise: Promise<void>
        _reflowResolve: () => void
      }
    }

    declare module '@interactjs/core/InteractEvent' {
      // eslint-disable-next-line no-shadow
      interface PhaseMap {
        reflow?: true
      }
    }

    export function install (scope: Scope) {
      const {
        /** @lends Interactable */
        // eslint-disable-next-line no-shadow
        Interactable,
      } = scope

      scope.actions.phases.reflow = true

      /**
       * ```js
       * const interactable = interact(target)
       * const drag = { name: drag, axis: 'x' }
       * const resize = { name: resize, edges: { left: true, bottom: true }
       *
       * interactable.reflow(drag)
       * interactable.reflow(resize)
       * ```
       *
       * Start an action sequence to re-apply modifiers, check drops, etc.
       *
       * @param { Object } action The action to begin
       * @param { string } action.name The name of the action
       * @returns { Promise } A promise that resolves to the `Interactable` when actions on all targets have ended
       */
      Interactable.prototype.reflow = function (action) {
        return reflow(this, action, scope)
      }
    }

    function reflow<T extends Interact.ActionName> (interactable: Interactable, action: ActionProps<T>, scope: Scope): Promise<Interactable> {
      const elements = (is.string(interactable.target)
        ? arr.from(interactable._context.querySelectorAll(interactable.target))
        : [interactable.target]) as Interact.Element[]

      // tslint:disable-next-line variable-name
      const Promise = (win.window as any).Promise
      const promises: Array<Promise<null>> | null = Promise ? [] : null

      for (const element of elements) {
        const rect = interactable.getRect(element as HTMLElement | SVGElement)

        if (!rect) { break }

        const runningInteraction = arr.find(
          scope.interactions.list,
          (interaction: Interaction) => {
            return interaction.interacting() &&
              interaction.interactable === interactable &&
              interaction.element === element &&
              interaction.prepared.name === action.name
          })
        let reflowPromise: Promise<null>

        if (runningInteraction) {
          runningInteraction.move()

          if (promises) {
            reflowPromise = runningInteraction._reflowPromise || new Promise((resolve: any) => {
              runningInteraction._reflowResolve = resolve
            })
          }
        }
        else {
          const xywh = rectUtils.tlbrToXywh(rect)
          const coords = {
            page     : { x: xywh.x, y: xywh.y },
            client   : { x: xywh.x, y: xywh.y },
            timeStamp: scope.now(),
          }

          const event = pointerUtils.coordsToEvent(coords)
          reflowPromise = startReflow<T>(scope, interactable, element, action, event)
        }

        if (promises) {
          promises.push(reflowPromise)
        }
      }

      return promises && Promise.all(promises).then(() => interactable)
    }

    function startReflow<T extends Interact.ActionName> (scope: Scope, interactable: Interactable, element: Interact.Element, action: ActionProps<T>, event: any) {
      const interaction = scope.interactions.new({ pointerType: 'reflow' })
      const signalArg = {
        interaction,
        event,
        pointer: event,
        eventTarget: element,
        phase: 'reflow',
      } as const

      interaction.interactable = interactable
      interaction.element = element
      interaction.prepared = extend({}, action)
      interaction.prevEvent = event
      interaction.updatePointer(event, event, element, true)

      interaction._doPhase(signalArg)

      const reflowPromise = (win.window as unknown as any).Promise
        ? new (win.window as unknown as any).Promise((resolve: any) => {
          interaction._reflowResolve = resolve
        })
        : null

      interaction._reflowPromise = reflowPromise
      interaction.start(action, interactable, element)

      if (interaction._interacting) {
        interaction.move(signalArg)
        interaction.end(event)
      }
      else {
        interaction.stop()
      }

      interaction.removePointer(event, event)
      interaction.pointerIsDown = false

      return reflowPromise
    }

    export default {
      id: 'reflow',
      install,
      listeners: {
        // remove completed reflow interactions
        'interactions:stop': ({ interaction }, scope) => {
          if (interaction.pointerType === 'reflow') {
            if (interaction._reflowResolve) {
              interaction._reflowResolve()
            }

            arr.remove(scope.interactions.list, interaction)
          }
        },
      },
    } as Interact.Plugin