feat(admin): follow presets on mltext fields per locale
- formState: changedLocales and presetUpdates hold the preset-follow rules for text and mltext pairs - FormView applies presetUpdates on create, tracks per-locale hand edits of ML fields and follows the active ML locale - rebuilt modules/boardwalk/dist
This commit is contained in:
@@ -92,6 +92,91 @@ export function presetValue(type: string, source: unknown): string {
|
||||
.replace(/^-+|-+$/g, '')
|
||||
}
|
||||
|
||||
/** The locales whose text differs between two locale maps: every locale on
|
||||
* either side, a missing key counting as ''. A value that is not a map counts
|
||||
* as an empty map. */
|
||||
export function changedLocales(previous: unknown, next: unknown): string[] {
|
||||
const before = localeRecord(previous)
|
||||
const after = localeRecord(next)
|
||||
const out: string[] = []
|
||||
for (const locale of new Set([...Object.keys(before), ...Object.keys(after)])) {
|
||||
if ((before[locale] ?? '') !== (after[locale] ?? '')) {
|
||||
out.push(locale)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/** Reports whether the administrator edited a preset target by hand: the
|
||||
* whole field when no locale is given, one locale of an ML target otherwise. */
|
||||
export type PresetEdited = (target: string, locale?: string) => boolean
|
||||
|
||||
function isMLType(type: string | undefined): boolean {
|
||||
return type === 'mltext' || type === 'mlmarkdown'
|
||||
}
|
||||
|
||||
/**
|
||||
* The preset targets of `source` (fields.yaml `preset`) to rewrite after the
|
||||
* source changed from `previous` to `next`: target name -> new value. Targets
|
||||
* are text and mltext fields; a preset on any other type is ignored.
|
||||
*
|
||||
* - text -> text: the target takes presetValue of the source, until edited.
|
||||
* - ML -> text: the target takes presetValue of the source's active-locale text.
|
||||
* - text -> ML: only the target's active locale is written; its other locales stay.
|
||||
* - ML -> ML: per locale. Each locale whose source text changed rewrites the
|
||||
* same locale of the target, unless the administrator edited that locale by
|
||||
* hand; the target's other locales stay.
|
||||
*
|
||||
* A preset never writes a locale the administrator neither typed in (source)
|
||||
* nor sees (target), so untouched locales keep the server's fallback.
|
||||
*/
|
||||
export function presetUpdates(
|
||||
fields: FormField[],
|
||||
source: string,
|
||||
previous: unknown,
|
||||
next: unknown,
|
||||
values: AdminRecord,
|
||||
activeLocale: string,
|
||||
isEdited: PresetEdited,
|
||||
): AdminRecord {
|
||||
const out: AdminRecord = {}
|
||||
const sourceML = isMLType(fields.find((field) => field.name === source)?.type)
|
||||
for (const field of fields) {
|
||||
const preset = field.preset
|
||||
if (!preset || preset.field !== source || (field.type !== 'text' && field.type !== 'mltext')) {
|
||||
continue
|
||||
}
|
||||
if (field.type === 'text') {
|
||||
if (isEdited(field.name)) {
|
||||
continue
|
||||
}
|
||||
out[field.name] = presetValue(preset.type, sourceML ? (localeRecord(next)[activeLocale] ?? '') : next)
|
||||
continue
|
||||
}
|
||||
if (!sourceML) {
|
||||
if (isEdited(field.name, activeLocale)) {
|
||||
continue
|
||||
}
|
||||
out[field.name] = { ...localeRecord(values[field.name]), [activeLocale]: presetValue(preset.type, next) }
|
||||
continue
|
||||
}
|
||||
const texts = localeRecord(next)
|
||||
const target = { ...localeRecord(values[field.name]) }
|
||||
let written = false
|
||||
for (const locale of changedLocales(previous, next)) {
|
||||
if (isEdited(field.name, locale)) {
|
||||
continue
|
||||
}
|
||||
target[locale] = presetValue(preset.type, texts[locale] ?? '')
|
||||
written = true
|
||||
}
|
||||
if (written) {
|
||||
out[field.name] = target
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/** Initial values of a new record: schema defaults, toggles off, no ids.
|
||||
* ML fields seed an empty string for every enabled locale. */
|
||||
export function initialValues(fields: FormField[], enabledLocales: readonly string[] = []): AdminRecord {
|
||||
|
||||
Reference in New Issue
Block a user