194 lines
7.2 KiB
Vue
194 lines
7.2 KiB
Vue
<template>
|
|
<div>
|
|
<input type="hidden" :name="_prefix + 'TOTAL_FORMS'" :value="items.length || 0"/>
|
|
<template v-for="(value,name) in formData.management" v-bind:key="name">
|
|
<input type="hidden" :name="_prefix + name.toUpperCase()"
|
|
:value="value"/>
|
|
</template>
|
|
|
|
<a-rows ref="rows" :set="set" :context="this"
|
|
:columns="visibleFields" :columnsOrderable="columnsOrderable"
|
|
:orderable="orderable" @move="moveItem" @colmove="onColumnMove"
|
|
@cell="e => $emit('cell', e)">
|
|
|
|
<template #header-head>
|
|
<template v-if="orderable">
|
|
<th style="max-width:2em" :title="orderField.label"
|
|
:aria-label="orderField.label"
|
|
:aria-description="orderField.help || ''">
|
|
<span class="icon">
|
|
<i class="fa fa-arrow-down-1-9"></i>
|
|
</span>
|
|
</th>
|
|
<slot name="rows-header-head"></slot>
|
|
</template>
|
|
</template>
|
|
|
|
<template #row-head="data">
|
|
<input v-if="orderable" type="hidden"
|
|
:name="_prefix + data.row + '-' + orderBy"
|
|
:value="data.row"/>
|
|
<input type="hidden" :name="_prefix + data.row + '-id'"
|
|
:value="data.item ? data.item.id : ''"/>
|
|
|
|
<template v-for="field of hiddenFields" v-bind:key="field.name">
|
|
<input type="hidden"
|
|
v-if="!(field.name in ['id', orderBy])"
|
|
:name="_prefix + data.row + '-' + field.name"
|
|
:value="field.value in [null, undefined] ? data.item.data[name] : field.value"/>
|
|
</template>
|
|
|
|
<slot name="row-head" v-bind="data">
|
|
<td v-if="orderable">{{ data.row+1 }}</td>
|
|
</slot>
|
|
</template>
|
|
|
|
<template v-for="(field,slot) of fieldSlots" v-bind:key="field.name"
|
|
v-slot:[slot]="data">
|
|
<slot :name="slot" v-bind="data" :field="field" :input-name="_prefix + data.cell.row + '-' + field.name">
|
|
<div class="field">
|
|
<div class="control">
|
|
<slot :name="'control-' + field.name" v-bind="data" :field="field" :input-name="_prefix + data.cell.row + '-' + field.name"/>
|
|
</div>
|
|
<p v-for="[error,index] in data.item.error(field.name)" class="help is-danger" v-bind:key="index">
|
|
{{ error }}
|
|
</p>
|
|
</div>
|
|
</slot>
|
|
</template>
|
|
|
|
<template #row-tail="data">
|
|
<slot v-if="$slots['row-tail']" name="row-tail" v-bind="data"/>
|
|
<td class="align-right pr-0">
|
|
<button type="button" class="button square"
|
|
@click.stop="removeItem(data.row, data.item)"
|
|
:title="labels.remove_item"
|
|
:aria-label="labels.remove_item">
|
|
<span class="icon"><i class="fa fa-trash" /></span>
|
|
</button>
|
|
</td>
|
|
</template>
|
|
</a-rows>
|
|
<div class="a-formset-footer flex-row">
|
|
<div class="flex-grow-1 flex-row">
|
|
<slot name="footer"/>
|
|
</div>
|
|
<div class="flex-grow-1 align-right">
|
|
<button type="button" class="button square is-warning p-2"
|
|
@click="reset()"
|
|
:title="labels.discard_changes"
|
|
:aria-label="labels.discard_changes"
|
|
>
|
|
<span class="icon"><i class="fa fa-rotate" /></span>
|
|
</button>
|
|
<button type="button" class="button square is-primary p-2"
|
|
@click="onActionAdd"
|
|
:title="labels.add_item"
|
|
:aria-label="labels.add_item"
|
|
>
|
|
<span class="icon">
|
|
<i class="fa fa-plus"/></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
import {cloneDeep} from 'lodash'
|
|
import Model, {Set} from '../model'
|
|
|
|
import ARows from './ARows'
|
|
|
|
export default {
|
|
emit: ['cell', 'move', 'colmove', 'load'],
|
|
components: {ARows},
|
|
|
|
props: {
|
|
labels: Object,
|
|
|
|
//! If provided call this function instead of adding an item to rows on "+" button click.
|
|
actionAdd: Function,
|
|
|
|
//! If True, columns can be reordered
|
|
columnsOrderable: Boolean,
|
|
//! Field name used for ordering
|
|
orderBy: String,
|
|
|
|
//! Formset data as returned by get_formset_data
|
|
formData: Object,
|
|
//! Model class used for item's set
|
|
model: {type: Function, default: Model},
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
set: new Set(Model),
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
// ---- fields
|
|
_prefix() { return this.formData.prefix ? this.formData.prefix + '-' : '' },
|
|
fields() { return this.formData.fields },
|
|
orderField() { return this.orderBy && this.fields.find(f => f.name == this.orderBy) },
|
|
orderable() { return !!this.orderField },
|
|
|
|
hiddenFields() { return this.fields.filter(f => f.hidden && !(this.orderable && f == this.orderField)) },
|
|
visibleFields() { return this.fields.filter(f => !f.hidden) },
|
|
|
|
fieldSlots() { return this.visibleFields.reduce(
|
|
(slots, f) => ({...slots, ['row-' + f.name]: f}),
|
|
{}
|
|
)},
|
|
|
|
items() { return this.set.items },
|
|
rows() { return this.$refs.rows },
|
|
},
|
|
|
|
methods: {
|
|
onCellEvent(event) { this.$emit('cell', event) },
|
|
onColumnMove(event) { this.$emit('colmove', event) },
|
|
onActionAdd() {
|
|
if(this.actionAdd)
|
|
return this.actionAdd(this)
|
|
this.set.push()
|
|
},
|
|
|
|
moveItem(event) {
|
|
const {from, to} = event
|
|
const set_ = event.set || this.set
|
|
set_.move(from, to);
|
|
this.$emit('move', {...event, seŧ: set_})
|
|
},
|
|
|
|
removeItem(row) {
|
|
const item = this.items[row]
|
|
if(item.id) {
|
|
// TODO
|
|
}
|
|
else {
|
|
this.items.splice(row,1)
|
|
}
|
|
},
|
|
|
|
//! Load items into set
|
|
load(items=[], reset=false) {
|
|
if(reset)
|
|
this.set.items = []
|
|
for(var item of items)
|
|
this.set.push(cloneDeep(item))
|
|
this.$emit('load', items)
|
|
},
|
|
|
|
//! Reset forms to initials
|
|
reset() {
|
|
this.load(this.formData?.initials || [], true)
|
|
},
|
|
},
|
|
|
|
mounted() {
|
|
this.reset()
|
|
}
|
|
}
|
|
</script>
|