@veltra/desktop 1.3.4 → 1.3.6

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.
Files changed (75) hide show
  1. package/dist/components/action/action-group.vue.d.ts +2 -2
  2. package/dist/components/action/action.vue.d.ts +1 -1
  3. package/dist/components/auto-complete/auto-complete.vue.d.ts +2 -2
  4. package/dist/components/cascade/cascade.js +8 -2
  5. package/dist/components/cascade/cascade.js.map +1 -1
  6. package/dist/components/cascade/cascade.vue.d.ts +4 -4
  7. package/dist/components/cascade/style2.css +4 -0
  8. package/dist/components/checkbox/checkbox-button.vue.d.ts +1 -1
  9. package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
  10. package/dist/components/code-editor/code-editor.vue.d.ts +2 -2
  11. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  12. package/dist/components/date-picker/date-picker.vue.d.ts +2 -2
  13. package/dist/components/date-range-picker/date-range-picker.js +5 -2
  14. package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
  15. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +2 -2
  16. package/dist/components/date-range-picker/style2.css +4 -0
  17. package/dist/components/empty/empty.vue.d.ts +1 -1
  18. package/dist/components/form-item/form-item.js.map +1 -1
  19. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js +2 -2
  20. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js.map +1 -1
  21. package/dist/components/form-item/style2.css +2 -1
  22. package/dist/components/grid-input/style2.css +3 -0
  23. package/dist/components/input/input.vue.d.ts +4 -4
  24. package/dist/components/multi-select/multi-select.js +67 -40
  25. package/dist/components/multi-select/multi-select.js.map +1 -1
  26. package/dist/components/multi-select/multi-select.vue.d.ts +6 -6
  27. package/dist/components/multi-select/style2.css +23 -10
  28. package/dist/components/multi-tree-select/multi-tree-select.js +52 -35
  29. package/dist/components/multi-tree-select/multi-tree-select.js.map +1 -1
  30. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +4 -4
  31. package/dist/components/multi-tree-select/style2.css +26 -16
  32. package/dist/components/number-input/number-input.vue.d.ts +4 -4
  33. package/dist/components/number-range-input/number-range-input.vue.d.ts +1 -1
  34. package/dist/components/password-input/password-input.vue.d.ts +1 -1
  35. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
  36. package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
  37. package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +1 -1
  38. package/dist/components/select/select.js +159 -95
  39. package/dist/components/select/select.js.map +1 -1
  40. package/dist/components/select/select.vue.d.ts +2 -2
  41. package/dist/components/select/style2.css +4 -17
  42. package/dist/components/select/use-options.js +67 -26
  43. package/dist/components/select/use-options.js.map +1 -1
  44. package/dist/components/steps/steps.vue.d.ts +2 -2
  45. package/dist/components/textarea/style2.css +1 -0
  46. package/dist/components/textarea/textarea.vue.d.ts +5 -5
  47. package/dist/components/tip/tip.vue.d.ts +1 -1
  48. package/dist/components/tree/use-tree-nodes.js +14 -17
  49. package/dist/components/tree/use-tree-nodes.js.map +1 -1
  50. package/dist/components/tree-select/style2.css +6 -15
  51. package/dist/components/tree-select/tree-select.js +95 -41
  52. package/dist/components/tree-select/tree-select.js.map +1 -1
  53. package/dist/components/tree-select/tree-select.vue.d.ts +4 -4
  54. package/dist/types/tree.d.ts +4 -1
  55. package/package.json +6 -6
  56. package/src/components/cascade/cascade.vue +15 -2
  57. package/src/components/cascade/style.scss +5 -0
  58. package/src/components/date-range-picker/date-range-picker.vue +9 -1
  59. package/src/components/date-range-picker/style.scss +5 -0
  60. package/src/components/form-item/form-item.vue +2 -2
  61. package/src/components/form-item/style.scss +2 -1
  62. package/src/components/grid-input/style.scss +1 -0
  63. package/src/components/multi-select/multi-select.vue +50 -19
  64. package/src/components/multi-select/style.scss +27 -4
  65. package/src/components/multi-tree-select/multi-tree-select.vue +34 -14
  66. package/src/components/multi-tree-select/style.scss +28 -9
  67. package/src/components/select/__test__/select.test.ts +110 -0
  68. package/src/components/select/select.vue +130 -46
  69. package/src/components/select/style.scss +5 -14
  70. package/src/components/select/use-options.ts +83 -39
  71. package/src/components/textarea/style.scss +1 -0
  72. package/src/components/tree/use-tree-nodes.ts +19 -24
  73. package/src/components/tree-select/style.scss +4 -8
  74. package/src/components/tree-select/tree-select.vue +90 -17
  75. package/src/types/tree.ts +4 -1
