Skip to content

Commit

Permalink
refa: enhance exported apis
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed May 26, 2023
1 parent 323980f commit ca3560c
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 102 deletions.
4 changes: 2 additions & 2 deletions packages/form/src/extensions/bitset.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<script lang="ts" setup>
import { PropType } from 'vue'
import { Schema, useConfig } from '../utils'
import { Schema, useModel } from '../utils'
import SchemaBase from '../base.vue'
defineProps({
Expand All @@ -33,6 +33,6 @@ defineProps({
defineEmits(['update:modelValue'])
const config = useConfig()
const config = useModel()
</script>
4 changes: 2 additions & 2 deletions packages/form/src/extensions/object.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<script lang="ts" setup>
import { PropType, computed } from 'vue'
import { Schema, useConfig, useI18nText } from '../utils'
import { Schema, useModel, useI18nText } from '../utils'
import SchemaBase from '../base.vue'
defineOptions({
Expand All @@ -53,7 +53,7 @@ defineEmits(['update:modelValue'])
const tt = useI18nText()
const config = useConfig()
const config = useModel()
const description = computed(() => tt(props.schema.meta.description))
Expand Down
4 changes: 2 additions & 2 deletions packages/form/src/extensions/radio.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<script lang="ts" setup>
import { PropType } from 'vue'
import { getChoices, Schema, useConfig, useI18nText } from '../utils'
import { getChoices, Schema, useModel, useI18nText } from '../utils'
import SchemaBase from '../base.vue'
defineProps({
Expand All @@ -35,6 +35,6 @@ defineEmits(['update:modelValue'])
const tt = useI18nText()
const config = useConfig()
const config = useModel()
</script>
4 changes: 2 additions & 2 deletions packages/form/src/extensions/tuple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<script lang="ts" setup>
import { computed, PropType } from 'vue'
import { Schema, useConfig } from '../utils'
import { Schema, useModel } from '../utils'
import SchemaBase from '../base.vue'
import SchemaPrimitive from '../primitive.vue'
Expand All @@ -38,7 +38,7 @@ const props = defineProps({
defineEmits(['update:modelValue'])
const config = useConfig()
const config = useModel()
const valid = computed(() => {
return props.schema.list.every(item => ['string', 'number', 'boolean'].includes(item.type))
Expand Down
3 changes: 2 additions & 1 deletion packages/form/src/extensions/union.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
<script lang="ts" setup>
import { computed, PropType, ref, watch, WatchStopHandle } from 'vue'
import { check, deepEqual, getChoices, getFallback, isNullable, Schema, useI18nText } from '../utils'
import { deepEqual, isNullable } from 'cosmokit'
import { check, getChoices, getFallback, Schema, useI18nText } from '../utils'
const props = defineProps({
schema: {} as PropType<Schema>,
Expand Down
112 changes: 112 additions & 0 deletions packages/form/src/form.vue
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>
187 changes: 100 additions & 87 deletions packages/form/src/index.ts
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
Loading

0 comments on commit ca3560c

Please sign in to comment.