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:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, provide, readonly, ref, watchEffect } from 'vue'
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, provide, readonly, ref, watch, watchEffect } from 'vue'
|
||||
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
|
||||
import { ArrowLeft, Trash2 } from '@lucide/vue'
|
||||
import { api } from '../api/client'
|
||||
@@ -17,6 +17,8 @@ import { FORM_ASSETS, FORM_ENABLED_LOCALES, FORM_LOCALE, FORM_PATCH, FORM_SESSIO
|
||||
import { RELATION_MANAGER, needsRecord } from '../components/form/registry'
|
||||
import {
|
||||
DEFAULT_TAB,
|
||||
ML_LOCALE_CHANGE,
|
||||
changedLocales,
|
||||
contextAllows,
|
||||
editablePayload,
|
||||
fieldErrors,
|
||||
@@ -24,7 +26,7 @@ import {
|
||||
initialValues,
|
||||
mergeMLValue,
|
||||
panelDomId,
|
||||
presetValue,
|
||||
presetUpdates,
|
||||
schemaEnabledLocales,
|
||||
snapshot,
|
||||
tabDomId,
|
||||
@@ -205,24 +207,60 @@ async function load(): Promise<void> {
|
||||
values.value = initialValues(fields.value, schemaEnabledLocales(schema.value.meta))
|
||||
}
|
||||
adopt(recordResult?.data)
|
||||
mlLocale.value = firstMLLocale()
|
||||
activeTab.value = tabs.value[0]?.key ?? DEFAULT_TAB
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
// Preset fields (fields.yaml `preset`, UI-SPEC S7): on create, a text field
|
||||
// follows its source field until the administrator edits it by hand; that
|
||||
// stops it for as long as the form is open. On update nothing follows.
|
||||
// Preset fields (fields.yaml `preset`, UI-SPEC S7): on create, a text or
|
||||
// mltext field follows its text or mltext source until the administrator
|
||||
// edits it by hand; that stops it for as long as the form is open. An mltext
|
||||
// pair follows per locale and a hand edit of one locale stops only that
|
||||
// locale; a mixed pair follows the active ML locale (presetUpdates). On
|
||||
// update nothing follows.
|
||||
const edited = new Set<string>()
|
||||
// The locales of each ML field the administrator edited by hand.
|
||||
const editedLocales = new Map<string, Set<string>>()
|
||||
// The locale every ML control shows (MLTextField broadcasts the choice).
|
||||
const mlLocale = ref('en')
|
||||
|
||||
function firstMLLocale(): string {
|
||||
return schemaEnabledLocales(schema.value?.meta)[0] ?? 'en'
|
||||
}
|
||||
|
||||
function onMLLocale(event: Event): void {
|
||||
const locale = (event as CustomEvent<unknown>).detail
|
||||
if (typeof locale === 'string' && locale !== '') {
|
||||
mlLocale.value = locale
|
||||
}
|
||||
}
|
||||
|
||||
// FormGrid remounts the new panel's ML controls on the first enabled locale.
|
||||
watch(activeTab, () => {
|
||||
mlLocale.value = firstMLLocale()
|
||||
})
|
||||
|
||||
function isEdited(target: string, locale?: string): boolean {
|
||||
return locale === undefined ? edited.has(target) : (editedLocales.get(target)?.has(locale) ?? false)
|
||||
}
|
||||
|
||||
function update(name: string, value: unknown): void {
|
||||
const previous = values.value[name]
|
||||
const next = { ...values.value, [name]: value }
|
||||
edited.add(name)
|
||||
if (mode === 'create') {
|
||||
for (const field of fields.value) {
|
||||
if (field.preset?.field === name && field.type === 'text' && !edited.has(field.name)) {
|
||||
next[field.name] = presetValue(field.preset.type, value)
|
||||
}
|
||||
const type = fields.value.find((field) => field.name === name)?.type
|
||||
if (type === 'mltext' || type === 'mlmarkdown') {
|
||||
let locales = editedLocales.get(name)
|
||||
if (!locales) {
|
||||
locales = new Set<string>()
|
||||
editedLocales.set(name, locales)
|
||||
}
|
||||
for (const locale of changedLocales(previous, value)) {
|
||||
locales.add(locale)
|
||||
}
|
||||
}
|
||||
if (mode === 'create') {
|
||||
Object.assign(next, presetUpdates(fields.value, name, previous, value, values.value, mlLocale.value, isEdited))
|
||||
}
|
||||
values.value = next
|
||||
if (errors.value[name]) {
|
||||
@@ -411,8 +449,14 @@ function onBeforeUnload(event: BeforeUnloadEvent): void {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => window.addEventListener('beforeunload', onBeforeUnload))
|
||||
onBeforeUnmount(() => window.removeEventListener('beforeunload', onBeforeUnload))
|
||||
onMounted(() => {
|
||||
window.addEventListener('beforeunload', onBeforeUnload)
|
||||
window.addEventListener(ML_LOCALE_CHANGE, onMLLocale)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('beforeunload', onBeforeUnload)
|
||||
window.removeEventListener(ML_LOCALE_CHANGE, onMLLocale)
|
||||
})
|
||||
|
||||
// The header's last breadcrumb is this record's title (design: Top header).
|
||||
watchEffect(() => setRecordCrumb(loading.value ? '' : title.value))
|
||||
|
||||
Reference in New Issue
Block a user