@veltra/desktop 1.0.14 → 1.0.15

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 (38) hide show
  1. package/dist/components/action/action-group.vue.d.ts +3 -3
  2. package/dist/components/action/action.vue.d.ts +2 -2
  3. package/dist/components/auto-complete/auto-complete.vue.d.ts +2 -2
  4. package/dist/components/cascade/cascade.vue.d.ts +2 -2
  5. package/dist/components/checkbox/checkbox-button.vue.d.ts +1 -1
  6. package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
  7. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  8. package/dist/components/date-picker/date-picker.vue.d.ts +2 -2
  9. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +2 -2
  10. package/dist/components/expression-editor/components/variable-picker.js +1 -0
  11. package/dist/components/expression-editor/components/variable-picker.js.map +1 -1
  12. package/dist/components/expression-editor/core/editor.js +1 -0
  13. package/dist/components/expression-editor/core/editor.js.map +1 -1
  14. package/dist/components/expression-editor/style2.css +5 -4
  15. package/dist/components/input/input.vue.d.ts +2 -2
  16. package/dist/components/multi-select/multi-select.vue.d.ts +2 -2
  17. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +2 -2
  18. package/dist/components/notification/notification.vue.d.ts +1 -1
  19. package/dist/components/number/number.vue.d.ts +1 -1
  20. package/dist/components/number-input/number-input.vue.d.ts +2 -2
  21. package/dist/components/number-range-input/number-range-input.vue.d.ts +1 -1
  22. package/dist/components/password-input/password-input.vue.d.ts +1 -1
  23. package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
  24. package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +1 -1
  25. package/dist/components/select/select.vue.d.ts +2 -2
  26. package/dist/components/tabs/tabs-horizontal.vue.d.ts +4 -4
  27. package/dist/components/tabs/tabs-vertical.vue.d.ts +3 -3
  28. package/dist/components/tabs/tabs.vue.d.ts +4 -4
  29. package/dist/components/textarea/textarea.vue.d.ts +2 -2
  30. package/dist/components/tip/tip.js +4 -5
  31. package/dist/components/tip/tip.js.map +1 -1
  32. package/dist/components/tree-select/tree-select.vue.d.ts +2 -2
  33. package/package.json +11 -11
  34. package/src/components/expression-editor/__test__/editor.test.ts +47 -0
  35. package/src/components/expression-editor/components/variable-picker.vue +1 -0
  36. package/src/components/expression-editor/core/editor.ts +1 -0
  37. package/src/components/expression-editor/style.scss +5 -4
  38. package/src/components/tip/tip.vue +4 -3
@@ -16,13 +16,13 @@ type __VLS_Slots = {
16
16
  default?: () => VNode[];
17
17
  };
18
18
  declare const __VLS_base: _$vue.DefineComponent<_Props, {}, {}, {}, {}, _$vue.ComponentOptionsMixin, _$vue.ComponentOptionsMixin, {}, string, _$vue.PublicProps, Readonly<_Props> & Readonly<{}>, {
19
- type: ColorType;
20
- max: number;
21
19
  size: "small" | "default" | "large";
20
+ type: ColorType;
22
21
  text: boolean;
23
22
  hover: boolean;
24
- loading: boolean;
25
23
  circle: boolean;
24
+ max: number;
25
+ loading: boolean;
26
26
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
27
27
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
28
28
  declare const _default: typeof __VLS_export;
@@ -15,11 +15,11 @@ declare const __VLS_base: _$vue.DefineComponent<ActionProps, {}, {}, {}, {}, _$v
15
15
  }, string, _$vue.PublicProps, Readonly<ActionProps> & Readonly<{
16
16
  onRun?: (() => any) | undefined;
17
17
  }>, {
18
- type: ButtonType;
19
18
  size: _$_veltra_utils0.ComponentSize;
19
+ type: ButtonType;
20
20
  text: boolean;
21
- loading: boolean;
22
21
  circle: boolean;
22
+ loading: boolean;
23
23
  inDropdown: boolean;
24
24
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
25
25
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -24,10 +24,10 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
24
24
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
25
25
  onSelect?: ((value: string) => any) | undefined;
26
26
  }>, {
27
- placeholder: string;
28
- clearable: boolean;
29
27
  disabled: boolean;
30
28
  readonly: boolean;
29
+ placeholder: string;
30
+ clearable: boolean;
31
31
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
32
32
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
33
33
  declare const _default: typeof __VLS_export;
@@ -11,6 +11,8 @@ declare const __VLS_export: _$vue.DefineComponent<CascadeProps, {}, {}, {}, {},
11
11
  onClear?: (() => any) | undefined;
12
12
  onChange?: ((...args: [value?: string | undefined, label?: string | undefined, item?: Record<string, any> | undefined] | [value: string[], label: string[], data: Record<string, any>[]]) => any) | undefined;
13
13
  }>, {
14
+ disabled: boolean;
15
+ readonly: boolean;
14
16
  labelKey: string;
15
17
  valueKey: string;
16
18
  childrenKey: string;
@@ -18,8 +20,6 @@ declare const __VLS_export: _$vue.DefineComponent<CascadeProps, {}, {}, {}, {},
18
20
  placeholder: string;
19
21
  clearable: boolean;
20
22
  filterable: boolean;
21
- disabled: boolean;
22
- readonly: boolean;
23
23
  visibilityLimit: number;
24
24
  separator: string;
25
25
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
@@ -20,9 +20,9 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
20
20
  "onUpdate:modelValue"?: ((checked: boolean) => any) | undefined;
21
21
  onChange?: ((checked: boolean) => any) | undefined;
22
22
  }>, {
23
- type: _$_veltra_utils0.ColorType;
24
23
  disabled: boolean;
25
24
  readonly: boolean;
25
+ type: _$_veltra_utils0.ColorType;
26
26
  round: boolean;
27
27
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
28
28
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -14,10 +14,10 @@ declare const __VLS_export: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {},
14
14
  }, string, _$vue.PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
15
15
  "onUpdate:modelValue"?: ((value: any[]) => any) | undefined;
