Dc3e4 new matrix field (#484)

* fix password reset bug

* wip: matrix input

* wip: matrix input

* wip: matrix input

* Fixed matric input component logic

* matrix input cleanup

* fix lint errors

* table border and radius

* cleanup, linting

* fix component methos

* wip matrix input

* matrix condition for contains and not contain

* patch matrix input condition logic

* linting

* refactor and cleanup

* fix syntax error

* Polished the matrix input

* Fix linting

---------

Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
Favour Olayinka
2024-08-23 14:28:21 +01:00
committed by GitHub
parent fedc382594
commit 1adac8e00f
25 changed files with 919 additions and 85 deletions

View File

@@ -187,6 +187,11 @@
/>
</div>
<MatrixFieldOptions
:model-value="field"
@update:model-value="field = $event"
/>
<!-- Text Options -->
<div
v-if="field.type === 'text' && displayBasedOnAdvanced"
@@ -290,7 +295,6 @@
Advanced options for your select/multiselect fields.
</p>
<text-area-input
v-model="optionsText"
:name="field.id + '_options_text'"
class="mt-3"
label="Set selection options"
@@ -423,6 +427,15 @@
label="Pre-filled value"
:multiple="field.type === 'multi_select'"
/>
<template v-else-if="field.type === 'matrix'">
<MatrixInput
:form="field"
:rows="field.rows"
:columns="field.columns"
name="prefill"
label="Pre-filled value"
/>
</template>
<date-input
v-else-if="field.type === 'date' && field.prefill_today !== true"
name="prefill"
@@ -594,7 +607,7 @@
:field="field"
/>
<custom-field-validation
<custom-field-validation
class="py-2 px-4 border-b pb-16"
:form="form"
:field="field"
@@ -608,12 +621,13 @@ import countryCodes from '~/data/country_codes.json'
import CountryFlag from 'vue-country-flag-next'
import FormBlockLogicEditor from '../../components/form-logic-components/FormBlockLogicEditor.vue'
import CustomFieldValidation from '../../components/CustomFieldValidation.vue'
import MatrixFieldOptions from './MatrixFieldOptions.vue'
import { format } from 'date-fns'
import { default as _has } from 'lodash/has'
export default {
name: 'FieldOptions',
components: { CountryFlag, FormBlockLogicEditor, CustomFieldValidation },
components: { CountryFlag, FormBlockLogicEditor, CustomFieldValidation, MatrixFieldOptions },
props: {
field: {
type: Object,
@@ -682,12 +696,6 @@ export default {
}
return true
},
optionsText() {
if (!this.field[this.field.type]) return ''
return this.field[this.field.type].options.map(option => {
return option.name
}).join('\n')
}
},
watch: {
@@ -868,6 +876,13 @@ export default {
date: {
date_format: this.dateFormatOptions[0].value,
time_format: this.timeFormatOptions[0].value
},
matrix: {
rows:['Row 1'],
columns: [1 ,2 ,3],
selection_data:{
'Row 1': null
}
}
}
if (this.field.type in defaultFieldValues) {
@@ -877,6 +892,9 @@ export default {
}
})
}
},
updateMatrixField(newField) {
this.field = newField
}
}
}

View File

@@ -0,0 +1,132 @@
<template>
<div
v-if="localField.type === 'matrix'"
class="border-b py-2 px-4"
>
<h3 class="font-semibold block text-lg">
Matrix
</h3>
<p class="text-gray-400 mb-3 text-xs">
Advanced options for matrix.
</p>
<div class="grid grid-cols-2 gap-4">
<div class="">
<div
v-for="(row, i) in localField.rows"
:key="i"
class="flex items-center space-x-2"
>
<text-input
v-model="localField.rows[i]"
name="rows"
wrapper-class="mb-1"
@update:model-value="updateField"
/>
<button @click="removeMatrixRow(i)">
<Icon
name="heroicons:trash"
class="text-gray-300 w-4 h-4 mb-2"
/>
</button>
</div>
<UButton
size="xs"
color="gray"
icon="i-heroicons-plus"
@click="addMatrixRow"
>
Add row
</UButton>
</div>
<div class="">
<div
v-for="(column, i) in localField.columns"
:key="i"
class="flex items-center space-x-2"
>
<text-input
v-model="localField.columns[i]"
wrapper-class="mb-1"
@update:model-value="updateField"
/>
<button @click="removeMatrixColumn(i)">
<Icon
name="heroicons:trash"
class="text-gray-300 w-4 h-4 mb-2"
/>
</button>
</div>
<UButton
size="xs"
color="gray"
icon="i-heroicons-plus"
@click="addMatrixColumn"
>
Add column
</UButton>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch, computed } from 'vue'
const props = defineProps({
modelValue: {
type: Object,
required: true
},
})
const emit = defineEmits(['update:modelValue'])
const localField = ref({ ...props.modelValue })
watch(() => props.modelValue, (newField) => {
localField.value = { ...newField }
}, { deep: true })
const selectionData = computed(() => {
return Object.fromEntries(localField.value.rows?.map(row => [row, '']))
})
function updateField() {
emit('update:modelValue', { ...localField.value })
}
function addMatrixRow() {
localField.value.rows.push(generateUniqueLabel(localField.value.rows, 'Row'))
localField.value.selection_data = selectionData.value
updateField()
}
function removeMatrixRow(index) {
localField.value.rows.splice(index, 1)
localField.value.selection_data = selectionData.value
updateField()
}
function addMatrixColumn() {
localField.value.columns.push(generateUniqueLabel(localField.value.columns, null))
localField.value.selection_data = selectionData.value
updateField()
}
function removeMatrixColumn(index) {
localField.value.columns.splice(index, 1)
localField.value.selection_data = selectionData.value
updateField()
}
function generateUniqueLabel(array, prefix = null) {
let uniqueNumber = 1 // Start checking from 1
let label = prefix ? `${prefix} ${uniqueNumber}` : uniqueNumber
while (array.includes(label)) {
uniqueNumber++ // Increment if the number is found in the array
label = prefix ? `${prefix} ${uniqueNumber}` : uniqueNumber
}
return label // Return the first unique number found
}
</script>

View File

@@ -0,0 +1,52 @@
<template>
<p class="font-semibold">Prefilled values</p>
<select-input
v-for="row in matrixData"
:key="row.label"
name="prefill"
class="mt-3"
:options="row.options"
:label="row.label"
v-model="selection[row.label]"
@update:model-value="onSelection"
/>
</template>
<script>
export default {
name: 'MatrixPrefilledValues',
props: {
field: {
type: Object,
required: false
},
form: {
type: Object,
required: false
}
},
data() {
return {
selection: {}
}
},
computed: {
matrixData() {
const options = this.field.columns || []
return (this.field.row || []).map(row => {
return {
label: row,
options: options?.map(option => ({ name: option, value: option }))
}
})
},
},
mounted() {
this.selection = this.field.prefill ?? this.field.selection_data ?? {}
},
methods: {
onSelection() {
this.field.prefill = this.selection
}
}
}
</script>