feat: relation field and picker

This commit is contained in:
Florian Sylvain committed 2026-10-23 00:13:42 +02:00
1 parent b45c99be3b
commit 37a77c9c22
92 files changed
+6826 -80

No files matched your search

+64 -1
View File
@@ -3,19 +3,30 @@ import type { BuiltinField } from '@/lib/contentTypes'
import { fieldTypeSpec, type FieldCellKind } from '@/lib/fieldTypes'
import { formatDayValue } from '@/lib/entryValue'
import { isMissingImage, type MediaSummary } from '@/lib/mediaField'
import { isMissingRelation, relationIdsOf, type RelationSummary } from '@/lib/relationField'
import { formatAbsoluteDateFromIso } from '@/lib/relativeDate'
export { formatDayValue }
export type CellKind = 'empty' | FieldCellKind
export interface RelationChip {
id: number
label: string
missing: boolean
}
export interface CellView {
kind: CellKind
text: string
yes?: boolean
image?: MediaSummary
chips?: RelationChip[]
more?: number
}
export const RELATION_CHIPS_SHOWN = 2
export const EMPTY_TEXT = '–'
const EMPTY: CellView = { kind: 'empty', text: '' }
@@ -48,7 +59,57 @@ function thumbnailCell(value: unknown, image: MediaSummary | undefined): CellVie
}
}
export function describeCell(field: BuiltinField, value: unknown, image?: MediaSummary): CellView {
function summaryField(first: { key: string; type: string }): BuiltinField {
return {
key: first.key,
label: '',
type: first.type,
required: false,
unique: false,
default: null,
min: null,
max: null,
options: null,
}
}
export function relationLabel(summary: RelationSummary) {
if (isMissingRelation(summary)) {
return t('Entry no longer exists')
}
const first = summary.first_field
const cell = describeCell(summaryField(first), first.value)
return cell.kind === 'empty' || cell.kind === 'thumbnail' || cell.kind === 'relation'
? t('Entry {0}', [summary.id])
: plainCellText(cell)
}
function relationCell(value: unknown, summaries: RelationSummary[]): CellView {
const ids = relationIdsOf(value)
if (ids.length === 0) {
return EMPTY
}
const chips = ids.map((id): RelationChip => {
const summary = summaries.find((candidate) => candidate.id === id) ?? {
id,
missing: true as const,
}
return { id, label: relationLabel(summary), missing: isMissingRelation(summary) }
})
return {
kind: 'relation',
text: chips.map((chip) => chip.label).join(', '),
chips: chips.slice(0, RELATION_CHIPS_SHOWN),
more: Math.max(0, chips.length - RELATION_CHIPS_SHOWN),
}
}
export function describeCell(
field: BuiltinField,
value: unknown,
image?: MediaSummary,
relations: RelationSummary[] = [],
): CellView {
if (value === undefined || value === null || value === '') {
return EMPTY
}
@@ -56,6 +117,8 @@ export function describeCell(field: BuiltinField, value: unknown, image?: MediaS
switch (kind) {
case 'thumbnail':
return thumbnailCell(value, image)
case 'relation':
return relationCell(value, relations)
case 'boolean':
return { kind, text: '', yes: value === true }
case 'number':