feat: Add TheTable form element
This commit is contained in:
@@ -166,6 +166,8 @@ function getResolvedComponent(formElement: FormElementDto) {
|
||||
return resolveComponent('TheEditor')
|
||||
case 'DATE':
|
||||
return resolveComponent('TheDate')
|
||||
case 'TABLE':
|
||||
return resolveComponent('TheTable')
|
||||
default:
|
||||
return resolveComponent('Unimplemented')
|
||||
}
|
||||
|
||||
170
legalconsenthub/app/components/formelements/TheTable.vue
Normal file
170
legalconsenthub/app/components/formelements/TheTable.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<UTable :data="tableData" :columns="tableColumns" class="w-full">
|
||||
<template v-for="col in dataColumns" :key="col.key" #[`${col.key}-cell`]="slotProps">
|
||||
<UInput
|
||||
:model-value="getCellValue(slotProps.row as TableRow<TableRowData>, col.key)"
|
||||
:disabled="disabled"
|
||||
class="w-full min-w-32"
|
||||
@update:model-value="
|
||||
(val: string | number) => updateCell((slotProps.row as TableRow<TableRowData>).index, col.key, String(val))
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template #actions-cell="{ row }">
|
||||
<UButton
|
||||
v-if="!disabled"
|
||||
icon="i-lucide-trash-2"
|
||||
color="error"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
:aria-label="$t('applicationForms.formElements.table.removeRow')"
|
||||
@click="removeRow(row.index)"
|
||||
/>
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
<div v-if="tableData.length === 0" class="text-center text-dimmed py-4">
|
||||
{{ $t('applicationForms.formElements.table.noData') }}
|
||||
</div>
|
||||
|
||||
<UButton v-if="!disabled" variant="outline" size="sm" leading-icon="i-lucide-plus" @click="addRow">
|
||||
{{ $t('applicationForms.formElements.table.addRow') }}
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormOptionDto } from '~~/.api-client'
|
||||
import type { TableColumn, TableRow } from '@nuxt/ui'
|
||||
|
||||
const props = defineProps<{
|
||||
formOptions: FormOptionDto[]
|
||||
disabled?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||
}>()
|
||||
|
||||
type TableRowData = Record<string, string>
|
||||
|
||||
interface DataColumn {
|
||||
key: string
|
||||
colIndex: number
|
||||
}
|
||||
|
||||
const dataColumns = computed<DataColumn[]>(() =>
|
||||
props.formOptions.map((_, index) => ({
|
||||
key: `col_${index}`,
|
||||
colIndex: index
|
||||
}))
|
||||
)
|
||||
|
||||
const tableColumns = computed<TableColumn<TableRowData>[]>(() => {
|
||||
const columns: TableColumn<TableRowData>[] = props.formOptions.map((option, index) => ({
|
||||
accessorKey: `col_${index}`,
|
||||
header: option.label || ''
|
||||
}))
|
||||
|
||||
if (!props.disabled) {
|
||||
columns.push({
|
||||
id: 'actions',
|
||||
header: ''
|
||||
})
|
||||
}
|
||||
|
||||
return columns
|
||||
})
|
||||
|
||||
const tableData = computed<TableRowData[]>(() => {
|
||||
if (props.formOptions.length === 0) return []
|
||||
|
||||
const columnData: string[][] = props.formOptions.map((option) => {
|
||||
try {
|
||||
const parsed = JSON.parse(option.value || '[]')
|
||||
return Array.isArray(parsed) ? parsed : []
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
const rowCount = Math.max(...columnData.map((col) => col.length), 0)
|
||||
|
||||
const rows: TableRowData[] = []
|
||||
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
||||
const row: TableRowData = {}
|
||||
props.formOptions.forEach((_, colIndex) => {
|
||||
row[`col_${colIndex}`] = columnData[colIndex]?.[rowIndex] ?? ''
|
||||
})
|
||||
rows.push(row)
|
||||
}
|
||||
|
||||
return rows
|
||||
})
|
||||
|
||||
function getCellValue(row: TableRow<TableRowData>, columnKey: string): string {
|
||||
return row.original[columnKey] ?? ''
|
||||
}
|
||||
|
||||
function updateCell(rowIndex: number, columnKey: string, value: string) {
|
||||
const colIndex = parseInt(columnKey.replace('col_', ''), 10)
|
||||
|
||||
const updatedOptions = props.formOptions.map((option, index) => {
|
||||
if (index !== colIndex) return option
|
||||
|
||||
let columnValues: string[]
|
||||
try {
|
||||
columnValues = JSON.parse(option.value || '[]')
|
||||
if (!Array.isArray(columnValues)) columnValues = []
|
||||
} catch {
|
||||
columnValues = []
|
||||
}
|
||||
|
||||
while (columnValues.length <= rowIndex) {
|
||||
columnValues.push('')
|
||||
}
|
||||
columnValues[rowIndex] = value
|
||||
|
||||
return { ...option, value: JSON.stringify(columnValues) }
|
||||
})
|
||||
|
||||
emit('update:formOptions', updatedOptions)
|
||||
}
|
||||
|
||||
function addRow() {
|
||||
const updatedOptions = props.formOptions.map((option) => {
|
||||
let columnValues: string[]
|
||||
try {
|
||||
columnValues = JSON.parse(option.value || '[]')
|
||||
if (!Array.isArray(columnValues)) columnValues = []
|
||||
} catch {
|
||||
columnValues = []
|
||||
}
|
||||
|
||||
columnValues.push('')
|
||||
|
||||
return { ...option, value: JSON.stringify(columnValues) }
|
||||
})
|
||||
|
||||
emit('update:formOptions', updatedOptions)
|
||||
}
|
||||
|
||||
function removeRow(rowIndex: number) {
|
||||
const updatedOptions = props.formOptions.map((option) => {
|
||||
let columnValues: string[]
|
||||
try {
|
||||
columnValues = JSON.parse(option.value || '[]')
|
||||
if (!Array.isArray(columnValues)) columnValues = []
|
||||
} catch {
|
||||
columnValues = []
|
||||
}
|
||||
|
||||
columnValues.splice(rowIndex, 1)
|
||||
|
||||
return { ...option, value: JSON.stringify(columnValues) }
|
||||
})
|
||||
|
||||
emit('update:formOptions', updatedOptions)
|
||||
}
|
||||
</script>
|
||||
@@ -23,7 +23,13 @@
|
||||
"title": "Titel",
|
||||
"text": "Text",
|
||||
"unimplemented": "Element nicht implementiert:",
|
||||
"richTextPlaceholder": "Schreiben Sie hier Ihre Ergänzungen..."
|
||||
"richTextPlaceholder": "Schreiben Sie hier Ihre Ergänzungen...",
|
||||
"table": {
|
||||
"addRow": "Zeile hinzufügen",
|
||||
"removeRow": "Zeile entfernen",
|
||||
"emptyValue": "Keine Eingabe",
|
||||
"noData": "Keine Daten vorhanden"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"draft": "Entwurf",
|
||||
|
||||
@@ -23,7 +23,13 @@
|
||||
"title": "Title",
|
||||
"text": "Text",
|
||||
"unimplemented": "Element unimplemented:",
|
||||
"richTextPlaceholder": "Write your additions here..."
|
||||
"richTextPlaceholder": "Write your additions here...",
|
||||
"table": {
|
||||
"addRow": "Add row",
|
||||
"removeRow": "Remove row",
|
||||
"emptyValue": "No input",
|
||||
"noData": "No data available"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"draft": "Draft",
|
||||
|
||||
Reference in New Issue
Block a user