{{define "component/sortableTableTrigger"}}
<a-icon type="drag"
  class="sortable-icon"
  style="cursor: move;"
  @mouseup="mouseUpHandler"
  @mousedown="mouseDownHandler"
  @click="clickHandler" />
{{end}}

{{define "component/sortableTable"}}
<script>
  const DRAGGABLE_ROW_CLASS = 'draggable-row';
  const findParentRowElement = (el) => {
    if (!el || !el.tagName) {
      return null;
    } else if (el.classList.contains(DRAGGABLE_ROW_CLASS)) {
      return el;
    } else if (el.parentNode) {
      return findParentRowElement(el.parentNode);
    } else {
      return null;
    }
  }
  Vue.component('a-table-sortable', {
    data() {
      return {
        sortingElementIndex: null,
        newElementIndex: null,
      };
    },
    props: ['data-source', 'customRow'],
    inheritAttrs: false,
    provide() {
      const sortable = {}
      Object.defineProperty(sortable, "setSortableIndex", {
        enumerable: true,
        get: () => this.setCurrentSortableIndex,
      });
      Object.defineProperty(sortable, "resetSortableIndex", {
        enumerable: true,
        get: () => this.resetSortableIndex,
      });
      return {
        sortable,
      }
    },
    render: function(createElement) {
      return createElement('a-table', {
        class: {
          'ant-table-is-sorting': this.isDragging(),
        },
        props: {
          ...this.$attrs,
          'data-source': this.records,
          customRow: (record, index) => this.customRowRender(record, index),
        },
        on: this.$listeners,
        nativeOn: {
          drop: (e) => this.dropHandler(e),
        },
        scopedSlots: this.$scopedSlots,
      }, this.$slots.default, )
    },
    created() {
      this.$memoSort = {};
    },
    methods: {
      isDragging() {
        const currentIndex = this.sortingElementIndex;
        return currentIndex !== null && currentIndex !== undefined;
      },
      resetSortableIndex(e, index) {
        this.sortingElementIndex = null;
        this.newElementIndex = null;
        this.$memoSort = {};
      },
      setCurrentSortableIndex(e, index) {
        this.sortingElementIndex = index;
      },
      dragStartHandler(e, index) {
        if (!this.isDragging()) {
          e.preventDefault();
          return;
        }
        const hideDragImage = this.$el.cloneNode(true);
        hideDragImage.id = "hideDragImage-hide";
        hideDragImage.style.opacity = 0;
        e.dataTransfer.setDragImage(hideDragImage, 0, 0);
      },
      dragStopHandler(e, index) {
        const hideDragImage = document.getElementById('hideDragImage-hide');
        if (hideDragImage) hideDragImage.remove();
        this.resetSortableIndex(e, index);
      },
      dragOverHandler(e, index) {
        if (!this.isDragging()) {
          return;
        }
        e.preventDefault();
        const currentIndex = this.sortingElementIndex;
        if (index === currentIndex) {
          this.newElementIndex = null;
          return;
        }
        const row = findParentRowElement(e.target);
        if (!row) {
          return;
        }
        const rect = row.getBoundingClientRect();
        const offsetTop = e.pageY - rect.top;
        if (offsetTop < rect.height / 2) {
          this.newElementIndex = Math.max(index - 1, 0);
        } else {
          this.newElementIndex = index;
        }
      },
      dropHandler(e) {
        if (this.isDragging()) {
          this.$emit('onsort', this.sortingElementIndex, this.newElementIndex);
        }
      },
      customRowRender(record, index) {
        const parentMethodResult = this.customRow?.(record, index) || {};
        const newIndex = this.newElementIndex;
        const currentIndex = this.sortingElementIndex;
        return {
          ...parentMethodResult,
          attrs: {
            ...(parentMethodResult?.attrs || {}),
            draggable: true,
          },
          on: {
            ...(parentMethodResult?.on || {}),
            dragstart: (e) => this.dragStartHandler(e, index),
            dragend: (e) => this.dragStopHandler(e, index),
            dragover: (e) => this.dragOverHandler(e, index),
          },
          class: {
            ...(parentMethodResult?.class || {}),
            [DRAGGABLE_ROW_CLASS]: true,
            ['dragging']: this.isDragging() ? (newIndex === null ? index === currentIndex : index === newIndex) : false,
          },
        };
      }
    },
    computed: {
      records() {
        const newIndex = this.newElementIndex;
        const currentIndex = this.sortingElementIndex;
        if (!this.isDragging() || newIndex === null || currentIndex === newIndex) {
          return this.dataSource;
        }
        if (this.$memoSort.newIndex === newIndex) {
          return this.$memoSort.list;
        }
        let list = [...this.dataSource];
        list.splice(newIndex, 0, list.splice(currentIndex, 1)[0]);
        this.$memoSort = {
          newIndex,
          list,
        };
        return list;
      }
    }
  });
  Vue.component('table-sort-trigger', {
    template: `{{template "component/sortableTableTrigger"}}`,
    props: ['item-index'],
    inject: ['sortable'],
    methods: {
      mouseDownHandler(e) {
        if (this.sortable) {
          this.sortable.setSortableIndex(e, this.itemIndex);
        }
      },
      mouseUpHandler(e) {
        if (this.sortable) {
          this.sortable.resetSortableIndex(e, this.itemIndex);
        }
      },
      clickHandler(e) {
        e.preventDefault();
      },
    }
  })
</script>
<style>
  @media only screen and (max-width: 767px) {
    .sortable-icon {
      display: none;
    }
  }
  .ant-table-is-sorting .draggable-row td {
    background-color: #ffffff !important;
  }
  .dark .ant-table-is-sorting .draggable-row td {
    background-color: var(--dark-color-surface-100) !important;
  }
  .ant-table-is-sorting .dragging td {
    background-color: rgb(232 244 242) !important;
    color: rgba(0, 0, 0, 0.3);
  }
  .dark .ant-table-is-sorting .dragging td {
    background-color: var(--dark-color-table-hover) !important;
    color: rgba(255, 255, 255, 0.3);
  }
  .ant-table-is-sorting .dragging {
    opacity: 1;
    box-shadow: 1px -2px 2px #008771;
    transition: all 0.2s;
  }
  .ant-table-is-sorting .dragging .ant-table-row-index {
    opacity: 0.3;
  }
</style>
{{end}}