<template>
  <div
    class="ctx-menu"
    :style="style"
    :hidden="!ctxMenuData"
    v-click-outside="resetCtx"
  >
    <!-- Check if there are options data -->
    <div v-if="ctxMenuData">
      <!-- Use template tag to loop through the options -->
      <div v-for="(item, index) in ctxMenuData">
        <!--
            Then check opton type default is undeifined then if the type is not divider
          -->
        <!-- Make sure to create a unique ref is will be needed later -->
        <div
          v-if="item.type !== 'divider'"
          :key="index"
          :ref="'ctx_' + index"
          :id="'ctx_' + index"
        >
          <span class="ctx-menu-option">
            <svg class="nav-icon-container" :class="'icon icon-' + item.icon">
              <use :xlink:href="'#icon-' + item.icon"></use>
            </svg>
            {{ item.title }}
          </span>
        </div>
        <div v-else :key="'else-' + index" class="ctx-menu-divider">
          <span></span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
// Access the eventBus instance

export default {
  data: () => ({
    style: null,
    ctxMenuData: null,
    // the options schema
    // [
    //   {
    //     title: string,
    //     type: string, // default is undefined
    //     handler: function
    //   }
    // ]
    ctxMenuRect: null,
    // {
    //   y:number,
    //   x:number
    // }
  }),
  methods: {
    resetCtx() {
      this.ctxMenuData = null;
      this.ctxMenuRect = null;
    },
    onContextMenu(ev, ctxMenuData) {
      // prevent default behaviours
      ev.preventDefault();
      ev.stopPropagation();
      this.ctxMenuData = ctxMenuData;
      this.ctxMenuRect = {
        x: ev.x,
        y: ev.y,
      };

      // then reevaluate and set context-menu position
      this.reevaluatePosition();

      // populate the option
      this.$nextTick(() => {
        this.onData();
      });
    },
    async reevaluatePosition() {
      if (this.ctxMenuRect) {
        // using $nextTick to daley and make sure that the context-menu
        // options are fully rendered which will help us
        // to get the accurate height
        await this.$nextTick();
        await this.$nextTick();
        let { x, y } = this.ctxMenuRect;
        // get the window current inner height and width

        let innerHeight = window?.innerHeight;
        let innerWidth = window?.innerWidth;
        // get the component height and width through element.getClientRects
        let height = 0,
          width = 0;

        let clientRect = this.$el.getClientRects();
        if (clientRect && clientRect.length) {
          height = clientRect[0].height;
          width = clientRect[0].width;
        }
        // then subtract window inner height and width with
        // context-menu event source points (x, y)
        let dY = innerHeight - y;
        let dX = innerWidth - x;
        // check if the context-menu height is not
        // longer than the available
        if (dY < height) {
          y = y - height;
        }
        if (dX < width) {
          x = x - width;
        }
        // set the position
        this.style = { left: x + "px", top: y + "px" };
      }
    },
    async onData() {
      const vm = this;

      // validate if the ctxMenuData is an array and the lenght is not less then 1
      if (Array.isArray(vm.ctxMenuData) && vm.ctxMenuData.length) {
        // loop through the options
        vm.ctxMenuData.forEach((item, index) => {
          // if vm option type is equal's to divider and the handler property value is a function
          if (item.type !== "divider" && typeof item.handler === "function") {
            // select the option element with the help of the refs id
            let refName = `ctx_${index}`;
            let refs = vm.$refs[refName];
            // accessing $refs prooerty with object square bracket notation alwasys returns arrays of
            // HTML Elements of Vue components instance
            // so you have to validate
            if (Array.isArray(refs) && refs.length) {
              let el = refs[0];
              // then attach click event and pass an arrow function as a the
              // event handler callback
              el.addEventListener(
                "click",
                () => {
                  // then on click on the option
                  // envoke the handler
                  // and reset the the ctxMenuData to hide the context-menu
                  item.handler();
                  vm.resetCtx();
                },
                false
              );
            }
          }
        });
      }
    },
  },
  mounted() {
    // Listen on contextmenu event through the $root instance
    const { $eventBus } = useNuxtApp();
    $eventBus.emit("contextmenu", (data) => {
      // if the data is null reset and handler the action
      if (data === null) this.resetCtx();
      else this.onContextMenu(data.event, data.ctxMenuData);
    });
  },
  beforeDestroy() {
    // this.$root.$off("contextmenu", () => {});
  },
};
</script>

<style scoped>
.ctx-menu {
  min-width: 150px;
  height: fit-content;
  padding: 10px 0;
  position: fixed;
  background-color: #fff;
  box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26), 0 2px 10px 0 rgba(0, 0, 0, 0.16);
  border-radius: 3px;
  z-index: 1111111;
}
.ctx-menu-option {
  display: flex;
  align-items: center;
  height: 35px;
  padding: 0 10px;
  cursor: pointer;
}
.ctx-menu-option:hover {
  background-color: #f3f9ff;
}
.ctx-menu-divider {
  height: 16px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.ctx-menu-divider > span {
  width: 50px;
  height: 1px;
  background-color: #dcdfe6;
}
</style>