feat(12.2-04): edit related records in modals and defer relation work on new records
- RelationManager renders create/link/delete/unlink in declared order with one primary, opens the child modal (update or view form) or the pivot modal on row click, and deletes selected children behind a busy confirm - RelationChildModal creates and edits children with its own session key (X-Child-Session-Key) so uploads and dates work inside it - RelationPivotModal edits link details; the picker links one record with its pivot values when the relation has a pivot form - deferrable managers render on the create screen with owner id 0, the form's X-Session-Key and the pending note, and mark the form dirty - the registry resolves RelationManager lazily (child forms close an import cycle); DataTable gains openable rows and a trailing cell - relation lang keys in en and pl; rebuilt boardwalk dist
This commit is contained in:
@@ -13,7 +13,10 @@ import CellValue from './CellValue.vue'
|
||||
// (asc -> desc -> none) with aria-sort, row links to the record route, eight
|
||||
// skeleton rows while loading and an `empty` slot for the empty states.
|
||||
// The relation variant (design screen 5) has 56px rows, an initials avatar
|
||||
// before the first column's text and the remaining columns muted.
|
||||
// before the first column's text and the remaining columns muted. Phase
|
||||
// 12.2: with `openable` its rows open (the first cell's text is a button and
|
||||
// a row click outside other controls does the same), and `trailing` adds a
|
||||
// 56px last cell filled by the `trailing` slot.
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -26,6 +29,10 @@ const props = withDefaults(
|
||||
sortable?: boolean
|
||||
sort?: { column: string; dir: 'asc' | 'desc' } | null
|
||||
variant?: 'list' | 'relation'
|
||||
/** Rows open on click (relation variant): emits `open`. */
|
||||
openable?: boolean
|
||||
/** Adds a trailing 56px cell per row, filled by the `trailing` slot. */
|
||||
trailing?: boolean
|
||||
}>(),
|
||||
{
|
||||
loading: false,
|
||||
@@ -35,9 +42,15 @@ const props = withDefaults(
|
||||
sortable: false,
|
||||
sort: null,
|
||||
variant: 'list',
|
||||
openable: false,
|
||||
trailing: false,
|
||||
},
|
||||
)
|
||||
const emit = defineEmits<{ 'update:selected': [ids: RowId[]]; sort: [column: string] }>()
|
||||
const emit = defineEmits<{
|
||||
'update:selected': [ids: RowId[]]
|
||||
sort: [column: string]
|
||||
open: [row: AdminRecord, trigger: HTMLElement | null]
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@@ -60,6 +73,11 @@ function openRow(event: MouseEvent, row: AdminRecord): void {
|
||||
if (target?.closest('a, button, input, label, select')) {
|
||||
return
|
||||
}
|
||||
if (props.openable) {
|
||||
const opener = (event.currentTarget as HTMLElement | null)?.querySelector<HTMLElement>('[data-row-open]') ?? null
|
||||
emit('open', row, opener)
|
||||
return
|
||||
}
|
||||
const to = linkOf(row)
|
||||
if (to) {
|
||||
void router.push(to)
|
||||
@@ -135,7 +153,13 @@ function cellClass(column: ListColumn, columnIndex: number): string {
|
||||
return relation.value || column.relation ? 'text-muted' : ''
|
||||
}
|
||||
|
||||
const span = computed(() => Math.max(props.columns.length + (props.selectable ? 1 : 0), 1))
|
||||
const span = computed(() =>
|
||||
Math.max(props.columns.length + (props.selectable ? 1 : 0) + (props.trailing ? 1 : 0), 1),
|
||||
)
|
||||
|
||||
function openFrom(event: MouseEvent, row: AdminRecord): void {
|
||||
emit('open', row, event.currentTarget as HTMLElement | null)
|
||||
}
|
||||
|
||||
// Varied skeleton bar widths, as in the design.
|
||||
const widths = ['w-3/5', 'w-2/5', 'w-1/2', 'w-3/4', 'w-1/3', 'w-2/3', 'w-1/2', 'w-2/5']
|
||||
@@ -182,6 +206,7 @@ const widths = ['w-3/5', 'w-2/5', 'w-1/2', 'w-3/4', 'w-1/3', 'w-2/3', 'w-1/2', '
|
||||
</button>
|
||||
<template v-else>{{ column.label }}</template>
|
||||
</th>
|
||||
<td v-if="trailing" class="w-[56px] pr-5" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody :aria-busy="loading ? 'true' : undefined">
|
||||
@@ -193,6 +218,7 @@ const widths = ['w-3/5', 'w-2/5', 'w-1/2', 'w-3/4', 'w-1/3', 'w-2/3', 'w-1/2', '
|
||||
<td v-for="column in columns" :key="column.key" class="px-3.5 first:pl-5 last:pr-5">
|
||||
<span :class="widths[(n - 1) % widths.length]" class="block h-3 rounded-[6px] bg-skel" />
|
||||
</td>
|
||||
<td v-if="trailing" class="pr-5" />
|
||||
</tr>
|
||||
</template>
|
||||
<tr v-else-if="rows.length === 0">
|
||||
@@ -208,8 +234,8 @@ const widths = ['w-3/5', 'w-2/5', 'w-1/2', 'w-3/4', 'w-1/3', 'w-2/3', 'w-1/2', '
|
||||
:key="rowKey(row, index)"
|
||||
:class="[
|
||||
rowHeight,
|
||||
isSelected(row) ? 'bg-sel' : linkOf(row) ? 'hover:bg-hover' : '',
|
||||
linkOf(row) ? 'cursor-pointer' : '',
|
||||
isSelected(row) ? 'bg-sel' : linkOf(row) || openable ? 'hover:bg-hover' : '',
|
||||
linkOf(row) || openable ? 'cursor-pointer' : '',
|
||||
]"
|
||||
:aria-selected="selectable ? (isSelected(row) ? 'true' : 'false') : undefined"
|
||||
class="border-b border-border transition-colors duration-150 ease-out"
|
||||
@@ -241,13 +267,25 @@ const widths = ['w-3/5', 'w-2/5', 'w-1/2', 'w-3/4', 'w-1/3', 'w-2/3', 'w-1/2', '
|
||||
aria-hidden="true"
|
||||
>{{ initials(row[column.key]) }}</span
|
||||
>
|
||||
<CellValue :column="column" :value="row[column.key]" />
|
||||
<button
|
||||
v-if="openable"
|
||||
type="button"
|
||||
data-row-open
|
||||
class="min-w-0 truncate text-left font-semibold text-text"
|
||||
@click="openFrom($event, row)"
|
||||
>
|
||||
<CellValue :column="column" :value="row[column.key]" />
|
||||
</button>
|
||||
<CellValue v-else :column="column" :value="row[column.key]" />
|
||||
</span>
|
||||
<RouterLink v-else-if="columnIndex === 0 && linkOf(row)" :to="linkOf(row)!" class="text-text no-underline">
|
||||
<CellValue :column="column" :value="row[column.key]" />
|
||||
</RouterLink>
|
||||
<CellValue v-else :column="column" :value="row[column.key]" />
|
||||
</td>
|
||||
<td v-if="trailing" data-trailing class="w-[56px] pr-5">
|
||||
<slot name="trailing" :row="row" />
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user