<template>
    <tr>
        <slot name="head" :item="item" :row="row"/>
        <template v-for="(attr,col) in columns" :key="col">
            <slot name="cell-before" :item="item" :cell="cells[col]"
                    :attr="attr"/>
            <component :is="cellTag" :class="['cell', 'cell-' + attr]" :data-col="col"
                    :draggable="orderable"
                    @dragstart="onDragStart" @dragover="onDragOver" @drop="onDrop">
                <slot :name="attr" :item="item" :cell="cells[col]"
                        :data="itemData" :attr="attr" :emit="cellEmit"
                        :value="itemData && itemData[attr]">
                    {{ itemData && itemData[attr] }}
                </slot>
                <slot name="cell" :item="item" :cell="cells[col]"
                        :data="itemData" :attr="attr" :emit="cellEmit"
                        :value="itemData && itemData[attr]"/>
            </component>
            <slot name="cell-after" :item="item" :col="col" :cell="cells[col]"
                    :attr="attr"/>
        </template>
        <slot name="tail" :item="item" :row="row"/>
    </tr>
</template>
<script>
import {isReactive, toRefs} from 'vue'
import Model from '../model'

export default {
    emit: ['move', 'cell'],

    props: {
        //! Item to display in row
        item: Object,
        //! Columns to display, as items' attributes
        columns: Array,
        //! Default cell's info
        cell: {type: Object, default() { return {row: 0}}},
        //! Cell component tag
        cellTag: {type: String, default: 'td'},
        //! If true, can reorder cell by drag & drop
        orderable: {type: Boolean, default: false},
    },

    computed: {
        /**
         * Row index
         */
        row() { return this.cell && this.cell.row || 0 },

        /**
         * Item's data if model instance, otherwise item
         */
        itemData() {
            return this.item instanceof Model ? this.item.data : this.item;
        },

        /**
         * Computed cell infos
         */
        cells() {
            const cell = isReactive(this.cell) && toRefs(this.cell) || this.cell || {}
            const cells = []
            for(var col in this.columns)
                cells.push({...cell, col: Number(col)})
            return cells
        },
    },

    methods: {
        /**
         * Emit a 'cell' event.
         * Event data: `{name, cell, data, item}`
         * @param {Number} col: cell column's index
         * @param {String} name: cell's event name
         * @param {} data: cell's event data
         */
        cellEmit(name, cell, data) {
            this.$emit('cell', {
                name, cell, data,
                item: this.item,
            })
        },

        onDragStart(ev) {
            const dataset = ev.target.dataset;
            const data = `cell:${dataset.col}`
            ev.dataTransfer.setData("text/cell", data)
            ev.dataTransfer.dropEffect = 'move'
        },

        onDragOver(ev) {
            ev.preventDefault()
            ev.dataTransfer.dropEffect = 'move'
        },

        /**
         * Handle drop event, emit `'move': { from, to }`.
         */
        onDrop(ev) {
            const data = ev.dataTransfer.getData("text/cell")
            if(!data || !data.startsWith('cell:'))
                return

            ev.preventDefault()
            this.$emit('move', {
                from: Number(data.slice(5)),
                to: Number(ev.target.dataset.col),
            })
        },

        /**
         * Return DOM node for cells at provided position `col`
         */
        getCellEl(col) {
            const els = this.$el.querySelectorAll(this.cellTag)
            for(var el of els)
                if(col == Number(el.dataset.col))
                    return el;
            return null
        },

        /**
         * Focus cell's form input. If from is provided, related focus
         */
        focus(col, from) {
            if(from)
                col += from.col

            const target = this.getCellEl(col)
            if(!target)
                return
            const control = target.querySelector('input:not([type="hidden"])') ||
                            target.querySelector('button') ||
                            target.querySelector('select') ||
                            target.querySelector('a');
            control && control.focus()
        }
    },

    mounted() {
        this.$el.__row = this
    },
}

</script>
