forked from rc/aircox
147 lines
4.4 KiB
Vue
147 lines
4.4 KiB
Vue
<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>
|