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:
@@ -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>
|
||||
121
client/components/forms/MatrixInput.vue
Normal file
121
client/components/forms/MatrixInput.vue
Normal 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>
|
||||
27
client/components/forms/components/CheckboxIcon.vue
Normal file
27
client/components/forms/components/CheckboxIcon.vue
Normal 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>
|
||||
29
client/components/forms/components/RadioButtonIcon.vue
Normal file
29
client/components/forms/components/RadioButtonIcon.vue
Normal 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>
|
||||
Reference in New Issue
Block a user