@@ -1 +1 @@
1
- {"version":3,"file":"tree-select.js","names":["$attrs","$slots"],"sources":["../../../src/components/tree-select/tree-select.vue"],"sourcesContent":["<template>\n <u-dropdown\n v-if=\"!readonly\"\n v-bind=\"$attrs\"\n :class=\"[cls.b, bem.is('disabled', disabled)]\"\n trigger=\"click\"\n :content-class=\"[cls.e('panel'), cls.em('panel', size), contentClass]\"\n :content-style=\"contentStyle\"\n ref=\"dropdownRef\"\n :min-width=\"minWidth\"\n :width=\"width\"\n :disabled=\"disabled\"\n @update:visible=\"handleDropdownVisible\"\n >\n <template #trigger>\n <u-input\n :size=\"size\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :clearable=\"clearable\"\n :model-value=\"text ?? (model ? label : undefined)\"\n @clear=\"handleClear\"\n native-readonly\n >\n <template #prefix v-if=\"$slots.prefix\">\n <slot name=\"prefix\" />\n </template>\n <template #suffix>\n <u-icon :class=\"cls.e('arrow')\"><ArrowDown /></u-icon>\n </template>\n </u-input>\n </template>\n <template #content>\n <!-- 过滤器 -->\n <div v-if=\"filterable\" :class=\"[cls.e('content-filter'), cls.m(size)]\">\n <u-input placeholder=\"输入关键字进行过滤\" v-model=\"qs\">\n <template #suffix>\n <u-icon><Search /></u-icon>\n </template>\n </u-input>\n </div>\n <!-- 菜单列表 -->\n\n <u-tree\n v-bind=\"treeProps\"\n :selected=\"model\"\n @update:selected=\"handleSelect\"\n ref=\"treeRef\"\n selectable\n :class=\"cls.e('content-tree')\"\n :slots=\"slots\"\n scroll-to-view\n />\n </template>\n </u-dropdown>\n\n <template v-else>\n {{ text || label || FORM_EMPTY_CONTENT }}\n </template>\n</template>\n\n<script lang=\"ts\" setup>\nimport { dfs, o } from '@cat-kit/core'\nimport { useFormFallbackProps } from '@veltra/compositions'\nimport { ArrowDown, Search } from '@veltra/icons/normal'\nimport { bem, fieldKey, FORM_EMPTY_CONTENT } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { computed, nextTick, shallowRef, watch } from 'vue'\n\nimport type { TreeSelectProps, TreeSelectEmits, TreeExposed } from '../../types'\nimport { UDropdown } from '../dropdown'\nimport { UIcon } from '../icon'\nimport { UInput } from '../input'\nimport { UTree } from '../tree'\nimport type { TreeSlotsScope } from '../tree/di'\n\ndefineOptions({ name: 'UTreeSelect', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<TreeSelectProps>(), {\n labelKey: 'label',\n valueKey: 'value',\n placeholder: '请选择',\n expandAll: false,\n clearable: true,\n disabled: undefined,\n readonly: undefined,\n filterable: false,\n minWidth: '280px'\n})\n\nconst emit = defineEmits<TreeSelectEmits>()\n\nconst slots = defineSlots<{\n /** 默认插槽 */\n default?: (props: TreeSlotsScope) => any\n /** 前缀插槽 */\n prefix?: () => any\n}>()\n\nconst treeProps = computed(() => {\n return o(props as Record<string, any>).omit([\n 'tips',\n 'field',\n 'placeholder',\n 'disabled',\n 'label',\n 'readonly',\n 'contentClass',\n 'contentStyle',\n 'minWidth',\n 'width'\n ])\n})\n\nconst cls = bem('tree-select')\n\nconst labelKey = computed(() => fieldKey(props.labelKey, 'label'))\nconst valueKey = computed(() => fieldKey(props.valueKey, 'value'))\n\n/**过滤 */\nconst qs = shallowRef('')\nwatch(qs, (qs) => {\n treeRef.value?.filter(qs)\n})\n\nconst model = defineModel<string | number>()\n\nconst label = shallowRef<string>()\n\nconst { formProps } = injectFormContext()\n\nconst { size, disabled, readonly } = useFormFallbackProps([formProps ?? {}, props])\n\nconst treeRef = shallowRef<TreeExposed>()\n\nconst dropdownRef = shallowRef<InstanceType<typeof UDropdown>>()\n\n/**清空 */\nconst handleClear = () => {\n model.value = ''\n label.value = undefined\n emit('clear')\n emit('change', undefined)\n emit('update:text', '')\n}\n\nlet changedByEvent = false\nwatch(\n [() => props.data, model],\n ([data, model]) => {\n if (changedByEvent) return\n if (!data?.length || model === undefined) {\n label.value = undefined\n model = undefined\n return\n }\n\n let founded = false\n const childrenKey = props.childrenKey ?? 'children'\n data.some((item) => {\n dfs(\n item,\n (v) => {\n if (v[valueKey.value] === model) {\n label.value = v[labelKey.value]\n founded = true\n return true\n }\n },\n childrenKey\n )\n\n return founded\n })\n },\n { immediate: true }\n)\n\nconst handleSelect = (selected?: string | number, selectedData?: Record<string, any>) => {\n changedByEvent = true\n nextTick(() => {\n changedByEvent = false\n })\n\n model.value = selected ?? ''\n\n if (selectedData) {\n label.value = o(selectedData).get(labelKey.value)\n } else {\n label.value = ''\n }\n\n emit('change', selectedData)\n emit('update:text', label.value)\n dropdownRef.value?.close()\n}\n\nconst handleDropdownVisible = (visible: boolean) => {\n if (!visible) {\n qs.value = ''\n }\n}\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"tree-select.js","names":["$attrs","$slots"],"sources":["../../../src/components/tree-select/tree-select.vue"],"sourcesContent":["<template>\n <u-dropdown\n v-if=\"!readonly\"\n v-bind=\"$attrs\"\n :class=\"[cls.b, bem.is('disabled', disabled)]\"\n trigger=\"click\"\n :content-class=\"[cls.e('panel'), cls.em('panel', size), contentClass]\"\n :content-style=\"contentStyle\"\n ref=\"dropdownRef\"\n v-model:visible=\"dropdownVisible\"\n :min-width=\"minWidth\"\n :width=\"width\"\n :disabled=\"disabled\"\n @update:visible=\"handleDropdownVisible\"\n @mouseenter=\"hovered = true\"\n @mouseleave=\"hovered = false\"\n >\n <template #trigger>\n <u-input\n ref=\"inputRef\"\n :size=\"size\"\n :disabled=\"disabled\"\n :placeholder=\"inputPlaceholder\"\n :clearable=\"false\"\n :model-value=\"inputValue\"\n @update:model-value=\"handleQueryInput\"\n :native-readonly=\"!filterable || !querying\"\n @click.capture=\"handleTriggerClickCapture\"\n >\n <template #prefix v-if=\"$slots.prefix\">\n <slot name=\"prefix\" />\n </template>\n <template #suffix>\n <transition name=\"zoom-in\" mode=\"out-in\">\n <u-icon\n v-if=\"showClear\"\n :class=\"cls.e('clear')\"\n title=\"清除\"\n key=\"clear\"\n @click.stop=\"handleClear\"\n >\n <Close />\n </u-icon>\n <u-icon v-else :class=\"cls.e('arrow')\" key=\"arrow\"><ArrowDown /></u-icon>\n </transition>\n </template>\n </u-input>\n </template>\n <template #content>\n <!-- 菜单列表 -->\n\n <u-tree\n v-bind=\"treeProps\"\n :selected=\"model\"\n @update:selected=\"handleSelect\"\n ref=\"treeRef\"\n selectable\n :class=\"cls.e('content-tree')\"\n :slots=\"slots\"\n scroll-to-view\n />\n </template>\n </u-dropdown>\n\n <template v-else>\n {{ text || label || FORM_EMPTY_CONTENT }}\n </template>\n</template>\n\n<script lang=\"ts\" setup>\nimport { dfs, o } from '@cat-kit/core'\nimport { useFormFallbackProps } from '@veltra/compositions'\nimport { ArrowDown, Close } from '@veltra/icons/normal'\nimport { bem, fieldKey, FORM_EMPTY_CONTENT } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { computed, nextTick, shallowRef, useTemplateRef, watch } from 'vue'\n\nimport type { TreeSelectProps, TreeSelectEmits, TreeExposed, InputExposed } from '../../types'\nimport { UDropdown } from '../dropdown'\nimport { UIcon } from '../icon'\nimport { UInput } from '../input'\nimport { UTree } from '../tree'\nimport type { TreeSlotsScope } from '../tree/di'\n\ndefineOptions({ name: 'UTreeSelect', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<TreeSelectProps>(), {\n labelKey: 'label',\n valueKey: 'value',\n placeholder: '请选择',\n expandAll: false,\n clearable: true,\n disabled: undefined,\n readonly: undefined,\n filterable: false,\n minWidth: '280px'\n})\n\nconst emit = defineEmits<TreeSelectEmits>()\n\nconst slots = defineSlots<{\n /** 默认插槽 */\n default?: (props: TreeSlotsScope) => any\n /** 前缀插槽 */\n prefix?: () => any\n}>()\n\nconst treeProps = computed(() => {\n return o(props as Record<string, any>).omit([\n 'tips',\n 'field',\n 'placeholder',\n 'disabled',\n 'label',\n 'readonly',\n 'contentClass',\n 'contentStyle',\n 'minWidth',\n 'width'\n ])\n})\n\nconst cls = bem('tree-select')\n\nconst labelKey = computed(() => fieldKey(props.labelKey, 'label'))\nconst valueKey = computed(() => fieldKey(props.valueKey, 'value'))\n\n/**过滤 */\nconst qs = shallowRef('')\nwatch(qs, (qs) => {\n treeRef.value?.filter(qs)\n})\n\nconst model = defineModel<string | number>()\n\nconst label = shallowRef<string>()\n\nconst dropdownVisible = shallowRef(false)\n\n/**\n * 是否处于查询态:决定触发输入框显示查询串还是选中标签。\n * 与面板可见状态解耦 —— 选择后立即退出查询态,\n * 不等面板关闭动画结束(否则显示值会延迟一个动画时长才恢复)。\n */\nconst querying = shallowRef(false)\n\nconst inputRef = useTemplateRef<InputExposed>('inputRef')\n\n/** 触发输入框的值:查询态下承载查询串,否则展示选中标签 */\nconst inputValue = computed(() => {\n if (props.filterable && querying.value) return qs.value\n return props.text ?? (model.value ? label.value : undefined)\n})\n\n/** 查询态下已选标签降级为占位提示 */\nconst inputPlaceholder = computed(() => {\n const display = props.text ?? (model.value ? label.value : undefined)\n if (props.filterable && querying.value && display) {\n return display\n }\n return props.placeholder\n})\n\nfunction handleQueryInput(value: string) {\n if (!props.filterable) return\n qs.value = value\n}\n\nconst hovered = shallowRef(false)\n\n/** 悬停且存在选中值时展示清除按钮(替代下拉箭头) */\nconst showClear = computed(() => {\n if (!props.clearable || disabled.value || !hovered.value) return false\n return !!(props.text ?? (model.value ? label.value : undefined))\n})\n\n/**\n * 过滤模式下拦截输入区域的点击:\n * 阻止 dropdown 的 trigger 开合切换,保持面板展开以不中断输入。\n * 非输入区域(箭头、留白)放行,维持原开合行为。\n */\nfunction handleTriggerClickCapture(e: MouseEvent) {\n if (!props.filterable || disabled.value) return\n if (!(e.target instanceof HTMLInputElement)) return\n\n e.stopPropagation()\n if (!dropdownVisible.value) dropdownRef.value?.open()\n}\n\nwatch(dropdownVisible, (visible) => {\n if (visible && props.filterable) {\n // 面板展开后进入查询态并聚焦输入框,可以立即输入查询\n querying.value = true\n nextTick(() => inputRef.value?.el?.focus())\n } else if (!visible) {\n querying.value = false\n }\n})\n\nconst { formProps } = injectFormContext()\n\nconst { size, disabled, readonly } = useFormFallbackProps([formProps ?? {}, props])\n\nconst treeRef = shallowRef<TreeExposed>()\n\nconst dropdownRef = shallowRef<InstanceType<typeof UDropdown>>()\n\n/**清空 */\nconst handleClear = () => {\n model.value = ''\n label.value = undefined\n emit('clear')\n emit('change', undefined)\n emit('update:text', '')\n}\n\nlet changedByEvent = false\nwatch(\n [() => props.data, model],\n ([data, model]) => {\n if (changedByEvent) return\n if (!data?.length || model === undefined) {\n label.value = undefined\n model = undefined\n return\n }\n\n let founded = false\n const childrenKey = props.childrenKey ?? 'children'\n data.some((item) => {\n dfs(\n item,\n (v) => {\n if (v[valueKey.value] === model) {\n label.value = v[labelKey.value]\n founded = true\n return true\n }\n },\n childrenKey\n )\n\n return founded\n })\n },\n { immediate: true }\n)\n\nconst handleSelect = (selected?: string | number, selectedData?: Record<string, any>) => {\n changedByEvent = true\n nextTick(() => {\n changedByEvent = false\n })\n\n model.value = selected ?? ''\n\n if (selectedData) {\n label.value = o(selectedData).get(labelKey.value)\n } else {\n label.value = ''\n }\n\n emit('change', selectedData)\n emit('update:text', label.value)\n // 立即退出查询态,输入框同步恢复显示选中标签\n querying.value = false\n qs.value = ''\n dropdownRef.value?.close()\n}\n\nconst handleDropdownVisible = (visible: boolean) => {\n if (!visible) {\n qs.value = ''\n }\n}\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsFA,MAAM,QAAQ;EAYd,MAAM,OAAO;EAEb,MAAM,QAAQ,SAAA;EAOd,MAAM,YAAY,eAAe;GAC/B,OAAO,EAAE,KAA4B,CAAC,CAAC,KAAK;IAC1C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH,CAAC;EAED,MAAM,MAAM,IAAI,aAAa;EAE7B,MAAM,WAAW,eAAe,SAAS,MAAM,UAAU,OAAO,CAAC;EACjE,MAAM,WAAW,eAAe,SAAS,MAAM,UAAU,OAAO,CAAC;;EAGjE,MAAM,KAAK,WAAW,EAAE;EACxB,MAAM,KAAK,OAAO;GAChB,QAAQ,OAAO,OAAO,EAAE;EAC1B,CAAC;EAED,MAAM,QAAQ,SAA4B,SAAA,YAAC;EAE3C,MAAM,QAAQ,WAAmB;EAEjC,MAAM,kBAAkB,WAAW,KAAK;;;;;;EAOxC,MAAM,WAAW,WAAW,KAAK;EAEjC,MAAM,WAAW,eAA6B,UAAU;;EAGxD,MAAM,aAAa,eAAe;GAChC,IAAI,MAAM,cAAc,SAAS,OAAO,OAAO,GAAG;GAClD,OAAO,MAAM,SAAS,MAAM,QAAQ,MAAM,QAAQ,KAAA;EACpD,CAAC;;EAGD,MAAM,mBAAmB,eAAe;GACtC,MAAM,UAAU,MAAM,SAAS,MAAM,QAAQ,MAAM,QAAQ,KAAA;GAC3D,IAAI,MAAM,cAAc,SAAS,SAAS,SACxC,OAAO;GAET,OAAO,MAAM;EACf,CAAC;EAED,SAAS,iBAAiB,OAAe;GACvC,IAAI,CAAC,MAAM,YAAY;GACvB,GAAG,QAAQ;EACb;EAEA,MAAM,UAAU,WAAW,KAAK;;EAGhC,MAAM,YAAY,eAAe;GAC/B,IAAI,CAAC,MAAM,aAAa,SAAS,SAAS,CAAC,QAAQ,OAAO,OAAO;GACjE,OAAO,CAAC,EAAE,MAAM,SAAS,MAAM,QAAQ,MAAM,QAAQ,KAAA;EACvD,CAAC;;;;;;EAOD,SAAS,0BAA0B,GAAe;GAChD,IAAI,CAAC,MAAM,cAAc,SAAS,OAAO;GACzC,IAAI,EAAE,EAAE,kBAAkB,mBAAmB;GAE7C,EAAE,gBAAgB;GAClB,IAAI,CAAC,gBAAgB,OAAO,YAAY,OAAO,KAAK;EACtD;EAEA,MAAM,kBAAkB,YAAY;GAClC,IAAI,WAAW,MAAM,YAAY;IAE/B,SAAS,QAAQ;IACjB,eAAe,SAAS,OAAO,IAAI,MAAM,CAAC;GAC5C,OAAO,IAAI,CAAC,SACV,SAAS,QAAQ;EAErB,CAAC;EAED,MAAM,EAAE,cAAc,kBAAkB;EAExC,MAAM,EAAE,MAAM,UAAU,aAAa,qBAAqB,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC;EAElF,MAAM,UAAU,WAAwB;EAExC,MAAM,cAAc,WAA2C;;EAG/D,MAAM,oBAAoB;GACxB,MAAM,QAAQ;GACd,MAAM,QAAQ,KAAA;GACd,KAAK,OAAO;GACZ,KAAK,UAAU,KAAA,CAAS;GACxB,KAAK,eAAe,EAAE;EACxB;EAEA,IAAI,iBAAiB;EACrB,MACE,OAAO,MAAM,MAAM,KAAK,IACvB,CAAC,MAAM,WAAW;GACjB,IAAI,gBAAgB;GACpB,IAAI,CAAC,MAAM,UAAU,UAAU,KAAA,GAAW;IACxC,MAAM,QAAQ,KAAA;IACd,QAAQ,KAAA;IACR;GACF;GAEA,IAAI,UAAU;GACd,MAAM,cAAc,MAAM,eAAe;GACzC,KAAK,MAAM,SAAS;IAClB,IACE,OACC,MAAM;KACL,IAAI,EAAE,SAAS,WAAW,OAAO;MAC/B,MAAM,QAAQ,EAAE,SAAS;MACzB,UAAU;MACV,OAAO;KACT;IACF,GACA,WACF;IAEA,OAAO;GACT,CAAC;EACH,GACA,EAAE,WAAW,KAAK,CACpB;EAEA,MAAM,gBAAgB,UAA4B,iBAAuC;GACvF,iBAAiB;GACjB,eAAe;IACb,iBAAiB;GACnB,CAAC;GAED,MAAM,QAAQ,YAAY;GAE1B,IAAI,cACF,MAAM,QAAQ,EAAE,YAAY,CAAC,CAAC,IAAI,SAAS,KAAK;QAEhD,MAAM,QAAQ;GAGhB,KAAK,UAAU,YAAY;GAC3B,KAAK,eAAe,MAAM,KAAK;GAE/B,SAAS,QAAQ;GACjB,GAAG,QAAQ;GACX,YAAY,OAAO,MAAM;EAC3B;EAEA,MAAM,yBAAyB,YAAqB;GAClD,IAAI,CAAC,SACH,GAAG,QAAQ;EAEf;;WAhRW,MAAA,QAAA,KAAA,UAAA,GADT,YA6Da,MAAA,WAAA,GA7Db,WA6Da,EAAA,KAAA,EAAA,GA3DHA,KAAAA,QAAM;IACb,OAAK,CAAG,MAAA,GAAA,CAAG,CAAC,GAAG,MAAA,GAAA,CAAG,CAAC,GAAE,YAAa,MAAA,QAAA,CAAQ,CAAA;IAC3C,SAAQ;IACP,iBAAa;KAAG,MAAA,GAAA,CAAG,CAAC,EAAC,OAAA;KAAW,MAAA,GAAA,CAAG,CAAC,GAAE,SAAU,MAAA,IAAA,CAAI;KAAG,QAAA;IAAY;IACnE,iBAAe,QAAA;aACZ;IAAJ,KAAI;IACI,SAAS,gBAAA;+DAAA,gBAAe,QAAA,SAIf,qBAAA;IAHhB,aAAW,QAAA;IACX,OAAO,QAAA;IACP,UAAU,MAAA,QAAA;IAEV,cAAU,OAAA,OAAA,OAAA,MAAA,WAAE,QAAA,QAAO;IACnB,cAAU,OAAA,OAAA,OAAA,MAAA,WAAE,QAAA,QAAO;;IAET,SAAO,cA6BN,CA5BV,YA4BU,MAAA,aAAA,GAAA;cA3BJ;KAAJ,KAAI;KACH,MAAM,MAAA,IAAA;KACN,UAAU,MAAA,QAAA;KACV,aAAa,iBAAA;KACb,WAAW;KACX,eAAa,WAAA;KACb,uBAAoB;KACpB,mBAAe,CAAG,QAAA,cAAU,CAAK,SAAA;qBAClB;;KAKL,QAAM,cAYF,CAXb,YAWa,YAAA;MAXD,MAAK;MAAU,MAAK;;6BASrB,CAPD,UAAA,SAAA,UAAA,GADR,YAQS,MAAA,WAAA,GAAA;OANN,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,OAAA,CAAA;OACb,OAAM;OACN,KAAI;OACH,SAAK,cAAO,aAAW,CAAA,MAAA,CAAA;;8BAEf,CAAT,YAAS,MAAA,KAAA,CAAA,CAAA,CAAA;;wCAEX,YAAyE,MAAA,WAAA,GAAA;OAAzD,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,OAAA,CAAA;OAAW,KAAI;;8BAAqB,CAAb,YAAa,MAAA,SAAA,CAAA,CAAA,CAAA;;;;;;QAd5CC,KAAAA,OAAO,SAAA;WAApB;uBACa,CAAtB,WAAsB,KAAA,QAAA,QAAA,CAAA,CAAA;;;;;;;;;IAkBjB,SAAO,cACH,CAAb,mBAAA,QAAA,GAEA,YASE,MAAA,WAAA,GATF,WACU,UAQR,OARiB;KAChB,UAAU,MAAA;KACV,qBAAiB;cACd;KAAJ,KAAI;KACJ,YAAA;KACC,OAAO,MAAA,GAAA,CAAG,CAAC,EAAC,cAAA;KACL;KACR,kBAAA;;;;;;;;;;;uBAKN,mBAEW,UAAA,EAAA,KAAA,EAAA,GAAA,CAAA,gBAAA,gBADN,QAAA,QAAQ,MAAA,SAAS,MAAA,kBAAA,CAAkB,GAAA,CAAA,CAAA,GAAA,EAAA"}
@@ -17,17 +17,15 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
17
17
  "update:modelValue": (value: string | number | undefined) => any;
18
18
  } & {
19
19
  "update:modelValue": (value?: string | number | undefined) => any;
20
- change: (selectedData?: Record<string, any> | undefined) => any;
21
20
  clear: () => any;
21
+ change: (selectedData?: Record<string, any> | undefined) => any;
22
22
  "update:text": (text?: string | undefined) => any;
23
23
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
24
24
  "onUpdate:modelValue"?: ((value?: string | number | undefined) => any) | undefined;
25
- onChange?: ((selectedData?: Record<string, any> | undefined) => any) | undefined;
26
25
  onClear?: (() => any) | undefined;
26
+ onChange?: ((selectedData?: Record<string, any> | undefined) => any) | undefined;
27
27
  "onUpdate:text"?: ((text?: string | undefined) => any) | undefined;
28
28
  }>, {
29
- disabled: boolean;
30
- readonly: boolean;
31
29
  expandAll: boolean;
32
30
  labelKey: string;
33
31
  valueKey: string;
@@ -35,6 +33,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
35
33
  clearable: boolean;
36
34
  filterable: boolean;
37
35
  minWidth: string;
36
+ disabled: boolean;
37
+ readonly: boolean;
38
38
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
39
39
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
40
40
  declare const _default: typeof __VLS_export;
@@ -33,7 +33,10 @@ interface TreeProps {
33
33
  childrenKey?: string;
34
34
  /** 数据 */
35
35
  data?: Record<string, any>[];
36
- /** 禁止单选或多选的节点 */
36
+ /**
37
+ * 禁止单选或多选的节点。
38
+ * 在整棵树构建完成后调用,可安全访问 `node.children` / `node.isLeaf`。
39
+ */
37
40
  disabledNode?: (item: Record<string, any>, node: TreeNode$1) => boolean;
38
41
  /** 可多选 */
39
42
  checkable?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veltra/desktop",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -57,11 +57,11 @@
57
57
  "@cat-kit/core": ">=1.1.8",
58
58
  "@cat-kit/excel": ">=1.0.0",
59
59
  "@cat-kit/fe": "^1.1.8",
60
- "@veltra/compositions": "1.3.4",
61
- "@veltra/directives": "1.3.4",
62
- "@veltra/icons": "1.3.4",
63
- "@veltra/styles": "1.3.4",
64
- "@veltra/utils": "1.3.4",
60
+ "@veltra/compositions": "1.3.6",
61
+ "@veltra/directives": "1.3.6",
62
+ "@veltra/icons": "1.3.6",
63
+ "@veltra/styles": "1.3.6",
64
+ "@veltra/utils": "1.3.6",
65
65
  "vue": ">=3.5.40"
66
66
  },
67
67
  "devDependencies": {
@@ -2,7 +2,13 @@
2
2
  <u-dropdown
3
3
  v-if="!readonly"
4
4
  v-bind="$attrs"
5
- :class="[cls.b, bem.is('disabled', disabled), bem.is('multiple', multiple), cls.m(size)]"
5
+ :class="[
6
+ cls.b,
7
+ bem.is('disabled', disabled),
8
+ bem.is('multiple', multiple),
9
+ cls.m(size),
10
+ bem.is('focus', multiple && dropdownVisible)
11
+ ]"
6
12
  :content-class="[cls.e('panel'), cls.em('panel', size)]"
7
13
  trigger="click"
8
14
  ref="dropdownRef"
@@ -10,7 +16,7 @@
10
16
  width="auto"
11
17
  @mouseenter="hovered = true"
12
18
  @mouseleave="hovered = false"
13
- @update:visible="!$event && (qs = '')"
19
+ @update:visible="handleDropdownVisible"
14
20
  >
15
21
  <template #trigger>
16
22
  <!-- 单选展示 -->
@@ -233,6 +239,13 @@ function handleClear() {
233
239
  // 过滤
234
240
  const qs = shallowRef<string>('')
235
241
 
242
+ const dropdownVisible = shallowRef(false)
243
+
244
+ const handleDropdownVisible = (visible: boolean) => {
245
+ dropdownVisible.value = visible
246
+ if (!visible) qs.value = ''
247
+ }
248
+
236
249
  watch([qs, forest], ([qs, forest]) => {
237
250
  const { filterable } = props
238
251
  if (!filterable || !qs) {
@@ -23,6 +23,11 @@ $menu-name: $root-name + '-panel-item';
23
23
  &:hover {
24
24
  border-color: fn.use-var(color, primary);
25
25
  }
26
+
27
+ @include m.is(focus) {
28
+ border-color: fn.use-var(color, primary);
29
+ box-shadow: fn.use-var(focus-ring);
30
+ }
26
31
  }
27
32
 
28
33
  @include m.size using ($size) {
@@ -10,6 +10,7 @@
10
10
  :disabled="disabled"
11
11
  @mouseenter.native="hovered = true"
12
12
  @mouseleave.native="hovered = false"
13
+ @update:visible="dropdownVisible = $event"
13
14
  >
14
15
  <template #trigger>
15
16
  <input
@@ -87,8 +88,15 @@ const emit = defineEmits<DateRangePickerEmits>()
87
88
 
88
89
  const cls = bem('date-range-picker')
89
90
 
91
+ const dropdownVisible = shallowRef(false)
92
+
90
93
  const className = computed(() => {
91
- return [cls.b, cls.m(size.value), bem.is('disabled', disabled.value)]
94
+ return [
95
+ cls.b,
96
+ cls.m(size.value),
97
+ bem.is('disabled', disabled.value),
98
+ bem.is('focus', dropdownVisible.value)
99
+ ]
92
100
  })
93
101
 
94
102
  const { formProps } = injectFormContext()
@@ -32,6 +32,11 @@ $root-name: date-range-picker;
32
32
  border-color: fn.use-var(color, primary);
33
33
  }
34
34
 
35
+ @include m.is(focus) {
36
+ border-color: fn.use-var(color, primary);
37
+ box-shadow: fn.use-var(focus-ring);
38
+ }
39
+
35
40
  @include m.e(input) {
36
41
  outline: none;
37
42
  border: none;
@@ -7,11 +7,11 @@
7
7
  >
8
8
  <u-tip v-if="tips" :content="tips" :class="cls.e('tips')" :show-delay="500">
9
9
  <span :class="cls.e('tip-label')">
10
- <slot name="label">{{ label }}:</slot>
10
+ <slot name="label">{{ label }}{{ labelPosition === 'top' ? '' : ':' }}</slot>
11
11
  </span>
12
12
  </u-tip>
13
13
  <template v-else>
14
- <slot name="label">{{ label }}:</slot>
14
+ <slot name="label">{{ label }}{{ labelPosition === 'top' ? '' : ':' }}</slot>
15
15
  </template>
16
16
  </label>
17
17
 
@@ -28,7 +28,7 @@ $root-name: form-item;
28
28
  color: fn.use-var(text-color, main);
29
29
  text-align: right;
30
30
  line-height: 1.3;
31
- font-weight: 300;
31
+ font-weight: 400;
32
32
 
33
33
  @include m.flex($justify: flex-end);
34
34
 
@@ -99,6 +99,7 @@ $root-name: form-item;
99
99
  padding-right: 0;
100
100
  padding-bottom: 4px;
101
101
  text-align: left;
102
+ color: fn.use-var(text-color, second);
102
103
  justify-content: flex-start;
103
104
  }
104
105
  }
@@ -26,6 +26,7 @@ $root-name: grid-input;
26
26
 
27
27
  @include m.is(focus) {
28
28
  border-color: fn.use-var(color, primary);
29
+ box-shadow: fn.use-var(focus-ring);
29
30
 
30
31
  @keyframes fade {
31
32
  0% {
@@ -4,7 +4,7 @@
4
4
  v-bind="$attrs"
5
5
  trigger="click"
6
6
  ref="dropdownRef"
7
- :class="[cls.b, cls.m(size), bem.is('disabled', disabled)]"
7
+ :class="[cls.b, cls.m(size), bem.is('disabled', disabled), bem.is('focus', dropdownVisible)]"
8
8
  :content-class="[cls.e('panel'), cls.em('panel', size), contentClass]"
9
9
  :content-style="contentStyle"
10
10
  :min-width="minWidth"
@@ -16,11 +16,11 @@
16
16
  >
17
17
  <!-- 触发 -->
18
18
  <template #trigger>
19
- <span :class="cls.e('placeholder')" v-show="!model?.length">
19
+ <span :class="cls.e('placeholder')" v-if="!filterable && !model?.length">
20
20
  {{ placeholder }}
21
21
  </span>
22
22
 
23
- <div v-if="model?.length" :class="cls.e('tags')">
23
+ <div v-if="model?.length || filterable" :class="cls.e('tags')">
24
24
  <u-tag
25
25
  v-for="option of tags"
26
26
  :key="option[valueKey]"
@@ -31,6 +31,19 @@
31
31
  {{ option[labelKey] }}
32
32
  </u-tag>
33
33
  <u-tag v-if="restTag" size="small"> {{ restTag }}+ </u-tag>
34
+
35
+ <!-- 内嵌查询输入框:承载过滤与回车创建 -->
36
+ <input
37
+ v-if="filterable"
38
+ ref="inputRef"
39
+ :class="cls.e('input')"
40
+ v-model="queryString"
41
+ :placeholder="model?.length ? '' : placeholder"
42
+ :disabled="disabled"
43
+ @click.stop="handleInputClick"
44
+ @focus="handleInputFocus"
45
+ @keydown.enter.prevent="handleCreateByEnter"
46
+ />
34
47
  </div>
35
48
 
36
49
  <transition name="zoom-in" mode="out-in">
@@ -62,19 +75,6 @@
62
75
  <span> 已选 {{ model?.length }}/{{ max ?? options?.length ?? 0 }} </span>
63
76
  </div>
64
77
 
65
- <!-- 过滤器 -->
66
- <div v-if="filterable" :class="cls.e('content-filter')">
67
- <u-input
68
- placeholder="输入关键字进行过滤"
69
- v-model="queryString"
70
- @keydown.enter.prevent="handleCreateByEnter"
71
- >
72
- <template #suffix>
73
- <u-icon><Search /></u-icon>
74
- </template>
75
- </u-input>
76
- </div>
77
-
78
78
  <!-- 多选列表 -->
79
79
  <!--
80
80
  虚拟化启用时内容容器 height 由 useVirtualizer 命令式写入;
@@ -143,10 +143,18 @@
143
143
  <script lang="ts" setup>
144
144
  import { o as chainObj } from '@cat-kit/core'
145
145
  import { useFormFallbackProps, useVirtualizer } from '@veltra/compositions'
146
- import { ArrowDown, Close, Search } from '@veltra/icons/normal'
146
+ import { ArrowDown, Close } from '@veltra/icons/normal'
147
147
  import { bem, fieldKey, FORM_EMPTY_CONTENT } from '@veltra/utils'
148
148
  import { injectFormContext } from '@veltra/utils'
149
- import { computed, shallowRef, shallowReactive, watch, provide } from 'vue'
149
+ import {
150
+ computed,
151
+ nextTick,
152
+ shallowRef,
153
+ shallowReactive,
154
+ watch,
155
+ provide,
156
+ useTemplateRef
157
+ } from 'vue'
150
158
 
151
159
  import type {
152
160
  MultiSelectEmits,
@@ -158,7 +166,6 @@ import { UCheckbox } from '../checkbox'
158
166
  import { UDropdown } from '../dropdown'
159
167
  import { UEmpty } from '../empty'
160
168
  import { UIcon } from '../icon'
161
- import { UInput } from '../input'
162
169
  import { UScroll } from '../scroll'
163
170
  import { useOptions } from '../select/use-options'
164
171
  import { UTag } from '../tag'
@@ -319,12 +326,35 @@ const restTag = computed(() => {
319
326
  return (model.value?.length ?? 0) - tags.value.length
320
327
  })
321
328
 
329
+ const dropdownVisible = shallowRef(false)
330
+
331
+ const inputRef = useTemplateRef<HTMLInputElement>('inputRef')
332
+
322
333
  const handleDropdownVisible = (visible: boolean) => {
334
+ dropdownVisible.value = visible
323
335
  if (!visible) {
324
336
  queryString.value = ''
325
337
  }
326
338
  }
327
339
 
340
+ /**
341
+ * 拦截输入区域的点击(@click.stop):阻止 dropdown 的 trigger 开合切换,
342
+ * 保持面板展开以不中断输入。
343
+ */
344
+ const handleInputClick = () => {
345
+ if (!dropdownVisible.value) dropdownRef.value?.open()
346
+ }
347
+
348
+ const handleInputFocus = () => {
349
+ if (!dropdownVisible.value) dropdownRef.value?.open()
350
+ }
351
+
352
+ /** 勾选后把焦点还给输入框,便于连续输入过滤 */
353
+ const refocusInput = () => {
354
+ if (!filterable.value) return
355
+ nextTick(() => inputRef.value?.focus())
356
+ }
357
+
328
358
  const handleCheck = (option: Record<string, any>, checked: boolean) => {
329
359
  if (checked) {
330
360
  if (option.__isTemp && props.creatable) {
@@ -352,6 +382,7 @@ const handleCheck = (option: Record<string, any>, checked: boolean) => {
352
382
 
353
383
  emitChange()
354
384
  dropdownRef.value?.updateDropdown()
385
+ refocusInput()
355
386
  }
356
387
 
357
388
  /** 全选 */
@@ -36,13 +36,18 @@ $root-name: multi-select;
36
36
  border-color: fn.use-var(color, primary);
37
37
  }
38
38
 
39
+ @include m.is(focus) {
40
+ border-color: fn.use-var(color, primary);
41
+ box-shadow: fn.use-var(focus-ring);
42
+ }
43
+
39
44
  @include m.e(ripple) {
40
45
  background-color: fn.use-var(color, primary, light-7);
41
46
  opacity: 0.5;
42
47
  }
43
48
 
44
49
  @include m.e(options) {
45
- max-height: 180px;
50
+ max-height: 240px;
46
51
  }
47
52
 
48
53
  @include m.e(options-wrap) {
@@ -79,6 +84,27 @@ $root-name: multi-select;
79
84
  @include m.flex($justify: flex-start, $align: center, $wrap: wrap);
80
85
  }
81
86
 
87
+ @include m.e(input) {
88
+ flex: 1;
89
+ min-width: 60px;
90
+ padding: 0;
91
+ margin: 0;
92
+ border: none;
93
+ outline: none;
94
+ background: transparent;
95
+ font: inherit;
96
+ color: fn.use-var(text-color, main);
97
+ cursor: text;
98
+
99
+ &::placeholder {
100
+ color: fn.use-var(text-color, placeholder);
101
+ }
102
+
103
+ &:disabled {
104
+ cursor: not-allowed;
105
+ }
106
+ }
107
+
82
108
  @include m.e(readonly-tags) {
83
109
  padding-left: 0;
84
110
  padding-right: 0;
@@ -104,9 +130,6 @@ $root-name: multi-select;
104
130
 
105
131
  @include m.e(panel) {
106
132
  @include m.size using ($size) {
107
- @include m.bem($root-name, content-filter) {
108
- padding: calc(fn.use-var(gap, $size) / 2) fn.use-var(gap, $size);
109
- }
110
133
  @include m.bem($root-name, content-header) {
111
134
  padding: calc(fn.use-var(gap, $size) / 2) calc(fn.use-var(gap, $size) * 2);
112
135
  border-bottom: fn.use-var(border);
@@ -2,7 +2,7 @@
2
2
  <u-dropdown
3
3
  v-if="!readonly"
4
4
  v-bind="$attrs"
5
- :class="[cls.b, cls.m(size), bem.is('disabled', disabled)]"
5
+ :class="[cls.b, cls.m(size), bem.is('disabled', disabled), bem.is('focus', dropdownVisible)]"
6
6
  trigger="click"
7
7
  :content-class="[cls.e('panel'), cls.em('panel', size), contentClass]"
8
8
  :content-style="contentStyle"
@@ -16,12 +16,12 @@
16
16
  >
17
17
  <template #trigger>
18
18
  <!-- 默认展示 -->
19
- <span :class="cls.e('placeholder')" v-show="!tags.length">
19
+ <span :class="cls.e('placeholder')" v-if="!filterable && !tags.length">
20
20
  {{ placeholder }}
21
21
  </span>
22
22
 
23
23
  <!-- 选择的数据项 -->
24
- <div v-if="tags.length" :class="cls.e('tags')">
24
+ <div v-if="tags.length || filterable" :class="cls.e('tags')">
25
25
  <u-tag
26
26
  v-for="(tag, index) in visibleTags"
27
27
  :key="tag[valueKey]"
@@ -31,6 +31,18 @@
31
31
  {{ tag[labelKey] }}
32
32
  </u-tag>
33
33
  <u-tag v-if="hiddenCount > 0">+{{ hiddenCount }}</u-tag>
34
+
35
+ <!-- 内嵌查询输入框:承载过滤 -->
36
+ <input
37
+ v-if="filterable"
38
+ ref="inputRef"
39
+ :class="cls.e('input')"
40
+ v-model="qs"
41
+ :placeholder="tags.length ? '' : placeholder"
42
+ :disabled="disabled"
43
+ @click.stop="handleInputClick"
44
+ @focus="handleInputFocus"
45
+ />
34
46
  </div>
35
47
  <!-- 清空 icon -->
36
48
  <transition name="zoom-in" mode="out-in">
@@ -59,14 +71,6 @@
59
71
  {{ allExpanded ? '收起全部' : '展开全部' }}
60
72
  </u-button>
61
73
  </div>
62
- <!-- 过滤器 -->
63
- <div v-if="filterable" :class="[cls.e('content-filter'), cls.m(size)]">
64
- <u-input placeholder="输入关键字进行过滤" v-model="qs">
65
- <template #suffix>
66
- <u-icon><Search /></u-icon>
67
- </template>
68
- </u-input>
69
- </div>
70
74
  <!-- 菜单列表 -->
71
75
  <u-tree
72
76
  v-bind="treeProps"
@@ -98,10 +102,10 @@
98
102
  <script lang="ts" setup>
99
103
  import { dfs, o } from '@cat-kit/core'
100
104
  import { useFormFallbackProps } from '@veltra/compositions'
101
- import { ArrowDown, Close, Search } from '@veltra/icons/normal'
105
+ import { ArrowDown, Close } from '@veltra/icons/normal'
102
106
  import { bem, fieldKey, FORM_EMPTY_CONTENT } from '@veltra/utils'
103
107
  import { injectFormContext } from '@veltra/utils'
104
- import { computed, nextTick, shallowRef, watch } from 'vue'
108
+ import { computed, nextTick, shallowRef, useTemplateRef, watch } from 'vue'
105
109
 
106
110
  import type { MultiTreeSelectProps, MultiTreeSelectEmits, TreeExposed } from '../../types'
107
111
  import type { DropdownExposed } from '../../types'
@@ -109,7 +113,6 @@ import { UButton } from '../button'
109
113
  import { UCheckbox } from '../checkbox'
110
114
  import { UDropdown } from '../dropdown'
111
115
  import { UIcon } from '../icon'
112
- import { UInput } from '../input'
113
116
  import { UTag } from '../tag'
114
117
  import { UTree } from '../tree'
115
118
  import type { TreeSlotsScope } from '../tree/di'
@@ -306,9 +309,26 @@ const hiddenCount = computed(() => {
306
309
  return 0
307
310
  })
308
311
 
312
+ const dropdownVisible = shallowRef(false)
313
+
314
+ const inputRef = useTemplateRef<HTMLInputElement>('inputRef')
315
+
309
316
  const handleDropdownVisible = (visible: boolean) => {
317
+ dropdownVisible.value = visible
310
318
  if (!visible) {
311
319
  qs.value = ''
312
320
  }
313
321
  }
322
+
323
+ /**
324
+ * 拦截输入区域的点击(@click.stop):阻止 dropdown 的 trigger 开合切换,
325
+ * 保持面板展开以不中断输入。
326
+ */
327
+ const handleInputClick = () => {
328
+ if (!dropdownVisible.value) dropdownRef.value?.open()
329
+ }
330
+
331
+ const handleInputFocus = () => {
332
+ if (!dropdownVisible.value) dropdownRef.value?.open()
333
+ }
314
334
  </script>
@@ -22,6 +22,11 @@ $root-name: multi-tree-select;
22
22
  border-color: fn.use-var(color, primary);
23
23
  }
24
24
 
25
+ @include m.is(focus) {
26
+ border-color: fn.use-var(color, primary);
27
+ box-shadow: fn.use-var(focus-ring);
28
+ }
29
+
25
30
  @include m.size using ($size) {
26
31
  min-height: fn.use-var(form-component-height, $size);
27
32
  border-radius: fn.use-var(radius, $size);
@@ -58,16 +63,33 @@ $root-name: multi-tree-select;
58
63
  color: fn.use-var(text-color, placeholder);
59
64
  }
60
65
 
61
- @include m.e(filter) {
62
- margin-bottom: 4px;
63
- }
64
-
65
66
  @include m.e(tags) {
66
67
  flex-grow: 1;
67
68
  overflow: hidden;
68
69
  @include m.flex($justify: flex-start, $align: center, $wrap: wrap);
69
70
  }
70
71
 
72
+ @include m.e(input) {
73
+ flex: 1;
74
+ min-width: 60px;
75
+ padding: 0;
76
+ margin: 0;
77
+ border: none;
78
+ outline: none;
79
+ background: transparent;
80
+ font: inherit;
81
+ color: fn.use-var(text-color, main);
82
+ cursor: text;
83
+
84
+ &::placeholder {
85
+ color: fn.use-var(text-color, placeholder);
86
+ }
87
+
88
+ &:disabled {
89
+ cursor: not-allowed;
90
+ }
91
+ }
92
+
71
93
  @include m.e(readonly-tags) {
72
94
  padding-left: 0;
73
95
  padding-right: 0;
@@ -90,21 +112,18 @@ $root-name: multi-tree-select;
90
112
 
91
113
  @include m.e(panel) {
92
114
  @include m.size using ($size) {
93
- @include m.bem($root-name, content-filter) {
94
- padding: calc(fn.use-var(gap, $size) / 2) fn.use-var(gap, $size);
95
- }
96
115
  @include m.bem($root-name, content-header) {
97
116
  padding: calc(fn.use-var(gap, $size) / 2) fn.use-var(gap, $size);
98
117
  border-bottom: fn.use-var(border);
99
118
  }
100
119
 
101
120
  @include m.bem($root-name, content-tree) {
102
- padding: calc(fn.use-var(gap, $size) / 2) fn.use-var(gap, $size);
121
+ padding: fn.use-var(gap, $size);
103
122
  }
104
123
  }
105
124
  }
106
125
 
107
126
  @include m.e(content-tree) {
108
- max-height: 272px;
127
+ max-height: 240px;
109
128
  }
110
129
  }