Skip to content

Commit

Permalink
feat: enhance length check for array
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Mar 17, 2024
1 parent b08e36b commit 0de846e
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 18 deletions.
11 changes: 6 additions & 5 deletions packages/core/src/index.ts
@@ -1,4 +1,4 @@
import { clone, deepEqual, Dict, isNullable, isPlainObject, pick, valueMap } from 'cosmokit'
import { clone, deepEqual, Dict, filterKeys, isNullable, isPlainObject, pick, valueMap } from 'cosmokit'

const kSchema = Symbol.for('schemastery')

Expand Down Expand Up @@ -212,7 +212,7 @@ function getInner(value: any) {
}

function extractKeys(data: any) {
return Object.fromEntries(Object.entries(data ?? {}).filter(([key]) => !key.startsWith('$')))
return filterKeys(data ?? {}, key => !key.startsWith('$'))
}

Schema.prototype.i18n = function i18n(messages) {
Expand Down Expand Up @@ -421,10 +421,10 @@ Schema.extend('const', (data, { value }) => {
throw new TypeError(`expected ${value} but got ${data}`)
})

function checkWithinRange(data: number, meta: Schemastery.Meta<any>, description: string) {
function checkWithinRange(data: number, meta: Schemastery.Meta<any>, description: string, skipMin = false) {
const { max = Infinity, min = -Infinity } = meta
if (data > max) throw new TypeError(`expected ${description} <= ${max} but got ${data}`)
if (data < min) throw new TypeError(`expected ${description} >= ${min} but got ${data}`)
if (data < min && !skipMin) throw new TypeError(`expected ${description} >= ${min} but got ${data}`)
}

Schema.extend('string', (data, { meta }) => {
Expand Down Expand Up @@ -519,7 +519,8 @@ function property(data: any, key: keyof any, schema: Schema, options?: Schemaste

Schema.extend('array', (data, { inner, meta }, options) => {
if (!Array.isArray(data)) throw new TypeError(`expected array but got ${data}`)
checkWithinRange(data.length, meta, 'array length')
while (data.length && isNullable(data[data.length - 1])) data.pop()
checkWithinRange(data.length, meta, 'array length', !isNullable(inner!.meta.default))
return [data.map((_, index) => property(data, index, inner!, options))]
})

Expand Down
14 changes: 9 additions & 5 deletions packages/form/src/extensions/group.vue
Expand Up @@ -6,7 +6,11 @@
<template #prefix><slot name="prefix"></slot></template>
<template #suffix><slot name="suffix"></slot></template>
<template #control>
<el-button @click="insert(entries.length)" :disabled="disabled">{{ t('entry.add-item') }}</el-button>
<el-button
v-if="!isFixedLength"
@click="insert(entries.length)"
:disabled="disabled || isMax"
>{{ t('entry.add-item') }}</el-button>
</template>
<template #collapse>
<k-schema
Expand All @@ -33,15 +37,15 @@
<span class="k-menu-icon"><icon-arrow-down></icon-arrow-down></span>
{{ t('entry.move-down') }}
</div>
<div class="k-menu-item" :class="{ disabled }" @click="del(index)">
<div v-if="!isFixedLength" class="k-menu-item" :class="{ disabled: disabled || isMin }" @click="del(index)">
<span class="k-menu-icon"><icon-delete></icon-delete></span>
{{ t('entry.del-item') }}
</div>
<div class="k-menu-item" :class="{ disabled }" @click="insert(index)">
<div v-if="!isFixedLength" class="k-menu-item" :class="{ disabled: disabled || isMax }" @click="insert(index)">
<span class="k-menu-icon"><icon-insert-before></icon-insert-before></span>
{{ t('entry.insert-before') }}
</div>
<div class="k-menu-item" :class="{ disabled }" @click="insert(index + 1)">
<div v-if="!isFixedLength" class="k-menu-item" :class="{ disabled: disabled || isMax }" @click="insert(index + 1)">
<span class="k-menu-icon"><icon-insert-after></icon-insert-after></span>
{{ t('entry.insert-after') }}
</div>
Expand Down Expand Up @@ -85,7 +89,7 @@ defineProps({
defineEmits(['update:modelValue'])
const { entries, up, down, insert, del } = useEntries()
const { entries, up, down, insert, del, isMax, isMin, isFixedLength } = useEntries()
const { t, setLocaleMessage } = useI18n({
messages: {
Expand Down
22 changes: 15 additions & 7 deletions packages/form/src/extensions/table.vue
Expand Up @@ -6,7 +6,11 @@
<template #prefix><slot name="prefix"></slot></template>
<template #suffix><slot name="suffix"></slot></template>
<template #control>
<el-button @click="insert(entries.length)" :disabled="disabled">{{ t('entry.add-row') }}</el-button>
<el-button
v-if="!isFixedLength"
@click="insert(entries.length)"
:disabled="disabled || isMax"
>{{ t('entry.add-row') }}</el-button>
</template>
<div class="bottom k-schema-table-container" ref="container" v-if="columns && entries.length">
<table class="k-schema-table">
Expand Down Expand Up @@ -62,24 +66,28 @@
</td>
<td v-if="!disabled" class="button"
:class="{ disabled: !i }"
@click.stop="up(i)"
@mouseenter="handleMouseEnter($event, null)"
@mouseleave="handleMouseLeave($event, null)">
<div class="inner" @click.stop="up(i)">
<div class="inner">
<icon-arrow-up></icon-arrow-up>
</div>
</td>
<td v-if="!disabled" class="button"
:class="{ disabled: i === entries.length - 1 }"
@click.stop="down(i)"
@mouseenter="handleMouseEnter($event, null)"
@mouseleave="handleMouseLeave($event, null)">
<div class="inner" @click.stop="down(i)">
<div class="inner">
<icon-arrow-down></icon-arrow-down>
</div>
</td>
<td v-if="!disabled" class="button"
<td v-if="!disabled && !isFixedLength" class="button"
:class="{ disabled: isMin }"
@click.stop="del(i)"
@mouseenter="handleMouseEnter($event, null)"
@mouseleave="handleMouseLeave($event, null)">
<div class="inner" @click.stop="del(i)">
<div class="inner">
<icon-delete></icon-delete>
</div>
</td>
Expand Down Expand Up @@ -124,7 +132,7 @@ defineEmits(['update:modelValue'])

const columns = computed(() => toColumns(props.schema.inner))

const { entries, insert, del, up, down } = useEntries()
const { entries, insert, del, up, down, isMax, isMin, isFixedLength } = useEntries()

interface Rect {
el: HTMLElement
Expand Down Expand Up @@ -257,6 +265,7 @@ if (import.meta.hot) {
td.button {
width: 2rem;
color: var(--k-text-light);
cursor: pointer;

&:hover {
color: var(--k-color-active);
Expand All @@ -268,7 +277,6 @@ if (import.meta.hot) {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}

.k-icon {
Expand Down
21 changes: 20 additions & 1 deletion packages/form/src/utils.ts
Expand Up @@ -130,7 +130,16 @@ export function useEntries() {

const entries = useModel<[string, any][]>({
strict: true,
input: (config) => Object.entries(config),
input: (config) => {
const result = Object.entries(config)
if (props.schema.type === 'array') {
const padding = (props.schema.meta.min ?? 0) - result.length
for (let i = 0; i < padding; i++) {
result.push(['' + result.length, null])
}
}
return result
},
output: (config) => {
if (props.schema.type === 'array') {
return config.map(([, value]) => value)
Expand All @@ -144,8 +153,18 @@ export function useEntries() {
},
})

const isFixedLength = computed(() => {
return props.schema.meta.min && props.schema.meta.min === props.schema.meta.max
})

const isMax = computed(() => entries.value.length >= props.schema.meta.max)
const isMin = computed(() => entries.value.length >= props.schema.meta.max)

return {
entries,
isMax,
isMin,
isFixedLength,
up(index: number) {
if (props.schema.type === 'dict') {
entries.value.splice(index - 1, 0, ...entries.value.splice(index, 1))
Expand Down

0 comments on commit 0de846e

Please sign in to comment.