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

@@ -16,60 +16,45 @@
theme.default.input,
theme.default.borderRadius,
{
'mb-2': index !== options.length,
'!ring-red-500 !ring-2 !border-transparent': hasError,
'!cursor-not-allowed !bg-gray-200': disabled,
},
]"
>
<template
v-if="options && options.length"
>
<div
v-for="(option) in options"
:key="option[optionKey]"
:role="multiple?'checkbox':'radio'"
:aria-checked="isSelected(option[optionKey])"
:class="[
theme.FlatSelectInput.spacing.vertical,
theme.FlatSelectInput.fontSize,
theme.FlatSelectInput.option,
]"
@click="onSelect(option[optionKey])"
<template
v-if="options && options.length"
>
<template v-if="multiple">
<Icon
v-if="isSelected(option[optionKey])"
name="material-symbols:check-box"
class="text-inherit"
:color="color"
:class="[theme.FlatSelectInput.icon]"
/>
<Icon
v-else
name="material-symbols:check-box-outline-blank"
:class="[theme.FlatSelectInput.icon,theme.FlatSelectInput.unselectedIcon]"
/>
</template>
<template v-else>
<Icon
v-if="isSelected(option[optionKey])"
name="material-symbols:radio-button-checked-outline"
class="text-inherit"
:color="color"
:class="[theme.FlatSelectInput.icon]"
/>
<Icon
v-else
name="material-symbols:radio-button-unchecked"
:class="[theme.FlatSelectInput.icon,theme.FlatSelectInput.unselectedIcon]"
/>
</template>
<p class="flex-grow">
{{ option[displayKey] }}
</p>
</div>
</template>
<div
v-for="(option) in options"
:key="option[optionKey]"
:role="multiple?'checkbox':'radio'"
:aria-checked="isSelected(option[optionKey])"
:class="[
theme.FlatSelectInput.spacing.vertical,
theme.FlatSelectInput.fontSize,
theme.FlatSelectInput.option,
]"
@click="onSelect(option[optionKey])"
>
<template v-if="multiple">
<CheckboxIcon
:is-checked="isSelected(option[optionKey])"
:color="color"
:theme="theme"
/>
</template>
<template v-else>
<RadioButtonIcon
:is-checked="isSelected(option[optionKey])"
:color="color"
:theme="theme"
/>
</template>
<p class="flex-grow">
{{ option[displayKey] }}
</p>
</div>
</template>
<div
v-else
:class="[
@@ -96,13 +81,15 @@
<script>
import {inputProps, useFormInput} from "./useFormInput.js"
import InputWrapper from "./components/InputWrapper.vue"
import RadioButtonIcon from "./components/RadioButtonIcon.vue"
import CheckboxIcon from "./components/CheckboxIcon.vue"
/**
* Options: {name,value} objects
*/
export default {
name: "FlatSelectInput",
components: {InputWrapper},
components: {InputWrapper, RadioButtonIcon, CheckboxIcon},
props: {
...inputProps,
@@ -156,4 +143,4 @@ export default {
},
},
}
</script>
</script>

View File

@@ -0,0 +1,121 @@
<template>
<input-wrapper v-bind="inputWrapperProps">
<template #label>
<slot name="label" />
</template>
<div
class="border border-gray-300"
:class="[
theme.default.borderRadius,
{
'!ring-red-500 !ring-2 !border-transparent': hasError,
'!cursor-not-allowed !bg-gray-300': disabled,
},
]"
>
<table
class="w-full table-fixed overflow-hidden"
>
<thead class="">
<tr>
<th />
<td
v-for="column in columns"
:key="column"
class="border-l border-gray-300"
>
<div class="p-2 w-full flex items-center justify-center capitalize text-sm truncate">
{{ column }}
</div>
</td>
</tr>
</thead>
<tbody>
<tr
v-for="row, rowIndex in rows"
:key="rowIndex"
class="border-t border-gray-300"
>
<td>
<div class="w-full flex-grow p-2 text-sm truncate">
{{ row }}
</div>
</td>
<td
v-for="column in columns"
:key="row + column"
class="border-l border-gray-300 hover:!bg-gray-100 dark:hover:!bg-gray-800"
>
<div
v-if="compVal"
class="w-full flex items-center justify-center"
role="radio"
:aria-checked="compVal[row] === column"
:class="[
theme.FlatSelectInput.spacing.vertical,
theme.FlatSelectInput.fontSize,
theme.FlatSelectInput.option,
]"
@click="onSelect(row, column)"
>
<RadioButtonIcon
:key="row+column"
:is-checked="compVal[row] === column"
:color="color"
:theme="theme"
/>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<template #help>
<slot name="help" />
</template>
<template #error>
<slot name="error" />
</template>
</input-wrapper>
</template>
<script>
import {inputProps, useFormInput} from "./useFormInput.js"
import InputWrapper from "./components/InputWrapper.vue"
import RadioButtonIcon from "./components/RadioButtonIcon.vue"
export default {
name: "MatrixInput",
components: {InputWrapper, RadioButtonIcon},
props: {
...inputProps,
rows: {type: Array, required: true},
columns: {type: Array, required: true},
},
setup(props, context) {
return {
...useFormInput(props, context),
}
},
data() {
return {
}
},
computed: {},
beforeMount() {
if (!this.compVal || typeof this.compVal !== 'object') {
this.compVal = {}
}
},
methods: {
onSelect(row, column) {
if (this.compVal[row] === column && !this.required) {
this.compVal[row] = null
} else {
this.compVal[row] = column
}
},
},
}
</script>

