aircox-radiocampus/assets/src/components/ARow.vue
2022-12-12 12:29:05 +01:00

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>