Files
2024-09-01 18:54:23 +05:00

195 lines
5.1 KiB
Vue

<template>
<DefaultField
:field="currentField"
:errors="errors"
:show-help-text="showHelpText"
:full-width-content="fullWidthContent"
>
<template #field>
<div v-if="value.length > 0" class="space-y-4" :dusk="fieldAttribute">
<RepeaterRow
v-for="(item, index) in value"
:dusk="`${index}-repeater-row`"
:data-repeater-id="valueMap.get(item)"
:item="item"
:index="index"
:key="valueMap.get(item)"
@click="removeItem"
:errors="errors"
:sortable="currentField.sortable && value.length > 1"
@move-up="moveUp"
@move-down="moveDown"
:field="currentField"
:via-parent="fieldAttribute"
/>
</div>
<div>
<div
class="text-center"
:class="{
'bg-gray-50 dark:bg-gray-900 rounded-lg border-4 dark:border-gray-600 border-dashed py-3':
value.length === 0,
}"
>
<Dropdown v-if="currentField.repeatables.length > 1">
<Button
variant="link"
leading-icon="plus-circle"
trailing-icon="chevron-down"
>
{{ __('Add item') }}
</Button>
<template #menu>
<DropdownMenu class="py-1">
<DropdownMenuItem
@click="() => addItem(repeatable.type)"
as="button"
v-for="repeatable in currentField.repeatables"
class="space-x-2"
>
<span><Icon solid :type="repeatable.icon" /></span>
<span>{{ repeatable.singularLabel }}</span>
</DropdownMenuItem>
</DropdownMenu>
</template>
</Dropdown>
<InvertedButton
v-else
@click="addItem(currentField.repeatables[0].type)"
type="button"
>
<span>{{
__('Add :resource', {
resource: currentField.repeatables[0].singularLabel,
})
}}</span>
</InvertedButton>
</div>
</div>
</template>
</DefaultField>
</template>
<script>
import { FormField, HandlesValidationErrors } from '@/mixins'
import cloneDeep from 'lodash/cloneDeep'
import { uid } from 'uid/single'
import { computed } from 'vue'
import { Button } from 'laravel-nova-ui'
export default {
mixins: [FormField, HandlesValidationErrors],
components: { Button },
provide() {
return {
removeFile: this.removeFile,
shownViaNewRelationModal: computed(() => this.shownViaNewRelationModal),
viaResource: computed(() => this.viaResource),
viaResourceId: computed(() => this.viaResourceId),
viaRelationship: computed(() => this.viaRelationship),
resourceName: computed(() => this.resourceName),
resourceId: computed(() => this.resourceId),
}
},
data: () => ({
valueMap: new WeakMap(),
}),
beforeMount() {
this.value.map(repeatable => {
this.valueMap.set(repeatable, uid())
return repeatable
})
},
methods: {
/**
* Return the field default value.
*/
fieldDefaultValue() {
return []
},
removeFile(attribute) {
const {
resourceName,
resourceId,
relatedResourceName,
relatedResourceId,
viaRelationship,
} = this
const uri =
viaRelationship && relatedResourceName && relatedResourceId
? `/nova-api/${resourceName}/${resourceId}/${relatedResourceName}/${relatedResourceId}/field/${attribute}?viaRelationship=${viaRelationship}`
: `/nova-api/${resourceName}/${resourceId}/field/${attribute}`
Nova.request().delete(uri)
},
fill(formData) {
this.finalPayload.forEach((repeatable, i) => {
const attribute = `${this.fieldAttribute}[${i}]`
formData.append(`${attribute}[type]`, repeatable.type)
Object.keys(repeatable.fields).forEach(key => {
formData.append(
`${attribute}[fields][${key}]`,
repeatable.fields[key]
)
})
})
},
addItem(repeatableType) {
const repeatable = this.currentField.repeatables.find(
t => t.type === repeatableType
)
const copy = cloneDeep(repeatable)
this.valueMap.set(copy, uid())
this.value.push(copy)
},
removeItem(index) {
const item = this.value.splice(index, 1)
this.valueMap.delete(item)
},
moveUp(index) {
const item = this.value.splice(index, 1)
this.value.splice(Math.max(0, index - 1), 0, item[0])
},
moveDown(index) {
const item = this.value.splice(index, 1)
this.value.splice(Math.min(this.value.length, index + 1), 0, item[0])
},
},
computed: {
finalPayload() {
return this.value.map(repeatable => {
const formData = new FormData()
const fields = {}
repeatable.fields.forEach(f => f.fill && f.fill(formData))
for (const pair of formData.entries()) {
fields[pair[0]] = pair[1]
}
return { type: repeatable.type, fields }
})
},
},
}
</script>