View File

@@ -0,0 +1,27 @@
<template>
<Icon
:name="isChecked ? 'material-symbols:check-box' : 'material-symbols:check-box-outline-blank'"
:class="[
theme.FlatSelectInput.icon,
isChecked ? '' : theme.FlatSelectInput.unselectedIcon,
]"
:color="isChecked ? color : undefined"
/>
</template>
<script setup>
const props = defineProps({
isChecked: {
type: Boolean,
required: true
},
color: {
type: String,
default: ''
},
theme: {
type: Object,
required: true
}
})
</script>

View File

@@ -0,0 +1,29 @@
<template>
<Icon
:name="isChecked ? 'ic:round-radio-button-checked' : 'ic:round-radio-button-unchecked'"
:class="[
theme.FlatSelectInput.icon,
isChecked ? '' : theme.FlatSelectInput.unselectedIcon,
]"
:color="isChecked ? color : undefined"
/>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
isChecked: {
type: Boolean,
required: true
},
color: {
type: String,
default: ''
},
theme: {
type: Object,
required: true
}
})
</script>

View File

@@ -448,6 +448,8 @@ export default {
formData[field.id] = urlPrefill.getAll(field.id + '[]')
} else if (field.type === 'date' && field.prefill_today === true) { // For Prefill with 'today'
formData[field.id] = new Date().toISOString()
} else if (field.type === 'matrix') {
formData[field.id] = {...field.prefill}
} else { // Default prefill if any
formData[field.id] = field.prefill
}

View File

