feat(16.2-01): translatable plugin fields, element uploads and save notices in the admin
- MLPluginField wraps PluginField in the MLField locale switcher; the
element gets the active content locale and that locale's string
- isMLField treats a translatable plugin field as a locale map in the form
state, FormView adopt and the relation child modal
- summer-upload posts the file with the admin session to the field upload
route and answers summer-upload-result {id, url} or {id, error}
- noticeLines shows meta.notices under each field as a warning status line
(text only) and in the saved toast; the stored value is reloaded
- docs/backend/field-types.md documents the full element contract;
modules/boardwalk/dist rebuilt
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
// Helpers shared by the record form and the settings form: context
|
||||
// filtering, the save payload, 422 detail mapping and focus.
|
||||
import type { AdminRecord, ErrorBody, FormField } from '../../api/types'
|
||||
import type { AdminRecord, ErrorBody, FieldNotice, FormField } from '../../api/types'
|
||||
import { permissionValues } from './control'
|
||||
import { isMLFieldType, localeRecord } from './mlLocale'
|
||||
import { isMLField, localeRecord } from './mlLocale'
|
||||
import { isRegisteredField } from './registry'
|
||||
|
||||
export { ML_LOCALE_CHANGE, broadcastMLLocale, isMLFieldType, localeRecord } from './mlLocale'
|
||||
export { ML_LOCALE_CHANGE, broadcastMLLocale, isMLField, isMLFieldType, localeRecord } from './mlLocale'
|
||||
|
||||
/** The screen a field is filtered for: the two form modes and the read-only preview (D-11). */
|
||||
export type FormMode = 'create' | 'update' | 'preview'
|
||||
@@ -65,7 +65,7 @@ export function editablePayload(fields: FormField[], values: AdminRecord, mode:
|
||||
out[field.name] = permissionValues(field, value)
|
||||
continue
|
||||
}
|
||||
if (isMLFieldType(field.type)) {
|
||||
if (isMLField(field)) {
|
||||
out[field.name] = localeRecord(value)
|
||||
continue
|
||||
}
|
||||
@@ -136,7 +136,7 @@ export function presetUpdates(
|
||||
isEdited: PresetEdited,
|
||||
): AdminRecord {
|
||||
const out: AdminRecord = {}
|
||||
const sourceML = isMLFieldType(fields.find((field) => field.name === source)?.type)
|
||||
const sourceML = isMLField(fields.find((field) => field.name === source))
|
||||
for (const field of fields) {
|
||||
const preset = field.preset
|
||||
if (!preset || preset.field !== source || (field.type !== 'text' && field.type !== 'mltext')) {
|
||||
@@ -184,7 +184,7 @@ export function initialValues(fields: FormField[], enabledLocales: readonly stri
|
||||
out[field.name] = false
|
||||
} else if (field.type === 'relation' && field.multiple) {
|
||||
out[field.name] = []
|
||||
} else if (isMLFieldType(field.type)) {
|
||||
} else if (isMLField(field)) {
|
||||
const seed: Record<string, string> = {}
|
||||
for (const code of enabledLocales) {
|
||||
seed[code] = ''
|
||||
@@ -225,12 +225,12 @@ export function recordDisplayName(
|
||||
values: AdminRecord,
|
||||
enabledLocales: readonly string[],
|
||||
): string {
|
||||
const first = fields?.find((field) => field.type === 'text' || isMLFieldType(field.type))
|
||||
const first = fields?.find((field) => field.type === 'text' || isMLField(field))
|
||||
const value = first ? values[first.name] : undefined
|
||||
if (typeof value === 'string' || typeof value === 'number') {
|
||||
return String(value).trim()
|
||||
}
|
||||
if (!first || !isMLFieldType(first.type)) {
|
||||
if (!first || !isMLField(first)) {
|
||||
return ''
|
||||
}
|
||||
const texts = localeRecord(value)
|
||||
@@ -246,6 +246,23 @@ export function recordDisplayName(
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* A save's meta.notices (D-10, 16.2) as field name -> lines shown under the
|
||||
* field: a notice about one content locale reads "<LOCALE>: message". Absent
|
||||
* or empty notices give an empty map.
|
||||
*/
|
||||
export function noticeLines(notices: readonly FieldNotice[] | null | undefined): Record<string, string[]> {
|
||||
const out: Record<string, string[]> = {}
|
||||
for (const notice of notices ?? []) {
|
||||
if (!notice || typeof notice.message !== 'string' || notice.message === '' || typeof notice.field !== 'string') {
|
||||
continue
|
||||
}
|
||||
const line = notice.locale ? `${notice.locale.toUpperCase()}: ${notice.message}` : notice.message
|
||||
;(out[notice.field] ??= []).push(line)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/** D-10 422 details (field -> messages) as a string-list map. */
|
||||
export function fieldErrors(details: ErrorBody['details'] | undefined): Record<string, string[]> {
|
||||
const out: Record<string, string[]> = {}
|
||||
|
||||
Reference in New Issue
Block a user