Closed MaikoTan closed 1 year ago
The workflow above not working, which means we should pass the transformers recursively into every single Schema
object. Also, since Koishi's schema was read from the backend, a function could not be serialized in this case, which make it difficult to implement.
So a better method is to add transformers on schemastery-vue
, I have figured out a method, see if this satisfy you. This approach do not require useLocale
or something, theoretically any localization function should work. I could produce a PR if you have no comments on it.
diff is here, or see the text version below.
diff --git a/packages/form/src/group.vue b/packages/form/src/group.vue
index 2390d22..4cfe124 100644
--- a/packages/form/src/group.vue
+++ b/packages/form/src/group.vue
@@ -12,7 +12,7 @@
<el-input v-model="entries[index][0]"></el-input>
</template>
<template #description>
- <k-markdown :source="schema.inner.meta.description"></k-markdown>
+ <k-markdown :source="getDescription(schema.inner, transformers)"></k-markdown>
</template>
<template #menu>
<el-dropdown-item divided :disabled="!index" command="up">上移</el-dropdown-item>
:...skipping...
diff --git a/packages/form/src/group.vue b/packages/form/src/group.vue
index 2390d22..4cfe124 100644
--- a/packages/form/src/group.vue
+++ b/packages/form/src/group.vue
@@ -12,7 +12,7 @@
<el-input v-model="entries[index][0]"></el-input>
</template>
<template #description>
- <k-markdown :source="schema.inner.meta.description"></k-markdown>
+ <k-markdown :source="getDescription(schema.inner, transformers)"></k-markdown>
</template>
<template #menu>
<el-dropdown-item divided :disabled="!index" command="up">上移</el-dropdown-item>
@@ -28,7 +28,8 @@
:schema="{ ...schema.inner, meta: { ...schema.inner.meta, description: '' } }"
:disabled="disabled"
:instant="instant"
- :prefix="schema.type === 'array' ? `${prefix.slice(0, -1)}[${key}].` : prefix + key + '.'">
+ :prefix="schema.type === 'array' ? `${prefix.slice(0, -1)}[${key}].` : prefix + key + '.'"
+ :transformers="transformers">
<span class="prefix">{{ prefix }}</span>
<span>{{ key }}</span>
</k-schema>
@@ -43,6 +44,7 @@
:disabled="disabled"
:instant="instant"
:prefix="schema.type === 'array' ? `${prefix.slice(0, -1)}[${key}].` : prefix + key + '.'"
+ :transformers="transformers"
@command="handleCommand($event, index)">
<template #menu>
<el-dropdown-item divided :disabled="!index" command="up">上移</el-dropdown-item>
@@ -64,7 +66,7 @@
<script lang="ts" setup>
import { PropType, ref, watch, WatchStopHandle } from 'vue'
-import { getFallback, isObjectSchema, Schema } from './utils'
+import { getFallback, getDescription, isObjectSchema, Schema, Transformers } from './utils'
import SchemaItem from './item.vue'
function handleCommand(action: string, index?: number) {
@@ -99,6 +101,7 @@ const props = defineProps({
disabled: Boolean,
instant: Boolean,
signal: Boolean,
+ transformers: { type: Object as PropType<Transformers>, default: () => ({}) },
})
const emit = defineEmits(['update:modelValue', 'update:signal'])
diff --git a/packages/form/src/schema.vue b/packages/form/src/schema.vue
index da63ada..b10b94b 100644
--- a/packages/form/src/schema.vue
+++ b/packages/form/src/schema.vue
@@ -3,14 +3,15 @@
<template v-else-if="schema.type === 'const' || schema.type === 'never'"></template>
<template v-else-if="schema.type === 'object'">
- <h2 class="k-schema-header" v-if="schema.meta.description">{{ schema.meta.description }}</h2>
+ <h2 class="k-schema-header" v-if="schema.meta.description">{{ getDescription(schema, transformers) }}</h2>
<k-schema v-for="(item, key) in schema.dict" :key="key"
v-model="config[key]"
:schema="item"
:initial="initial?.[key]"
:instant="instant"
:disabled="disabled"
- :prefix="prefix + key + '.'">
+ :prefix="prefix + key + '.'"
+ :transformers="transformers">
<span class="prefix">{{ prefix }}</span>
<span>{{ key }}</span>
</k-schema>
@@ -24,7 +25,8 @@
:schema="{ ...item, meta: { ...item.meta, ...schema.meta } }"
:instant="instant"
:disabled="disabled"
- :prefix="prefix">
+ :prefix="prefix"
+ :transformers="transformers">
<slot></slot>
</k-schema>
</template>
@@ -45,7 +47,7 @@
</template>
<template #description>
- <k-markdown :source="schema.meta.description"></k-markdown>
+ <k-markdown :source="getDescription(schema, transformers)"></k-markdown>
</template>
<template #right>
@@ -55,7 +57,7 @@
v-for="(item, index) in choices"
:key="index"
:value="index"
- :label="item.meta.description || item.value"
+ :label="getDescription(item, transformers) || item.value"
></el-option>
</el-select>
</template>
@@ -80,7 +82,7 @@
v-model="config"
:disabled="disabled"
:label="item.value"
- >{{ item.meta.description || item.value }}</el-radio>
+ >{{ getDescription(item, transformers) || item.value }}</el-radio>
</li>
</ul>
@@ -112,7 +114,7 @@
<!-- top level array / dict -->
<template v-else>
<h2 class="k-schema-header">
- {{ schema.meta.description || '配置列表' }}
+ {{ getDescription(schema, transformers) || '配置列表' }}
<el-button solid @click="signal = true" :disabled="disabled">添加项</el-button>
</h2>
<schema-group v-model:signal="signal"
@@ -130,6 +132,7 @@
:instant="instant"
:disabled="disabled"
:prefix="prefix"
+ :transformers="transformers"
></k-schema>
</template>
</template>
@@ -138,7 +141,7 @@
import { watch, ref, computed } from 'vue'
import type { PropType } from 'vue'
-import { deepEqual, getChoices, getFallback, Schema, validate } from './utils'
+import { deepEqual, getChoices, getDescription, getFallback, Schema, Transformers, validate } from './utils'
import { clone, isNullable, valueMap } from 'cosmokit'
import BitCheckbox from './bit.vue'
import SchemaItem from './item.vue'
@@ -154,6 +157,7 @@ const props = defineProps({
disabled: Boolean,
branch: Boolean,
prefix: { type: String, default: '' },
+ transformers: { type: Object as PropType<Transformers>, default: () => ({}) },
})
const emit = defineEmits(['update:modelValue', 'command'])
@@ -196,7 +200,7 @@ watch(() => props.schema, (value) => {
const selectModel = computed({
get() {
if (active.value === props.schema) return
- return active.value.meta.description || active.value.value
+ return getDescription(active.value, props.transformers) || active.value.value
},
set(index) {
if (active.value === choices.value[index]) return
diff --git a/packages/form/src/utils.ts b/packages/form/src/utils.ts
index 9fb3841..915c2c4 100644
--- a/packages/form/src/utils.ts
+++ b/packages/form/src/utils.ts
@@ -78,3 +78,19 @@ export function hasTitle(schema: Schema, root?: boolean): boolean {
return false
}
}
+
+export type Transformer<I = any, O = I> = (value: I, schema: Schema) => O
+
+export interface Transformers {
+ description: Transformer<string>
+}
+
+export function getDescription(schema: Schema, transformers: Transformers): string | undefined {
+ if (!schema?.meta?.description) return
+
+ const description = schema.meta.description
+ if (transformers?.description) {
+ return transformers.description(description, schema)
+ }
+ return description
+}
已经实现。
Summary
Hitorically
schemastery
andschemastery-vue
didn't support i18n, you could only get what you written in the.description('whatever')
part.Recently I have met the situation that we should render i18n description with
schemastery-vue
, so I digged intoschemastery
's code and found a way to hackily add i18n description outside ofschemastery
.Click to Expand
The localise function I used is coming from `element-plus`, but I think any function has a shape like `(desc: string) => string` probably work, either. ```ts import { useLocale } from 'element-plus' export function hackSchemaDescription(schema: Schema, id: string) { if (!schema) return const { type, meta } = schema if (meta?.description) { schema.meta.description = t(`components.${id}.${meta.description}`) } switch (type) { case 'dict': case 'bitset': case 'object': Object.entries(schema.dict).forEach(([_, value]) => { hackSchemaDescription(value, id) }) break case 'array': case 'tuple': case 'union': case 'intersect': schema.list?.forEach((value) => { hackSchemaDescription(value, id) }) break } return schema } ```But I think this feature should be implemented by
schemastery
itself.Proposed Workflow
Schema
's constructor, which could be namedoption
, which has a type like:option?.transform?.description(value)
within the code below (maybe between L235 and L236):https://github.com/shigma/schemastery/blob/560ae3ce42ac3f7f6a4ff32383a3038b8d3f8f79/packages/core/src/index.ts#L233-L241
Additional Information
As what I using for the localisation is
element-plus
's built-inuseLocale
, the composable function could automatically trigger the re-render when user switches the language. But I don't know if others could do the same thing. Why I noticed this was thatschemastery
is usingObject.assign
to put thedescription
into theSchema
object (or function?), I am not very familiar with this, but I was trapped in this before.