feat(admin): render mltextarea fields per locale

- MLField.vue is the one locale-switching wrapper; MLTextField,
  MLMarkdownField and the new MLTextareaField are shells around it
- isMLFieldType replaces the inline ML type-name checks in formState,
  FormView and RelationChildModal
- register mltextarea as a group-labelled control; rebuild dist
This commit is contained in:
Jakub Zych
2026-10-06 21:36:04 +02:00
parent 3596df4ff5
commit a913eec50f
15 changed files with 275 additions and 162 deletions

View File

@@ -1,78 +1,19 @@
<script setup lang="ts">
import { computed, inject, onMounted, onUnmounted, ref } from 'vue'
// The mlmarkdown control: MLField around MarkdownField.
import type { FieldControlProps } from '../control'
import MLField from './MLField.vue'
import MarkdownField from './MarkdownField.vue'
import { FORM_ENABLED_LOCALES } from '../formContext'
import { ML_LOCALE_CHANGE, broadcastMLLocale, localeRecord } from '../mlLocale'
const props = defineProps<FieldControlProps>()
const emit = defineEmits<{ 'update:modelValue': [value: Record<string, string>] }>()
const values = computed(() => localeRecord(props.modelValue))
const enabledLocales = inject(FORM_ENABLED_LOCALES, null)
const locales = computed(() => {
const codes = enabledLocales?.value
return codes && codes.length > 0 ? [...codes] : []
})
const activeLocale = ref(locales.value[0] ?? 'en')
const labelledBy = computed(() => `${props.controlId}-label`)
const editorId = computed(() => `${props.controlId}-editor`)
const activeText = computed(() => values.value[activeLocale.value] ?? '')
const markdownField = computed(() => ({ ...props.field, type: 'markdown' }))
function setLocale(locale: string) {
if (!locale) {
return
}
activeLocale.value = locale
}
function onLocaleInput(event: Event) {
const locale = (event.target as HTMLSelectElement).value
setLocale(locale)
broadcastMLLocale(locale)
}
function onText(text: string) {
emit('update:modelValue', { ...values.value, [activeLocale.value]: text })
}
function onBroadcast(event: Event) {
const locale = (event as CustomEvent<string>).detail
if (typeof locale === 'string' && locale !== '') {
setLocale(locale)
}
}
onMounted(() => {
window.addEventListener(ML_LOCALE_CHANGE, onBroadcast)
})
onUnmounted(() => {
window.removeEventListener(ML_LOCALE_CHANGE, onBroadcast)
})
</script>
<template>
<div class="flex flex-col gap-1.5" role="group" :aria-labelledby="labelledBy" data-ml-field="mlmarkdown">
<div class="flex flex-wrap items-center gap-2">
<select
data-ml-locale
class="rounded-control border border-border-strong bg-surface px-2 py-1 text-[13px]"
:value="activeLocale"
:aria-label="`Locale for ${field.label || field.name}`"
@change="onLocaleInput"
>
<option v-for="code in locales" :key="code" :value="code">{{ code }}</option>
</select>
</div>
<MarkdownField
:field="markdownField"
:model-value="activeText"
:control-id="editorId"
:invalid="invalid"
:described-by="describedBy"
@update:model-value="onText"
/>
</div>
<MLField
v-bind="props"
:inner="MarkdownField"
inner-type="markdown"
ml-type="mlmarkdown"
@update:model-value="emit('update:modelValue', $event)"
/>
</template>