@veltra/desktop 1.7.19 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/batch-edit/batch-edit-form.js +40 -26
- package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
- package/dist/components/batch-edit/use-handlers.js +3 -3
- package/dist/components/batch-edit/use-handlers.js.map +1 -1
- package/dist/components/batch-edit/use-shortcut-key.js +4 -2
- package/dist/components/batch-edit/use-shortcut-key.js.map +1 -1
- package/dist/components/card/style2.css +0 -2
- package/dist/components/dropdown/dropdown.js +5 -3
- package/dist/components/dropdown/dropdown.js.map +1 -1
- package/dist/components/expression-editor/components/path-preview.js +3 -6
- package/dist/components/expression-editor/components/path-preview.js.map +1 -1
- package/dist/components/expression-editor/components/variable-picker.js +49 -12
- package/dist/components/expression-editor/components/variable-picker.js.map +1 -1
- package/dist/components/expression-editor/core/editor.js +20 -0
- package/dist/components/expression-editor/core/editor.js.map +1 -1
- package/dist/components/expression-editor/expression-editor.js +53 -18
- package/dist/components/expression-editor/expression-editor.js.map +1 -1
- package/dist/components/expression-editor/style2.css +135 -73
- package/dist/components/form/form.js.map +1 -1
- package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js +0 -1
- package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/form/style2.css +8 -3
- package/dist/components/index.d.ts +26 -26
- package/dist/components/index.js +52 -52
- package/dist/components/multi-select/multi-select.js +16 -12
- package/dist/components/multi-select/multi-select.js.map +1 -1
- package/dist/components/multi-select/style2.css +7 -0
- package/dist/components/multi-tree-select/multi-tree-select.js +82 -56
- package/dist/components/multi-tree-select/multi-tree-select.js.map +1 -1
- package/dist/components/multi-tree-select/style2.css +7 -0
- package/dist/components/select/select.js +7 -3
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/select/style2.css +7 -0
- package/dist/components/select/use-options.js +14 -3
- package/dist/components/select/use-options.js.map +1 -1
- package/dist/components/tree-select/style2.css +7 -0
- package/dist/components/tree-select/tree-select.js +49 -17
- package/dist/components/tree-select/tree-select.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/types/dropdown.d.ts +10 -0
- package/dist/types/multi-tree-select.d.ts +7 -1
- package/dist/types/tree-select.d.ts +7 -1
- package/package.json +1 -1
- package/src/components/batch-edit/__test__/batch-edit.test.ts +112 -1
- package/src/components/batch-edit/batch-edit-form.vue +21 -2
- package/src/components/batch-edit/use-handlers.ts +6 -5
- package/src/components/batch-edit/use-shortcut-key.ts +5 -2
- package/src/components/card/style.scss +0 -2
- package/src/components/dropdown/dropdown.vue +9 -4
- package/src/components/expression-editor/components/path-preview.vue +1 -2
- package/src/components/expression-editor/components/variable-picker.vue +54 -13
- package/src/components/expression-editor/core/editor.ts +28 -0
- package/src/components/expression-editor/expression-editor.vue +47 -17
- package/src/components/expression-editor/style.scss +167 -82
- package/src/components/form/form.vue +0 -1
- package/src/components/form/style.scss +11 -4
- package/src/components/multi-select/multi-select.vue +11 -2
- package/src/components/multi-select/style.scss +6 -0
- package/src/components/multi-tree-select/multi-tree-select.vue +58 -9
- package/src/components/multi-tree-select/style.scss +6 -0
- package/src/components/select/__test__/select.test.ts +78 -0
- package/src/components/select/select.vue +13 -3
- package/src/components/select/style.scss +6 -0
- package/src/components/select/use-options.ts +25 -4
- package/src/components/tree-select/__test__/tree-select.test.ts +116 -0
- package/src/components/tree-select/style.scss +6 -0
- package/src/components/tree-select/tree-select.vue +63 -13
- package/src/types/dropdown.ts +10 -0
- package/src/types/multi-tree-select.ts +12 -1
- package/src/types/tree-select.ts +12 -1
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
@update:visible="handleVisibleChange($event)"
|
|
10
10
|
>
|
|
11
11
|
<template #content>
|
|
12
|
-
<div :class="cls.e('picker')" @mousedown.prevent>
|
|
12
|
+
<div ref="root" :class="cls.e('picker')" @mousedown.prevent>
|
|
13
13
|
<!-- 面包屑:仅逐级模式且非根级显示 -->
|
|
14
14
|
<div v-if="!isFlat && navPath.length > 0" :class="cls.e('breadcrumbs')">
|
|
15
15
|
<span :class="cls.e('breadcrumb-item')">
|
|
@@ -39,7 +39,12 @@
|
|
|
39
39
|
|
|
40
40
|
<div :class="cls.e('picker-body')">
|
|
41
41
|
<div :class="cls.e('panel')" v-if="currentList.length > 0">
|
|
42
|
-
<u-scroll
|
|
42
|
+
<u-scroll
|
|
43
|
+
ref="listScroll"
|
|
44
|
+
tag="ul"
|
|
45
|
+
:class="cls.e('panel-list')"
|
|
46
|
+
:content-class="cls.e('panel-content')"
|
|
47
|
+
>
|
|
43
48
|
<li
|
|
44
49
|
v-for="(item, idx) in currentList"
|
|
45
50
|
:key="`${idx}-${item.value}`"
|
|
@@ -49,7 +54,12 @@
|
|
|
49
54
|
@mouseenter="activeIndex = idx"
|
|
50
55
|
>
|
|
51
56
|
<span :class="cls.e('item-label')">{{ item.label }}</span>
|
|
52
|
-
<u-icon
|
|
57
|
+
<u-icon
|
|
58
|
+
v-if="!isFlat && hasChildren(item)"
|
|
59
|
+
:class="cls.e('item-expand')"
|
|
60
|
+
title="展开下一级"
|
|
61
|
+
@click.stop="expandInto(item)"
|
|
62
|
+
>
|
|
53
63
|
<ArrowRight />
|
|
54
64
|
</u-icon>
|
|
55
65
|
</li>
|
|
@@ -61,6 +71,21 @@
|
|
|
61
71
|
|
|
62
72
|
<PathPreview v-if="isFlat && activePath.length > 0" :path="activePath" />
|
|
63
73
|
</div>
|
|
74
|
+
|
|
75
|
+
<div v-if="currentList.length > 0" :class="cls.e('picker-hint')">
|
|
76
|
+
<template v-if="isFlat">
|
|
77
|
+
<span><kbd>↑</kbd><kbd>↓</kbd> 选择</span>
|
|
78
|
+
<span><kbd>Enter</kbd> 确认</span>
|
|
79
|
+
<span><kbd>Esc</kbd> 关闭</span>
|
|
80
|
+
</template>
|
|
81
|
+
<template v-else>
|
|
82
|
+
<span><kbd>↑</kbd><kbd>↓</kbd> 选择</span>
|
|
83
|
+
<span><kbd>→</kbd> 展开</span>
|
|
84
|
+
<span><kbd>←</kbd> 返回</span>
|
|
85
|
+
<span><kbd>Enter</kbd> 确认</span>
|
|
86
|
+
<span><kbd>Esc</kbd> 关闭</span>
|
|
87
|
+
</template>
|
|
88
|
+
</div>
|
|
64
89
|
</div>
|
|
65
90
|
</template>
|
|
66
91
|
</u-tip>
|
|
@@ -69,7 +94,7 @@
|
|
|
69
94
|
<script lang="ts" setup>
|
|
70
95
|
import { ArrowRight } from '@veltra/icons/normal'
|
|
71
96
|
import { bem } from '@veltra/utils'
|
|
72
|
-
import { computed, nextTick, ref, watch } from 'vue'
|
|
97
|
+
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue'
|
|
73
98
|
|
|
74
99
|
import type { VariableItem } from '../../../types'
|
|
75
100
|
import { UEmpty } from '../../empty'
|
|
@@ -107,6 +132,8 @@ const cls = bem('expression-editor')
|
|
|
107
132
|
|
|
108
133
|
const navPath = ref<VariableItem[]>([])
|
|
109
134
|
const activeIndex = ref(0)
|
|
135
|
+
const root = useTemplateRef('root')
|
|
136
|
+
const listScroll = useTemplateRef<InstanceType<typeof UScroll>>('listScroll')
|
|
110
137
|
|
|
111
138
|
const isFlat = computed(() => props.filter !== '')
|
|
112
139
|
|
|
@@ -192,13 +219,19 @@ function selectActive() {
|
|
|
192
219
|
return
|
|
193
220
|
}
|
|
194
221
|
// 默认 'leaf':进入下一级
|
|
195
|
-
|
|
196
|
-
activeIndex.value = 0
|
|
222
|
+
expandInto(item)
|
|
197
223
|
return
|
|
198
224
|
}
|
|
199
225
|
emit('select', item)
|
|
200
226
|
}
|
|
201
227
|
|
|
228
|
+
/** 进入 item 的下一级(点击展开图标 / leaf 模式点击分支项共用) */
|
|
229
|
+
function expandInto(item: VariableItem) {
|
|
230
|
+
if (!hasChildren(item)) return
|
|
231
|
+
navPath.value = [...navPath.value, item]
|
|
232
|
+
activeIndex.value = 0
|
|
233
|
+
}
|
|
234
|
+
|
|
202
235
|
function flattenSourceFor(it: VariableItem): VariableItem {
|
|
203
236
|
const flat = it as FlatItem
|
|
204
237
|
// 还原为「不带 __path 的纯 VariableItem」,避免泄漏内部字段
|
|
@@ -220,11 +253,20 @@ function moveActive(delta: -1 | 1) {
|
|
|
220
253
|
|
|
221
254
|
function scrollActiveIntoView() {
|
|
222
255
|
void nextTick(() => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
256
|
+
// 在面板根节点内查找,避免同页多个编辑器时滚错列表
|
|
257
|
+
const item = root.value?.querySelector(`[data-idx="${activeIndex.value}"]`)
|
|
258
|
+
// expose 代理运行时会解包 ref,类型上按 unknown 收窄
|
|
259
|
+
const scroller = listScroll.value?.containerRef as unknown
|
|
260
|
+
if (!(item instanceof HTMLElement) || !(scroller instanceof HTMLElement)) return
|
|
261
|
+
// 只滚面板列表自身:原生 scrollIntoView 会沿所有滚动祖先链滚动,
|
|
262
|
+
// 面板贴近视口边缘时会连带滚动页面,导致面板与触发器错位抖动
|
|
263
|
+
const top =
|
|
264
|
+
item.getBoundingClientRect().top - scroller.getBoundingClientRect().top + scroller.scrollTop
|
|
265
|
+
const bottom = top + item.offsetHeight
|
|
266
|
+
if (top < scroller.scrollTop) {
|
|
267
|
+
scroller.scrollTop = top
|
|
268
|
+
} else if (bottom > scroller.scrollTop + scroller.clientHeight) {
|
|
269
|
+
scroller.scrollTop = bottom - scroller.clientHeight
|
|
228
270
|
}
|
|
229
271
|
})
|
|
230
272
|
}
|
|
@@ -287,8 +329,7 @@ function handleKeydown(e: KeyboardEvent): boolean {
|
|
|
287
329
|
if (isFlat.value) return false
|
|
288
330
|
const item = currentList.value[activeIndex.value]
|
|
289
331
|
if (item && hasChildren(item)) {
|
|
290
|
-
|
|
291
|
-
activeIndex.value = 0
|
|
332
|
+
expandInto(item)
|
|
292
333
|
return true
|
|
293
334
|
}
|
|
294
335
|
return false
|
|
@@ -43,6 +43,11 @@ export interface EditorAPI {
|
|
|
43
43
|
getCaretOffset(): number | null
|
|
44
44
|
/** 把光标设置到指定偏移(不会修改模型) */
|
|
45
45
|
setCaretOffset(offset: number): void
|
|
46
|
+
/**
|
|
47
|
+
* 取序列化偏移处字符的视口矩形(mention 锚点定位用)。
|
|
48
|
+
* 偏移落在 text 段上返回该字符矩形;落在 chip 上返回 chip 元素矩形;越界返回 null。
|
|
49
|
+
*/
|
|
50
|
+
getRectAtOffset(offset: number): DOMRect | null
|
|
46
51
|
/**
|
|
47
52
|
* 用一个 var chip 替换 [start, end) 区间。
|
|
48
53
|
* 调用方通常在 mention 选中时用:start = `@` 起点、end = 当前光标。
|
|
@@ -321,6 +326,28 @@ export function createEditor(opts: EditorOptions): EditorAPI {
|
|
|
321
326
|
return null
|
|
322
327
|
}
|
|
323
328
|
|
|
329
|
+
function getRectAtOffset(offset: number): DOMRect | null {
|
|
330
|
+
let pos = 0
|
|
331
|
+
for (const child of Array.from(opts.container.childNodes)) {
|
|
332
|
+
const len = childSerialLength(child)
|
|
333
|
+
if (offset >= pos && offset < pos + len) {
|
|
334
|
+
if (isChipElement(child)) return (child as HTMLElement).getBoundingClientRect()
|
|
335
|
+
const textNode = firstTextDescendant(child)
|
|
336
|
+
if (!textNode) return (child as HTMLElement).getBoundingClientRect()
|
|
337
|
+
const local = Math.min(offset - pos, textNode.data.length)
|
|
338
|
+
const range = document.createRange()
|
|
339
|
+
range.setStart(textNode, local)
|
|
340
|
+
range.setEnd(textNode, Math.min(local + 1, textNode.data.length))
|
|
341
|
+
const rects = range.getClientRects()
|
|
342
|
+
return rects.length > 0
|
|
343
|
+
? (rects[0] as DOMRect)
|
|
344
|
+
: (child as HTMLElement).getBoundingClientRect()
|
|
345
|
+
}
|
|
346
|
+
pos += len
|
|
347
|
+
}
|
|
348
|
+
return null
|
|
349
|
+
}
|
|
350
|
+
|
|
324
351
|
/** input 后:从 DOM 反推 doc,并触发 onChange(如有变化)。 */
|
|
325
352
|
function syncFromDOM(): void {
|
|
326
353
|
if (composing) return
|
|
@@ -478,6 +505,7 @@ export function createEditor(opts: EditorOptions): EditorAPI {
|
|
|
478
505
|
},
|
|
479
506
|
getCaretOffset,
|
|
480
507
|
setCaretOffset: setCaret,
|
|
508
|
+
getRectAtOffset,
|
|
481
509
|
replaceRangeWithVar,
|
|
482
510
|
replaceVarAt,
|
|
483
511
|
rerender: () => render(),
|
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :class="
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
<div ref="rootEl" :class="cls.b">
|
|
3
|
+
<!-- 提示行独立在边框盒(shell)之外 -->
|
|
4
|
+
<div v-if="showHint" :class="cls.e('hint')">输入 <kbd>@</kbd> 唤起变量面板</div>
|
|
5
|
+
|
|
6
|
+
<div :class="shellClass">
|
|
7
|
+
<div :class="cls.e('body')">
|
|
8
|
+
<div
|
|
9
|
+
ref="container"
|
|
10
|
+
:class="cls.e('container')"
|
|
11
|
+
:contenteditable="!readonly && !disabled"
|
|
12
|
+
:data-empty="isEmpty || undefined"
|
|
13
|
+
:data-placeholder="props.placeholder"
|
|
14
|
+
@keydown="onKeydown"
|
|
15
|
+
@blur="onBlur"
|
|
16
|
+
></div>
|
|
17
|
+
|
|
18
|
+
<div v-if="showPlaceholder" :class="cls.e('placeholder')">
|
|
19
|
+
{{ props.placeholder }}
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<!-- mention 面板的定位锚点:跟随 `@` 字符位置(见 positionMentionAnchor) -->
|
|
24
|
+
<span ref="mentionAnchor" :class="cls.e('mention-anchor')" aria-hidden="true"></span>
|
|
15
25
|
</div>
|
|
16
26
|
|
|
17
27
|
<VariablePicker
|
|
@@ -63,11 +73,14 @@ const { formProps } = injectFormContext()
|
|
|
63
73
|
const { size, disabled, readonly } = useFormFallbackProps([formProps ?? {}, props])
|
|
64
74
|
|
|
65
75
|
const containerRef = useTemplateRef<HTMLDivElement>('container')
|
|
76
|
+
const rootElRef = useTemplateRef<HTMLDivElement>('rootEl')
|
|
77
|
+
const mentionAnchorRef = useTemplateRef<HTMLSpanElement>('mentionAnchor')
|
|
66
78
|
const pickerRef = useTemplateRef<{ handleKeydown: (e: KeyboardEvent) => boolean }>('pickerRef')
|
|
67
79
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
cls.
|
|
80
|
+
/** 视觉盒(边框 / 背景 / 聚焦光晕 / 尺寸态)挂在 shell 上,根节点只做布局 */
|
|
81
|
+
const shellClass = computed(() => [
|
|
82
|
+
cls.e('shell'),
|
|
83
|
+
cls.em('shell', size.value),
|
|
71
84
|
bem.is('disabled', disabled.value),
|
|
72
85
|
bem.is('readonly', readonly.value)
|
|
73
86
|
])
|
|
@@ -80,6 +93,7 @@ const mention: MentionAPI = createMention()
|
|
|
80
93
|
const currentDoc = shallowRef<Doc>([])
|
|
81
94
|
const isEmpty = computed(() => currentDoc.value.length === 0)
|
|
82
95
|
const showPlaceholder = computed(() => isEmpty.value && !disabled.value)
|
|
96
|
+
const showHint = computed(() => isEmpty.value && !disabled.value && !readonly.value)
|
|
83
97
|
|
|
84
98
|
const pickerMode = shallowRef<'mention' | 'reselect' | null>(null)
|
|
85
99
|
const pickerTriggerDom = shallowRef<HTMLElement | undefined>(undefined)
|
|
@@ -88,6 +102,18 @@ const pickerFilter = shallowRef('')
|
|
|
88
102
|
|
|
89
103
|
const pickerVisible = computed(() => pickerMode.value !== null)
|
|
90
104
|
|
|
105
|
+
/** 把 mention 锚点移到 `@` 字符下方,面板据此定位 */
|
|
106
|
+
function positionMentionAnchor(anchorOffset: number) {
|
|
107
|
+
const rootEl = rootElRef.value
|
|
108
|
+
const anchor = mentionAnchorRef.value
|
|
109
|
+
const rect = editorRef.value?.getRectAtOffset(anchorOffset)
|
|
110
|
+
if (!rootEl || !anchor || !rect) return
|
|
111
|
+
// absolute 定位以 padding box 为基准,需扣除根节点边框宽度
|
|
112
|
+
const rootRect = rootEl.getBoundingClientRect()
|
|
113
|
+
anchor.style.left = `${rect.left - rootRect.left - rootEl.clientLeft}px`
|
|
114
|
+
anchor.style.top = `${rect.bottom - rootRect.top - rootEl.clientTop}px`
|
|
115
|
+
}
|
|
116
|
+
|
|
91
117
|
function syncMention() {
|
|
92
118
|
const editor = editorRef.value
|
|
93
119
|
if (!editor) return
|
|
@@ -97,7 +123,8 @@ function syncMention() {
|
|
|
97
123
|
if (state) {
|
|
98
124
|
pickerMode.value = 'mention'
|
|
99
125
|
pickerFilter.value = state.filter
|
|
100
|
-
|
|
126
|
+
positionMentionAnchor(state.anchorOffset)
|
|
127
|
+
pickerTriggerDom.value = mentionAnchorRef.value ?? containerRef.value ?? undefined
|
|
101
128
|
} else if (pickerMode.value === 'mention') {
|
|
102
129
|
closePicker()
|
|
103
130
|
}
|
|
@@ -135,6 +162,9 @@ onMounted(() => {
|
|
|
135
162
|
pickerFilter.value = ''
|
|
136
163
|
pickerTriggerDom.value = chipEl
|
|
137
164
|
reselectingSegIdx.value = segIndex
|
|
165
|
+
// chip 的 mousedown 被 preventDefault,编辑器没有焦点时点击 chip 焦点不会转移,
|
|
166
|
+
// 键盘导航依赖 container 的 keydown,这里主动把焦点拉回编辑器
|
|
167
|
+
containerRef.value?.focus()
|
|
138
168
|
}
|
|
139
169
|
})
|
|
140
170
|
editorRef.value = editor
|
|
@@ -5,48 +5,71 @@
|
|
|
5
5
|
$root-name: expression-editor;
|
|
6
6
|
|
|
7
7
|
@include m.b($root-name) {
|
|
8
|
+
// 根节点只做布局(提示行在视觉盒之外),视觉盒由 __shell 承担
|
|
8
9
|
position: relative;
|
|
9
|
-
background-color: fn.use-var(bg-color, top);
|
|
10
|
-
border-width: fn.use-var(border, width);
|
|
11
|
-
border-style: fn.use-var(border, style);
|
|
12
|
-
border-color: fn.use-var(border, muted-color);
|
|
13
|
-
border-radius: fn.use-var(radius, default);
|
|
14
10
|
width: 100%;
|
|
15
11
|
|
|
16
|
-
@include m.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
@include m.e(shell) {
|
|
13
|
+
background-color: fn.use-var(bg-color, top);
|
|
14
|
+
border-width: fn.use-var(border, width);
|
|
15
|
+
border-style: fn.use-var(border, style);
|
|
16
|
+
border-color: fn.use-var(border, muted-color);
|
|
17
|
+
border-radius: fn.use-var(radius, default);
|
|
18
|
+
transition:
|
|
19
|
+
border-color fn.use-var(transition, fast) fn.use-var(transition, ease),
|
|
20
|
+
box-shadow fn.use-var(transition, fast) fn.use-var(transition, ease);
|
|
21
|
+
|
|
22
|
+
@include m.is-not(disabled, readonly) {
|
|
23
|
+
&:hover {
|
|
24
|
+
border-color: fn.use-var(color, primary);
|
|
25
|
+
}
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
&:focus-within {
|
|
28
|
+
border-color: fn.use-var(color, primary);
|
|
29
|
+
box-shadow: fn.use-var(focus-ring);
|
|
30
|
+
outline: none;
|
|
31
|
+
}
|
|
24
32
|
}
|
|
25
|
-
}
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
@include m.is('disabled') {
|
|
35
|
+
background-color: fn.use-var(color, disabled);
|
|
36
|
+
cursor: not-allowed;
|
|
37
|
+
border-color: fn.use-var(border, muted-color);
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
&:hover {
|
|
40
|
+
border-color: fn.use-var(border, muted-color);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@include m.is('readonly') {
|
|
45
|
+
background-color: fn.use-var(bg-color, middle);
|
|
46
|
+
cursor: default;
|
|
33
47
|
border-color: fn.use-var(border, muted-color);
|
|
48
|
+
user-select: text;
|
|
49
|
+
|
|
50
|
+
&:hover {
|
|
51
|
+
border-color: fn.use-var(border, muted-color);
|
|
52
|
+
}
|
|
34
53
|
}
|
|
35
|
-
}
|
|
36
54
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
border-color: fn.use-var(border, muted-color);
|
|
41
|
-
user-select: text;
|
|
55
|
+
@include m.size using ($size) {
|
|
56
|
+
min-height: fn.use-var(form-component-height, $size);
|
|
57
|
+
padding: fn.use-var(gap, $size);
|
|
42
58
|
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
@include m.bem($root-name, placeholder) {
|
|
60
|
+
font-size: fn.use-var(font-size-main, $size);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@include m.bem($root-name, container) {
|
|
64
|
+
font-size: fn.use-var(font-size-main, $size);
|
|
65
|
+
}
|
|
45
66
|
}
|
|
46
67
|
}
|
|
47
68
|
|
|
48
69
|
@include m.e(placeholder) {
|
|
49
70
|
position: absolute;
|
|
71
|
+
top: 0;
|
|
72
|
+
left: 0;
|
|
50
73
|
color: fn.use-var(text-color, placeholder);
|
|
51
74
|
pointer-events: none;
|
|
52
75
|
user-select: none;
|
|
@@ -54,6 +77,43 @@ $root-name: expression-editor;
|
|
|
54
77
|
font-weight: 400;
|
|
55
78
|
}
|
|
56
79
|
|
|
80
|
+
// 提示 + 输入区的定位层:placeholder 相对它对齐,hint 出现时不至于错位
|
|
81
|
+
@include m.e(body) {
|
|
82
|
+
position: relative;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 编辑器上方的 `@` 使用提示(空内容时显示),位于边框盒之外
|
|
86
|
+
@include m.e(hint) {
|
|
87
|
+
font-size: fn.use-var(font-size-main, small);
|
|
88
|
+
color: fn.use-var(text-color, placeholder);
|
|
89
|
+
user-select: none;
|
|
90
|
+
margin-bottom: 4px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// mention 面板定位锚点:跟随 `@` 字符,零尺寸不参与绘制
|
|
94
|
+
@include m.e(mention-anchor) {
|
|
95
|
+
position: absolute;
|
|
96
|
+
width: 0;
|
|
97
|
+
height: 0;
|
|
98
|
+
pointer-events: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 键位提示(编辑器 hint 与面板 footer 共用)
|
|
102
|
+
@include m.e(hint, picker-hint) {
|
|
103
|
+
kbd {
|
|
104
|
+
display: inline-block;
|
|
105
|
+
min-width: 18px;
|
|
106
|
+
padding: 1px 4px;
|
|
107
|
+
border: fn.use-var(border);
|
|
108
|
+
border-radius: fn.use-var(radius, small);
|
|
109
|
+
font-size: 11px;
|
|
110
|
+
line-height: 1.4;
|
|
111
|
+
text-align: center;
|
|
112
|
+
color: fn.use-var(text-color, second);
|
|
113
|
+
background-color: fn.use-var(bg-color, top);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
57
117
|
@include m.e(container) {
|
|
58
118
|
position: relative;
|
|
59
119
|
outline: none;
|
|
@@ -117,70 +177,92 @@ $root-name: expression-editor;
|
|
|
117
177
|
}
|
|
118
178
|
|
|
119
179
|
// ─── 变量选择面板 ──────────────────────────────────────────
|
|
180
|
+
// 面板以 tip 为浮层载体,但按库内标准下拉面板形态渲染:
|
|
181
|
+
// 负 margin 抵消 tip 内容自带的 gap 内边距,由 picker 自绘边框卡片
|
|
120
182
|
@include m.e(picker) {
|
|
121
|
-
|
|
183
|
+
margin: calc(#{fn.use-var(gap, default)} * -1);
|
|
184
|
+
min-width: 240px;
|
|
185
|
+
display: flex;
|
|
186
|
+
flex-direction: column;
|
|
187
|
+
overflow: hidden;
|
|
122
188
|
background-color: fn.use-var(bg-color, top);
|
|
189
|
+
border: fn.use-var(border);
|
|
190
|
+
border-radius: fn.use-var(radius, default);
|
|
191
|
+
backdrop-filter: fn.use-var(bg-filter);
|
|
192
|
+
-webkit-backdrop-filter: fn.use-var(bg-filter);
|
|
123
193
|
}
|
|
124
194
|
|
|
125
195
|
@include m.e(picker-body) {
|
|
196
|
+
flex: 1 1 auto;
|
|
197
|
+
min-height: 0;
|
|
126
198
|
display: flex;
|
|
127
199
|
align-items: stretch;
|
|
128
|
-
min-height: 200px;
|
|
129
200
|
}
|
|
130
201
|
|
|
131
202
|
@include m.e(panel) {
|
|
132
203
|
flex: 1 1 auto;
|
|
133
204
|
min-width: 0;
|
|
134
205
|
max-height: 320px;
|
|
206
|
+
display: flex;
|
|
207
|
+
flex-direction: column;
|
|
135
208
|
}
|
|
136
209
|
|
|
137
210
|
@include m.e(panel-list) {
|
|
138
|
-
|
|
139
|
-
min-height:
|
|
211
|
+
flex: 1 1 auto;
|
|
212
|
+
min-height: 0;
|
|
140
213
|
}
|
|
141
214
|
|
|
142
215
|
@include m.e(panel-content) {
|
|
143
|
-
padding: 4px
|
|
216
|
+
padding: 4px;
|
|
144
217
|
}
|
|
145
218
|
|
|
146
219
|
@include m.e(item) {
|
|
147
|
-
padding: 6px 12px;
|
|
148
|
-
cursor: pointer;
|
|
149
|
-
user-select: none;
|
|
150
220
|
display: flex;
|
|
151
221
|
align-items: center;
|
|
152
222
|
justify-content: space-between;
|
|
153
|
-
gap:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
color 0.12s ease;
|
|
157
|
-
color: fn.use-var(text-color, main);
|
|
158
|
-
font-size: 13px;
|
|
159
|
-
line-height: 1.4;
|
|
223
|
+
gap: fn.use-var(gap, default);
|
|
224
|
+
height: fn.use-var(form-component-height, default);
|
|
225
|
+
padding: 0 fn.use-var(gap, default);
|
|
160
226
|
border-radius: fn.use-var(radius, small);
|
|
161
|
-
|
|
227
|
+
font-size: fn.use-var(font-size-main, default);
|
|
228
|
+
color: fn.use-var(text-color, main);
|
|
229
|
+
cursor: pointer;
|
|
230
|
+
user-select: none;
|
|
231
|
+
transition:
|
|
232
|
+
background-color 0.15s ease,
|
|
233
|
+
color 0.15s ease;
|
|
162
234
|
|
|
163
235
|
@include m.is(active) {
|
|
164
|
-
background-color: fn.use-var(color,
|
|
165
|
-
color: fn.use-var(text-color,
|
|
166
|
-
|
|
167
|
-
@include m.bem($root-name, item-expand) {
|
|
168
|
-
color: fn.use-var(text-color, white);
|
|
169
|
-
}
|
|
236
|
+
background-color: fn.use-var(bg-color, hover);
|
|
237
|
+
color: fn.use-var(text-color, title);
|
|
170
238
|
}
|
|
171
239
|
}
|
|
172
240
|
|
|
173
241
|
@include m.e(item-label) {
|
|
174
242
|
flex: 1;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
white-space: nowrap;
|
|
243
|
+
min-width: 0;
|
|
244
|
+
@include m.ellipsis;
|
|
178
245
|
}
|
|
179
246
|
|
|
180
247
|
@include m.e(item-expand) {
|
|
181
248
|
flex-shrink: 0;
|
|
182
249
|
color: fn.use-var(text-color, placeholder);
|
|
183
250
|
font-size: 12px;
|
|
251
|
+
// 扩大点击热区:u-icon 是固定 1em 盒(全局 border-box),padding 会把 svg 挤成 0 宽,
|
|
252
|
+
// 这里改回 content-box 让字形保持原尺寸,再用负 margin 抵消布局占位
|
|
253
|
+
box-sizing: content-box;
|
|
254
|
+
padding: 6px;
|
|
255
|
+
margin: -6px;
|
|
256
|
+
border-radius: fn.use-var(radius, small);
|
|
257
|
+
cursor: pointer;
|
|
258
|
+
transition:
|
|
259
|
+
color 0.15s ease,
|
|
260
|
+
background-color 0.15s ease;
|
|
261
|
+
|
|
262
|
+
&:hover {
|
|
263
|
+
color: fn.use-var(color, primary);
|
|
264
|
+
background-color: fn.use-var(bg-color, hover);
|
|
265
|
+
}
|
|
184
266
|
}
|
|
185
267
|
|
|
186
268
|
@include m.e(empty) {
|
|
@@ -189,30 +271,51 @@ $root-name: expression-editor;
|
|
|
189
271
|
text-align: center;
|
|
190
272
|
}
|
|
191
273
|
|
|
274
|
+
// 面板底部键盘操作提示
|
|
275
|
+
@include m.e(picker-hint) {
|
|
276
|
+
display: flex;
|
|
277
|
+
align-items: center;
|
|
278
|
+
flex-wrap: wrap;
|
|
279
|
+
gap: 4px 12px;
|
|
280
|
+
padding: 6px 10px;
|
|
281
|
+
border-top: fn.use-var(border);
|
|
282
|
+
font-size: fn.use-var(font-size-main, small);
|
|
283
|
+
color: fn.use-var(text-color, placeholder);
|
|
284
|
+
user-select: none;
|
|
285
|
+
|
|
286
|
+
span {
|
|
287
|
+
display: inline-flex;
|
|
288
|
+
align-items: center;
|
|
289
|
+
gap: 3px;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
192
293
|
// ─── 面包屑 ───────────────────────────────────────────────
|
|
193
294
|
@include m.e(breadcrumbs) {
|
|
194
295
|
display: flex;
|
|
195
296
|
align-items: center;
|
|
196
297
|
flex-wrap: wrap;
|
|
197
|
-
|
|
298
|
+
gap: 2px;
|
|
299
|
+
padding: 6px 8px;
|
|
300
|
+
background-color: fn.use-var(bg-color, middle);
|
|
198
301
|
border-bottom: fn.use-var(border);
|
|
199
|
-
font-size:
|
|
302
|
+
font-size: fn.use-var(font-size-main, small);
|
|
200
303
|
color: fn.use-var(text-color, second);
|
|
201
304
|
}
|
|
202
305
|
|
|
203
306
|
@include m.e(breadcrumb-item) {
|
|
204
307
|
display: inline-flex;
|
|
205
308
|
align-items: center;
|
|
206
|
-
gap:
|
|
309
|
+
gap: 2px;
|
|
207
310
|
}
|
|
208
311
|
|
|
209
312
|
@include m.e(breadcrumb-link) {
|
|
313
|
+
padding: 3px 6px;
|
|
314
|
+
border-radius: fn.use-var(radius, small);
|
|
210
315
|
cursor: pointer;
|
|
211
316
|
transition:
|
|
212
|
-
color 0.
|
|
213
|
-
background-color 0.
|
|
214
|
-
padding: 2px 6px;
|
|
215
|
-
border-radius: fn.use-var(radius, small);
|
|
317
|
+
color 0.15s ease,
|
|
318
|
+
background-color 0.15s ease;
|
|
216
319
|
|
|
217
320
|
&:hover {
|
|
218
321
|
color: fn.use-var(color, primary);
|
|
@@ -226,7 +329,7 @@ $root-name: expression-editor;
|
|
|
226
329
|
}
|
|
227
330
|
|
|
228
331
|
@include m.e(breadcrumb-separator) {
|
|
229
|
-
font-size:
|
|
332
|
+
font-size: 12px;
|
|
230
333
|
color: fn.use-var(text-color, placeholder);
|
|
231
334
|
}
|
|
232
335
|
|
|
@@ -236,7 +339,7 @@ $root-name: expression-editor;
|
|
|
236
339
|
border-left: fn.use-var(border);
|
|
237
340
|
padding: 8px 12px;
|
|
238
341
|
background-color: fn.use-var(bg-color, middle);
|
|
239
|
-
font-size:
|
|
342
|
+
font-size: fn.use-var(font-size-main, small);
|
|
240
343
|
}
|
|
241
344
|
|
|
242
345
|
@include m.e(path-preview-title) {
|
|
@@ -257,6 +360,8 @@ $root-name: expression-editor;
|
|
|
257
360
|
gap: 4px;
|
|
258
361
|
line-height: 1.6;
|
|
259
362
|
color: fn.use-var(text-color, main);
|
|
363
|
+
// 层级缩进由模板传入的 --depth 驱动
|
|
364
|
+
padding-left: calc(var(--depth, 0) * 12px);
|
|
260
365
|
|
|
261
366
|
@include m.is(active) {
|
|
262
367
|
color: fn.use-var(color, primary);
|
|
@@ -264,28 +369,8 @@ $root-name: expression-editor;
|
|
|
264
369
|
}
|
|
265
370
|
}
|
|
266
371
|
|
|
267
|
-
@include m.e(path-tree-branch) {
|
|
268
|
-
color: fn.use-var(text-color, placeholder);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
372
|
@include m.e(path-tree-label) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
white-space: nowrap;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
@include m.size using ($size) {
|
|
278
|
-
min-height: fn.use-var(form-component-height, $size);
|
|
279
|
-
padding: fn.use-var(gap, $size);
|
|
280
|
-
|
|
281
|
-
@include m.bem($root-name, placeholder) {
|
|
282
|
-
top: fn.use-var(gap, $size);
|
|
283
|
-
left: fn.use-var(gap, $size);
|
|
284
|
-
font-size: fn.use-var(font-size-main, $size);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
@include m.bem($root-name, container) {
|
|
288
|
-
font-size: fn.use-var(font-size-main, $size);
|
|
289
|
-
}
|
|
373
|
+
min-width: 0;
|
|
374
|
+
@include m.ellipsis;
|
|
290
375
|
}
|
|
291
376
|
}
|