/**
 * v-clickoutside
 * @desc 点击元素外面才会触发的事件
 * @example
 * <div v-clickoutside="handleClose">
 */

import { inBrowser } from '../utils/common-var';
import { on } from './utils';

const ctx = Symbol('@@clickoutside');
const nodeList = new Map();

let startClick;
let nid = 0;
let isFirst = true;

function createDocumentHandler(el: HTMLElement, binding: Record<string, any>, vnode: any) {
  if (inBrowser && isFirst) {
    isFirst = false;
    on(document, 'mousedown', (e: Event) => {
      startClick = e;
    });
    on(document, 'mouseup', (e: Event) => {
      for (const [id, node] of nodeList) {
        node[ctx].documentHandler(e, startClick);
      }
    });
  }

  return function (mouseup: Event, mousedown: Event) {
    if (
      !vnode ||
      !binding.instance ||
      !mouseup.target ||
      !mousedown.target ||
      el.contains(mouseup.target as HTMLElement) ||
      el.contains(mousedown.target as HTMLElement) ||
      el === mouseup.target
    ) {
      return;
    }
    el[ctx].bindingFn && el[ctx].bindingFn();
  };
}

const clickoutsideDirective = {
  beforeMount: function (el: HTMLElement, binding: Record<string, any>, vnode: any) {
    nid++;
    nodeList.set(nid, el);
    el[ctx] = {
      nid,
      documentHandler: createDocumentHandler(el, binding, vnode),
      bindingFn: binding.value
    };
  },

  updated: function (el: HTMLElement, binding: Record<string, any>, vnode: any) {
    el[ctx].documentHandler = createDocumentHandler(el, binding, vnode);
    el[ctx].bindingFn = binding.value;
  },

  unmounted: function (el: HTMLElement) {
    nodeList.delete(el[ctx].nid);
    delete el[ctx];
  }
};

export default clickoutsideDirective;