-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
231 additions
and
102 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
<template> | ||
<form class="k-form"> | ||
<slot name="prolog"></slot> | ||
<h2 class="k-schema-header" v-if="showHeader || !hasTitle(resolved)"> | ||
<slot name="title">{{ t('title') }}</slot> | ||
</h2> | ||
<k-schema | ||
v-model="config" | ||
:initial="initial" | ||
:schema="resolved" | ||
:disabled="disabled" | ||
></k-schema> | ||
<slot name="epilog"></slot> | ||
</form> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import { computed, PropType } from 'vue' | ||
import { useI18n } from 'vue-i18n' | ||
import { getChoices, Schema } from './utils' | ||
import zhCN from './locales/zh-CN.yml' | ||
import enUS from './locales/en-US.yml' | ||
const props = defineProps({ | ||
schema: {} as PropType<Schema>, | ||
initial: {}, | ||
modelValue: {}, | ||
disabled: Boolean, | ||
showHeader: Boolean, | ||
}) | ||
const resolved = computed(() => { | ||
return props.schema && new Schema(props.schema) | ||
}) | ||
function hasTitle(schema: Schema): boolean { | ||
if (!schema) return true | ||
if (schema.type === 'object') { | ||
if (schema.meta.description) return true | ||
const entries = Object.entries(schema.dict).filter(([, value]) => !value.meta.hidden) | ||
if (!entries.length) return true | ||
return hasTitle(schema.dict[entries[0][0]]) | ||
} else if (schema.type === 'intersect') { | ||
return hasTitle(schema.list[0]) | ||
} else if (schema.type === 'union') { | ||
const choices = getChoices(schema) | ||
return choices.length === 1 ? hasTitle(choices[0]) : false | ||
} else { | ||
return false | ||
} | ||
} | ||
const emit = defineEmits(['update:modelValue']) | ||
const config = computed({ | ||
get: () => props.modelValue, | ||
set: emit.bind(null, 'update:modelValue'), | ||
}) | ||
const { t, setLocaleMessage } = useI18n({ | ||
messages: { | ||
'zh-CN': zhCN, | ||
'en-US': enUS, | ||
}, | ||
}) | ||
if (import.meta.hot) { | ||
import.meta.hot.accept('../locales/zh-CN.yml', (module) => { | ||
setLocaleMessage('zh-CN', module.default) | ||
}) | ||
import.meta.hot.accept('../locales/en-US.yml', (module) => { | ||
setLocaleMessage('en-US', module.default) | ||
}) | ||
} | ||
</script> | ||
|
||
<style lang="scss"> | ||
@media screen and (max-width: 480px) { | ||
.k-form { | ||
margin: 0 -1.5rem; | ||
.k-schema-header { | ||
padding: 0 1.5rem; | ||
} | ||
.k-schema-item { | ||
padding: 0.5rem 1.5rem; | ||
.k-schema-main { | ||
display: block; | ||
min-height: unset; | ||
} | ||
.k-schema-right { | ||
display: block; | ||
> :first-child { | ||
margin-top: 0.25rem; | ||
} | ||
> :last-child { | ||
margin-bottom: 0.25rem; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,103 +1,116 @@ | ||
import { App, Component } from 'vue' | ||
import { useEntries, useModel } from './utils' | ||
import SchemaBase from './base.vue' | ||
import SchemaPrimitive from './primitive.vue' | ||
import Bitset from './extensions/bitset.vue' | ||
import Group from './extensions/group.vue' | ||
import Intersect from './extensions/intersect.vue' | ||
import Object from './extensions/object.vue' | ||
import Radio from './extensions/radio.vue' | ||
import Table from './extensions/table.vue' | ||
import Textarea from './extensions/textarea.vue' | ||
import Tuple from './extensions/tuple.vue' | ||
import Union from './extensions/union.vue' | ||
import Schema from './schema.vue' | ||
|
||
export { SchemaBase, SchemaPrimitive } | ||
import Primitive from './primitive.vue' | ||
import SchemaBitset from './extensions/bitset.vue' | ||
import SchemaGroup from './extensions/group.vue' | ||
import SchemaIntersect from './extensions/intersect.vue' | ||
import SchemaObject from './extensions/object.vue' | ||
import SchemaRadio from './extensions/radio.vue' | ||
import SchemaTable from './extensions/table.vue' | ||
import SchemaTextarea from './extensions/textarea.vue' | ||
import SchemaTuple from './extensions/tuple.vue' | ||
import SchemaUnion from './extensions/union.vue' | ||
import KSchema from './schema.vue' | ||
import KForm from './form.vue' | ||
|
||
export * from 'cosmokit' | ||
|
||
export { Primitive } | ||
export { Schema, useI18nText } from './utils' | ||
|
||
export * from './icons' | ||
export * from './utils' | ||
|
||
function form(app: App) { | ||
app.component('k-schema', Schema) | ||
export const form = Object.assign(SchemaBase, { | ||
useModel, | ||
useEntries, | ||
extensions: new Set(), | ||
install(app: App) { | ||
app.component('k-form', KForm) | ||
app.component('k-schema', KSchema) | ||
}, | ||
}) as typeof SchemaBase & { | ||
useModel: typeof useModel | ||
useEntries: typeof useEntries | ||
extensions: Set<form.Extension> | ||
install: (app: App) => void | ||
} | ||
|
||
namespace form { | ||
export namespace form { | ||
export interface Extension { | ||
type: string | ||
role?: string | ||
validate?: (value: any, schema: any) => boolean | ||
component: Component | ||
} | ||
|
||
export const extensions = new Set<Extension>() | ||
|
||
extensions.add({ | ||
type: 'bitset', | ||
component: Bitset, | ||
validate: value => typeof value === 'number', | ||
}) | ||
|
||
extensions.add({ | ||
type: 'array', | ||
component: Group, | ||
validate: value => Array.isArray(value), | ||
}) | ||
|
||
extensions.add({ | ||
type: 'dict', | ||
component: Group, | ||
validate: value => typeof value === 'object', | ||
}) | ||
|
||
extensions.add({ | ||
type: 'object', | ||
component: Object, | ||
validate: value => typeof value === 'object', | ||
}) | ||
|
||
extensions.add({ | ||
type: 'intersect', | ||
component: Intersect, | ||
validate: value => typeof value === 'object', | ||
}) | ||
|
||
extensions.add({ | ||
type: 'union', | ||
role: 'radio', | ||
component: Radio, | ||
}) | ||
|
||
extensions.add({ | ||
type: 'array', | ||
role: 'table', | ||
component: Table, | ||
validate: value => Array.isArray(value), | ||
}) | ||
|
||
extensions.add({ | ||
type: 'dict', | ||
role: 'table', | ||
component: Table, | ||
validate: value => typeof value === 'object', | ||
}) | ||
|
||
extensions.add({ | ||
type: 'string', | ||
role: 'textarea', | ||
component: Textarea, | ||
validate: value => typeof value === 'string', | ||
}) | ||
|
||
extensions.add({ | ||
type: 'tuple', | ||
component: Tuple, | ||
validate: value => Array.isArray(value), | ||
}) | ||
|
||
extensions.add({ | ||
type: 'union', | ||
component: Union, | ||
}) | ||
} | ||
|
||
form.extensions.add({ | ||
type: 'bitset', | ||
component: SchemaBitset, | ||
validate: value => typeof value === 'number', | ||
}) | ||
|
||
form.extensions.add({ | ||
type: 'array', | ||
component: SchemaGroup, | ||
validate: value => Array.isArray(value), | ||
}) | ||
|
||
form.extensions.add({ | ||
type: 'dict', | ||
component: SchemaGroup, | ||
validate: value => typeof value === 'object', | ||
}) | ||
|
||
form.extensions.add({ | ||
type: 'object', | ||
component: SchemaObject, | ||
validate: value => typeof value === 'object', | ||
}) | ||
|
||
form.extensions.add({ | ||
type: 'intersect', | ||
component: SchemaIntersect, | ||
validate: value => typeof value === 'object', | ||
}) | ||
|
||
form.extensions.add({ | ||
type: 'union', | ||
role: 'radio', | ||
component: SchemaRadio, | ||
}) | ||
|
||
form.extensions.add({ | ||
type: 'array', | ||
role: 'table', | ||
component: SchemaTable, | ||
validate: value => Array.isArray(value), | ||
}) | ||
|
||
form.extensions.add({ | ||
type: 'dict', | ||
role: 'table', | ||
component: SchemaTable, | ||
validate: value => typeof value === 'object', | ||
}) | ||
|
||
form.extensions.add({ | ||
type: 'string', | ||
role: 'textarea', | ||
component: SchemaTextarea, | ||
validate: value => typeof value === 'string', | ||
}) | ||
|
||
form.extensions.add({ | ||
type: 'tuple', | ||
component: SchemaTuple, | ||
validate: value => Array.isArray(value), | ||
}) | ||
|
||
form.extensions.add({ | ||
type: 'union', | ||
component: SchemaUnion, | ||
}) | ||
|
||
export default form |
Oops, something went wrong.