Enhance form column settings and table display

- Add "Show all" and "Hide all" buttons in column settings modal
- Improve column width handling and default width management
- Update OpenSelect and OpenText components to support more flexible prop types
- Add z-index to table header for better visual hierarchy
- Refactor column width property from `cell_width` to `width`
This commit is contained in:
Julien Nahum
2025-02-17 11:27:58 +01:00
parent 0d6bd1bfde
commit 55b0f57741
8 changed files with 203 additions and 38 deletions

View File

@@ -1,5 +1,8 @@
<template>
<UButtonGroup size="xs" orientation="horizontal">
<UButtonGroup
size="xs"
orientation="horizontal"
>
<UButton
v-track.delete_record_click
size="sm"

View File

@@ -2,10 +2,14 @@
<modal
compact-header
:show="show"
v-bind="$attrs"
@close="$emit('close')"
>
<template #icon>
<Icon name="heroicons:adjustments-horizontal" class="w-8 h-8" />
<Icon
name="heroicons:adjustments-horizontal"
class="w-8 h-8"
/>
</template>
<template #title>
Manage Columns
@@ -21,7 +25,24 @@
class="font-semibold mb-2"
:class="{ 'mt-4': sectionIndex > 0 }"
>
{{ section.title }}
<div class="flex items-center justify-between">
<span>{{ section.title }}</span>
<div class="flex items-center gap-2 text-xs text-gray-500">
<button
class="hover:text-gray-700"
@click="toggleAllColumns(section.fields, true)"
>
Show all
</button>
<span class="text-gray-300">|</span>
<button
class="hover:text-gray-700"
@click="toggleAllColumns(section.fields, false)"
>
Hide all
</button>
</div>
</div>
</h4>
<div class="border border-gray-300">
<div class="grid grid-cols-[1fr,auto,auto] gap-4 px-4 py-2 bg-gray-50 border-b border-gray-300">
@@ -46,8 +67,8 @@
</p>
<div class="flex justify-center w-20">
<ToggleSwitchInput
v-model="displayColumns[field.id]"
wrapper-class="mb-0"
v-model="computedDisplayColumns[field.id]"
wrapper-class="my-0"
label=""
:name="`display-${field.id}`"
@update:model-value="onChangeDisplayColumns"
@@ -55,8 +76,8 @@
</div>
<div class="flex justify-center w-20">
<ToggleSwitchInput
v-model="wrapColumns[field.id]"
wrapper-class="mb-0"
v-model="computedWrapColumns[field.id]"
wrapper-class="my-0"
label=""
:name="`wrap-${field.id}`"
/>
@@ -85,6 +106,14 @@ const props = defineProps({
columns: {
type: Array,
default: () => []
},
displayColumns: {
type: Object,
default: () => ({})
},
wrapColumns: {
type: Object,
default: () => ({})
}
})
@@ -122,16 +151,24 @@ const sections = computed(() => [
])
// Column preferences storage
const storageKey = computed(() => `column-preferences-formid-${props.form.id}`)
const columnPreferences = useStorage(
computed(() => props.form ? `column-preferences-formid-${props.form.id}` : null),
storageKey.value,
{
display: {},
wrap: {},
widths: {}
},
localStorage,
{
onError: (error) => {
console.error('Storage error:', error)
}
}
)
const displayColumns = computed({
const computedDisplayColumns = computed({
get: () => columnPreferences.value.display,
set: (val) => {
columnPreferences.value.display = val
@@ -139,7 +176,7 @@ const displayColumns = computed({
}
})
const wrapColumns = computed({
const computedWrapColumns = computed({
get: () => columnPreferences.value.wrap,
set: (val) => {
columnPreferences.value.wrap = val
@@ -164,12 +201,18 @@ function preserveColumnWidths(newColumns, existingColumns = []) {
// Then fallback to form properties
const existing = existingColumns?.find(e => e.id === col.id)
const width = storedWidth || currentCol?.cell_width || currentCol?.width || existing?.cell_width || existing?.width || col.width || 150
// Convert any non-numeric width to default
const defaultWidth = 250
let width = storedWidth || currentCol?.width || existing?.width || defaultWidth
// If width is not a number or is 'full', use default width
if (typeof width !== 'number' || isNaN(width)) {
width = defaultWidth
}
return {
...col,
width,
cell_width: width
width
}
})
}
@@ -187,8 +230,8 @@ watch(() => props.columns, (newColumns) => {
const widths = {}
newColumns.forEach(col => {
if (col.cell_width) {
widths[col.id] = col.cell_width
if (col.width) {
widths[col.id] = col.width
}
})
@@ -199,42 +242,63 @@ watch(() => props.columns, (newColumns) => {
watch(() => props.form, (newForm) => {
if (!newForm) return
const properties = newForm.properties || []
const properties = candidatesProperties.value
const storedPrefs = columnPreferences.value
const removedProperties = newForm.removed_properties || []
// Initialize display columns if not set
if (!Object.keys(storedPrefs.display).length) {
// Set all non-removed properties to visible by default
properties.forEach((field) => {
storedPrefs.display[field.id] = true
})
// Also handle removed properties
removedProperties.forEach((field) => {
storedPrefs.display[field.id] = false
})
}
// Initialize wrap columns if not set
if (!Object.keys(storedPrefs.wrap).length) {
properties.forEach((field) => {
[...properties, ...removedProperties].forEach((field) => {
storedPrefs.wrap[field.id] = false
})
}
// Initialize widths if not set
if (!Object.keys(storedPrefs.widths).length) {
[...properties, ...removedProperties].forEach((field) => {
const defaultWidth = 150
storedPrefs.widths[field.id] = field.width || defaultWidth
})
}
// Emit initial values
emit('update:displayColumns', storedPrefs.display)
emit('update:wrapColumns', storedPrefs.wrap)
// Emit initial columns (all visible by default)
const initialColumns = clonedeep(candidatesProperties.value)
.concat(props.form?.removed_properties || [])
.filter((field) => storedPrefs.display[field.id] !== false) // Show all columns by default unless explicitly hidden
// Emit initial columns (all non-removed visible by default)
const initialColumns = clonedeep(properties)
.concat(removedProperties)
.filter((field) => storedPrefs.display[field.id] !== false)
// Preserve any existing column widths
const columnsWithWidths = preserveColumnWidths(initialColumns, props.form.properties)
const columnsWithWidths = preserveColumnWidths(initialColumns, properties)
emit('update:columns', columnsWithWidths)
}, { immediate: true })
function toggleAllColumns(fields, show) {
fields.forEach((field) => {
computedDisplayColumns.value[field.id] = show
})
onChangeDisplayColumns()
}
function onChangeDisplayColumns() {
if (!import.meta.client) return
const properties = clonedeep(candidatesProperties.value)
.concat(props.form?.removed_properties || [])
.filter((field) => displayColumns.value[field.id] === true)
.filter((field) => computedDisplayColumns.value[field.id] === true)
// Preserve existing column widths when toggling visibility
const columnsWithWidths = preserveColumnWidths(properties, props.form.properties)

View File

@@ -10,11 +10,14 @@
<!-- Settings Modal -->
<form-columns-settings-modal
v-if="form"
:show="showColumnsModal"
:form="form"
:columns="properties"
v-model:display-columns="displayColumns"
v-model:wrap-columns="wrapColumns"
:display-columns="displayColumns"
:wrap-columns="wrapColumns"
@update:display-columns="displayColumns = $event"
@update:wrap-columns="wrapColumns = $event"
@close="showColumnsModal = false"
@update:columns="onColumnUpdated"
/>
@@ -225,6 +228,7 @@ export default {
},
onColumnUpdated(columns) {
this.properties = columns
this.dataChanged()
},
onUpdateRecord(submission) {
this.recordStore.save(submission)

View File

@@ -7,7 +7,7 @@
<thead
:id="'table-header-' + tableHash"
ref="header"
class="n-table-head top-0"
class="n-table-head top-0 z-10"
:class="{ absolute: data.length !== 0 }"
style="will-change: transform; transform: translate3d(0px, 0px, 0px)"
>
@@ -18,7 +18,7 @@
:key="col.id"
scope="col"
:allow-resize="allowResize"
:width="col.cell_width ? col.cell_width + 'px' : 'auto'"
:width="col.width ? col.width + 'px' : '150px'"
class="n-table-cell p-0 relative"
@resize-width="resizeCol(col, $event)"
>
@@ -68,7 +68,7 @@
<td
v-for="(col, colIndex) in columns"
:key="col.id"
:style="{ width: col.cell_width + 'px' }"
:style="{ width: col.width ? col.width + 'px' : '150px' }"
class="n-table-cell border-gray-100 dark:border-gray-900 text-sm p-2 overflow-hidden"
:class="[
{
@@ -179,7 +179,8 @@ export default {
type: Boolean,
},
scrollParent: {
type: [Boolean]
type: [Boolean, Object],
default: null
},
},
emits: ["updated", "deleted", "resize", "update-columns"],
@@ -294,7 +295,7 @@ export default {
if (this.internalColumns) {
this.$nextTick(() => {
this.internalColumns.forEach((col) => {
if (!_has(col, "cell_width")) {
if (!_has(col, "width")) {
if (
this.allowResize &&
this.internalColumns.length &&
@@ -315,7 +316,7 @@ export default {
resizeCol(col, width) {
if (!this.form) return
const index = this.internalColumns.findIndex((c) => c.id === col.id)
this.internalColumns[index].cell_width = width
this.internalColumns[index].width = width
this.setColumns(this.internalColumns)
this.$nextTick(() => {
this.$emit("resize")

View File

@@ -24,8 +24,14 @@ export default {
components: { OpenTag },
props: {
value: {
type: Object,
type: [String, Object, Array],
required: false,
default: null
},
property: {
type: Object,
required: true
}
},
data() {
@@ -34,10 +40,7 @@ export default {
computed: {
valueIsObject() {
if (typeof this.value === "object" && this.value !== null) {
return true
}
return false
return Array.isArray(this.value) || (typeof this.value === "object" && this.value !== null)
},
},
}

View File

@@ -7,8 +7,9 @@ export default {
components: {},
props: {
value: {
type: String,
required: true,
type: [String, Number],
required: false,
default: null
},
},
}