@veltra/desktop 1.7.10 → 1.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/button/button.js +0 -1
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/drawer/drawer.js +18 -10
- package/dist/components/drawer/drawer.js.map +1 -1
- package/dist/components/drawer/style2.css +15 -0
- package/dist/components/form-item/form-item.js.map +1 -1
- package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js +13 -6
- package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/types/action.d.ts +6 -1
- package/dist/types/button.d.ts +5 -1
- package/dist/types/drawer.d.ts +6 -3
- package/dist/types/layout.d.ts +2 -0
- package/package.json +5 -5
- package/src/components/button/__test__/button.test.ts +48 -0
- package/src/components/button/button.vue +1 -8
- package/src/components/drawer/__test__/drawer.test.ts +118 -0
- package/src/components/drawer/drawer.vue +5 -2
- package/src/components/drawer/style.scss +15 -0
- package/src/components/form/__test__/form.test.ts +45 -1
- package/src/components/form-item/__test__/form-item.test.ts +40 -1
- package/src/components/form-item/form-item.vue +12 -8
- package/src/types/action.ts +6 -1
- package/src/types/button.ts +5 -1
- package/src/types/drawer.ts +6 -3
- package/src/types/layout.ts +2 -0
|
@@ -65,7 +65,6 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
65
65
|
return withDirectives((openBlock(), createElementBlock("button", {
|
|
66
66
|
class: normalizeClass(classList.value),
|
|
67
67
|
type: "button",
|
|
68
|
-
"aria-label": "button",
|
|
69
68
|
ref_key: "buttonRef",
|
|
70
69
|
ref: buttonRef,
|
|
71
70
|
onClick: handleClick
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","names":[],"sources":["../../../src/components/button/button.vue"],"sourcesContent":["<template>\n <button
|
|
1
|
+
{"version":3,"file":"button.js","names":[],"sources":["../../../src/components/button/button.vue"],"sourcesContent":["<template>\n <button :class=\"classList\" type=\"button\" ref=\"buttonRef\" v-ripple=\"ripple\" @click=\"handleClick\">\n <!-- 加载图标 -->\n <u-icon v-if=\"loading\" :class=\"[bem.is('loading')]\" :size=\"iconSize\">\n <component :is=\"loadingIcon\" />\n </u-icon>\n\n <!-- 左侧图标 -->\n <u-icon v-if=\"!!icon && iconPosition === 'left' && !loading\" :size=\"iconSize\">\n <component :is=\"icon\" />\n </u-icon>\n\n <slot />\n\n <!-- 右侧图标 -->\n <u-icon v-if=\"!!icon && iconPosition === 'right'\" :size=\"iconSize\">\n <component :is=\"icon\" />\n </u-icon>\n </button>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFallbackProps } from '@veltra/compositions'\nimport { vRipple } from '@veltra/directives'\nimport { Loading } from '@veltra/icons'\nimport { bem } from '@veltra/utils'\nimport { computed, shallowRef } from 'vue'\n\nimport type { ButtonEmits, ButtonProps, _ButtonExposed, ComponentSize } from '../../types'\nimport { UIcon } from '../icon'\n\ndefineOptions({ name: 'UButton' })\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n iconPosition: 'left',\n loadingIcon: () => Loading,\n disabled: false,\n propagate: true\n})\n\nconst { size } = useFallbackProps([props], { size: 'default' as ComponentSize })\n\nconst emit = defineEmits<ButtonEmits>()\n\nconst cls = bem('button')\n\nconst classList = computed(() => {\n return [\n cls.b,\n cls.m(size.value),\n props.type && cls.m('color-' + props.type),\n bem.is('circle', props.circle),\n bem.is('disabled', props.disabled),\n bem.is('loading', props.loading),\n bem.is('plain', props.plain),\n bem.is('text', props.text)\n ]\n})\n\nconst ripple = computed(() => {\n if (props.disabled || props.loading || props.text) return false\n if (props.plain && props.type) {\n return bem.is(`ripple-${props.type}`)\n }\n return true\n})\n\nconst handleClick = (e: MouseEvent) => {\n if (props.disabled || props.loading) {\n e.stopPropagation()\n return\n }\n\n !props.propagate && e.stopPropagation()\n\n emit('click', e)\n}\n\nconst buttonRef = shallowRef<HTMLButtonElement>()\n\nconst exposed: _ButtonExposed = { el: buttonRef }\n\ndefineExpose(exposed)\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCA,MAAM,QAAQ;EAOd,MAAM,EAAE,SAAS,iBAAiB,CAAC,KAAK,GAAG,EAAE,MAAM,UAA2B,CAAC;EAE/E,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,QAAQ;EAExB,MAAM,YAAY,eAAe;GAC/B,OAAO;IACL,IAAI;IACJ,IAAI,EAAE,KAAK,KAAK;IAChB,MAAM,QAAQ,IAAI,EAAE,WAAW,MAAM,IAAI;IACzC,IAAI,GAAG,UAAU,MAAM,MAAM;IAC7B,IAAI,GAAG,YAAY,MAAM,QAAQ;IACjC,IAAI,GAAG,WAAW,MAAM,OAAO;IAC/B,IAAI,GAAG,SAAS,MAAM,KAAK;IAC3B,IAAI,GAAG,QAAQ,MAAM,IAAI;GAC3B;EACF,CAAC;EAED,MAAM,SAAS,eAAe;GAC5B,IAAI,MAAM,YAAY,MAAM,WAAW,MAAM,MAAM,OAAO;GAC1D,IAAI,MAAM,SAAS,MAAM,MACvB,OAAO,IAAI,GAAG,UAAU,MAAM,MAAM;GAEtC,OAAO;EACT,CAAC;EAED,MAAM,eAAe,MAAkB;GACrC,IAAI,MAAM,YAAY,MAAM,SAAS;IACnC,EAAE,gBAAgB;IAClB;GACF;GAEA,CAAC,MAAM,aAAa,EAAE,gBAAgB;GAEtC,KAAK,SAAS,CAAC;EACjB;EAEA,MAAM,YAAY,WAA8B;EAIhD,SAAa,EAFqB,IAAI,UAEzB,CAAO;;GAjFlB,OAAA,gBAAA,UAAA,GAAA,mBAiBS,UAAA;IAjBA,OAAK,eAAE,UAAA,KAAS;IAAE,MAAK;IAAa,SAAA;IAAJ,KAAI;IAA+B,SAAO;;IACjF,mBAAA,QAAA;IACc,QAAA,WAAd,UAAA,GAAA,YAES,MAAA,WAAA,GAAA;;KAFe,OAAK,eAAA,CAAG,MAAA,GAAA,CAAG,CAAC,GAAE,SAAA,CAAA,CAAA;KAAe,MAAM,QAAA;;KACzD,SAAA,cAA+B,EAA/B,UAAA,GAAA,YAA+B,wBAAf,QAAA,WAAW,CAAA,EAAA,CAAA;;;IAG7B,mBAAA,QAAA;IACgB,CAAA,CAAA,QAAA,QAAQ,QAAA,iBAAY,UAAA,CAAgB,QAAA,WAApD,UAAA,GAAA,YAES,MAAA,WAAA,GAAA;;KAFqD,MAAM,QAAA;;KAClE,SAAA,cAAwB,EAAxB,UAAA,GAAA,YAAwB,wBAAR,QAAA,IAAI,CAAA,EAAA,CAAA;;;IAGtB,WAAQ,KAAA,QAAA,SAAA;IAER,mBAAA,QAAA;IACgB,CAAA,CAAA,QAAA,QAAQ,QAAA,iBAAY,WAApC,UAAA,GAAA,YAES,MAAA,WAAA,GAAA;;KAF0C,MAAM,QAAA;;KACvD,SAAA,cAAwB,EAAxB,UAAA,GAAA,YAAwB,wBAAR,QAAA,IAAI,CAAA,EAAA,CAAA;;;GAf2C,GAAA,CAAA,IAAA,CAAA,CAAA,MAAA,OAAA,GAAA,OAAA,KAAM,CAAA,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import _sfc_main$1 from "../icon/icon.js";
|
|
2
|
-
import { Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createVNode, defineComponent, mergeModels, mergeProps, normalizeClass, normalizeStyle, openBlock, renderSlot, shallowRef, unref, useModel, withCtx, withModifiers } from "vue";
|
|
2
|
+
import { Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, mergeModels, mergeProps, normalizeClass, normalizeStyle, openBlock, renderSlot, shallowRef, toDisplayString, unref, useModel, withCtx, withModifiers } from "vue";
|
|
3
3
|
import { bem, zIndex } from "@veltra/utils";
|
|
4
4
|
import { Close } from "@veltra/icons/normal";
|
|
5
5
|
//#region src/components/drawer/drawer.vue
|
|
@@ -54,7 +54,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
54
54
|
return openBlock(), createBlock(Teleport, { to: "body" }, [createVNode(Transition, {
|
|
55
55
|
name: "fade",
|
|
56
56
|
mode: "out-in",
|
|
57
|
-
onEnter
|
|
57
|
+
onEnter,
|
|
58
|
+
onAfterLeave: _cache[1] || (_cache[1] = ($event) => emit("closed"))
|
|
58
59
|
}, {
|
|
59
60
|
default: withCtx(() => [overlayVisible.value ? (openBlock(), createElementBlock("div", {
|
|
60
61
|
key: 0,
|
|
@@ -70,14 +71,21 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
70
71
|
class: drawerClass.value,
|
|
71
72
|
style: drawerStyle.value,
|
|
72
73
|
onClick: _cache[0] || (_cache[0] = withModifiers(() => {}, ["stop"]))
|
|
73
|
-
}), [
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
}), [
|
|
75
|
+
__props.title ? (openBlock(), createElementBlock("div", {
|
|
76
|
+
key: 0,
|
|
77
|
+
class: normalizeClass(unref(cls).e("header"))
|
|
78
|
+
}, [createElementVNode("span", { class: normalizeClass(unref(cls).e("title")) }, toDisplayString(__props.title), 3)], 2)) : createCommentVNode("v-if", true),
|
|
79
|
+
__props.showClose ? (openBlock(), createElementBlock("span", {
|
|
80
|
+
key: 1,
|
|
81
|
+
class: normalizeClass(unref(cls).e("close")),
|
|
82
|
+
onClick: close
|
|
83
|
+
}, [createVNode(unref(_sfc_main$1), null, {
|
|
84
|
+
default: withCtx(() => [createVNode(unref(Close))]),
|
|
85
|
+
_: 1
|
|
86
|
+
})], 2)) : createCommentVNode("v-if", true),
|
|
87
|
+
renderSlot(_ctx.$slots, "default")
|
|
88
|
+
], 16)) : createCommentVNode("v-if", true)]),
|
|
81
89
|
_: 3
|
|
82
90
|
}, 8, ["name"])], 6)) : createCommentVNode("v-if", true)]),
|
|
83
91
|
_: 3
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.js","names":["$attrs"],"sources":["../../../src/components/drawer/drawer.vue"],"sourcesContent":["<template>\n <Teleport to=\"body\">\n <transition name=\"fade\" mode=\"out-in\" @enter=\"onEnter\">\n <div v-if=\"overlayVisible\" :class=\"overlayCls.b\" :style=\"{ zIndex: zIndex() }\" @click=\"close\">\n <transition :name=\"transitionName\" appear @after-leave=\"onAfterDrawerLeave\">\n <div\n v-bind=\"$attrs\"\n v-if=\"drawerVisible\"\n :class=\"drawerClass\"\n :style=\"drawerStyle\"\n @click.stop\n >\n <span :class=\"cls.e('close')\" @click=\"close\" v-if=\"showClose\">\n <u-icon><Close /></u-icon>\n </span>\n <slot />\n </div>\n </transition>\n </div>\n </transition>\n </Teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Close } from '@veltra/icons/normal'\nimport { bem, zIndex } from '@veltra/utils'\nimport { computed, shallowRef } from 'vue'\n\nimport type { DrawerProps, DrawerEmits } from '../../types'\nimport { UIcon } from '../icon'\n\ndefineOptions({ name: 'UDrawer', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<DrawerProps>(), { direction: 'right'
|
|
1
|
+
{"version":3,"file":"drawer.js","names":["$attrs"],"sources":["../../../src/components/drawer/drawer.vue"],"sourcesContent":["<template>\n <Teleport to=\"body\">\n <transition name=\"fade\" mode=\"out-in\" @enter=\"onEnter\" @after-leave=\"emit('closed')\">\n <div v-if=\"overlayVisible\" :class=\"overlayCls.b\" :style=\"{ zIndex: zIndex() }\" @click=\"close\">\n <transition :name=\"transitionName\" appear @after-leave=\"onAfterDrawerLeave\">\n <div\n v-bind=\"$attrs\"\n v-if=\"drawerVisible\"\n :class=\"drawerClass\"\n :style=\"drawerStyle\"\n @click.stop\n >\n <div v-if=\"title\" :class=\"cls.e('header')\">\n <span :class=\"cls.e('title')\">{{ title }}</span>\n </div>\n <span :class=\"cls.e('close')\" @click=\"close\" v-if=\"showClose\">\n <u-icon><Close /></u-icon>\n </span>\n <slot />\n </div>\n </transition>\n </div>\n </transition>\n </Teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Close } from '@veltra/icons/normal'\nimport { bem, zIndex } from '@veltra/utils'\nimport { computed, shallowRef } from 'vue'\n\nimport type { DrawerProps, DrawerEmits } from '../../types'\nimport { UIcon } from '../icon'\n\ndefineOptions({ name: 'UDrawer', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<DrawerProps>(), { direction: 'right' })\n\nconst emit = defineEmits<DrawerEmits>()\n\nconst cls = bem('drawer')\nconst overlayCls = bem('drawer-overlay')\n\nconst overlayVisible = defineModel<boolean>({ default: false })\n\nconst drawerVisible = shallowRef(false)\n\n// 根据方向计算过渡动画名称\nconst transitionName = computed(() => {\n return `drawer-slide-${props.direction}`\n})\n\n// 计算抽屉样式类\nconst drawerClass = computed(() => {\n return [cls.b, bem.is(props.direction)]\n})\n\n// 计算抽屉样式\nconst drawerStyle = computed(() => {\n const style: Record<string, string> = {}\n\n return style\n})\n\nconst onEnter = () => {\n drawerVisible.value = true\n}\n\nconst onAfterDrawerLeave = () => {\n overlayVisible.value = false\n}\n\n// 关闭抽屉\nconst close = () => {\n drawerVisible.value = false\n emit('close')\n}\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;EAoCA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,QAAQ;EACxB,MAAM,aAAa,IAAI,gBAAgB;EAEvC,MAAM,iBAAiB,SAAoB,SAAA,YAAmB;EAE9D,MAAM,gBAAgB,WAAW,KAAK;EAGtC,MAAM,iBAAiB,eAAe;GACpC,OAAO,gBAAgB,MAAM;EAC/B,CAAC;EAGD,MAAM,cAAc,eAAe;GACjC,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,MAAM,SAAS,CAAC;EACxC,CAAC;EAGD,MAAM,cAAc,eAAe;GAGjC,OAAO,CAAA;EACT,CAAC;EAED,MAAM,gBAAgB;GACpB,cAAc,QAAQ;EACxB;EAEA,MAAM,2BAA2B;GAC/B,eAAe,QAAQ;EACzB;EAGA,MAAM,cAAc;GAClB,cAAc,QAAQ;GACtB,KAAK,OAAO;EACd;;GA3EE,OAAA,UAAA,GAAA,YAsBW,UAAA,EAtBD,IAAG,OAAM,GAAA,CACjB,YAoBa,YAAA;IApBD,MAAK;IAAO,MAAK;IAAiB;IAAU,cAAW,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,QAAA;;IACvE,SAAA,cAkBM,CAlBK,eAAA,SAAX,UAAA,GAAA,mBAkBM,OAAA;;KAlBsB,OAAK,eAAE,MAAA,UAAA,CAAU,CAAC,CAAC;KAAG,OAAK,eAAA,EAAA,QAAY,MAAA,MAAA,CAAM,CAAA,EAAA,CAAA;KAAO,SAAO;IACrF,GAAA,CAAA,YAgBa,YAAA;KAhBA,MAAM,eAAA;KAAgB,QAAA;KAAQ,cAAa;;KACtD,SAAA,cAcM,CAZE,cAAA,SAFR,UAAA,GAAA,mBAcM,OAdN,WAcM,EAAA,KAAA,EAAA,GAbIA,KAAAA,QAAM;MAEb,OAAO,YAAA;MACP,OAAO,YAAA;MACP,SAAK,OAAA,OAAA,OAAA,KAAA,oBAAN,CAAA,GAAW,CAAA,MAAA,CAAA;;MAEA,QAAA,SAAX,UAAA,GAAA,mBAEM,OAAA;;OAFa,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,QAAA,CAAA;MAC7B,GAAA,CAAA,mBAAgD,QAAA,EAAzC,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,OAAA,CAAA,EAAc,GAAA,gBAAA,QAAA,KAAK,GAAA,CAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,QAAA,IAAA;MAEW,QAAA,aAAnD,UAAA,GAAA,mBAEO,QAAA;;OAFA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,OAAA,CAAA;OAAY,SAAO;MACpC,GAAA,CAAA,YAA0B,MAAA,WAAA,GAAA,MAAA;OAAlB,SAAA,cAAS,CAAT,YAAS,MAAA,KAAA,CAAA,CAAA,CAAA;;;MAEnB,WAAQ,KAAA,QAAA,SAAA"}
|
|
@@ -34,6 +34,21 @@
|
|
|
34
34
|
align-items: center;
|
|
35
35
|
justify-content: center;
|
|
36
36
|
}
|
|
37
|
+
.u-drawer__header {
|
|
38
|
+
height: 48px;
|
|
39
|
+
flex-shrink: 0;
|
|
40
|
+
padding: 0 var(--u-gap-default);
|
|
41
|
+
font-size: var(--u-font-size-title-default);
|
|
42
|
+
border-bottom: var(--u-border);
|
|
43
|
+
display: flex;
|
|
44
|
+
justify-content: flex-start;
|
|
45
|
+
align-items: center;
|
|
46
|
+
flex-wrap: nowrap;
|
|
47
|
+
}
|
|
48
|
+
.u-drawer__title {
|
|
49
|
+
font-weight: 600;
|
|
50
|
+
line-height: 1em;
|
|
51
|
+
}
|
|
37
52
|
.u-drawer.is-right {
|
|
38
53
|
top: 4px;
|
|
39
54
|
bottom: 4px;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-item.js","names":[],"sources":["../../../src/components/form-item/form-item.vue"],"sourcesContent":["<template>\n <u-grid-item :span=\"span\" :class=\"className\">\n <label\n v-if=\"props.label || $slots.label\"\n :class=\"[cls.e('label'), bem.is('required', !!rules?.required)]\"\n :style=\"labelStyles\"\n >\n <u-tip v-if=\"tips\" :content=\"tips\" :class=\"cls.e('tips')\" :show-delay=\"500\">\n <span :class=\"cls.e('tip-label')\">\n <slot name=\"label\">{{ label }}{{ labelPosition === 'top' ? '' : ':' }}</slot>\n </span>\n </u-tip>\n <template v-else>\n <slot name=\"label\">{{ label }}{{ labelPosition === 'top' ? '' : ':' }}</slot>\n </template>\n </label>\n\n <section :class=\"cls.e('wrapper')\">\n <div :class=\"cls.e('content')\">\n <ContentSlot
|
|
1
|
+
{"version":3,"file":"form-item.js","names":[],"sources":["../../../src/components/form-item/form-item.vue"],"sourcesContent":["<template>\n <u-grid-item :span=\"span\" :class=\"className\">\n <label\n v-if=\"props.label || $slots.label\"\n :class=\"[cls.e('label'), bem.is('required', !!rules?.required)]\"\n :style=\"labelStyles\"\n >\n <u-tip v-if=\"tips\" :content=\"tips\" :class=\"cls.e('tips')\" :show-delay=\"500\">\n <span :class=\"cls.e('tip-label')\">\n <slot name=\"label\">{{ label }}{{ labelPosition === 'top' ? '' : ':' }}</slot>\n </span>\n </u-tip>\n <template v-else>\n <slot name=\"label\">{{ label }}{{ labelPosition === 'top' ? '' : ':' }}</slot>\n </template>\n </label>\n\n <section :class=\"cls.e('wrapper')\">\n <div :class=\"cls.e('content')\">\n <ContentSlot>\n <slot></slot>\n </ContentSlot>\n </div>\n\n <!-- 只有表单控件处于非只读状态时,才显示错误提示 -->\n <section :class=\"cls.e('error')\" v-if=\"!readonly && !formProps?.noTips\">\n <transition name=\"form-item-tips\" mode=\"out-in\">\n <span :class=\"cls.e('error-text')\" v-if=\"!!errorTip\">\n {{ errorTip }}\n </span>\n </transition>\n </section>\n </section>\n </u-grid-item>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { o } from '@cat-kit/core'\nimport { useConfig, useFallbackProps } from '@veltra/compositions'\nimport { bem, extractNormalVNodes, withUnit } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport {\n type CSSProperties,\n type SetupContext,\n type VNode,\n cloneVNode,\n computed,\n onBeforeUnmount,\n shallowRef,\n watch\n} from 'vue'\n\nimport type { FormItemEmits, FormItemProps, ComponentSize } from '../../types'\nimport { UGridItem } from '../grid'\nimport { UTip } from '../tip'\nimport { formItemCls as cls, defineField } from './helper'\nimport { validateField } from './validate'\n\ndefineOptions({ name: 'UFormItem' })\n\nconst props = withDefaults(defineProps<FormItemProps>(), { readonly: undefined })\n\nconst emit = defineEmits<FormItemEmits>()\n\ndefineSlots<{\n /** 标签插槽 */\n label?: () => any\n default?: () => any\n}>()\n\n/** 表单组件上下文 */\nconst {\n formProps,\n registerField,\n unregisterField,\n shouldValidate,\n handleFieldUpdate,\n handleFieldChange\n} = injectFormContext()\n\nfunction wrapControlChange(node: VNode) {\n if (!node || typeof node.type === 'symbol') return node\n\n return cloneVNode(node, {\n onChange: (...args: any[]) => {\n emit('change', ...args)\n if (props.field) handleFieldChange?.(props.field, ...args)\n }\n })\n}\n\n/**\n * 承载默认插槽并拦截控件 change。\n * 必须经填充插槽接收内容(而非直接读 UFormItem 的 $slots):\n * 读父级 $slots 时组件自身没有更新路径,父级重渲染后动态 props(disabled、data 等)会冻结在首帧。\n */\nfunction ContentSlot(_: unknown, { slots }: SetupContext) {\n return extractNormalVNodes(slots.default?.() ?? []).map(wrapControlChange)\n}\n\nconst { config } = useConfig()\n\nconst { size, readonly } = useFallbackProps([formProps ?? {}, props], {\n size: 'default' as ComponentSize,\n readonly: false\n})\n\nconst errorTip = shallowRef<string>()\n/** 递增序号,丢弃过期的异步校验结果 */\nlet validateSeq = 0\n/** 最近一次 validate 的 Promise,供过期调用等待最新结果 */\nlet latestValidatePromise: Promise<boolean> = Promise.resolve(true)\n\nconst labelPosition = computed(() => props.labelPosition ?? formProps?.labelPosition ?? 'left')\n\nconst className = computed(() => {\n return [\n cls.b,\n cls.m(size.value),\n bem.is('error', !!errorTip.value),\n bem.is('label-top', labelPosition.value === 'top')\n ].join(' ')\n})\n\n/** label样式 */\nconst labelStyles = computed<CSSProperties>(() => {\n if (labelPosition.value === 'top') return {}\n return {\n width: withUnit(props.labelWidth ?? formProps?.labelWidth ?? config.form.labelWidth, 'px')\n }\n})\n\nconst fieldItem = defineField({\n clearValidate() {\n errorTip.value = ''\n },\n async validate() {\n if (!props.field || !formProps?.model || !props.rules || !shouldValidate?.()) return true\n\n const seq = ++validateSeq\n\n latestValidatePromise = validateField(formProps.model!, props.field!, props.rules!).then(\n (tip) => {\n if (seq !== validateSeq) return latestValidatePromise\n\n errorTip.value = tip\n return !errorTip.value\n }\n )\n return latestValidatePromise\n }\n})\n\nwatch(\n () => props.field,\n (field, oldField) => {\n if (oldField) {\n unregisterField?.(oldField)\n }\n if (field) {\n registerField?.(field, fieldItem)\n }\n },\n { immediate: true }\n)\n\nlet stopWatchFieldValue: (() => void) | undefined\n\nwatch(\n [() => formProps?.model, () => props.field],\n ([model, field]) => {\n stopWatchFieldValue?.()\n\n if (!field || !model) return\n\n stopWatchFieldValue = watch(\n () => o(model).get(field),\n (value) => {\n handleFieldUpdate?.(field, value)\n fieldItem.validate()\n }\n )\n },\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n props.field && unregisterField?.(props.field)\n})\n</script>\n"],"mappings":""}
|
|
@@ -2,9 +2,9 @@ import _sfc_main from "../tip/tip.js";
|
|
|
2
2
|
import _sfc_main$1 from "../grid/grid-item.js";
|
|
3
3
|
import { defineField, formItemCls } from "./helper.js";
|
|
4
4
|
import { validateField } from "./validate.js";
|
|
5
|
-
import { Transition, cloneVNode, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, renderSlot, shallowRef, toDisplayString, unref,
|
|
5
|
+
import { Transition, cloneVNode, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, renderSlot, shallowRef, toDisplayString, unref, watch, withCtx } from "vue";
|
|
6
6
|
import { useConfig, useFallbackProps } from "@veltra/compositions";
|
|
7
|
-
import { bem, injectFormContext, withUnit } from "@veltra/utils";
|
|
7
|
+
import { bem, extractNormalVNodes, injectFormContext, withUnit } from "@veltra/utils";
|
|
8
8
|
import { o } from "@cat-kit/core";
|
|
9
9
|
//#region src/components/form-item/form-item.vue?vue&type=script&setup=true&lang.tsx
|
|
10
10
|
var form_item_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
@@ -29,7 +29,6 @@ var form_item_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
|
|
|
29
29
|
setup(__props, { emit: __emit }) {
|
|
30
30
|
const props = __props;
|
|
31
31
|
const emit = __emit;
|
|
32
|
-
const slots = useSlots();
|
|
33
32
|
/** 表单组件上下文 */
|
|
34
33
|
const { formProps, registerField, unregisterField, shouldValidate, handleFieldUpdate, handleFieldChange } = injectFormContext();
|
|
35
34
|
function wrapControlChange(node) {
|
|
@@ -39,8 +38,13 @@ var form_item_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
|
|
|
39
38
|
if (props.field) handleFieldChange?.(props.field, ...args);
|
|
40
39
|
} });
|
|
41
40
|
}
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
/**
|
|
42
|
+
* 承载默认插槽并拦截控件 change。
|
|
43
|
+
* 必须经填充插槽接收内容(而非直接读 UFormItem 的 $slots):
|
|
44
|
+
* 读父级 $slots 时组件自身没有更新路径,父级重渲染后动态 props(disabled、data 等)会冻结在首帧。
|
|
45
|
+
*/
|
|
46
|
+
function ContentSlot(_, { slots }) {
|
|
47
|
+
return extractNormalVNodes(slots.default?.() ?? []).map(wrapControlChange);
|
|
44
48
|
}
|
|
45
49
|
const { config } = useConfig();
|
|
46
50
|
const { size, readonly } = useFallbackProps([formProps ?? {}, props], {
|
|
@@ -115,7 +119,10 @@ var form_item_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
|
|
|
115
119
|
default: withCtx(() => [createElementVNode("span", { class: normalizeClass(unref(formItemCls).e("tip-label")) }, [renderSlot(_ctx.$slots, "label", {}, () => [createTextVNode(toDisplayString(__props.label) + toDisplayString(labelPosition.value === "top" ? "" : ":"), 1)])], 2)]),
|
|
116
120
|
_: 3
|
|
117
121
|
}, 8, ["content", "class"])) : renderSlot(_ctx.$slots, "label", {}, () => [createTextVNode(toDisplayString(__props.label) + toDisplayString(labelPosition.value === "top" ? "" : ":"), 1)], void 0, 1)], 6)) : createCommentVNode("v-if", true), createElementVNode("section", { class: normalizeClass(unref(formItemCls).e("wrapper")) }, [
|
|
118
|
-
createElementVNode("div", { class: normalizeClass(unref(formItemCls).e("content")) }, [createVNode(ContentSlot
|
|
122
|
+
createElementVNode("div", { class: normalizeClass(unref(formItemCls).e("content")) }, [createVNode(ContentSlot, null, {
|
|
123
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
124
|
+
_: 3
|
|
125
|
+
})], 2),
|
|
119
126
|
createCommentVNode(" 只有表单控件处于非只读状态时,才显示错误提示 "),
|
|
120
127
|
!unref(readonly) && !unref(formProps)?.noTips ? (openBlock(), createElementBlock("section", {
|
|
121
128
|
key: 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-item.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/form-item/form-item.vue"],"sourcesContent":["<template>\n <u-grid-item :span=\"span\" :class=\"className\">\n <label\n v-if=\"props.label || $slots.label\"\n :class=\"[cls.e('label'), bem.is('required', !!rules?.required)]\"\n :style=\"labelStyles\"\n >\n <u-tip v-if=\"tips\" :content=\"tips\" :class=\"cls.e('tips')\" :show-delay=\"500\">\n <span :class=\"cls.e('tip-label')\">\n <slot name=\"label\">{{ label }}{{ labelPosition === 'top' ? '' : ':' }}</slot>\n </span>\n </u-tip>\n <template v-else>\n <slot name=\"label\">{{ label }}{{ labelPosition === 'top' ? '' : ':' }}</slot>\n </template>\n </label>\n\n <section :class=\"cls.e('wrapper')\">\n <div :class=\"cls.e('content')\">\n <ContentSlot
|
|
1
|
+
{"version":3,"file":"form-item.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/components/form-item/form-item.vue"],"sourcesContent":["<template>\n <u-grid-item :span=\"span\" :class=\"className\">\n <label\n v-if=\"props.label || $slots.label\"\n :class=\"[cls.e('label'), bem.is('required', !!rules?.required)]\"\n :style=\"labelStyles\"\n >\n <u-tip v-if=\"tips\" :content=\"tips\" :class=\"cls.e('tips')\" :show-delay=\"500\">\n <span :class=\"cls.e('tip-label')\">\n <slot name=\"label\">{{ label }}{{ labelPosition === 'top' ? '' : ':' }}</slot>\n </span>\n </u-tip>\n <template v-else>\n <slot name=\"label\">{{ label }}{{ labelPosition === 'top' ? '' : ':' }}</slot>\n </template>\n </label>\n\n <section :class=\"cls.e('wrapper')\">\n <div :class=\"cls.e('content')\">\n <ContentSlot>\n <slot></slot>\n </ContentSlot>\n </div>\n\n <!-- 只有表单控件处于非只读状态时,才显示错误提示 -->\n <section :class=\"cls.e('error')\" v-if=\"!readonly && !formProps?.noTips\">\n <transition name=\"form-item-tips\" mode=\"out-in\">\n <span :class=\"cls.e('error-text')\" v-if=\"!!errorTip\">\n {{ errorTip }}\n </span>\n </transition>\n </section>\n </section>\n </u-grid-item>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { o } from '@cat-kit/core'\nimport { useConfig, useFallbackProps } from '@veltra/compositions'\nimport { bem, extractNormalVNodes, withUnit } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport {\n type CSSProperties,\n type SetupContext,\n type VNode,\n cloneVNode,\n computed,\n onBeforeUnmount,\n shallowRef,\n watch\n} from 'vue'\n\nimport type { FormItemEmits, FormItemProps, ComponentSize } from '../../types'\nimport { UGridItem } from '../grid'\nimport { UTip } from '../tip'\nimport { formItemCls as cls, defineField } from './helper'\nimport { validateField } from './validate'\n\ndefineOptions({ name: 'UFormItem' })\n\nconst props = withDefaults(defineProps<FormItemProps>(), { readonly: undefined })\n\nconst emit = defineEmits<FormItemEmits>()\n\ndefineSlots<{\n /** 标签插槽 */\n label?: () => any\n default?: () => any\n}>()\n\n/** 表单组件上下文 */\nconst {\n formProps,\n registerField,\n unregisterField,\n shouldValidate,\n handleFieldUpdate,\n handleFieldChange\n} = injectFormContext()\n\nfunction wrapControlChange(node: VNode) {\n if (!node || typeof node.type === 'symbol') return node\n\n return cloneVNode(node, {\n onChange: (...args: any[]) => {\n emit('change', ...args)\n if (props.field) handleFieldChange?.(props.field, ...args)\n }\n })\n}\n\n/**\n * 承载默认插槽并拦截控件 change。\n * 必须经填充插槽接收内容(而非直接读 UFormItem 的 $slots):\n * 读父级 $slots 时组件自身没有更新路径,父级重渲染后动态 props(disabled、data 等)会冻结在首帧。\n */\nfunction ContentSlot(_: unknown, { slots }: SetupContext) {\n return extractNormalVNodes(slots.default?.() ?? []).map(wrapControlChange)\n}\n\nconst { config } = useConfig()\n\nconst { size, readonly } = useFallbackProps([formProps ?? {}, props], {\n size: 'default' as ComponentSize,\n readonly: false\n})\n\nconst errorTip = shallowRef<string>()\n/** 递增序号,丢弃过期的异步校验结果 */\nlet validateSeq = 0\n/** 最近一次 validate 的 Promise,供过期调用等待最新结果 */\nlet latestValidatePromise: Promise<boolean> = Promise.resolve(true)\n\nconst labelPosition = computed(() => props.labelPosition ?? formProps?.labelPosition ?? 'left')\n\nconst className = computed(() => {\n return [\n cls.b,\n cls.m(size.value),\n bem.is('error', !!errorTip.value),\n bem.is('label-top', labelPosition.value === 'top')\n ].join(' ')\n})\n\n/** label样式 */\nconst labelStyles = computed<CSSProperties>(() => {\n if (labelPosition.value === 'top') return {}\n return {\n width: withUnit(props.labelWidth ?? formProps?.labelWidth ?? config.form.labelWidth, 'px')\n }\n})\n\nconst fieldItem = defineField({\n clearValidate() {\n errorTip.value = ''\n },\n async validate() {\n if (!props.field || !formProps?.model || !props.rules || !shouldValidate?.()) return true\n\n const seq = ++validateSeq\n\n latestValidatePromise = validateField(formProps.model!, props.field!, props.rules!).then(\n (tip) => {\n if (seq !== validateSeq) return latestValidatePromise\n\n errorTip.value = tip\n return !errorTip.value\n }\n )\n return latestValidatePromise\n }\n})\n\nwatch(\n () => props.field,\n (field, oldField) => {\n if (oldField) {\n unregisterField?.(oldField)\n }\n if (field) {\n registerField?.(field, fieldItem)\n }\n },\n { immediate: true }\n)\n\nlet stopWatchFieldValue: (() => void) | undefined\n\nwatch(\n [() => formProps?.model, () => props.field],\n ([model, field]) => {\n stopWatchFieldValue?.()\n\n if (!field || !model) return\n\n stopWatchFieldValue = watch(\n () => o(model).get(field),\n (value) => {\n handleFieldUpdate?.(field, value)\n fieldItem.validate()\n }\n )\n },\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n props.field && unregisterField?.(props.field)\n})\n</script>\n"],"mappings":"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"}
|
package/dist/types/action.d.ts
CHANGED
|
@@ -3,7 +3,12 @@ import { ColorType, DeconstructValue } from "@veltra/utils";
|
|
|
3
3
|
//#region src/types/action.d.ts
|
|
4
4
|
/** 操作组件属性 */
|
|
5
5
|
interface ActionProps extends ButtonProps {
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* 是否需要确认:true 时点击只弹确认气泡(UPopConfirm),点气泡里的「确认」才触发 `run`
|
|
8
|
+
* @default false
|
|
9
|
+
* @description 不要在本项的 `run` 回调里再调 `messageConfirm`:`run` 触发时气泡已经确认过一次,
|
|
10
|
+
* 会变成两次确认。需要弹窗级确认时去掉 `needConfirm`,只在 `run` 里用 `messageConfirm`
|
|
11
|
+
*/
|
|
7
12
|
needConfirm?: boolean;
|
|
8
13
|
/**
|
|
9
14
|
* 是否始终位于下拉菜单中,无视 `max` 限制
|
package/dist/types/button.d.ts
CHANGED
|
@@ -3,7 +3,11 @@ import { ColorType, ComponentProps, DeconstructValue } from "@veltra/utils";
|
|
|
3
3
|
//#region src/types/button.d.ts
|
|
4
4
|
/** 按钮类型 */
|
|
5
5
|
type ButtonType = ColorType;
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* 按钮属性类型
|
|
8
|
+
* @description 组件不注入默认 `aria-label`:可访问名来自默认插槽文本,
|
|
9
|
+
* 或你自己传入的 `aria-label`(透传到根元素);只有图标的按钮必须自行传 `aria-label`
|
|
10
|
+
*/
|
|
7
11
|
interface ButtonProps extends ComponentProps {
|
|
8
12
|
/** 按钮类型 */
|
|
9
13
|
type?: ButtonType;
|
package/dist/types/drawer.d.ts
CHANGED
|
@@ -12,15 +12,18 @@ interface DrawerProps {
|
|
|
12
12
|
direction?: DrawerDirection;
|
|
13
13
|
/** 是否显示关闭按钮 */
|
|
14
14
|
showClose?: boolean;
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* 抽屉标题;传入时在内容区上方渲染标题栏
|
|
17
|
+
* @description 不传则不渲染标题栏,内容仍全部来自默认插槽
|
|
18
|
+
*/
|
|
16
19
|
title?: string;
|
|
17
20
|
}
|
|
18
21
|
/** 抽屉组件定义的事件 */
|
|
19
22
|
interface DrawerEmits {
|
|
20
23
|
(e: 'update:modelValue', value: boolean): void;
|
|
21
|
-
/**
|
|
24
|
+
/** 开始关闭时触发(点遮罩、点关闭按钮) */
|
|
22
25
|
(e: 'close'): void;
|
|
23
|
-
/**
|
|
26
|
+
/** 完全关闭后触发:抽屉与遮罩的退出动画结束、节点已移除时 */
|
|
24
27
|
(e: 'closed'): void;
|
|
25
28
|
}
|
|
26
29
|
/** 抽屉组件暴露的属性和方法(组件内部使用) */
|
package/dist/types/layout.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ interface LayoutProps {
|
|
|
18
18
|
* const cols = ['200px', '1fr']
|
|
19
19
|
* ```
|
|
20
20
|
* [fr是什么?](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex_value)
|
|
21
|
+
* @description 默认插槽的每个直接子元素按顺序占一列,用自定义组件包一层时该组件必须是单根元素;
|
|
22
|
+
* 子元素渲染出 0 个(`v-if` 为假)或多个根节点时会挤动后面的列,此时自行给子元素写 `grid-column`
|
|
21
23
|
*/
|
|
22
24
|
cols?: string[] | string;
|
|
23
25
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veltra/desktop",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.11",
|
|
4
4
|
"homepage": "https://github.com/cabinet-fe/ultra-ui#readme",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/cabinet-fe/ultra-ui/issues"
|
|
@@ -80,12 +80,12 @@
|
|
|
80
80
|
"peerDependencies": {
|
|
81
81
|
"@cat-kit/core": ">=1.2.1",
|
|
82
82
|
"@cat-kit/fe": ">=1.2.1",
|
|
83
|
-
"@veltra/compositions": "^1.7.
|
|
84
|
-
"@veltra/directives": "^1.7.
|
|
83
|
+
"@veltra/compositions": "^1.7.11",
|
|
84
|
+
"@veltra/directives": "^1.7.11",
|
|
85
85
|
"@veltra/icons": "^1.5.0",
|
|
86
86
|
"@veltra/sheet-core": "^2.5.6",
|
|
87
|
-
"@veltra/styles": "^1.7.
|
|
88
|
-
"@veltra/utils": "^1.7.
|
|
87
|
+
"@veltra/styles": "^1.7.11",
|
|
88
|
+
"@veltra/utils": "^1.7.11",
|
|
89
89
|
"vue": ">=3.5.42"
|
|
90
90
|
},
|
|
91
91
|
"peerDependenciesMeta": {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { createApp, h } from 'vue'
|
|
3
|
+
|
|
4
|
+
import UButton from '../button.vue'
|
|
5
|
+
|
|
6
|
+
function mountButton(props: Record<string, unknown> = {}, slot = '新建用户') {
|
|
7
|
+
const host = document.createElement('div')
|
|
8
|
+
document.body.appendChild(host)
|
|
9
|
+
|
|
10
|
+
const app = createApp({ render: () => h(UButton, props, () => slot) })
|
|
11
|
+
app.mount(host)
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
host,
|
|
15
|
+
button: host.querySelector('button')!,
|
|
16
|
+
unmount() {
|
|
17
|
+
app.unmount()
|
|
18
|
+
host.remove()
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('UButton 原生属性与可访问名', () => {
|
|
24
|
+
it('不注入默认 aria-label:可访问名来自默认插槽文本', () => {
|
|
25
|
+
const { button, unmount } = mountButton()
|
|
26
|
+
|
|
27
|
+
expect(button.getAttribute('aria-label')).toBeNull()
|
|
28
|
+
expect(button.textContent?.trim()).toBe('新建用户')
|
|
29
|
+
|
|
30
|
+
unmount()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('自己传的 aria-label 透传到根元素,图标按钮仍可命名', () => {
|
|
34
|
+
const { button, unmount } = mountButton({ 'aria-label': '编辑' }, '')
|
|
35
|
+
|
|
36
|
+
expect(button.getAttribute('aria-label')).toBe('编辑')
|
|
37
|
+
|
|
38
|
+
unmount()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('原生 type 固定为 button,放进 form 里不会触发提交', () => {
|
|
42
|
+
const { button, unmount } = mountButton()
|
|
43
|
+
|
|
44
|
+
expect(button.getAttribute('type')).toBe('button')
|
|
45
|
+
|
|
46
|
+
unmount()
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<button
|
|
3
|
-
:class="classList"
|
|
4
|
-
type="button"
|
|
5
|
-
aria-label="button"
|
|
6
|
-
ref="buttonRef"
|
|
7
|
-
v-ripple="ripple"
|
|
8
|
-
@click="handleClick"
|
|
9
|
-
>
|
|
2
|
+
<button :class="classList" type="button" ref="buttonRef" v-ripple="ripple" @click="handleClick">
|
|
10
3
|
<!-- 加载图标 -->
|
|
11
4
|
<u-icon v-if="loading" :class="[bem.is('loading')]" :size="iconSize">
|
|
12
5
|
<component :is="loadingIcon" />
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { createApp, h, nextTick, ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
import UDrawer from '../drawer.vue'
|
|
5
|
+
|
|
6
|
+
function mountDrawer(
|
|
7
|
+
props: Record<string, unknown> = {},
|
|
8
|
+
handlers: { onClose?: () => void; onClosed?: () => void } = {}
|
|
9
|
+
) {
|
|
10
|
+
const host = document.createElement('div')
|
|
11
|
+
document.body.appendChild(host)
|
|
12
|
+
|
|
13
|
+
const model = ref(false)
|
|
14
|
+
const closedCount = ref(0)
|
|
15
|
+
const app = createApp({
|
|
16
|
+
render: () =>
|
|
17
|
+
h(
|
|
18
|
+
UDrawer,
|
|
19
|
+
{
|
|
20
|
+
...props,
|
|
21
|
+
modelValue: model.value,
|
|
22
|
+
'onUpdate:modelValue': (value: boolean) => {
|
|
23
|
+
model.value = value
|
|
24
|
+
},
|
|
25
|
+
onClose: () => handlers.onClose?.(),
|
|
26
|
+
onClosed: () => {
|
|
27
|
+
closedCount.value += 1
|
|
28
|
+
handlers.onClosed?.()
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
() => '抽屉内容'
|
|
32
|
+
)
|
|
33
|
+
})
|
|
34
|
+
app.mount(host)
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
host,
|
|
38
|
+
model,
|
|
39
|
+
closedCount,
|
|
40
|
+
panel: () => document.body.querySelector<HTMLElement>('.u-drawer'),
|
|
41
|
+
overlay: () => document.body.querySelector<HTMLElement>('.u-drawer-overlay'),
|
|
42
|
+
unmount() {
|
|
43
|
+
app.unmount()
|
|
44
|
+
host.remove()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function openDrawer(ctx: ReturnType<typeof mountDrawer>) {
|
|
50
|
+
ctx.model.value = true
|
|
51
|
+
await vi.waitFor(() => {
|
|
52
|
+
expect(ctx.panel()).toBeTruthy()
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
describe('UDrawer title', () => {
|
|
57
|
+
it('传入 title 时渲染标题栏', async () => {
|
|
58
|
+
const ctx = mountDrawer({ title: '用户详情' })
|
|
59
|
+
await openDrawer(ctx)
|
|
60
|
+
|
|
61
|
+
expect(ctx.panel()!.querySelector('.u-drawer__title')?.textContent).toBe('用户详情')
|
|
62
|
+
|
|
63
|
+
ctx.unmount()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('不传 title 时不渲染标题栏', async () => {
|
|
67
|
+
const ctx = mountDrawer()
|
|
68
|
+
await openDrawer(ctx)
|
|
69
|
+
|
|
70
|
+
expect(ctx.panel()!.querySelector('.u-drawer__header')).toBeNull()
|
|
71
|
+
|
|
72
|
+
ctx.unmount()
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe('UDrawer closed', () => {
|
|
77
|
+
it('关闭动画结束后触发 closed,且晚于 close', async () => {
|
|
78
|
+
const order: string[] = []
|
|
79
|
+
const ctx = mountDrawer(
|
|
80
|
+
{ showClose: true },
|
|
81
|
+
{ onClose: () => order.push('close'), onClosed: () => order.push('closed') }
|
|
82
|
+
)
|
|
83
|
+
await openDrawer(ctx)
|
|
84
|
+
|
|
85
|
+
ctx.panel()!.querySelector<HTMLElement>('.u-drawer__close')!.click()
|
|
86
|
+
await vi.waitFor(() => {
|
|
87
|
+
expect(order).toContain('closed')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
expect(order).toEqual(['close', 'closed'])
|
|
91
|
+
// 完全关闭后遮罩与抽屉都已移除
|
|
92
|
+
expect(ctx.overlay()).toBeNull()
|
|
93
|
+
|
|
94
|
+
ctx.unmount()
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('点遮罩关闭也触发 closed', async () => {
|
|
98
|
+
const ctx = mountDrawer()
|
|
99
|
+
await openDrawer(ctx)
|
|
100
|
+
|
|
101
|
+
ctx.overlay()!.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
|
|
102
|
+
await vi.waitFor(() => {
|
|
103
|
+
expect(ctx.closedCount.value).toBe(1)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
ctx.unmount()
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('从未打开时不触发 closed', async () => {
|
|
110
|
+
const ctx = mountDrawer()
|
|
111
|
+
await nextTick()
|
|
112
|
+
await nextTick()
|
|
113
|
+
|
|
114
|
+
expect(ctx.closedCount.value).toBe(0)
|
|
115
|
+
|
|
116
|
+
ctx.unmount()
|
|
117
|
+
})
|
|
118
|
+
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Teleport to="body">
|
|
3
|
-
<transition name="fade" mode="out-in" @enter="onEnter">
|
|
3
|
+
<transition name="fade" mode="out-in" @enter="onEnter" @after-leave="emit('closed')">
|
|
4
4
|
<div v-if="overlayVisible" :class="overlayCls.b" :style="{ zIndex: zIndex() }" @click="close">
|
|
5
5
|
<transition :name="transitionName" appear @after-leave="onAfterDrawerLeave">
|
|
6
6
|
<div
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
:style="drawerStyle"
|
|
11
11
|
@click.stop
|
|
12
12
|
>
|
|
13
|
+
<div v-if="title" :class="cls.e('header')">
|
|
14
|
+
<span :class="cls.e('title')">{{ title }}</span>
|
|
15
|
+
</div>
|
|
13
16
|
<span :class="cls.e('close')" @click="close" v-if="showClose">
|
|
14
17
|
<u-icon><Close /></u-icon>
|
|
15
18
|
</span>
|
|
@@ -31,7 +34,7 @@ import { UIcon } from '../icon'
|
|
|
31
34
|
|
|
32
35
|
defineOptions({ name: 'UDrawer', inheritAttrs: false })
|
|
33
36
|
|
|
34
|
-
const props = withDefaults(defineProps<DrawerProps>(), { direction: 'right'
|
|
37
|
+
const props = withDefaults(defineProps<DrawerProps>(), { direction: 'right' })
|
|
35
38
|
|
|
36
39
|
const emit = defineEmits<DrawerEmits>()
|
|
37
40
|
|
|
@@ -40,6 +40,21 @@ $direction-map: (
|
|
|
40
40
|
justify-content: center;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// 标题栏:传入 title 时渲染在内容区上方,其余行为与把标题写进默认插槽一致
|
|
44
|
+
@include m.e(header) {
|
|
45
|
+
height: $close-size;
|
|
46
|
+
flex-shrink: 0;
|
|
47
|
+
padding: 0 fn.use-var(gap, default);
|
|
48
|
+
font-size: fn.use-var(font-size-title, default);
|
|
49
|
+
border-bottom: fn.use-var(border);
|
|
50
|
+
@include m.flex();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@include m.e(title) {
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
line-height: 1em;
|
|
56
|
+
}
|
|
57
|
+
|
|
43
58
|
@include m.is(right, left) {
|
|
44
59
|
top: $edge-offset;
|
|
45
60
|
bottom: $edge-offset;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { createApp, h, nextTick, reactive } from 'vue'
|
|
2
|
+
import { createApp, h, nextTick, reactive, ref } from 'vue'
|
|
3
3
|
|
|
4
|
+
import { UInput } from '../../input'
|
|
4
5
|
import { USelect } from '../../select'
|
|
5
6
|
import { UForm } from '../index'
|
|
6
7
|
|
|
@@ -77,4 +78,47 @@ describe('UForm field events', () => {
|
|
|
77
78
|
app.unmount()
|
|
78
79
|
host.remove()
|
|
79
80
|
})
|
|
81
|
+
|
|
82
|
+
it('父级重渲染时动态 props 同步到插槽控件', async () => {
|
|
83
|
+
const host = document.createElement('div')
|
|
84
|
+
document.body.appendChild(host)
|
|
85
|
+
|
|
86
|
+
const model = reactive({ username: '' })
|
|
87
|
+
const disabled = ref(false)
|
|
88
|
+
const placeholder = ref('第一次')
|
|
89
|
+
|
|
90
|
+
const app = createApp({
|
|
91
|
+
render() {
|
|
92
|
+
return h(
|
|
93
|
+
UForm,
|
|
94
|
+
{ model },
|
|
95
|
+
{
|
|
96
|
+
default: () =>
|
|
97
|
+
h(UInput, {
|
|
98
|
+
field: 'username',
|
|
99
|
+
label: '账号',
|
|
100
|
+
disabled: disabled.value,
|
|
101
|
+
placeholder: placeholder.value
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
app.mount(host)
|
|
109
|
+
|
|
110
|
+
const input = () => host.querySelector('input')!
|
|
111
|
+
expect(input().disabled).toBe(false)
|
|
112
|
+
expect(input().placeholder).toBe('第一次')
|
|
113
|
+
|
|
114
|
+
disabled.value = true
|
|
115
|
+
placeholder.value = '第二次'
|
|
116
|
+
await nextTick()
|
|
117
|
+
|
|
118
|
+
expect(input().disabled).toBe(true)
|
|
119
|
+
expect(input().placeholder).toBe('第二次')
|
|
120
|
+
|
|
121
|
+
app.unmount()
|
|
122
|
+
host.remove()
|
|
123
|
+
})
|
|
80
124
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { createApp, h, nextTick, reactive } from 'vue'
|
|
2
|
+
import { createApp, h, nextTick, reactive, ref } from 'vue'
|
|
3
3
|
|
|
4
4
|
import { UForm } from '../../form'
|
|
5
|
+
import { UInput } from '../../input'
|
|
5
6
|
import { USelect } from '../../select'
|
|
6
7
|
import { UFormItem } from '../index'
|
|
7
8
|
|
|
@@ -87,4 +88,42 @@ describe('UFormItem change', () => {
|
|
|
87
88
|
app.unmount()
|
|
88
89
|
host.remove()
|
|
89
90
|
})
|
|
91
|
+
|
|
92
|
+
it('显式 UFormItem 内控件的动态 props 随父级更新', async () => {
|
|
93
|
+
const host = document.createElement('div')
|
|
94
|
+
document.body.appendChild(host)
|
|
95
|
+
|
|
96
|
+
const model = reactive({ name: '' })
|
|
97
|
+
const disabled = ref(false)
|
|
98
|
+
|
|
99
|
+
const app = createApp({
|
|
100
|
+
render() {
|
|
101
|
+
return h(
|
|
102
|
+
UForm,
|
|
103
|
+
{ model },
|
|
104
|
+
{
|
|
105
|
+
default: () =>
|
|
106
|
+
h(
|
|
107
|
+
UFormItem,
|
|
108
|
+
{ field: 'name', label: '姓名' },
|
|
109
|
+
{ default: () => h(UInput, { modelValue: model.name, disabled: disabled.value }) }
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
app.mount(host)
|
|
117
|
+
|
|
118
|
+
const input = () => host.querySelector('input')!
|
|
119
|
+
expect(input().disabled).toBe(false)
|
|
120
|
+
|
|
121
|
+
disabled.value = true
|
|
122
|
+
await nextTick()
|
|
123
|
+
|
|
124
|
+
expect(input().disabled).toBe(true)
|
|
125
|
+
|
|
126
|
+
app.unmount()
|
|
127
|
+
host.remove()
|
|
128
|
+
})
|
|
90
129
|
})
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
|
|
18
18
|
<section :class="cls.e('wrapper')">
|
|
19
19
|
<div :class="cls.e('content')">
|
|
20
|
-
<ContentSlot
|
|
20
|
+
<ContentSlot>
|
|
21
|
+
<slot></slot>
|
|
22
|
+
</ContentSlot>
|
|
21
23
|
</div>
|
|
22
24
|
|
|
23
25
|
<!-- 只有表单控件处于非只读状态时,才显示错误提示 -->
|
|
@@ -35,16 +37,16 @@
|
|
|
35
37
|
<script lang="tsx" setup>
|
|
36
38
|
import { o } from '@cat-kit/core'
|
|
37
39
|
import { useConfig, useFallbackProps } from '@veltra/compositions'
|
|
38
|
-
import { bem, withUnit } from '@veltra/utils'
|
|
40
|
+
import { bem, extractNormalVNodes, withUnit } from '@veltra/utils'
|
|
39
41
|
import { injectFormContext } from '@veltra/utils'
|
|
40
42
|
import {
|
|
41
43
|
type CSSProperties,
|
|
44
|
+
type SetupContext,
|
|
42
45
|
type VNode,
|
|
43
46
|
cloneVNode,
|
|
44
47
|
computed,
|
|
45
48
|
onBeforeUnmount,
|
|
46
49
|
shallowRef,
|
|
47
|
-
useSlots,
|
|
48
50
|
watch
|
|
49
51
|
} from 'vue'
|
|
50
52
|
|
|
@@ -60,8 +62,6 @@ const props = withDefaults(defineProps<FormItemProps>(), { readonly: undefined }
|
|
|
60
62
|
|
|
61
63
|
const emit = defineEmits<FormItemEmits>()
|
|
62
64
|
|
|
63
|
-
const slots = useSlots()
|
|
64
|
-
|
|
65
65
|
defineSlots<{
|
|
66
66
|
/** 标签插槽 */
|
|
67
67
|
label?: () => any
|
|
@@ -89,9 +89,13 @@ function wrapControlChange(node: VNode) {
|
|
|
89
89
|
})
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
/**
|
|
93
|
+
* 承载默认插槽并拦截控件 change。
|
|
94
|
+
* 必须经填充插槽接收内容(而非直接读 UFormItem 的 $slots):
|
|
95
|
+
* 读父级 $slots 时组件自身没有更新路径,父级重渲染后动态 props(disabled、data 等)会冻结在首帧。
|
|
96
|
+
*/
|
|
97
|
+
function ContentSlot(_: unknown, { slots }: SetupContext) {
|
|
98
|
+
return extractNormalVNodes(slots.default?.() ?? []).map(wrapControlChange)
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
const { config } = useConfig()
|
package/src/types/action.ts
CHANGED
|
@@ -4,7 +4,12 @@ import type { ButtonProps } from './button'
|
|
|
4
4
|
|
|
5
5
|
/** 操作组件属性 */
|
|
6
6
|
export interface ActionProps extends ButtonProps {
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* 是否需要确认:true 时点击只弹确认气泡(UPopConfirm),点气泡里的「确认」才触发 `run`
|
|
9
|
+
* @default false
|
|
10
|
+
* @description 不要在本项的 `run` 回调里再调 `messageConfirm`:`run` 触发时气泡已经确认过一次,
|
|
11
|
+
* 会变成两次确认。需要弹窗级确认时去掉 `needConfirm`,只在 `run` 里用 `messageConfirm`
|
|
12
|
+
*/
|
|
8
13
|
needConfirm?: boolean
|
|
9
14
|
|
|
10
15
|
/**
|
package/src/types/button.ts
CHANGED
|
@@ -4,7 +4,11 @@ import type { Component, ShallowRef } from 'vue'
|
|
|
4
4
|
/** 按钮类型 */
|
|
5
5
|
export type ButtonType = ColorType
|
|
6
6
|
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* 按钮属性类型
|
|
9
|
+
* @description 组件不注入默认 `aria-label`:可访问名来自默认插槽文本,
|
|
10
|
+
* 或你自己传入的 `aria-label`(透传到根元素);只有图标的按钮必须自行传 `aria-label`
|
|
11
|
+
*/
|
|
8
12
|
export interface ButtonProps extends ComponentProps {
|
|
9
13
|
/** 按钮类型 */
|
|
10
14
|
type?: ButtonType
|
package/src/types/drawer.ts
CHANGED
|
@@ -15,16 +15,19 @@ export interface DrawerProps {
|
|
|
15
15
|
|
|
16
16
|
/** 是否显示关闭按钮 */
|
|
17
17
|
showClose?: boolean
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* 抽屉标题;传入时在内容区上方渲染标题栏
|
|
20
|
+
* @description 不传则不渲染标题栏,内容仍全部来自默认插槽
|
|
21
|
+
*/
|
|
19
22
|
title?: string
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
/** 抽屉组件定义的事件 */
|
|
23
26
|
export interface DrawerEmits {
|
|
24
27
|
(e: 'update:modelValue', value: boolean): void
|
|
25
|
-
/**
|
|
28
|
+
/** 开始关闭时触发(点遮罩、点关闭按钮) */
|
|
26
29
|
(e: 'close'): void
|
|
27
|
-
/**
|
|
30
|
+
/** 完全关闭后触发:抽屉与遮罩的退出动画结束、节点已移除时 */
|
|
28
31
|
(e: 'closed'): void
|
|
29
32
|
}
|
|
30
33
|
|
package/src/types/layout.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface LayoutProps {
|
|
|
18
18
|
* const cols = ['200px', '1fr']
|
|
19
19
|
* ```
|
|
20
20
|
* [fr是什么?](https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex_value)
|
|
21
|
+
* @description 默认插槽的每个直接子元素按顺序占一列,用自定义组件包一层时该组件必须是单根元素;
|
|
22
|
+
* 子元素渲染出 0 个(`v-if` 为假)或多个根节点时会挤动后面的列,此时自行给子元素写 `grid-column`
|
|
21
23
|
*/
|
|
22
24
|
cols?: string[] | string
|
|
23
25
|
|