Skip to content

Commit

Permalink
feat: support more primitive types
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed May 31, 2023
1 parent e5b5639 commit ac7f65f
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 26 deletions.
2 changes: 1 addition & 1 deletion packages/form/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "schemastery-vue",
"description": "Type driven schema validator",
"version": "6.0.0",
"version": "6.1.0",
"main": "src/index.ts",
"repository": {
"type": "git",
Expand Down
53 changes: 35 additions & 18 deletions packages/form/src/extensions/table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<th v-for="([key, schema]) in columns" :key="key">
<span>{{ key === null ? t('entry.value') : tt(schema.meta.description) || key }}</span>
</th>
<th :colspan="3"></th>
<th colspan="3"></th>
</tr>
<tr v-for="([key], index) in entries">
<td
Expand All @@ -36,19 +36,18 @@
<td
v-for="([key, schema]) in columns"
:key="key"
:class="'k-schema-column-' + schema.type"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave">
<el-input
<schema-primitive
minimal
:schema="schema"
:disabled="disabled"
:modelValue="key === null ? entries[index][1] : entries[index][1]?.[key]"
@update:modelValue="key === null ? entries[index][1] = $event : (entries[index][1] ||= {})[key] = $event"
:disabled="disabled"
:type="schema.type === 'number' ? 'number' : 'text'"
:max="schema.meta.max"
:min="schema.meta.min"
:step="schema.meta.step"
@focus="handleFocus"
@blur="handleBlur"
></el-input>
></schema-primitive>
</td>
<td v-if="!disabled" class="button"
:class="{ disabled: !index }"
Expand Down Expand Up @@ -98,6 +97,7 @@ import { useI18n } from 'vue-i18n'
import { IconArrowUp, IconArrowDown, IconClose } from '../icons'
import { Schema, useEntries, useI18nText } from '../utils'
import SchemaBase from '../base.vue'
import SchemaPrimitive from '../primitive.vue'
import zhCN from '../locales/zh-CN.yml'
import enUS from '../locales/en-US.yml'
Expand All @@ -111,14 +111,23 @@ const props = defineProps({
defineEmits(['update:modelValue'])
function isPrimitive(schema: Schema): boolean {
if (['string', 'number', 'boolean'].includes(schema.type)) return true
if (schema.type === 'union') return schema.list.every(item => item.type === 'const')
}
function ensureColumns(entries: [string, Schema][]) {
if (entries.every(([, schema]) => isPrimitive(schema))) return entries
}
const columns = computed<[string, Schema][]>(() => {
const { inner } = props.schema
if (['string', 'number', 'boolean'].includes(inner.type)) {
if (isPrimitive(inner)) {
return [[null, inner]]
} else if (inner.type === 'tuple') {
return Object.entries(inner.list)
return ensureColumns(Object.entries(inner.list))
} else if (inner.type === 'object') {
return Object.entries(inner.dict)
return ensureColumns(Object.entries(inner.dict))
}
})
Expand Down Expand Up @@ -188,7 +197,7 @@ if (import.meta.hot) {
}
th {
padding: 0.5rem 0;
padding: 0.5rem 0.75rem;
line-height: 1rem;
}
Expand All @@ -199,10 +208,13 @@ if (import.meta.hot) {
background-color: var(--k-color-warning-fade);
}
.el-input__wrapper {
z-index: 1;
* .el-input__wrapper {
box-shadow: none;
}
.el-select .el-input .el-input__wrapper {
box-shadow: none !important;
}
}
td {
Expand All @@ -217,10 +229,6 @@ if (import.meta.hot) {
}
}
td.key {
width: 30%;
}
td.button {
width: 2rem;
color: var(--k-text-light);
Expand All @@ -242,6 +250,15 @@ if (import.meta.hot) {
height: 1rem;
}
}
.k-schema-column-number {
min-width: 8rem;
width: 10rem;
.el-input-number {
width: 100%;
}
}
}
</style>
55 changes: 48 additions & 7 deletions packages/form/src/primitive.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
<template>
<el-switch v-if="schema.type === 'boolean'" v-model="config" :class="{ nullable }" :disabled="disabled"></el-switch>
<template v-if="schema.type === 'boolean'">
<el-checkbox v-if="minimal" v-model="config" :class="{ nullable }" :disabled="disabled"></el-checkbox>
<el-switch v-else v-model="config" :class="{ nullable }" :disabled="disabled"></el-switch>
</template>

<template v-else-if="schema.type === 'number'">
<el-slider v-if="schema.meta.role === 'slider'" style="width: 200px"
v-model="config" :disabled="disabled" :max="schema.meta.max" :min="schema.meta.min" :step="schema.meta.step"
></el-slider>
<el-input-number v-else
v-model="config" :disabled="disabled" :max="schema.meta.max" :min="schema.meta.min" :step="schema.meta.step"
@focus="$emit('focus', $event)" @blur="$emit('blur', $event)"
></el-input-number>
</template>

<template v-else>
<template v-else-if="schema.type === 'string'">
<el-color-picker v-if="schema.meta.role === 'color'" v-model="config" show-alpha></el-color-picker>
<el-time-picker v-else-if="schema.meta.role === 'time'" v-model="date"></el-time-picker>
<el-date-picker v-else-if="['date', 'datetime'].includes(schema.meta.role)" :type="schema.meta.role" v-model="date"></el-date-picker>
<el-time-picker
v-else-if="schema.meta.role === 'time'" v-model="date"
@focus="$emit('focus', $event)" @blur="$emit('blur', $event)"
></el-time-picker>
<el-date-picker
v-else-if="['date', 'datetime'].includes(schema.meta.role)" :type="schema.meta.role" v-model="date"
@focus="$emit('focus', $event)" @blur="$emit('blur', $event)"
></el-date-picker>
<el-input v-else v-model="config" :disabled="disabled" :class="{ nullable }"
:style="{ width: isLink ? '16rem' : '12rem' }" :type="type">
:style="{ width: minimal ? '100%' : isLink ? '16rem' : '12rem' }" :type="type"
@focus="$emit('focus', $event)" @blur="$emit('blur', $event)">
<template #prefix v-if="nullable"></template>
<template #suffix v-if="isLink">
<icon-external @click="onClickExternal(config)"></icon-external>
Expand All @@ -26,28 +37,47 @@
</template>
</el-input>
</template>

<template v-else-if="schema.type === 'union'">
<el-select
v-model="selectModel"
filterable
:disabled="disabled"
@focus="$emit('focus', $event)" @blur="$emit('blur', $event)"
>
<el-option
v-for="(item, index) in schema.list"
:key="index"
:value="index"
:label="tt(item.meta.description) || item.value"
></el-option>
</el-select>
</template>
</template>

<script lang="ts" setup>
import { computed, PropType, ref } from 'vue'
import { IconExternal, IconEye, IconEyeSlash } from './icons'
import { isNullable } from 'cosmokit'
import { useModel } from './utils'
import { useI18nText, useModel } from './utils'
import Schema from 'schemastery'
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(['update:modelValue', 'focus', 'blur'])
const props = defineProps({
schema: {} as PropType<Schema>,
modelValue: {},
disabled: Boolean,
minimal: Boolean,
})
const showPass = ref(false)
const config = useModel()
const tt = useI18nText()
const nullable = computed(() => isNullable(config.value))
const isLink = computed(() => ['url', 'link'].includes(props.schema.meta.role))
Expand Down Expand Up @@ -77,6 +107,17 @@ const date = computed({
},
})
const selectModel = computed({
get() {
const item = props.schema.list.find(item => item.value === config.value)
if (!item) return
return tt(item.meta.description) || item.value
},
set(index) {
config.value = props.schema.list[index].value
},
})
function onClickExternal(value: string) {
if (!value) return
open(value, '_blank')
Expand Down

0 comments on commit ac7f65f

Please sign in to comment.