16
16
  }>, {
17
- labelKey: string;
18
- valueKey: string;
19
17
  disabled: boolean;
20
18
  readonly: boolean;
19
+ labelKey: string;
20
+ valueKey: string;
21
21
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
22
22
  declare const _default: typeof __VLS_export;
23
23
  //#endregion
@@ -11,8 +11,8 @@ declare const __VLS_export: _$vue.DefineComponent<DatePanelProps, {}, {}, {}, {}
11
11
  "onSelect:range-date"?: ((rangeDate?: [_$_cat_kit_core0.Dater, _$_cat_kit_core0.Dater] | undefined) => any) | undefined;
12
12
  "onSelect:date"?: ((date: _$_cat_kit_core0.Dater) => any) | undefined;
13
13
  }>, {
14
- type: "date" | "month" | "year";
15
14
  size: _$_veltra_utils0.ComponentSize;
15
+ type: "date" | "month" | "year";
16
16
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
17
17
  declare const _default: typeof __VLS_export;
18
18
  //#endregion
@@ -7,11 +7,11 @@ declare const __VLS_export: _$vue.DefineComponent<DatePickerProps, {}, {}, {}, {
7
7
  }, string, _$vue.PublicProps, Readonly<DatePickerProps> & Readonly<{
8
8
  "onUpdate:modelValue"?: ((value?: string | undefined) => any) | undefined;
9
9
  }>, {
10
+ disabled: boolean;
11
+ readonly: boolean;
10
12
  type: "date" | "month" | "year";
11
13
  placeholder: string;
12
14
  clearable: boolean;
13
- disabled: boolean;
14
- readonly: boolean;
15
15
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
16
16
  declare const _default: typeof __VLS_export;
17
17
  //#endregion
@@ -7,11 +7,11 @@ declare const __VLS_export: _$vue.DefineComponent<DateRangePickerProps, {}, {},
7
7
  }, string, _$vue.PublicProps, Readonly<DateRangePickerProps> & Readonly<{
8
8
  "onUpdate:modelValue"?: ((value?: [string, string] | undefined) => any) | undefined;
9
9
  }>, {
10
+ disabled: boolean;
11
+ readonly: boolean;
10
12
  type: "date" | "month" | "year";
11
13
  placeholder: [string, string];
12
14
  clearable: boolean;
13
- disabled: boolean;
14
- readonly: boolean;
15
15
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
16
16
  declare const _default: typeof __VLS_export;
17
17
  //#endregion
@@ -203,6 +203,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
203
203
  "hide-arrow": "",
204
204
  alignment: "start",
205
205
  direction: "bottom",
206
+ trigger: "click",
206
207
  "trigger-dom": __props.triggerDom,
207
208
  visible: __props.visible,
208
209
  "onUpdate:visible": _cache[2] || (_cache[2] = ($event) => handleVisibleChange($event))
@@ -1 +1 @@
1
- {"version":3,"file":"variable-picker.js","names":[],"sources":["../../../../src/components/expression-editor/components/variable-picker.vue"],"sourcesContent":["<template>\n <u-tip\n hide-arrow\n alignment=\"start\"\n direction=\"bottom\"\n :trigger-dom=\"triggerDom\"\n :visible=\"visible\"\n @update:visible=\"handleVisibleChange($event)\"\n >\n <template #content>\n <div :class=\"cls.e('picker')\" @mousedown.prevent>\n <!-- 面包屑:仅逐级模式且非根级显示 -->\n <div v-if=\"!isFlat && navPath.length > 0\" :class=\"cls.e('breadcrumbs')\">\n <span :class=\"cls.e('breadcrumb-item')\">\n <span :class=\"cls.e('breadcrumb-link')\" @click=\"navigateTo(0)\">全部变量</span>\n <u-icon :class=\"cls.e('breadcrumb-separator')\">\n <ArrowRight />\n </u-icon>\n </span>\n <span\n v-for=\"(crumb, i) in navPath\"\n :key=\"`${i}-${crumb.value}`\"\n :class=\"cls.e('breadcrumb-item')\"\n >\n <span\n :class=\"[\n cls.e('breadcrumb-link'),\n { [cls.em('breadcrumb-link', 'active')]: i === navPath.length - 1 }\n ]\"\n @click=\"navigateTo(i + 1)\"\n >{{ crumb.label }}</span\n >\n <u-icon v-if=\"i < navPath.length - 1\" :class=\"cls.e('breadcrumb-separator')\">\n <ArrowRight />\n </u-icon>\n </span>\n </div>\n\n <div :class=\"cls.e('picker-body')\">\n <div :class=\"cls.e('panel')\" v-if=\"currentList.length > 0\">\n <u-scroll tag=\"ul\" :class=\"cls.e('panel-list')\" :content-class=\"cls.e('panel-content')\">\n <li\n v-for=\"(item, idx) in currentList\"\n :key=\"`${idx}-${item.value}`\"\n :data-idx=\"idx\"\n :class=\"getItemCls(item, idx)\"\n @click=\"handleItemClick(item, idx)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <span :class=\"cls.e('item-label')\">{{ item.label }}</span>\n <u-icon v-if=\"!isFlat && hasChildren(item)\" :class=\"cls.e('item-expand')\">\n <ArrowRight />\n </u-icon>\n </li>\n </u-scroll>\n </div>\n <div v-else :class=\"cls.e('empty')\">\n <UEmpty description=\"暂无可用变量\" />\n </div>\n\n <PathPreview v-if=\"isFlat && activePath.length > 0\" :path=\"activePath\" />\n </div>\n </div>\n </template>\n </u-tip>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ArrowRight } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, nextTick, ref, watch } from 'vue'\n\nimport type { VariableItem } from '../../../types'\nimport { UEmpty } from '../../empty'\nimport { UIcon } from '../../icon'\nimport { UScroll } from '../../scroll'\nimport { UTip } from '../../tip'\nimport PathPreview from './path-preview.vue'\n\ndefineOptions({\n name: 'VariablePicker'\n})\n\ninterface FlatItem extends VariableItem {\n /** 仅扁平模式使用:从根到当前节点的路径 */\n __path: VariableItem[]\n}\n\nconst props = defineProps<{\n visible: boolean\n triggerDom?: HTMLElement\n /** 顶层变量树 */\n variables?: VariableItem[]\n /** 来自编辑器 mention 状态的过滤字符串。空 = 逐级模式;非空 = 扁平模式。 */\n filter: string\n /** 是否允许选中分支节点 */\n selectableLevels: 'leaf' | 'any'\n}>()\n\nconst emit = defineEmits<{\n (e: 'select', item: VariableItem): void\n (e: 'update:visible', visible: boolean): void\n /** 用户主动取消(按 Esc)。编辑器据此把 mention 状态置为 dismissed。 */\n (e: 'dismiss'): void\n}>()\n\nconst cls = bem('expression-editor')\n\nconst navPath = ref<VariableItem[]>([])\nconst activeIndex = ref(0)\n\nconst isFlat = computed(() => props.filter !== '')\n\nfunction hasChildren(item: VariableItem): boolean {\n return !!item.children && item.children.length > 0\n}\n\nfunction isLeaf(item: VariableItem): boolean {\n return !hasChildren(item)\n}\n\n/** 当前层级展示的列表(来源依模式而定) */\nconst currentList = computed<VariableItem[]>(() => {\n const variables = props.variables ?? []\n if (isFlat.value) return flatList.value\n let level: VariableItem[] = variables\n for (const node of navPath.value) {\n const found = level.find((it) => it.value === node.value)\n if (!found || !found.children) break\n level = found.children\n }\n return level\n})\n\n/** 扁平模式下:根据 selectableLevels 拍平 + 过滤 */\nconst flatList = computed<FlatItem[]>(() => {\n const variables = props.variables ?? []\n const out: FlatItem[] = []\n const includeBranches = props.selectableLevels === 'any'\n function walk(items: VariableItem[], path: VariableItem[]) {\n for (const it of items) {\n const nextPath = [...path, it]\n if (isLeaf(it)) {\n out.push({ ...it, __path: nextPath })\n } else {\n if (includeBranches) out.push({ ...it, __path: nextPath })\n walk(it.children!, nextPath)\n }\n }\n }\n walk(variables, [])\n const q = props.filter.toLowerCase()\n return out.filter((it) => it.label.toLowerCase().includes(q))\n})\n\n/** 当前 active 项;扁平模式下用于驱动路径预览 */\nconst activePath = computed<VariableItem[]>(() => {\n if (!isFlat.value) return []\n const item = flatList.value[activeIndex.value] as FlatItem | undefined\n return item ? item.__path : []\n})\n\nfunction getItemCls(_item: VariableItem, idx: number) {\n return [cls.e('item'), bem.is('active', idx === activeIndex.value)]\n}\n\nfunction navigateTo(i: number) {\n // 0 = 全部变量;i 之后表示进入到 navPath[i-1]\n navPath.value = navPath.value.slice(0, i)\n activeIndex.value = 0\n}\n\nfunction handleItemClick(item: VariableItem, idx: number) {\n activeIndex.value = idx\n selectActive()\n}\n\nfunction selectActive() {\n const item = currentList.value[activeIndex.value]\n if (!item) return\n\n if (isFlat.value) {\n // 扁平模式始终是「选中并替换」\n emit('select', flattenSourceFor(item))\n return\n }\n\n // 逐级模式\n if (hasChildren(item)) {\n if (props.selectableLevels === 'any') {\n // Enter 在分支项上:选中分支本身\n emit('select', item)\n return\n }\n // 默认 'leaf':进入下一级\n navPath.value = [...navPath.value, item]\n activeIndex.value = 0\n return\n }\n emit('select', item)\n}\n\nfunction flattenSourceFor(it: VariableItem): VariableItem {\n const flat = it as FlatItem\n // 还原为「不带 __path 的纯 VariableItem」,避免泄漏内部字段\n const out: VariableItem = { value: flat.value, label: flat.label }\n if (flat.type) out.type = flat.type\n if (flat.children) out.children = flat.children\n return out\n}\n\nfunction moveActive(delta: -1 | 1) {\n const max = currentList.value.length - 1\n if (max < 0) return\n let next = activeIndex.value + delta\n if (next < 0) next = 0\n if (next > max) next = max\n activeIndex.value = next\n scrollActiveIntoView()\n}\n\nfunction scrollActiveIntoView() {\n void nextTick(() => {\n const list = document.querySelector(`.${cls.e('panel-list')}`)\n if (!list) return\n const item = list.querySelector(`[data-idx=\"${activeIndex.value}\"]`)\n if (item && 'scrollIntoView' in item) {\n ;(item as HTMLElement).scrollIntoView({ block: 'nearest' })\n }\n })\n}\n\nfunction handleVisibleChange(v: boolean) {\n emit('update:visible', v)\n if (!v) reset()\n}\n\nfunction reset() {\n navPath.value = []\n activeIndex.value = 0\n}\n\nwatch(\n () => props.visible,\n (v) => {\n if (!v) reset()\n else activeIndex.value = 0\n }\n)\n\nwatch(\n () => props.filter,\n () => {\n // 过滤词变化:重置 active;逐级 → 扁平时清空 navPath\n activeIndex.value = 0\n if (isFlat.value) navPath.value = []\n }\n)\n\n/**\n * 由父级 forward 进来的键盘事件。\n * 返回 true 表示已处理(外部应 preventDefault)。\n */\nfunction handleKeydown(e: KeyboardEvent): boolean {\n if (!props.visible) return false\n\n switch (e.key) {\n case 'Escape':\n emit('dismiss')\n emit('update:visible', false)\n reset()\n return true\n case 'ArrowDown':\n moveActive(1)\n return true\n case 'ArrowUp':\n moveActive(-1)\n return true\n case 'ArrowLeft':\n // 仅逐级模式:返回上一级;扁平模式下不拦截,让光标正常移动并触发 mention dismiss\n if (!isFlat.value && navPath.value.length > 0) {\n navPath.value = navPath.value.slice(0, -1)\n activeIndex.value = 0\n return true\n }\n return false\n case 'ArrowRight': {\n if (isFlat.value) return false\n const item = currentList.value[activeIndex.value]\n if (item && hasChildren(item)) {\n navPath.value = [...navPath.value, item]\n activeIndex.value = 0\n return true\n }\n return false\n }\n case 'Enter':\n selectActive()\n return true\n default:\n return false\n }\n}\n\ndefineExpose({ handleKeydown })\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"variable-picker.js","names":[],"sources":["../../../../src/components/expression-editor/components/variable-picker.vue"],"sourcesContent":["<template>\n <u-tip\n hide-arrow\n alignment=\"start\"\n direction=\"bottom\"\n trigger=\"click\"\n :trigger-dom=\"triggerDom\"\n :visible=\"visible\"\n @update:visible=\"handleVisibleChange($event)\"\n >\n <template #content>\n <div :class=\"cls.e('picker')\" @mousedown.prevent>\n <!-- 面包屑:仅逐级模式且非根级显示 -->\n <div v-if=\"!isFlat && navPath.length > 0\" :class=\"cls.e('breadcrumbs')\">\n <span :class=\"cls.e('breadcrumb-item')\">\n <span :class=\"cls.e('breadcrumb-link')\" @click=\"navigateTo(0)\">全部变量</span>\n <u-icon :class=\"cls.e('breadcrumb-separator')\">\n <ArrowRight />\n </u-icon>\n </span>\n <span\n v-for=\"(crumb, i) in navPath\"\n :key=\"`${i}-${crumb.value}`\"\n :class=\"cls.e('breadcrumb-item')\"\n >\n <span\n :class=\"[\n cls.e('breadcrumb-link'),\n { [cls.em('breadcrumb-link', 'active')]: i === navPath.length - 1 }\n ]\"\n @click=\"navigateTo(i + 1)\"\n >{{ crumb.label }}</span\n >\n <u-icon v-if=\"i < navPath.length - 1\" :class=\"cls.e('breadcrumb-separator')\">\n <ArrowRight />\n </u-icon>\n </span>\n </div>\n\n <div :class=\"cls.e('picker-body')\">\n <div :class=\"cls.e('panel')\" v-if=\"currentList.length > 0\">\n <u-scroll tag=\"ul\" :class=\"cls.e('panel-list')\" :content-class=\"cls.e('panel-content')\">\n <li\n v-for=\"(item, idx) in currentList\"\n :key=\"`${idx}-${item.value}`\"\n :data-idx=\"idx\"\n :class=\"getItemCls(item, idx)\"\n @click=\"handleItemClick(item, idx)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <span :class=\"cls.e('item-label')\">{{ item.label }}</span>\n <u-icon v-if=\"!isFlat && hasChildren(item)\" :class=\"cls.e('item-expand')\">\n <ArrowRight />\n </u-icon>\n </li>\n </u-scroll>\n </div>\n <div v-else :class=\"cls.e('empty')\">\n <UEmpty description=\"暂无可用变量\" />\n </div>\n\n <PathPreview v-if=\"isFlat && activePath.length > 0\" :path=\"activePath\" />\n </div>\n </div>\n </template>\n </u-tip>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ArrowRight } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, nextTick, ref, watch } from 'vue'\n\nimport type { VariableItem } from '../../../types'\nimport { UEmpty } from '../../empty'\nimport { UIcon } from '../../icon'\nimport { UScroll } from '../../scroll'\nimport { UTip } from '../../tip'\nimport PathPreview from './path-preview.vue'\n\ndefineOptions({\n name: 'VariablePicker'\n})\n\ninterface FlatItem extends VariableItem {\n /** 仅扁平模式使用:从根到当前节点的路径 */\n __path: VariableItem[]\n}\n\nconst props = defineProps<{\n visible: boolean\n triggerDom?: HTMLElement\n /** 顶层变量树 */\n variables?: VariableItem[]\n /** 来自编辑器 mention 状态的过滤字符串。空 = 逐级模式;非空 = 扁平模式。 */\n filter: string\n /** 是否允许选中分支节点 */\n selectableLevels: 'leaf' | 'any'\n}>()\n\nconst emit = defineEmits<{\n (e: 'select', item: VariableItem): void\n (e: 'update:visible', visible: boolean): void\n /** 用户主动取消(按 Esc)。编辑器据此把 mention 状态置为 dismissed。 */\n (e: 'dismiss'): void\n}>()\n\nconst cls = bem('expression-editor')\n\nconst navPath = ref<VariableItem[]>([])\nconst activeIndex = ref(0)\n\nconst isFlat = computed(() => props.filter !== '')\n\nfunction hasChildren(item: VariableItem): boolean {\n return !!item.children && item.children.length > 0\n}\n\nfunction isLeaf(item: VariableItem): boolean {\n return !hasChildren(item)\n}\n\n/** 当前层级展示的列表(来源依模式而定) */\nconst currentList = computed<VariableItem[]>(() => {\n const variables = props.variables ?? []\n if (isFlat.value) return flatList.value\n let level: VariableItem[] = variables\n for (const node of navPath.value) {\n const found = level.find((it) => it.value === node.value)\n if (!found || !found.children) break\n level = found.children\n }\n return level\n})\n\n/** 扁平模式下:根据 selectableLevels 拍平 + 过滤 */\nconst flatList = computed<FlatItem[]>(() => {\n const variables = props.variables ?? []\n const out: FlatItem[] = []\n const includeBranches = props.selectableLevels === 'any'\n function walk(items: VariableItem[], path: VariableItem[]) {\n for (const it of items) {\n const nextPath = [...path, it]\n if (isLeaf(it)) {\n out.push({ ...it, __path: nextPath })\n } else {\n if (includeBranches) out.push({ ...it, __path: nextPath })\n walk(it.children!, nextPath)\n }\n }\n }\n walk(variables, [])\n const q = props.filter.toLowerCase()\n return out.filter((it) => it.label.toLowerCase().includes(q))\n})\n\n/** 当前 active 项;扁平模式下用于驱动路径预览 */\nconst activePath = computed<VariableItem[]>(() => {\n if (!isFlat.value) return []\n const item = flatList.value[activeIndex.value] as FlatItem | undefined\n return item ? item.__path : []\n})\n\nfunction getItemCls(_item: VariableItem, idx: number) {\n return [cls.e('item'), bem.is('active', idx === activeIndex.value)]\n}\n\nfunction navigateTo(i: number) {\n // 0 = 全部变量;i 之后表示进入到 navPath[i-1]\n navPath.value = navPath.value.slice(0, i)\n activeIndex.value = 0\n}\n\nfunction handleItemClick(item: VariableItem, idx: number) {\n activeIndex.value = idx\n selectActive()\n}\n\nfunction selectActive() {\n const item = currentList.value[activeIndex.value]\n if (!item) return\n\n if (isFlat.value) {\n // 扁平模式始终是「选中并替换」\n emit('select', flattenSourceFor(item))\n return\n }\n\n // 逐级模式\n if (hasChildren(item)) {\n if (props.selectableLevels === 'any') {\n // Enter 在分支项上:选中分支本身\n emit('select', item)\n return\n }\n // 默认 'leaf':进入下一级\n navPath.value = [...navPath.value, item]\n activeIndex.value = 0\n return\n }\n emit('select', item)\n}\n\nfunction flattenSourceFor(it: VariableItem): VariableItem {\n const flat = it as FlatItem\n // 还原为「不带 __path 的纯 VariableItem」,避免泄漏内部字段\n const out: VariableItem = { value: flat.value, label: flat.label }\n if (flat.type) out.type = flat.type\n if (flat.children) out.children = flat.children\n return out\n}\n\nfunction moveActive(delta: -1 | 1) {\n const max = currentList.value.length - 1\n if (max < 0) return\n let next = activeIndex.value + delta\n if (next < 0) next = 0\n if (next > max) next = max\n activeIndex.value = next\n scrollActiveIntoView()\n}\n\nfunction scrollActiveIntoView() {\n void nextTick(() => {\n const list = document.querySelector(`.${cls.e('panel-list')}`)\n if (!list) return\n const item = list.querySelector(`[data-idx=\"${activeIndex.value}\"]`)\n if (item && 'scrollIntoView' in item) {\n ;(item as HTMLElement).scrollIntoView({ block: 'nearest' })\n }\n })\n}\n\nfunction handleVisibleChange(v: boolean) {\n emit('update:visible', v)\n if (!v) reset()\n}\n\nfunction reset() {\n navPath.value = []\n activeIndex.value = 0\n}\n\nwatch(\n () => props.visible,\n (v) => {\n if (!v) reset()\n else activeIndex.value = 0\n }\n)\n\nwatch(\n () => props.filter,\n () => {\n // 过滤词变化:重置 active;逐级 → 扁平时清空 navPath\n activeIndex.value = 0\n if (isFlat.value) navPath.value = []\n }\n)\n\n/**\n * 由父级 forward 进来的键盘事件。\n * 返回 true 表示已处理(外部应 preventDefault)。\n */\nfunction handleKeydown(e: KeyboardEvent): boolean {\n if (!props.visible) return false\n\n switch (e.key) {\n case 'Escape':\n emit('dismiss')\n emit('update:visible', false)\n reset()\n return true\n case 'ArrowDown':\n moveActive(1)\n return true\n case 'ArrowUp':\n moveActive(-1)\n return true\n case 'ArrowLeft':\n // 仅逐级模式:返回上一级;扁平模式下不拦截,让光标正常移动并触发 mention dismiss\n if (!isFlat.value && navPath.value.length > 0) {\n navPath.value = navPath.value.slice(0, -1)\n activeIndex.value = 0\n return true\n }\n return false\n case 'ArrowRight': {\n if (isFlat.value) return false\n const item = currentList.value[activeIndex.value]\n if (item && hasChildren(item)) {\n navPath.value = [...navPath.value, item]\n activeIndex.value = 0\n return true\n }\n return false\n }\n case 'Enter':\n selectActive()\n return true\n default:\n return false\n }\n}\n\ndefineExpose({ handleKeydown })\n</script>\n"],"mappings":"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"}
@@ -274,6 +274,7 @@ function createEditor(opts) {
274
274
  insertPlainTextAtCaret(text);
275
275
  }
276
276
  function onSelectionChange() {
277
+ if (composing) return;
277
278
  if (!opts.onSelectionChange) return;
278
279
  const sel = document.getSelection();
279
280
  if (!sel || sel.rangeCount === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"editor.js","names":[],"sources":["../../../../src/components/expression-editor/core/editor.ts"],"sourcesContent":["import type { BEM } from '@veltra/utils'\n\nimport type { VariableItem } from '../../../types'\nimport { createChip, isChipElement, readChipType, readChipValue } from './chip'\nimport { normalize, parse, serialize, type Doc, type Segment } from './model'\n\nconst TEXT_SEG_ATTR = 'data-seg'\nconst TEXT_SEG_VALUE = 'text'\n\nexport interface EditorVariableInfo {\n value: string\n label: string\n type?: string\n}\n\nexport interface EditorOptions {\n container: HTMLElement\n cls: BEM<'expression-editor'>\n initialDoc: Doc\n /**\n * 在 DOM → 模型反推、字符串 → 模型反序列化时为 chip 补齐 label / type。\n * 用 getter 形式以便外层 `variables` props 变化时拿到最新映射。\n */\n getVariableMap?: () => Map<string, VariableItem>\n /** 模型每次更新(包括用户键入文本)后回调 */\n onChange?: (doc: Doc) => void\n /** 选中区域 / 光标偏移变化时回调,offset 为序列化字符串中的位置;无选中或不在编辑器内时为 null */\n onSelectionChange?: (info: { offset: number | null }) => void\n /** 用户点击某个 chip 的主体(重选) */\n onChipReselect?: (info: { chipEl: HTMLElement; segIndex: number; chipOffset: number }) => void\n /** 用户点击某个 chip 的 ×(删除) */\n onChipRemove?: (info: { chipEl: HTMLElement; segIndex: number }) => void\n}\n\nexport interface EditorAPI {\n /** 当前模型 */\n getDoc(): Doc\n /** 完全替换模型(会重渲染 DOM) */\n setDoc(doc: Doc): void\n /** 序列化为字符串(modelValue 形态) */\n getValue(): string\n /** 反序列化字符串为模型并替换(可选传入 variableMap 以补齐 label/type) */\n setValue(value: string): void\n /** 当前光标偏移(无 selection 返回 null) */\n getCaretOffset(): number | null\n /** 把光标设置到指定偏移(不会修改模型) */\n setCaretOffset(offset: number): void\n /**\n * 用一个 var chip 替换 [start, end) 区间。\n * 调用方通常在 mention 选中时用:start = `@` 起点、end = 当前光标。\n */\n replaceRangeWithVar(start: number, end: number, variable: EditorVariableInfo): void\n /** 移除指定段下标对应的 var chip(必须是 var 段)。 */\n removeVarAt(segIndex: number): void\n /** 替换指定段下标对应的 var chip 为新变量(必须是 var 段)。 */\n replaceVarAt(segIndex: number, variable: EditorVariableInfo): void\n /** 卸载所有事件监听(不会销毁 container DOM) */\n dispose(): void\n /** 强制根据当前模型重渲染 DOM(光标位置尽量保留) */\n rerender(): void\n /** 容器引用(只读) */\n readonly container: HTMLElement\n}\n\nexport function createEditor(opts: EditorOptions): EditorAPI {\n let doc: Doc = normalize(opts.initialDoc)\n let composing = false\n\n function emitChange(next: Doc) {\n doc = next\n opts.onChange?.(doc)\n }\n\n function makeTextSpan(value: string): HTMLSpanElement {\n const span = document.createElement('span')\n span.setAttribute(TEXT_SEG_ATTR, TEXT_SEG_VALUE)\n span.textContent = value\n return span\n }\n\n function makeChipSpan(seg: Extract<Segment, { kind: 'var' }>): HTMLElement {\n return createChip(seg, {\n cls: opts.cls,\n onReselect: (chipEl) => {\n const { segIndex, chipOffset } = locateChip(chipEl)\n if (segIndex < 0) return\n opts.onChipReselect?.({ chipEl, segIndex, chipOffset })\n },\n onRemove: (chipEl) => {\n const { segIndex } = locateChip(chipEl)\n if (segIndex < 0) return\n opts.onChipRemove?.({ chipEl, segIndex })\n }\n })\n }\n\n function locateChip(chipEl: HTMLElement): { segIndex: number; chipOffset: number } {\n let chipOffset = 0\n let segIdx = -1\n let i = 0\n for (const child of Array.from(opts.container.childNodes)) {\n if (child === chipEl) {\n segIdx = i\n return { segIndex: segIdx, chipOffset }\n }\n chipOffset += childSerialLength(child)\n i++\n }\n return { segIndex: -1, chipOffset: -1 }\n }\n\n function childSerialLength(node: Node): number {\n if (isChipElement(node)) return readChipValue(node).length + 2\n return (node.textContent ?? '').length\n }\n\n /** 渲染 doc 到 container:每个 var 两侧都补一个 text span(即便空),保证光标可停靠。 */\n function render(targetCaret?: number) {\n const container = opts.container\n container.innerHTML = ''\n\n // 在边界与每两个 var 之间确保有 text span 占位\n const expanded: Segment[] = []\n let lastWasVar = false\n let first = true\n for (const seg of doc) {\n if (first) {\n if (seg.kind === 'var') expanded.push({ kind: 'text', value: '' })\n first = false\n } else if (lastWasVar && seg.kind === 'var') {\n expanded.push({ kind: 'text', value: '' })\n }\n expanded.push(seg)\n lastWasVar = seg.kind === 'var'\n }\n if (lastWasVar || expanded.length === 0) expanded.push({ kind: 'text', value: '' })\n\n for (const seg of expanded) {\n if (seg.kind === 'text') container.appendChild(makeTextSpan(seg.value))\n else container.appendChild(makeChipSpan(seg))\n }\n\n if (typeof targetCaret === 'number') setCaret(targetCaret)\n }\n\n /** 从 DOM 反推模型:宽容裸 text node、空 text span。 */\n function readDocFromDOM(): Doc {\n const segs: Segment[] = []\n for (const child of Array.from(opts.container.childNodes)) {\n if (isChipElement(child)) {\n const value = readChipValue(child)\n const domType = readChipType(child)\n const fallbackLabel =\n child.querySelector(`.${opts.cls.e('chip-label')}`)?.textContent ?? value\n // 优先从 variableMap 取最新 label / type,保持与外部数据源一致\n const fromMap = opts.getVariableMap?.().get(value)\n const finalType = fromMap?.type ?? domType\n const seg: Segment = finalType\n ? { kind: 'var', value, label: fromMap?.label ?? fallbackLabel, type: finalType }\n : { kind: 'var', value, label: fromMap?.label ?? fallbackLabel }\n segs.push(seg)\n } else {\n segs.push({ kind: 'text', value: child.textContent ?? '' })\n }\n }\n return normalize(segs)\n }\n\n /**\n * 计算光标在序列化字符串中的全局偏移。\n * 规则:\n * - 在 text span / 裸 text node 中:父级 / 节点之前的 segments 长度累加 + selection 内偏移\n * - 在 chip 边界:根据 selection 在 chip 节点之前 / 之后判断\n */\n function getCaretOffset(): number | null {\n const sel = document.getSelection()\n if (!sel || sel.rangeCount === 0) return null\n const range = sel.getRangeAt(0)\n if (!opts.container.contains(range.startContainer)) return null\n\n let offset = 0\n let found = false\n for (const child of Array.from(opts.container.childNodes)) {\n if (found) break\n if (isChipElement(child)) {\n if (range.startContainer === child) {\n // selection.startOffset 表示子节点索引:0 表示 chip 起始,>=1 表示 chip 之后\n if (range.startOffset === 0) return offset\n return offset + childSerialLength(child)\n }\n if (range.startContainer === opts.container && getChildIndex(child) === range.startOffset) {\n // 容器层 selection:偏移即子节点索引\n return offset\n }\n offset += childSerialLength(child)\n } else {\n // text span 或裸 text 节点\n if (\n range.startContainer === child ||\n (child.contains(range.startContainer) && child !== opts.container)\n ) {\n // 计算 selection 在该 child textContent 中的偏移\n const localOffset = computeTextOffsetWithin(\n child,\n range.startContainer,\n range.startOffset\n )\n return offset + localOffset\n }\n if (range.startContainer === opts.container && getChildIndex(child) === range.startOffset) {\n return offset\n }\n offset += childSerialLength(child)\n }\n }\n if (\n range.startContainer === opts.container &&\n range.startOffset === opts.container.childNodes.length\n ) {\n return offset\n }\n return null\n }\n\n function getChildIndex(node: Node): number {\n let i = 0\n let cur: Node | null = node.previousSibling\n while (cur) {\n i++\n cur = cur.previousSibling\n }\n return i\n }\n\n function computeTextOffsetWithin(root: Node, target: Node, targetOffset: number): number {\n if (root === target) return targetOffset\n let total = 0\n function walk(n: Node): boolean {\n if (n === target) {\n total += targetOffset\n return true\n }\n if (n.nodeType === 3) {\n total += (n as Text).data.length\n return false\n }\n for (const c of Array.from(n.childNodes)) {\n if (walk(c)) return true\n }\n return false\n }\n walk(root)\n return total\n }\n\n /** 把光标放到 offset 处,offset 大于文档长度时落到末尾。 */\n function setCaret(offset: number): void {\n const sel = document.getSelection()\n if (!sel) return\n\n let remain = offset\n const children = Array.from(opts.container.childNodes)\n for (let i = 0; i < children.length; i++) {\n const child = children[i]!\n const len = childSerialLength(child)\n if (isChipElement(child)) {\n if (remain <= 0) {\n // 光标定到 chip 之前 → 落到容器层 child 索引\n placeAt(opts.container, i)\n return\n }\n if (remain < len) {\n // 落在 chip 内部 → 视作 chip 之后\n placeAt(opts.container, i + 1)\n return\n }\n remain -= len\n } else {\n if (remain <= len) {\n // 落在该 text span 内\n placeWithinText(child, remain)\n return\n }\n remain -= len\n }\n }\n // 落到末尾\n if (children.length === 0) {\n placeAt(opts.container, 0)\n return\n }\n const last = children[children.length - 1]!\n if (isChipElement(last)) placeAt(opts.container, children.length)\n else placeWithinText(last, (last.textContent ?? '').length)\n }\n\n function placeAt(parent: Node, index: number) {\n const sel = document.getSelection()\n if (!sel) return\n const range = document.createRange()\n range.setStart(parent, index)\n range.collapse(true)\n sel.removeAllRanges()\n sel.addRange(range)\n }\n\n function placeWithinText(span: Node, offset: number): void {\n const sel = document.getSelection()\n if (!sel) return\n const target = firstTextDescendant(span)\n const range = document.createRange()\n if (target) {\n const max = target.data.length\n range.setStart(target, Math.min(offset, max))\n } else {\n // 空 span:让光标落到 span 内(child index 0)\n range.setStart(span, 0)\n }\n range.collapse(true)\n sel.removeAllRanges()\n sel.addRange(range)\n }\n\n function firstTextDescendant(root: Node): Text | null {\n if (root.nodeType === 3) return root as Text\n for (const c of Array.from(root.childNodes)) {\n const t = firstTextDescendant(c)\n if (t) return t\n }\n return null\n }\n\n /** input 后:从 DOM 反推 doc,并触发 onChange(如有变化)。 */\n function syncFromDOM(): void {\n if (composing) return\n const next = readDocFromDOM()\n if (!docEqual(next, doc)) emitChange(next)\n }\n\n function docEqual(a: Doc, b: Doc): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n const x = a[i]!\n const y = b[i]!\n if (x.kind !== y.kind) return false\n if (x.kind === 'text' && y.kind === 'text' && x.value !== y.value) return false\n if (x.kind === 'var' && y.kind === 'var') {\n if (x.value !== y.value || x.label !== y.label || x.type !== y.type) return false\n }\n }\n return true\n }\n\n function onInput() {\n syncFromDOM()\n }\n function onCompositionStart() {\n composing = true\n }\n function onCompositionEnd() {\n composing = false\n syncFromDOM()\n }\n function onPaste(e: ClipboardEvent) {\n e.preventDefault()\n const text = e.clipboardData?.getData('text/plain') ?? ''\n if (!text) return\n insertPlainTextAtCaret(text)\n }\n function onSelectionChange() {\n if (!opts.onSelectionChange) return\n const sel = document.getSelection()\n if (!sel || sel.rangeCount === 0) {\n opts.onSelectionChange({ offset: null })\n return\n }\n const range = sel.getRangeAt(0)\n if (!opts.container.contains(range.startContainer)) {\n opts.onSelectionChange({ offset: null })\n return\n }\n opts.onSelectionChange({ offset: getCaretOffset() })\n }\n\n function insertPlainTextAtCaret(text: string) {\n const sel = document.getSelection()\n if (!sel || sel.rangeCount === 0) return\n const range = sel.getRangeAt(0)\n if (!opts.container.contains(range.startContainer)) return\n range.deleteContents()\n const node = document.createTextNode(text)\n range.insertNode(node)\n range.setStartAfter(node)\n range.collapse(true)\n sel.removeAllRanges()\n sel.addRange(range)\n syncFromDOM()\n }\n\n // 命令:替换区间为 var chip\n function replaceRangeWithVar(start: number, end: number, variable: EditorVariableInfo) {\n const a = Math.min(start, end)\n const b = Math.max(start, end)\n const left = sliceDoc(doc, 0, a)\n const right = sliceDoc(doc, b, totalLength(doc))\n const inserted: Segment = variable.type\n ? { kind: 'var', value: variable.value, label: variable.label, type: variable.type }\n : { kind: 'var', value: variable.value, label: variable.label }\n const next = normalize([...left, inserted, ...right])\n emitChange(next)\n render(a + variable.value.length + 2) // 光标定到 chip 之后\n }\n\n function removeVarAt(segIndex: number) {\n const target = doc[segIndex]\n if (!target || target.kind !== 'var') return\n const before = doc.slice(0, segIndex)\n const after = doc.slice(segIndex + 1)\n const caretOffset = sliceLength(before)\n const next = normalize([...before, ...after])\n emitChange(next)\n render(caretOffset)\n }\n\n function replaceVarAt(segIndex: number, variable: EditorVariableInfo) {\n const target = doc[segIndex]\n if (!target || target.kind !== 'var') return\n const next = doc.slice()\n const replaced: Segment = variable.type\n ? { kind: 'var', value: variable.value, label: variable.label, type: variable.type }\n : { kind: 'var', value: variable.value, label: variable.label }\n next[segIndex] = replaced\n const caretOffset = sliceLength(doc.slice(0, segIndex)) + variable.value.length + 2\n emitChange(normalize(next))\n render(caretOffset)\n }\n\n // 工具:基于序列化偏移的切片(不会切开 var)\n function sliceDoc(d: Doc, start: number, end: number): Doc {\n const out: Segment[] = []\n let pos = 0\n for (const seg of d) {\n const len = seg.kind === 'text' ? seg.value.length : seg.value.length + 2\n const segStart = pos\n const segEnd = pos + len\n if (segEnd <= start || segStart >= end) {\n pos += len\n continue\n }\n if (seg.kind === 'text') {\n const a = Math.max(0, start - segStart)\n const b = Math.min(len, end - segStart)\n const slice = seg.value.slice(a, b)\n if (slice) out.push({ kind: 'text', value: slice })\n } else {\n // var 是原子:只有当 [start, end) 完整覆盖该 var 时保留;否则抛弃\n if (start <= segStart && end >= segEnd) out.push(seg)\n }\n pos += len\n }\n return out\n }\n\n function sliceLength(d: Doc): number {\n let n = 0\n for (const s of d) n += s.kind === 'text' ? s.value.length : s.value.length + 2\n return n\n }\n\n function totalLength(d: Doc): number {\n return sliceLength(d)\n }\n\n // 绑定监听\n const container = opts.container\n container.addEventListener('input', onInput)\n container.addEventListener('compositionstart', onCompositionStart)\n container.addEventListener('compositionend', onCompositionEnd)\n container.addEventListener('paste', onPaste)\n document.addEventListener('selectionchange', onSelectionChange)\n\n // 初次渲染\n render()\n\n return {\n getDoc: () => doc,\n setDoc: (next) => {\n doc = normalize(next)\n render()\n opts.onChange?.(doc)\n },\n getValue: () => serialize(doc),\n setValue: (value) => {\n doc = parse(value, opts.getVariableMap?.())\n render()\n opts.onChange?.(doc)\n },\n getCaretOffset,\n setCaretOffset: setCaret,\n replaceRangeWithVar,\n removeVarAt,\n replaceVarAt,\n rerender: () => render(),\n dispose: () => {\n container.removeEventListener('input', onInput)\n container.removeEventListener('compositionstart', onCompositionStart)\n container.removeEventListener('compositionend', onCompositionEnd)\n container.removeEventListener('paste', onPaste)\n document.removeEventListener('selectionchange', onSelectionChange)\n },\n container\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"editor.js","names":[],"sources":["../../../../src/components/expression-editor/core/editor.ts"],"sourcesContent":["import type { BEM } from '@veltra/utils'\n\nimport type { VariableItem } from '../../../types'\nimport { createChip, isChipElement, readChipType, readChipValue } from './chip'\nimport { normalize, parse, serialize, type Doc, type Segment } from './model'\n\nconst TEXT_SEG_ATTR = 'data-seg'\nconst TEXT_SEG_VALUE = 'text'\n\nexport interface EditorVariableInfo {\n value: string\n label: string\n type?: string\n}\n\nexport interface EditorOptions {\n container: HTMLElement\n cls: BEM<'expression-editor'>\n initialDoc: Doc\n /**\n * 在 DOM → 模型反推、字符串 → 模型反序列化时为 chip 补齐 label / type。\n * 用 getter 形式以便外层 `variables` props 变化时拿到最新映射。\n */\n getVariableMap?: () => Map<string, VariableItem>\n /** 模型每次更新(包括用户键入文本)后回调 */\n onChange?: (doc: Doc) => void\n /** 选中区域 / 光标偏移变化时回调,offset 为序列化字符串中的位置;无选中或不在编辑器内时为 null */\n onSelectionChange?: (info: { offset: number | null }) => void\n /** 用户点击某个 chip 的主体(重选) */\n onChipReselect?: (info: { chipEl: HTMLElement; segIndex: number; chipOffset: number }) => void\n /** 用户点击某个 chip 的 ×(删除) */\n onChipRemove?: (info: { chipEl: HTMLElement; segIndex: number }) => void\n}\n\nexport interface EditorAPI {\n /** 当前模型 */\n getDoc(): Doc\n /** 完全替换模型(会重渲染 DOM) */\n setDoc(doc: Doc): void\n /** 序列化为字符串(modelValue 形态) */\n getValue(): string\n /** 反序列化字符串为模型并替换(可选传入 variableMap 以补齐 label/type) */\n setValue(value: string): void\n /** 当前光标偏移(无 selection 返回 null) */\n getCaretOffset(): number | null\n /** 把光标设置到指定偏移(不会修改模型) */\n setCaretOffset(offset: number): void\n /**\n * 用一个 var chip 替换 [start, end) 区间。\n * 调用方通常在 mention 选中时用:start = `@` 起点、end = 当前光标。\n */\n replaceRangeWithVar(start: number, end: number, variable: EditorVariableInfo): void\n /** 移除指定段下标对应的 var chip(必须是 var 段)。 */\n removeVarAt(segIndex: number): void\n /** 替换指定段下标对应的 var chip 为新变量(必须是 var 段)。 */\n replaceVarAt(segIndex: number, variable: EditorVariableInfo): void\n /** 卸载所有事件监听(不会销毁 container DOM) */\n dispose(): void\n /** 强制根据当前模型重渲染 DOM(光标位置尽量保留) */\n rerender(): void\n /** 容器引用(只读) */\n readonly container: HTMLElement\n}\n\nexport function createEditor(opts: EditorOptions): EditorAPI {\n let doc: Doc = normalize(opts.initialDoc)\n let composing = false\n\n function emitChange(next: Doc) {\n doc = next\n opts.onChange?.(doc)\n }\n\n function makeTextSpan(value: string): HTMLSpanElement {\n const span = document.createElement('span')\n span.setAttribute(TEXT_SEG_ATTR, TEXT_SEG_VALUE)\n span.textContent = value\n return span\n }\n\n function makeChipSpan(seg: Extract<Segment, { kind: 'var' }>): HTMLElement {\n return createChip(seg, {\n cls: opts.cls,\n onReselect: (chipEl) => {\n const { segIndex, chipOffset } = locateChip(chipEl)\n if (segIndex < 0) return\n opts.onChipReselect?.({ chipEl, segIndex, chipOffset })\n },\n onRemove: (chipEl) => {\n const { segIndex } = locateChip(chipEl)\n if (segIndex < 0) return\n opts.onChipRemove?.({ chipEl, segIndex })\n }\n })\n }\n\n function locateChip(chipEl: HTMLElement): { segIndex: number; chipOffset: number } {\n let chipOffset = 0\n let segIdx = -1\n let i = 0\n for (const child of Array.from(opts.container.childNodes)) {\n if (child === chipEl) {\n segIdx = i\n return { segIndex: segIdx, chipOffset }\n }\n chipOffset += childSerialLength(child)\n i++\n }\n return { segIndex: -1, chipOffset: -1 }\n }\n\n function childSerialLength(node: Node): number {\n if (isChipElement(node)) return readChipValue(node).length + 2\n return (node.textContent ?? '').length\n }\n\n /** 渲染 doc 到 container:每个 var 两侧都补一个 text span(即便空),保证光标可停靠。 */\n function render(targetCaret?: number) {\n const container = opts.container\n container.innerHTML = ''\n\n // 在边界与每两个 var 之间确保有 text span 占位\n const expanded: Segment[] = []\n let lastWasVar = false\n let first = true\n for (const seg of doc) {\n if (first) {\n if (seg.kind === 'var') expanded.push({ kind: 'text', value: '' })\n first = false\n } else if (lastWasVar && seg.kind === 'var') {\n expanded.push({ kind: 'text', value: '' })\n }\n expanded.push(seg)\n lastWasVar = seg.kind === 'var'\n }\n if (lastWasVar || expanded.length === 0) expanded.push({ kind: 'text', value: '' })\n\n for (const seg of expanded) {\n if (seg.kind === 'text') container.appendChild(makeTextSpan(seg.value))\n else container.appendChild(makeChipSpan(seg))\n }\n\n if (typeof targetCaret === 'number') setCaret(targetCaret)\n }\n\n /** 从 DOM 反推模型:宽容裸 text node、空 text span。 */\n function readDocFromDOM(): Doc {\n const segs: Segment[] = []\n for (const child of Array.from(opts.container.childNodes)) {\n if (isChipElement(child)) {\n const value = readChipValue(child)\n const domType = readChipType(child)\n const fallbackLabel =\n child.querySelector(`.${opts.cls.e('chip-label')}`)?.textContent ?? value\n // 优先从 variableMap 取最新 label / type,保持与外部数据源一致\n const fromMap = opts.getVariableMap?.().get(value)\n const finalType = fromMap?.type ?? domType\n const seg: Segment = finalType\n ? { kind: 'var', value, label: fromMap?.label ?? fallbackLabel, type: finalType }\n : { kind: 'var', value, label: fromMap?.label ?? fallbackLabel }\n segs.push(seg)\n } else {\n segs.push({ kind: 'text', value: child.textContent ?? '' })\n }\n }\n return normalize(segs)\n }\n\n /**\n * 计算光标在序列化字符串中的全局偏移。\n * 规则:\n * - 在 text span / 裸 text node 中:父级 / 节点之前的 segments 长度累加 + selection 内偏移\n * - 在 chip 边界:根据 selection 在 chip 节点之前 / 之后判断\n */\n function getCaretOffset(): number | null {\n const sel = document.getSelection()\n if (!sel || sel.rangeCount === 0) return null\n const range = sel.getRangeAt(0)\n if (!opts.container.contains(range.startContainer)) return null\n\n let offset = 0\n let found = false\n for (const child of Array.from(opts.container.childNodes)) {\n if (found) break\n if (isChipElement(child)) {\n if (range.startContainer === child) {\n // selection.startOffset 表示子节点索引:0 表示 chip 起始,>=1 表示 chip 之后\n if (range.startOffset === 0) return offset\n return offset + childSerialLength(child)\n }\n if (range.startContainer === opts.container && getChildIndex(child) === range.startOffset) {\n // 容器层 selection:偏移即子节点索引\n return offset\n }\n offset += childSerialLength(child)\n } else {\n // text span 或裸 text 节点\n if (\n range.startContainer === child ||\n (child.contains(range.startContainer) && child !== opts.container)\n ) {\n // 计算 selection 在该 child textContent 中的偏移\n const localOffset = computeTextOffsetWithin(\n child,\n range.startContainer,\n range.startOffset\n )\n return offset + localOffset\n }\n if (range.startContainer === opts.container && getChildIndex(child) === range.startOffset) {\n return offset\n }\n offset += childSerialLength(child)\n }\n }\n if (\n range.startContainer === opts.container &&\n range.startOffset === opts.container.childNodes.length\n ) {\n return offset\n }\n return null\n }\n\n function getChildIndex(node: Node): number {\n let i = 0\n let cur: Node | null = node.previousSibling\n while (cur) {\n i++\n cur = cur.previousSibling\n }\n return i\n }\n\n function computeTextOffsetWithin(root: Node, target: Node, targetOffset: number): number {\n if (root === target) return targetOffset\n let total = 0\n function walk(n: Node): boolean {\n if (n === target) {\n total += targetOffset\n return true\n }\n if (n.nodeType === 3) {\n total += (n as Text).data.length\n return false\n }\n for (const c of Array.from(n.childNodes)) {\n if (walk(c)) return true\n }\n return false\n }\n walk(root)\n return total\n }\n\n /** 把光标放到 offset 处,offset 大于文档长度时落到末尾。 */\n function setCaret(offset: number): void {\n const sel = document.getSelection()\n if (!sel) return\n\n let remain = offset\n const children = Array.from(opts.container.childNodes)\n for (let i = 0; i < children.length; i++) {\n const child = children[i]!\n const len = childSerialLength(child)\n if (isChipElement(child)) {\n if (remain <= 0) {\n // 光标定到 chip 之前 → 落到容器层 child 索引\n placeAt(opts.container, i)\n return\n }\n if (remain < len) {\n // 落在 chip 内部 → 视作 chip 之后\n placeAt(opts.container, i + 1)\n return\n }\n remain -= len\n } else {\n if (remain <= len) {\n // 落在该 text span 内\n placeWithinText(child, remain)\n return\n }\n remain -= len\n }\n }\n // 落到末尾\n if (children.length === 0) {\n placeAt(opts.container, 0)\n return\n }\n const last = children[children.length - 1]!\n if (isChipElement(last)) placeAt(opts.container, children.length)\n else placeWithinText(last, (last.textContent ?? '').length)\n }\n\n function placeAt(parent: Node, index: number) {\n const sel = document.getSelection()\n if (!sel) return\n const range = document.createRange()\n range.setStart(parent, index)\n range.collapse(true)\n sel.removeAllRanges()\n sel.addRange(range)\n }\n\n function placeWithinText(span: Node, offset: number): void {\n const sel = document.getSelection()\n if (!sel) return\n const target = firstTextDescendant(span)\n const range = document.createRange()\n if (target) {\n const max = target.data.length\n range.setStart(target, Math.min(offset, max))\n } else {\n // 空 span:让光标落到 span 内(child index 0)\n range.setStart(span, 0)\n }\n range.collapse(true)\n sel.removeAllRanges()\n sel.addRange(range)\n }\n\n function firstTextDescendant(root: Node): Text | null {\n if (root.nodeType === 3) return root as Text\n for (const c of Array.from(root.childNodes)) {\n const t = firstTextDescendant(c)\n if (t) return t\n }\n return null\n }\n\n /** input 后:从 DOM 反推 doc,并触发 onChange(如有变化)。 */\n function syncFromDOM(): void {\n if (composing) return\n const next = readDocFromDOM()\n if (!docEqual(next, doc)) emitChange(next)\n }\n\n function docEqual(a: Doc, b: Doc): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n const x = a[i]!\n const y = b[i]!\n if (x.kind !== y.kind) return false\n if (x.kind === 'text' && y.kind === 'text' && x.value !== y.value) return false\n if (x.kind === 'var' && y.kind === 'var') {\n if (x.value !== y.value || x.label !== y.label || x.type !== y.type) return false\n }\n }\n return true\n }\n\n function onInput() {\n syncFromDOM()\n }\n function onCompositionStart() {\n composing = true\n }\n function onCompositionEnd() {\n composing = false\n syncFromDOM()\n }\n function onPaste(e: ClipboardEvent) {\n e.preventDefault()\n const text = e.clipboardData?.getData('text/plain') ?? ''\n if (!text) return\n insertPlainTextAtCaret(text)\n }\n function onSelectionChange() {\n if (composing) return\n if (!opts.onSelectionChange) return\n const sel = document.getSelection()\n if (!sel || sel.rangeCount === 0) {\n opts.onSelectionChange({ offset: null })\n return\n }\n const range = sel.getRangeAt(0)\n if (!opts.container.contains(range.startContainer)) {\n opts.onSelectionChange({ offset: null })\n return\n }\n opts.onSelectionChange({ offset: getCaretOffset() })\n }\n\n function insertPlainTextAtCaret(text: string) {\n const sel = document.getSelection()\n if (!sel || sel.rangeCount === 0) return\n const range = sel.getRangeAt(0)\n if (!opts.container.contains(range.startContainer)) return\n range.deleteContents()\n const node = document.createTextNode(text)\n range.insertNode(node)\n range.setStartAfter(node)\n range.collapse(true)\n sel.removeAllRanges()\n sel.addRange(range)\n syncFromDOM()\n }\n\n // 命令:替换区间为 var chip\n function replaceRangeWithVar(start: number, end: number, variable: EditorVariableInfo) {\n const a = Math.min(start, end)\n const b = Math.max(start, end)\n const left = sliceDoc(doc, 0, a)\n const right = sliceDoc(doc, b, totalLength(doc))\n const inserted: Segment = variable.type\n ? { kind: 'var', value: variable.value, label: variable.label, type: variable.type }\n : { kind: 'var', value: variable.value, label: variable.label }\n const next = normalize([...left, inserted, ...right])\n emitChange(next)\n render(a + variable.value.length + 2) // 光标定到 chip 之后\n }\n\n function removeVarAt(segIndex: number) {\n const target = doc[segIndex]\n if (!target || target.kind !== 'var') return\n const before = doc.slice(0, segIndex)\n const after = doc.slice(segIndex + 1)\n const caretOffset = sliceLength(before)\n const next = normalize([...before, ...after])\n emitChange(next)\n render(caretOffset)\n }\n\n function replaceVarAt(segIndex: number, variable: EditorVariableInfo) {\n const target = doc[segIndex]\n if (!target || target.kind !== 'var') return\n const next = doc.slice()\n const replaced: Segment = variable.type\n ? { kind: 'var', value: variable.value, label: variable.label, type: variable.type }\n : { kind: 'var', value: variable.value, label: variable.label }\n next[segIndex] = replaced\n const caretOffset = sliceLength(doc.slice(0, segIndex)) + variable.value.length + 2\n emitChange(normalize(next))\n render(caretOffset)\n }\n\n // 工具:基于序列化偏移的切片(不会切开 var)\n function sliceDoc(d: Doc, start: number, end: number): Doc {\n const out: Segment[] = []\n let pos = 0\n for (const seg of d) {\n const len = seg.kind === 'text' ? seg.value.length : seg.value.length + 2\n const segStart = pos\n const segEnd = pos + len\n if (segEnd <= start || segStart >= end) {\n pos += len\n continue\n }\n if (seg.kind === 'text') {\n const a = Math.max(0, start - segStart)\n const b = Math.min(len, end - segStart)\n const slice = seg.value.slice(a, b)\n if (slice) out.push({ kind: 'text', value: slice })\n } else {\n // var 是原子:只有当 [start, end) 完整覆盖该 var 时保留;否则抛弃\n if (start <= segStart && end >= segEnd) out.push(seg)\n }\n pos += len\n }\n return out\n }\n\n function sliceLength(d: Doc): number {\n let n = 0\n for (const s of d) n += s.kind === 'text' ? s.value.length : s.value.length + 2\n return n\n }\n\n function totalLength(d: Doc): number {\n return sliceLength(d)\n }\n\n // 绑定监听\n const container = opts.container\n container.addEventListener('input', onInput)\n container.addEventListener('compositionstart', onCompositionStart)\n container.addEventListener('compositionend', onCompositionEnd)\n container.addEventListener('paste', onPaste)\n document.addEventListener('selectionchange', onSelectionChange)\n\n // 初次渲染\n render()\n\n return {\n getDoc: () => doc,\n setDoc: (next) => {\n doc = normalize(next)\n render()\n opts.onChange?.(doc)\n },\n getValue: () => serialize(doc),\n setValue: (value) => {\n doc = parse(value, opts.getVariableMap?.())\n render()\n opts.onChange?.(doc)\n },\n getCaretOffset,\n setCaretOffset: setCaret,\n replaceRangeWithVar,\n removeVarAt,\n replaceVarAt,\n rerender: () => render(),\n dispose: () => {\n container.removeEventListener('input', onInput)\n container.removeEventListener('compositionstart', onCompositionStart)\n container.removeEventListener('compositionend', onCompositionEnd)\n container.removeEventListener('paste', onPaste)\n document.removeEventListener('selectionchange', onSelectionChange)\n },\n container\n }\n}\n"],"mappings":"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"}
@@ -80,15 +80,16 @@
80
80
  height: 22px;
81
81
  line-height: 1;
82
82
  font-size: 12px;
83
- color: var(--u-text-color-white);
84
- background-color: var(--u-color-primary);
83
+ color: var(--u-color-primary);
84
+ background-color: var(--u-color-primary-light-9);
85
85
  border-radius: 999px;
86
86
  cursor: pointer;
87
87
  user-select: none;
88
88
  transition: background-color 0.12s ease, box-shadow 0.12s ease;
89
89
  }
90
90
  .u-expression-editor__chip:hover {
91
- background-color: var(--u-color-primary-light-2);
91
+ color: var(--u-color-primary);
92
+ background-color: var(--u-color-primary-light-8);
92
93
  box-shadow: 0 1px 4px var(--u-shadow-color);
93
94
  }
94
95
  .u-expression-editor__chip:hover .u-expression-editor__chip-close {
@@ -112,7 +113,7 @@
112
113
  overflow: hidden;
113
114
  font-size: 14px;
114
115
  line-height: 1;
115
- color: var(--u-text-color-white);
116
+ color: var(--u-color-primary);
116
117
  opacity: 0;
117
118
  transition: opacity 0.12s ease, max-width 0.12s ease, margin-left 0.12s ease;
118
119
  cursor: pointer;
@@ -34,10 +34,10 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, _InputExposed
34
34
  onBlur?: ((value?: string | undefined) => any) | undefined;
35
35
  "onNative:input"?: ((ev: Event) => any) | undefined;
36
36
  }>, {
37
- placeholder: string;
38
- clearable: boolean;
39
37
  disabled: boolean;
40
38
  readonly: boolean;
39
+ placeholder: string;
40
+ clearable: boolean;
41
41
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
42
42
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
43
43
  declare const _default: typeof __VLS_export;
@@ -28,13 +28,13 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
28
28
  "onUpdate:modelValue"?: ((value: any[]) => any) | undefined;
29
29
  onChange?: ((options: Record<string, any>[]) => any) | undefined;
30
30
  }>, {
31
+ disabled: boolean;
32
+ readonly: boolean;
31
33
  labelKey: string;
32
34
  valueKey: string;
33
35
  placeholder: string;
34
36
  clearable: boolean;
35
37
  minWidth: string;
36
- disabled: boolean;
37
- readonly: boolean;
38
38
  visibilityLimit: number;
39
39
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
40
40
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -22,6 +22,8 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
22
22
  onClear?: (() => any) | undefined;
23
23
  onChange?: ((checked: Record<string, any>[]) => any) | undefined;
24
24
  }>, {
25
+ disabled: boolean;
26
+ readonly: boolean;
25
27
  expandAll: boolean;
26
28
  labelKey: string;
27
29
  valueKey: string;
@@ -29,8 +31,6 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
29
31
  clearable: boolean;
30
32
  filterable: boolean;
31
33
  minWidth: string;
32
- disabled: boolean;
33
- readonly: boolean;
34
34
  visibilityLimit: number;
35
35
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
36
36
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -5,10 +5,10 @@ import * as _$_veltra_utils0 from "@veltra/utils";
5
5
  //#region src/components/notification/notification.vue.d.ts
6
6
  declare const __VLS_export: _$vue.DefineComponent<NotificationProps, _$_veltra_utils0.DeconstructValue<_NotificationExposed>, {}, {}, {}, _$vue.ComponentOptionsMixin, _$vue.ComponentOptionsMixin, {}, string, _$vue.PublicProps, Readonly<NotificationProps> & Readonly<{}>, {
7
7
  closable: boolean;
8
+ position: "top-left" | "top-right" | "bottom-left" | "bottom-right";
8
9
  duration: number;
9
10
  offset: number;
10
11
  buttonText: string;
11
- position: "top-left" | "top-right" | "bottom-left" | "bottom-right";
12
12
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
13
13
  declare const _default: typeof __VLS_export;
14
14
  //#endregion
@@ -3,8 +3,8 @@ import * as _$vue from "vue";
3
3
 
4
4
  //#region src/components/number/number.vue.d.ts
5
5
  declare const __VLS_export: _$vue.DefineComponent<NumberProps, {}, {}, {}, {}, _$vue.ComponentOptionsMixin, _$vue.ComponentOptionsMixin, {}, string, _$vue.PublicProps, Readonly<NumberProps> & Readonly<{}>, {
6
- duration: number;
7
6
  format: "currency" | "percent" | "decimal";
7
+ duration: number;
8
8
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
9
9
  declare const _default: typeof __VLS_export;
10
10
  //#endregion
@@ -20,10 +20,10 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
20
20
  "onUpdate:modelValue"?: ((value?: number | undefined) => any) | undefined;
21
21
  onChange?: ((value?: number | undefined) => any) | undefined;
22
22
  }>, {
23
- placeholder: string;
24
- clearable: boolean;
25
23
  disabled: boolean;
26
24
  readonly: boolean;
25
+ placeholder: string;
26
+ clearable: boolean;
27
27
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
28
28
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
29
29
  declare const _default: typeof __VLS_export;
@@ -24,9 +24,9 @@ declare const __VLS_export: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {},
24
24
  "onUpdate:start"?: ((value: number | undefined) => any) | undefined;
25
25
  "onUpdate:end"?: ((value: number | undefined) => any) | undefined;
26
26
  }>, {
27
- clearable: boolean;
28
27
  disabled: boolean;
29
28
  readonly: boolean;
29
+ clearable: boolean;
30
30
  startPlaceholder: string;
31
31
  endPlaceholder: string;
32
32
  separator: string;
@@ -15,9 +15,9 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
15
15
  }, string, _$vue.PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
16
16
  "onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
17
17
  }>, {
18
- clearable: boolean;
19
18
  disabled: boolean;
20
19
  readonly: boolean;
20
+ clearable: boolean;
21
21
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
22
22
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
23
23
  declare const _default: typeof __VLS_export;
@@ -16,10 +16,10 @@ declare const __VLS_export: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {},
16
16
  "onUpdate:modelValue"?: ((value: any) => any) | undefined;
17
17
  onChange?: ((item: Record<string, any>) => any) | undefined;
18
18
  }>, {
19
- labelKey: string;
20
- valueKey: string;
21
19
  disabled: boolean;
22
20
  readonly: boolean;
21
+ labelKey: string;
22
+ valueKey: string;
23
23
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
24
24
  declare const _default: typeof __VLS_export;
25
25
  //#endregion
@@ -12,9 +12,9 @@ declare const __VLS_export: _$vue.DefineComponent<RichTextEditorProps & {
12
12
  }> & Readonly<{
13
13
  "onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
14
14
  }>, {
15
- placeholder: string;
16
15
  disabled: boolean;
17
16
  readonly: boolean;
17
+ placeholder: string;
18
18
  format: RichTextFormat;
19
19
  toolbar: ToolbarItem[];
20
20
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
@@ -26,12 +26,12 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
26
26
  "onUpdate:text"?: ((value: any) => any) | undefined;
27
27
  "onUpdate:label"?: ((label?: string | undefined) => any) | undefined;
28
28
  }>, {
29
+ disabled: boolean;
30
+ readonly: boolean;
29
31
  labelKey: string;
30
32
  valueKey: string;
31
33
  placeholder: string;
32
34
  clearable: boolean;
33
- disabled: boolean;
34
- readonly: boolean;
35
35
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
36
36
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
37
37
  declare const _default: typeof __VLS_export;
@@ -16,17 +16,17 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
16
16
  "update:modelValue": (value: string | undefined) => any;
17
17
  } & {
18
18
  "update:modelValue": (value: string) => any;
19
- close: (item: TabItem, index: number) => any;
20
19
  click: (item: TabItem, index: number) => any;
20
+ close: (item: TabItem, index: number) => any;
21
21
  }, string, _$vue.PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
22
22
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
23
- onClose?: ((item: TabItem, index: number) => any) | undefined;
24
23
  onClick?: ((item: TabItem, index: number) => any) | undefined;
24
+ onClose?: ((item: TabItem, index: number) => any) | undefined;
25
25
  }>, {
26
26
  closable: boolean;
27
- position: "top" | "bottom";
28
- rounded: boolean;
29
27
  block: boolean;
28
+ rounded: boolean;
29
+ position: "top" | "bottom";
30
30
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
31
31
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
32
32
  declare const _default: typeof __VLS_export;
@@ -15,16 +15,16 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
15
15
  "update:modelValue": (value: string | undefined) => any;
16
16
  } & {
17
17
  "update:modelValue": (value: string) => any;
18
- close: (item: TabItem, index: number) => any;
19
18
  click: (item: TabItem, index: number) => any;
19
+ close: (item: TabItem, index: number) => any;
20
20
  }, string, _$vue.PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
21
21
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
22
- onClose?: ((item: TabItem, index: number) => any) | undefined;
23
22
  onClick?: ((item: TabItem, index: number) => any) | undefined;
23
+ onClose?: ((item: TabItem, index: number) => any) | undefined;
24
24
  }>, {
25
25
  closable: boolean;
26
- position: "left" | "right";
27
26
  rounded: boolean;
27
+ position: "left" | "right";
28
28
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
29
29
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
30
30
  declare const _default: typeof __VLS_export;
@@ -11,17 +11,17 @@ declare const __VLS_export: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {},
11
11
  "update:modelValue": (value: string | undefined) => any;
12
12
  } & {
13
13
  "update:modelValue": (value: string) => any;
14
- close: (item: TabItem, index: number) => any;
15
14
  click: (item: TabItem, index: number) => any;
15
+ close: (item: TabItem, index: number) => any;
16
16
  }, string, _$vue.PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
17
17
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
18
- onClose?: ((item: TabItem, index: number) => any) | undefined;
19
18
  onClick?: ((item: TabItem, index: number) => any) | undefined;
19
+ onClose?: ((item: TabItem, index: number) => any) | undefined;
20
20
  }>, {
21
21
  closable: boolean;
22
- position: "left" | "right" | "top" | "bottom";
23
- rounded: boolean;
24
22
  block: boolean;
23
+ rounded: boolean;
24
+ position: "left" | "right" | "top" | "bottom";
25
25
  keepAlive: boolean;
26
26
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
27
27
  declare const _default: typeof __VLS_export;
@@ -22,10 +22,10 @@ declare const __VLS_export: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {},
22
22
  onFocus?: (() => any) | undefined;
23
23
  onBlur?: (() => any) | undefined;
24
24
  }>, {
25
- placeholder: string;
26
- clearable: boolean;
27
25
  disabled: boolean;
28
26
  readonly: boolean;
27
+ placeholder: string;
28
+ clearable: boolean;
29
29
  resize: boolean;
30
30
  autosize: boolean;
31
31
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
@@ -87,7 +87,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
87
87
  const triggerDom = computed(() => {
88
88
  return props.triggerDom || triggerRef.value?.$el;
89
89
  });
90
- const { popperContainerId } = usePop({
90
+ const { update, popperContainerId } = usePop({
91
91
  triggerRef: triggerDom,
92
92
  contentRef,
93
93
  arrowRef,
@@ -98,10 +98,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
98
98
  }
99
99
  });
100
100
  watch(triggerDom, (dom) => {
101
- if (dom?.nodeType === Node.ELEMENT_NODE && visible.value) {
102
- close();
103
- nextTick(() => open());
104
- }
101
+ if (dom?.nodeType === Node.ELEMENT_NODE && visible.value) nextTick(() => {
102
+ update();
103
+ });
105
104
  });
106
105
  /** 外部节点 */
107
106
  const externalNode = shallowRef();
@@ -1 +1 @@
1
- {"version":3,"file":"tip.js","names":[],"sources":["../../../src/components/tip/tip.vue"],"sourcesContent":["<template>\n <!-- 触发 -->\n <UNodeRender v-bind=\"eventsHandlers\" :content=\"renderDefaultSlotTrigger()\" ref=\"triggerRef\" />\n\n <!-- 弹出内容 -->\n <teleport :to=\"`#${popperContainerId}`\">\n <transition name=\"tip\">\n <component\n v-if=\"tipVisible\"\n :is=\"contentTag\"\n :class=\"contentClass\"\n :style=\"[style, { zIndex: zIndex() }]\"\n ref=\"contentRef\"\n @mouseenter=\"eventsHandlers.onMouseenter\"\n @mouseleave=\"eventsHandlers.onMouseleave\"\n @click.stop\n v-click-outside=\"handleClickOutside\"\n >\n <UNodeRender v-if=\"externalNode\" :content=\"externalNode\" />\n\n <slot name=\"content\">{{ content }}</slot>\n\n <div :class=\"cls.e('arrow')\" v-if=\"!hideArrow\" ref=\"arrowRef\"></div>\n </component>\n </transition>\n </teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFallbackProps, usePop } from '@veltra/compositions'\nimport { vClickOutside } from '@veltra/directives'\nimport { bem, extractNormalVNodes, zIndex } from '@veltra/utils'\nimport { shallowRef, computed, useSlots, onBeforeUnmount, toRef, watch, nextTick } from 'vue'\n\nimport type { TipProps, ComponentSize, TipEmits } from '../../types'\nimport { UNodeRender } from '../node-render'\nimport { useNest } from './use-nest'\n\ndefineOptions({ name: 'Tip' })\n\nconst props = withDefaults(defineProps<TipProps>(), {\n content: '',\n trigger: 'hover',\n direction: 'top',\n alignment: 'center',\n contentTag: 'div',\n visible: undefined\n})\n\nconst emit = defineEmits<TipEmits>()\n\nconst cls = bem('tip')\nconst slots = useSlots()\nconst { size } = useFallbackProps([props], {\n size: 'default' as ComponentSize\n})\n\nconst contentClass = computed(() => {\n const fixed = [cls.e('content'), cls.m(size.value)]\n const className = props.class\n if (!Array.isArray(className)) {\n return [...fixed, className]\n }\n return [...fixed, ...className]\n})\n\nconst triggerRef = shallowRef<InstanceType<typeof UNodeRender>>()\nconst contentRef = shallowRef<HTMLElement>()\nconst arrowRef = shallowRef<HTMLElement>()\n\nconst _visible = shallowRef(false)\n\nconst visible = computed(() => {\n return props.visible !== undefined ? props.visible : _visible.value\n})\n\nfunction updateVisible(v: boolean) {\n if (props.visible === undefined) {\n _visible.value = v\n } else {\n emit('update:visible', v)\n }\n}\n\nconst tipVisible = useNest(visible)\n\nfunction renderDefaultSlotTrigger() {\n const nodes = slots.default?.()\n if (!nodes?.length) return null\n return extractNormalVNodes(nodes)[0]\n}\n\nconst eventsHandlers = computed(() => {\n const { trigger, disabled } = props\n\n const handlers: Record<string, Function> = {}\n\n if (disabled) return handlers\n\n if (trigger === 'click') {\n handlers.onClick = open\n } else if (trigger === 'hover') {\n handlers.onMouseenter = open\n handlers.onMouseleave = close\n }\n\n return handlers\n})\n\nconst handleClickOutside = (e: MouseEvent) => {\n if (props.trigger === 'hover') return\n\n // 点击的元素在触发元素中则啥也\n if (props.trigger === 'click' && triggerDom.value.contains(e.target as Node)) {\n return\n }\n close()\n}\n\nlet closeTimer: number | undefined = undefined\n\n/** 弹出 */\nconst open = (e?: PointerEvent) => {\n e?.stopPropagation()\n closeTimer !== undefined && clearTimeout(closeTimer)\n\n updateVisible(true)\n}\n/** 关闭 */\nconst close = () => {\n if (props.trigger === 'hover') {\n closeTimer = setTimeout(() => {\n updateVisible(false)\n }, 250)\n } else {\n updateVisible(false)\n }\n}\n\nconst triggerDom = computed(() => {\n return props.triggerDom || triggerRef.value?.$el\n})\n\nconst { popperContainerId } = usePop({\n triggerRef: triggerDom,\n contentRef,\n arrowRef,\n direction: toRef(() => props.direction),\n alignment: toRef(() => props.alignment),\n onTriggerPositionChange() {\n close()\n }\n})\n\nwatch(triggerDom, (dom) => {\n // TODO: 看起来不是很优雅\n if (dom?.nodeType === Node.ELEMENT_NODE && visible.value) {\n close()\n nextTick(() => open())\n }\n})\n\n/** 外部节点 */\nconst externalNode = shallowRef<any>()\n\nonBeforeUnmount(() => {\n clearTimeout(closeTimer)\n})\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"tip.js","names":[],"sources":["../../../src/components/tip/tip.vue"],"sourcesContent":["<template>\n <!-- 触发 -->\n <UNodeRender v-bind=\"eventsHandlers\" :content=\"renderDefaultSlotTrigger()\" ref=\"triggerRef\" />\n\n <!-- 弹出内容 -->\n <teleport :to=\"`#${popperContainerId}`\">\n <transition name=\"tip\">\n <component\n v-if=\"tipVisible\"\n :is=\"contentTag\"\n :class=\"contentClass\"\n :style=\"[style, { zIndex: zIndex() }]\"\n ref=\"contentRef\"\n @mouseenter=\"eventsHandlers.onMouseenter\"\n @mouseleave=\"eventsHandlers.onMouseleave\"\n @click.stop\n v-click-outside=\"handleClickOutside\"\n >\n <UNodeRender v-if=\"externalNode\" :content=\"externalNode\" />\n\n <slot name=\"content\">{{ content }}</slot>\n\n <div :class=\"cls.e('arrow')\" v-if=\"!hideArrow\" ref=\"arrowRef\"></div>\n </component>\n </transition>\n </teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFallbackProps, usePop } from '@veltra/compositions'\nimport { vClickOutside } from '@veltra/directives'\nimport { bem, extractNormalVNodes, zIndex } from '@veltra/utils'\nimport { shallowRef, computed, useSlots, onBeforeUnmount, toRef, watch, nextTick } from 'vue'\n\nimport type { TipProps, ComponentSize, TipEmits } from '../../types'\nimport { UNodeRender } from '../node-render'\nimport { useNest } from './use-nest'\n\ndefineOptions({ name: 'Tip' })\n\nconst props = withDefaults(defineProps<TipProps>(), {\n content: '',\n trigger: 'hover',\n direction: 'top',\n alignment: 'center',\n contentTag: 'div',\n visible: undefined\n})\n\nconst emit = defineEmits<TipEmits>()\n\nconst cls = bem('tip')\nconst slots = useSlots()\nconst { size } = useFallbackProps([props], {\n size: 'default' as ComponentSize\n})\n\nconst contentClass = computed(() => {\n const fixed = [cls.e('content'), cls.m(size.value)]\n const className = props.class\n if (!Array.isArray(className)) {\n return [...fixed, className]\n }\n return [...fixed, ...className]\n})\n\nconst triggerRef = shallowRef<InstanceType<typeof UNodeRender>>()\nconst contentRef = shallowRef<HTMLElement>()\nconst arrowRef = shallowRef<HTMLElement>()\n\nconst _visible = shallowRef(false)\n\nconst visible = computed(() => {\n return props.visible !== undefined ? props.visible : _visible.value\n})\n\nfunction updateVisible(v: boolean) {\n if (props.visible === undefined) {\n _visible.value = v\n } else {\n emit('update:visible', v)\n }\n}\n\nconst tipVisible = useNest(visible)\n\nfunction renderDefaultSlotTrigger() {\n const nodes = slots.default?.()\n if (!nodes?.length) return null\n return extractNormalVNodes(nodes)[0]\n}\n\nconst eventsHandlers = computed(() => {\n const { trigger, disabled } = props\n\n const handlers: Record<string, Function> = {}\n\n if (disabled) return handlers\n\n if (trigger === 'click') {\n handlers.onClick = open\n } else if (trigger === 'hover') {\n handlers.onMouseenter = open\n handlers.onMouseleave = close\n }\n\n return handlers\n})\n\nconst handleClickOutside = (e: MouseEvent) => {\n if (props.trigger === 'hover') return\n\n // 点击的元素在触发元素中则啥也\n if (props.trigger === 'click' && triggerDom.value.contains(e.target as Node)) {\n return\n }\n close()\n}\n\nlet closeTimer: number | undefined = undefined\n\n/** 弹出 */\nconst open = (e?: PointerEvent) => {\n e?.stopPropagation()\n closeTimer !== undefined && clearTimeout(closeTimer)\n\n updateVisible(true)\n}\n/** 关闭 */\nconst close = () => {\n if (props.trigger === 'hover') {\n closeTimer = setTimeout(() => {\n updateVisible(false)\n }, 250)\n } else {\n updateVisible(false)\n }\n}\n\nconst triggerDom = computed(() => {\n return props.triggerDom || triggerRef.value?.$el\n})\n\nconst { update, popperContainerId } = usePop({\n triggerRef: triggerDom,\n contentRef,\n arrowRef,\n direction: toRef(() => props.direction),\n alignment: toRef(() => props.alignment),\n onTriggerPositionChange() {\n close()\n }\n})\n\nwatch(triggerDom, (dom) => {\n // TODO: 看起来不是很优雅\n if (dom?.nodeType === Node.ELEMENT_NODE && visible.value) {\n nextTick(() => {\n void update()\n })\n }\n})\n\n/** 外部节点 */\nconst externalNode = shallowRef<any>()\n\nonBeforeUnmount(() => {\n clearTimeout(closeTimer)\n})\n</script>\n"],"mappings":"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"}
@@ -24,6 +24,8 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
24
24
  onChange?: ((value?: string | number | undefined, selectedData?: Record<string, any> | undefined) => any) | undefined;
25
25
  "onUpdate:text"?: ((text?: string | undefined) => any) | undefined;
26
26
  }>, {
27
+ disabled: boolean;
28
+ readonly: boolean;
27
29
  expandAll: boolean;
28
30
  labelKey: string;
29
31
  valueKey: string;
@@ -31,8 +33,6 @@ declare const __VLS_base: _$vue.DefineComponent<__VLS_PublicProps, {}, {}, {}, {
31
33
  clearable: boolean;
32
34
  filterable: boolean;
33
35
  minWidth: string;
34
- disabled: boolean;
35
- readonly: boolean;
36
36
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
37
37
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
38
38
  declare const _default: typeof __VLS_export;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veltra/desktop",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -61,22 +61,22 @@
61
61
  "@lexical/rich-text": "^0.43.0",
62
62
  "@lexical/selection": "^0.43.0",
63
63
  "@lexical/utils": "^0.43.0",
64
- "@veltra/compositions": "1.0.14",
65
- "@veltra/directives": "1.0.14",
66
- "@veltra/icons": "1.0.14",
67
- "@veltra/styles": "1.0.14",
68
- "@veltra/utils": "1.0.14",
64
+ "@veltra/compositions": "1.0.15",
65
+ "@veltra/directives": "1.0.15",
66
+ "@veltra/icons": "1.0.15",
67
+ "@veltra/styles": "1.0.15",
68
+ "@veltra/utils": "1.0.15",
69
69
  "@visactor/vtable": "^1.26.0",
70
70
  "codemirror": "^6.0.2",
71
71
  "docx-preview": "^0.3.7",
72
72
  "lexical": "^0.43.0"
73
73
  },
74
74
  "peerDependencies": {
75
- "@veltra/compositions": "1.0.14",
76
- "@veltra/directives": "1.0.14",
77
- "@veltra/icons": "1.0.14",
78
- "@veltra/styles": "1.0.14",
79
- "@veltra/utils": "1.0.14",
75
+ "@veltra/compositions": "1.0.15",
76
+ "@veltra/directives": "1.0.15",
77
+ "@veltra/icons": "1.0.15",
78
+ "@veltra/styles": "1.0.15",
79
+ "@veltra/utils": "1.0.15",
80
80
  "vue": "^3.5.33"
81
81
  }
82
82
  }
@@ -0,0 +1,47 @@
1
+ // @vitest-environment happy-dom
2
+ import { afterEach, describe, expect, it, vi } from 'vitest'
3
+
4
+ import { createEditor } from '../core/editor'
5
+ import { parse } from '../core/model'
6
+
7
+ const B = 'u-expression-editor'
8
+ const cls = {
9
+ b: B,
10
+ e: (n: string) => `${B}__${n}`,
11
+ m: (n: string) => `${B}--${n}`,
12
+ em: (e: string, m: string) => `${B}__${e}--${m}`,
13
+ create: () => cls
14
+ } as any
15
+
16
+ afterEach(() => {
17
+ document.body.innerHTML = ''
18
+ document.getSelection()?.removeAllRanges()
19
+ })
20
+
21
+ describe('createEditor composition', () => {
22
+ it('does not emit selection changes while IME composition text is pending', () => {
23
+ const container = document.createElement('div')
24
+ document.body.appendChild(container)
25
+
26
+ const onChange = vi.fn()
27
+ const onSelectionChange = vi.fn()
28
+ const editor = createEditor({
29
+ container,
30
+ cls,
31
+ initialDoc: parse('@'),
32
+ onChange,
33
+ onSelectionChange
34
+ })
35
+
36
+ container.dispatchEvent(new Event('compositionstart'))
37
+ container.querySelector('[data-seg="text"]')!.textContent = '@n'
38
+ container.dispatchEvent(new Event('input'))
39
+ editor.setCaretOffset(2)
40
+ document.dispatchEvent(new Event('selectionchange'))
41
+
42
+ expect(onChange).not.toHaveBeenCalled()
43
+ expect(onSelectionChange).not.toHaveBeenCalled()
44
+
45
+ editor.dispose()
46
+ })
47
+ })
@@ -3,6 +3,7 @@
3
3
  hide-arrow
4
4
  alignment="start"
5
5
  direction="bottom"
6
+ trigger="click"
6
7
  :trigger-dom="triggerDom"
7
8
  :visible="visible"
8
9
  @update:visible="handleVisibleChange($event)"
@@ -368,6 +368,7 @@ export function createEditor(opts: EditorOptions): EditorAPI {
368
368
  insertPlainTextAtCaret(text)
369
369
  }
370
370
  function onSelectionChange() {
371
+ if (composing) return
371
372
  if (!opts.onSelectionChange) return
372
373
  const sel = document.getSelection()
373
374
  if (!sel || sel.rangeCount === 0) {
@@ -81,8 +81,8 @@ $root-name: expression-editor;
81
81
  height: 22px;
82
82
  line-height: 1;
83
83
  font-size: 12px;
84
- color: fn.use-var(text-color, white);
85
- background-color: fn.use-var(color, primary);
84
+ color: fn.use-var(color, primary);
85
+ background-color: fn.use-var(color, primary, light-9);
86
86
  border-radius: 999px;
87
87
  cursor: pointer;
88
88
  user-select: none;
@@ -91,7 +91,8 @@ $root-name: expression-editor;
91
91
  box-shadow 0.12s ease;
92
92
 
93
93
  &:hover {
94
- background-color: fn.use-var(color, primary, light-2);
94
+ color: fn.use-var(color, primary);
95
+ background-color: fn.use-var(color, primary, light-8);
95
96
  box-shadow: 0 1px 4px fn.use-var(shadow, color);
96
97
 
97
98
  .#{vars.$namespace}#{$root-name}__chip-close {
@@ -120,7 +121,7 @@ $root-name: expression-editor;
120
121
  overflow: hidden;
121
122
  font-size: 14px;
122
123
  line-height: 1;
123
- color: fn.use-var(text-color, white);
124
+ color: fn.use-var(color, primary);
124
125
  opacity: 0;
125
126
  transition:
126
127
  opacity 0.12s ease,
@@ -141,7 +141,7 @@ const triggerDom = computed(() => {
141
141
  return props.triggerDom || triggerRef.value?.$el
142
142
  })
143
143
 
144
- const { popperContainerId } = usePop({
144
+ const { update, popperContainerId } = usePop({
145
145
  triggerRef: triggerDom,
146
146
  contentRef,
147
147
  arrowRef,
@@ -155,8 +155,9 @@ const { popperContainerId } = usePop({
155
155
  watch(triggerDom, (dom) => {
156
156
  // TODO: 看起来不是很优雅
157
157
  if (dom?.nodeType === Node.ELEMENT_NODE && visible.value) {
158
- close()
159
- nextTick(() => open())
158
+ nextTick(() => {
159
+ void update()
160
+ })
160
161
  }
161
162
  })
162
163