forked from dillingham/nova-items-field
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor of value handling, improvements to work with flexible-conten…
…t and show details as actual list
- Loading branch information
Showing
6 changed files
with
240 additions
and
141 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,20 @@ | ||
<template> | ||
<PanelItem :index="index" :field="field" /> | ||
<PanelItem :field="field" > | ||
<template #value> | ||
<ul> | ||
<li class="mb-1" style="list-style: square; margin-left: 1rem;" v-for="value of fieldValue"> | ||
{{ value }} | ||
</li> | ||
</ul> | ||
</template> | ||
</PanelItem> | ||
</template> | ||
|
||
<script> | ||
import HasFieldValue from "../mixins/HasFieldValue"; | ||
export default { | ||
mixins: [HasFieldValue], | ||
props: ['resource', 'resourceName', 'resourceId', 'field'], | ||
created() { | ||
if (Array.isArray(this.field.value)) { | ||
this.field.value = this.field.value.join(", "); | ||
} | ||
}, | ||
} | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,162 +1,176 @@ | ||
<template> | ||
<DefaultField :field="currentField" :full-width-content="currentField.fullWidth" :show-help-text="showHelpText"> | ||
<template #field> | ||
<div class="nova-items-field-input-wrapper flex mb-2" v-if="currentField.listFirst === false && ! maxReached"> | ||
<DefaultField :field="currentField" :full-width-content="currentField.fullWidth" :show-help-text="showHelpText"> | ||
<template #field> | ||
<div class="nova-items-field-input-wrapper flex mb-2" v-if="currentField.listFirst === false && ! maxReached"> | ||
<input | ||
v-model="newItem" | ||
:type="currentField.inputType" | ||
:placeholder="currentField.placeholder" | ||
autocomplete="new-password" | ||
@keydown.enter.prevent="addItem" | ||
class="flex-1 form-control form-input form-input-bordered" | ||
/> | ||
<button | ||
type="button" | ||
@click="addItem" | ||
v-html="currentField.createButtonValue" | ||
v-if="currentField.hideCreateButton === false" | ||
class="ml-3 cursor-pointer shadow relative bg-primary-500 hover:bg-primary-400 active:bg-primary-600 text-white dark:text-gray-900 cursor-pointer rounded text-sm font-bold focus:outline-none focus:ring inline-flex items-center justify-center h-9 px-3 shadow relative bg-primary-500 hover:bg-primary-400 active:bg-primary-600 text-white dark:text-gray-900" | ||
/> | ||
</div> | ||
<ul ref="novaitemslist" :style="maxHeight" v-if="items.length" class="nova-items-field-input-items list-reset"> | ||
<draggable :disabled="currentField.draggable === false" v-model="items" | ||
:item-key="currentField.attribute + '.' + index" handle=".sortable-handle"> | ||
<template #item="{ element, index }"> | ||
<li class="py-1"> | ||
<div class="nova-items-field-input-wrapper-item flex py-1 gap-2"> | ||
<button type="button" v-if="currentField.draggable === true" class="cursor-move sortable-handle px-4"> | ||
<Icon type="menu"/> | ||
</button> | ||
<input | ||
v-model="newItem" | ||
:value="element" | ||
:type="currentField.inputType" | ||
:placeholder="currentField.placeholder" | ||
v-on:keyup="updateItem(index, $event)" | ||
:name="currentField.name + '['+ index +']'" | ||
autocomplete="new-password" | ||
@keydown.enter.prevent="addItem" | ||
:class="{'border-danger': hasErrors(index)}" | ||
class="flex-1 form-control form-input form-input-bordered" | ||
/> | ||
> | ||
<button | ||
v-if="currentField.deleteButtonValue" | ||
type="button" | ||
@click="addItem" | ||
v-html="currentField.createButtonValue" | ||
v-if="currentField.hideCreateButton === false" | ||
class="ml-3 cursor-pointer shadow relative bg-primary-500 hover:bg-primary-400 active:bg-primary-600 text-white dark:text-gray-900 cursor-pointer rounded text-sm font-bold focus:outline-none focus:ring inline-flex items-center justify-center h-9 px-3 shadow relative bg-primary-500 hover:bg-primary-400 active:bg-primary-600 text-white dark:text-gray-900" | ||
/> | ||
</div> | ||
<ul ref="novaitemslist" :style="maxHeight" v-if="items.length" class="nova-items-field-input-items list-reset"> | ||
<draggable :disabled="currentField.draggable === false" v-model="items" :item-key="currentField.attribute + '.' + index" handle=".sortable-handle"> | ||
<template #item="{ element, index }"> | ||
<li class="py-1"> | ||
<div class="nova-items-field-input-wrapper-item flex py-1 gap-2"> | ||
<button type="button" v-if="currentField.draggable === true" class="cursor-move sortable-handle px-4"><Icon type="menu" /></button> | ||
<input | ||
:value="element" | ||
:type="currentField.inputType" | ||
v-on:keyup="updateItem(index, $event)" | ||
:name="currentField.name + '['+ index +']'" | ||
autocomplete="new-password" | ||
:class="{'border-danger': hasErrors(currentField.attribute + '.' + index)}" | ||
class="flex-1 form-control form-input form-input-bordered" | ||
> | ||
<button | ||
v-if="currentField.deleteButtonValue" | ||
type="button" | ||
@click="removeItem(index)" | ||
class="px-4 text-xl font-bold focus:outline-none focus:ring" | ||
v-html="currentField.deleteButtonValue" | ||
/> | ||
<button v-else type="button" @click="removeItem(index)" class="px-1 ml-1 toolbar-button"> | ||
<Icon type="x"/> | ||
</button> | ||
</div> | ||
<HelpText class="mt-2 help-text-error" v-if="hasErrors(currentField.attribute + '.' + index)"> | ||
{{ arrayErrors[currentField.attribute + '.' + index][0] }} | ||
</HelpText> | ||
</li> | ||
</template> | ||
</draggable> | ||
</ul> | ||
<div class="nova-items-field-input-wrapper flex mt-2" v-if="currentField.listFirst && ! maxReached"> | ||
<input | ||
v-model="newItem" | ||
:type="currentField.inputType" | ||
:placeholder="currentField.placeholder" | ||
class="flex-1 form-control form-input form-input-bordered" | ||
@keypress.enter.prevent="addItem" | ||
/> | ||
<button | ||
type="button" | ||
@click="addItem" | ||
v-html="currentField.createButtonValue" | ||
v-if="currentField.hideCreateButton === false" | ||
class="ml-3 cursor-pointer shadow relative bg-primary-500 hover:bg-primary-400 active:bg-primary-600 text-white dark:text-gray-900 cursor-pointer rounded text-sm font-bold focus:outline-none focus:ring inline-flex items-center justify-center h-9 px-3 shadow relative bg-primary-500 hover:bg-primary-400 active:bg-primary-600 text-white dark:text-gray-900" | ||
@click="removeItem(index)" | ||
class="px-4 text-xl font-bold focus:outline-none focus:ring" | ||
v-html="currentField.deleteButtonValue" | ||
/> | ||
</div> | ||
</template> | ||
</DefaultField> | ||
<button v-else type="button" @click="removeItem(index)" class="px-1 ml-1 toolbar-button"> | ||
<Icon type="x"/> | ||
</button> | ||
</div> | ||
<HelpText class="mt-2 help-text-error" v-if="hasErrors(index)"> | ||
{{ arrayErrors[index][0] }} | ||
</HelpText> | ||
</li> | ||
</template> | ||
</draggable> | ||
</ul> | ||
<div class="nova-items-field-input-wrapper flex mt-2" v-if="currentField.listFirst && ! maxReached"> | ||
<input | ||
v-model="newItem" | ||
:type="currentField.inputType" | ||
:placeholder="currentField.placeholder" | ||
class="flex-1 form-control form-input form-input-bordered" | ||
@keypress.enter.prevent="addItem" | ||
/> | ||
<button | ||
type="button" | ||
@click="addItem" | ||
v-html="currentField.createButtonValue" | ||
v-if="currentField.hideCreateButton === false" | ||
class="ml-3 cursor-pointer shadow relative bg-primary-500 hover:bg-primary-400 active:bg-primary-600 text-white dark:text-gray-900 cursor-pointer rounded text-sm font-bold focus:outline-none focus:ring inline-flex items-center justify-center h-9 px-3 shadow relative bg-primary-500 hover:bg-primary-400 active:bg-primary-600 text-white dark:text-gray-900" | ||
/> | ||
</div> | ||
<HelpText class="mt-2 help-text-error" v-if="hasErrors()"> | ||
{{ arrayErrors[''][0] }} | ||
</HelpText> | ||
</template> | ||
</DefaultField> | ||
</template> | ||
|
||
<style scoped> | ||
</style> | ||
|
||
<script> | ||
import draggable from 'vuedraggable' | ||
import { DependentFormField, HandlesValidationErrors } from 'laravel-nova' | ||
import {DependentFormField, HandlesValidationErrors} from 'laravel-nova' | ||
import HasFieldValue from "../mixins/HasFieldValue"; | ||
export default { | ||
mixins: [DependentFormField, HandlesValidationErrors], | ||
mixins: [DependentFormField, HandlesValidationErrors, HasFieldValue], | ||
props: ['resourceName', 'resourceId', 'field'], | ||
props: ['resourceName', 'resourceId', 'field'], | ||
components: {draggable}, | ||
components: { draggable }, | ||
data() | ||
{ | ||
return { | ||
value: '', | ||
items: [], | ||
newItem: '', | ||
arrayErrors: [] | ||
}; | ||
}, | ||
data() { | ||
return { | ||
value: '', | ||
items: [], | ||
newItem: '', | ||
arrayErrors: [] | ||
}; | ||
methods: { | ||
setInitialValue() | ||
{ | ||
this.value = JSON.stringify(this.fieldValue); | ||
this.items = this.fieldValue; | ||
}, | ||
methods: { | ||
setInitialValue() { | ||
this.value = this.field.value || []; | ||
this.items = this.field.value || []; | ||
}, | ||
fill(formData) | ||
{ | ||
formData.append(this.field.attribute, this.value || []) | ||
}, | ||
fill(formData) { | ||
formData.append(this.field.attribute, this.value || []) | ||
}, | ||
addItem() | ||
{ | ||
const item = this.newItem.trim(); | ||
if (item && !this.maxReached) { | ||
this.items.push(item) | ||
this.newItem = '' | ||
addItem() { | ||
const item = this.newItem.trim(); | ||
if (item && ! this.maxReached) { | ||
this.items.push(item) | ||
this.newItem = '' | ||
this.$nextTick(() => { | ||
if (this.field.maxHeight) { | ||
this.$refs.novaitemslist.scrollTop = this.$refs.novaitemslist.scrollHeight; | ||
} | ||
}) | ||
} | ||
}, | ||
this.$nextTick(() => { | ||
if(this.field.maxHeight){ | ||
this.$refs.novaitemslist.scrollTop = this.$refs.novaitemslist.scrollHeight; | ||
} | ||
}) | ||
} | ||
}, | ||
updateItem(index, event) | ||
{ | ||
this.items[index] = event.target.value | ||
}, | ||
updateItem(index, event) { | ||
this.items[index] = event.target.value | ||
}, | ||
removeItem(index) | ||
{ | ||
this.items.splice(index, 1) | ||
}, | ||
removeItem (index) { | ||
this.items.splice(index, 1) | ||
}, | ||
hasErrors(key) | ||
{ | ||
return this.arrayErrors.hasOwnProperty(key ?? ''); | ||
} | ||
}, | ||
computed: { | ||
maxHeight() | ||
{ | ||
if (this.field.maxHeight === false) { | ||
return ''; | ||
} | ||
hasErrors(key) { | ||
return this.arrayErrors.hasOwnProperty(key); | ||
} | ||
return `max-height: ${this.field.maxHeight}px; overflow: auto;`; | ||
}, | ||
computed: { | ||
maxHeight() { | ||
if (this.field.maxHeight === false) { | ||
return ''; | ||
} | ||
return `max-height: ${this.field.maxHeight}px; overflow: auto;`; | ||
}, | ||
maxReached() { | ||
return this.field.max !== false && this.items.length + 1 > this.field.max; | ||
} | ||
maxReached() | ||
{ | ||
return this.field.max !== false && this.items.length + 1 > this.field.max; | ||
} | ||
}, | ||
watch: { | ||
'items': { | ||
handler: function (items) { | ||
this.value = JSON.stringify(items); | ||
}, | ||
deep: true | ||
}, | ||
watch: { | ||
'items': { | ||
handler: function (items) { | ||
this.value = JSON.stringify(items); | ||
}, | ||
deep: true | ||
}, | ||
'errors': { | ||
handler: function (errors) { | ||
if(errors.errors.hasOwnProperty(this.field.attribute)) { | ||
this.arrayErrors = JSON.parse(errors.errors[this.field.attribute][0]) | ||
} | ||
}, | ||
deep: true | ||
} | ||
'errors': { | ||
handler: function (errors) { | ||
this.arrayErrors = errors.errors.hasOwnProperty(this.field.validationKey) ? JSON.parse(errors.errors[this.field.validationKey][0]) : {}; | ||
}, | ||
deep: true | ||
} | ||
} | ||
} | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,15 @@ | ||
<template> | ||
<div> | ||
<span v-if="field.value">{{ field.value.length }}</span> | ||
<span v-else>0</span> | ||
<span v-if="fieldValue">{{ fieldValue.length }}</span> | ||
<span v-else>0</span> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
import HasFieldValue from "../mixins/HasFieldValue"; | ||
export default { | ||
mixins: [HasFieldValue], | ||
props: ['resourceName', 'field'], | ||
} | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
export default { | ||
computed: { | ||
fieldValue() { | ||
let fieldValue = this.field.value; | ||
while (typeof fieldValue === 'string') { | ||
try { | ||
fieldValue = JSON.parse(fieldValue); | ||
} catch { | ||
fieldValue = null; | ||
} | ||
} | ||
if (! fieldValue) { | ||
fieldValue = []; | ||
} | ||
return fieldValue; | ||
} | ||
} | ||
}; |
Oops, something went wrong.