aircox/assets/src/components/AFormSet.vue
2024-04-22 23:54:44 +02:00

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>