@@ -6,7 +6,7 @@
:class="[
getFieldWidthClasses(field),
{
'group/nffield hover:bg-gray-100/50 relative hover:z-10 transition-colors hover:border-gray-200 dark:hover:bg-gray-900 border-dashed border border-transparent box-border dark:hover:border-blue-900 rounded-md':adminPreview,
'group/nffield hover:bg-gray-100/50 relative hover:z-10 transition-colors hover:border-gray-200 dark:hover:!bg-gray-900 border-dashed border border-transparent box-border dark:hover:border-blue-900 rounded-md':adminPreview,
'bg-blue-50 hover:!bg-blue-50 dark:bg-gray-800 rounded-md': beingEdited,
}]"
>
@@ -95,8 +95,12 @@
>
</div>
</template>
<div class="hidden group-hover/nffield:flex translate-x-full absolute right-0 top-0 h-full w-5 flex-col justify-center pl-1 pt-3">
<div class="flex items-center bg-gray-100 dark:bg-gray-800 border rounded-md h-12 text-gray-500 dark:text-gray-400 dark:border-gray-500 cursor-grab handle">
<div
class="hidden group-hover/nffield:flex translate-x-full absolute right-0 top-0 h-full w-5 flex-col justify-center pl-1 pt-3"
>
<div
class="flex items-center bg-gray-100 dark:bg-gray-800 border rounded-md h-12 text-gray-500 dark:text-gray-400 dark:border-gray-500 cursor-grab handle"
>
<Icon
name="clarity:drag-handle-line"
class="h-6 w-6 -ml-1 block shrink-0"
@@ -200,7 +204,8 @@ export default {
checkbox: 'CheckboxInput',
url: 'TextInput',
email: 'TextInput',
phone_number: 'TextInput'
phone_number: 'TextInput',
matrix: 'MatrixInput'
}[field.type]
},
isPublicFormPage() {
@@ -299,6 +304,11 @@ export default {
isDark: this.darkMode
}
if (field.type === 'matrix') {
inputProperties.rows = field.rows
inputProperties.columns = field.columns
}
if (['select', 'multi_select'].includes(field.type)) {
inputProperties.options = (_has(field, field.type))
? field[field.type].options.map(option => {

View File

@@ -204,6 +204,11 @@ export default {
title: "Signature Input",
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125" />',
},
{
name: "matrix",
title: "Matrix Input",
icon: '<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3.375 19.5h17.25m-17.25 0a1.125 1.125 0 0 1-1.125-1.125M3.375 19.5h7.5c.621 0 1.125-.504 1.125-1.125m-9.75 0V5.625m0 12.75v-1.5c0-.621.504-1.125 1.125-1.125m18.375 2.625V5.625m0 12.75c0 .621-.504 1.125-1.125 1.125m1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125m0 3.75h-7.5A1.125 1.125 0 0 1 12 18.375m9.75-12.75c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125m19.5 0v1.5c0 .621-.504 1.125-1.125 1.125M2.25 5.625v1.5c0 .621.504 1.125 1.125 1.125m0 0h17.25m-17.25 0h7.5c.621 0 1.125.504 1.125 1.125M3.375 8.25c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125m17.25-3.75h-7.5c-.621 0-1.125.504-1.125 1.125m8.625-1.125c.621 0 1.125.504 1.125 1.125v1.5c0 .621-.504 1.125-1.125 1.125m-17.25 0h7.5m-7.5 0c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125M12 10.875v-1.5m0 1.5c0 .621-.504 1.125-1.125 1.125M12 10.875c0 .621.504 1.125 1.125 1.125m-2.25 0c.621 0 1.125.504 1.125 1.125M13.125 12h7.5m-7.5 0c-.621 0-1.125.504-1.125 1.125M20.625 12c.621 0 1.125.504 1.125 1.125v1.5c0 .621-.504 1.125-1.125 1.125m-17.25 0h7.5M12 14.625v-1.5m0 1.5c0 .621-.504 1.125-1.125 1.125M12 14.625c0 .621.504 1.125 1.125 1.125m-2.25 0c.621 0 1.125.504 1.125 1.125m0 1.5v-1.5m0 0c0-.621.504-1.125 1.125-1.125m0 0h7.5"></path>',
},
],
layoutBlocks: [
{

View File

@@ -58,6 +58,7 @@ export default {
url: "TextInput",
email: "TextInput",
phone_number: "TextInput",
matrix: "MatrixInput",
},
}
},
@@ -93,6 +94,10 @@ export default {
} else if (this.property.type === "checkbox") {
componentData.label = this.property.name
}
else if (this.property.type === "matrix"){
componentData.rows = this.property.rows
componentData.columns = this.property.columns
}
return componentData
},
@@ -184,8 +189,9 @@ export default {
) {
this.content.value = {}
} else if (
typeof this.content.value === "boolean" ||
typeof this.content.value === "object"
this.property.type !== 'matrix' &&
(typeof this.content.value === 'boolean' ||
typeof this.content.value === 'object')
) {
this.content.value = null
}

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>

View File

@@ -140,6 +140,7 @@
import OpenText from "./components/OpenText.vue"
import OpenUrl from "./components/OpenUrl.vue"
import OpenSelect from "./components/OpenSelect.vue"
import OpenMatrix from "./components/OpenMatrix.vue"
import OpenDate from "./components/OpenDate.vue"
import OpenFile from "./components/OpenFile.vue"
import OpenCheckbox from "./components/OpenCheckbox.vue"
@@ -197,6 +198,7 @@ export default {
scale: shallowRef(OpenText),
slider: shallowRef(OpenText),
select: shallowRef(OpenSelect),
matrix: shallowRef(OpenMatrix),
multi_select: shallowRef(OpenSelect),
date: shallowRef(OpenDate),
files: shallowRef(OpenFile),

View File

@@ -0,0 +1,50 @@
<template>
<span
v-if="value"
class="-mb-2"
>
<template v-if="matrixData">
<div
v-for="(data) in matrixData"
:key="data.label+data.value"
class="mr-2 mb-1 text-gray-700 bg-gray-100 dark:text-gray-300 rounded-md flex px-2 text-sm w-max"
>
<span class="py-0.5 pr-1 border-r border-gray-300">{{ data.label }}</span>
<span class="py-0.5 pl-1">{{ data.value }}</span>
</div>
</template>
<open-tag
v-else
:opt="value"
/>
</span>
</template>
<script>
import OpenTag from "./OpenTag.vue"
export default {
components: { OpenTag },
props: {
value: {
type: Object,
},
},
data() {
return {}
},
computed: {
matrixData() {
if (typeof this.value === "object" && this.value !== null) {
return Object.entries(this.value).map(([label, value]) => {
return { label, value }
})
}
return null
},
},
}
</script>

View File

@@ -231,6 +231,34 @@
}
}
},
"matrix": {
"comparators": {
"equals": {
"expected_type": "object",
"format": {
"type": "object"
}
},
"does_not_equal": {
"expected_type": "object",
"format": {
"type":"object"
}
},
"contains": {
"expected_type": "object",
"format": {
"type":"object"
}
},
"does_not_contain": {
"expected_type": "object",
"format": {
"type":"object"
}
}
}
},
"number": {
"comparators": {
"equals": {

View File

@@ -1,3 +1,5 @@
import { default as _isEqual } from "lodash/isEqual"
export function conditionsMet(conditions, formData) {
if (conditions === undefined || conditions === null) {
return false
@@ -59,6 +61,8 @@ function propertyConditionMet(propertyCondition, value) {
return multiSelectConditionMet(propertyCondition, value)
case "files":
return filesConditionMet(propertyCondition, value)
case "matrix":
return matrixConditionMet(propertyCondition, value)
}
return false
}
@@ -67,6 +71,24 @@ function checkEquals(condition, fieldValue) {
return condition.value === fieldValue
}
function checkObjectEquals(condition, fieldValue) {
return _isEqual(condition.value, fieldValue)
}
function checkMatrixContains(condition, fieldValue)
{
if (typeof fieldValue === "undefined" || typeof fieldValue !== "object") {
return false
}
const conditionValue = condition.value
for (const key in conditionValue) {
if(conditionValue[key] == fieldValue[key]){
return true
}
}
return false
}
function checkContains(condition, fieldValue) {
return fieldValue ? fieldValue.includes(condition.value) : false
}
@@ -148,7 +170,7 @@ function checkPastWeek(condition, fieldValue) {
return (
fieldDate <= today &&
fieldDate >=
new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7)
new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7)
)
}
@@ -159,7 +181,7 @@ function checkPastMonth(condition, fieldValue) {
return (
fieldDate <= today &&
fieldDate >=
new Date(today.getFullYear(), today.getMonth() - 1, today.getDate())
new Date(today.getFullYear(), today.getMonth() - 1, today.getDate())
)
}
@@ -170,7 +192,7 @@ function checkPastYear(condition, fieldValue) {
return (
fieldDate <= today &&
fieldDate >=
new Date(today.getFullYear() - 1, today.getMonth(), today.getDate())
new Date(today.getFullYear() - 1, today.getMonth(), today.getDate())
)
}
@@ -181,7 +203,7 @@ function checkNextWeek(condition, fieldValue) {
return (
fieldDate >= today &&
fieldDate <=
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7)
new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7)
)
}
@@ -192,7 +214,7 @@ function checkNextMonth(condition, fieldValue) {
return (
fieldDate >= today &&
fieldDate <=
new Date(today.getFullYear(), today.getMonth() + 1, today.getDate())
new Date(today.getFullYear(), today.getMonth() + 1, today.getDate())
)
}
@@ -203,7 +225,7 @@ function checkNextYear(condition, fieldValue) {
return (
fieldDate >= today &&
fieldDate <=
new Date(today.getFullYear() + 1, today.getMonth(), today.getDate())
new Date(today.getFullYear() + 1, today.getMonth(), today.getDate())
)
}
@@ -371,3 +393,17 @@ function filesConditionMet(propertyCondition, value) {
}
return false
}
function matrixConditionMet(propertyCondition, value) {
switch (propertyCondition.operator) {
case "equals":
return checkObjectEquals(propertyCondition, value)
case "does_not_equal":
return !checkObjectEquals(propertyCondition, value)
case "contains":
return checkMatrixContains(propertyCondition, value)
case "does_not_contain":
return !checkMatrixContains(propertyCondition, value)
}
return false
}

View File

@@ -107,7 +107,7 @@ class FormPropertyLogicRule {
(type === "string" && typeof value !== "string") ||
(type === "boolean" && typeof value !== "boolean") ||
(type === "number" && typeof value !== "number") ||
(type === "object" && !Array.isArray(value))
(type === "object" && !(Array.isArray(value) || typeof value === 'object'))
) {
return false
}

View File

@@ -17,6 +17,7 @@ const defaultBlockNames = {
multi_select: "Multi Select",
files: "Files",
signature: "Signature",
matrix: "Matrix",
"nf-text": "Text Block",
"nf-page-break": "Page Break",
"nf-divider": "Divider",