@veltra/desktop 1.0.13 → 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.
- package/dist/components/action/action-group.js +110 -3
- package/dist/components/action/action-group.js.map +1 -1
- package/dist/components/action/action-group.vue.d.ts +16 -3
- package/dist/components/action/action.js +26 -13
- package/dist/components/action/action.js.map +1 -1
- package/dist/components/action/style.js +2 -1
- package/dist/components/action/style2.css +35 -7
- package/dist/components/batch-edit/batch-edit-form.js +106 -42
- package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
- package/dist/components/batch-edit/batch-edit-list.js +84 -47
- package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
- package/dist/components/batch-edit/batch-edit.js +42 -10
- package/dist/components/batch-edit/batch-edit.js.map +1 -1
- package/dist/components/batch-edit/style.js +2 -4
- package/dist/components/batch-edit/style2.css +202 -30
- package/dist/components/batch-edit/use-edit.js +45 -9
- package/dist/components/batch-edit/use-edit.js.map +1 -1
- package/dist/components/cascade/cascade.js +3 -1
- package/dist/components/cascade/cascade.js.map +1 -1
- package/dist/components/code-editor/code-editor.js +145 -49
- package/dist/components/code-editor/code-editor.js.map +1 -1
- package/dist/components/code-editor/code-editor.vue.d.ts +2 -0
- package/dist/components/code-editor/style2.css +87 -19
- package/dist/components/dialog/dialog.js +6 -2
- package/dist/components/dialog/dialog.js.map +1 -1
- package/dist/components/dialog/style2.css +2 -3
- package/dist/components/expression-editor/components/path-preview.js +27 -0
- package/dist/components/expression-editor/components/path-preview.js.map +1 -0
- package/dist/components/expression-editor/components/variable-picker.js +200 -153
- package/dist/components/expression-editor/components/variable-picker.js.map +1 -1
- package/dist/components/expression-editor/core/chip.js +54 -0
- package/dist/components/expression-editor/core/chip.js.map +1 -0
- package/dist/components/expression-editor/core/editor.js +425 -0
- package/dist/components/expression-editor/core/editor.js.map +1 -0
- package/dist/components/expression-editor/core/mention.js +91 -0
- package/dist/components/expression-editor/core/mention.js.map +1 -0
- package/dist/components/expression-editor/core/model.js +64 -0
- package/dist/components/expression-editor/core/model.js.map +1 -0
- package/dist/components/expression-editor/di.js +4 -10
- package/dist/components/expression-editor/di.js.map +1 -1
- package/dist/components/expression-editor/expression-editor.js +168 -114
- package/dist/components/expression-editor/expression-editor.js.map +1 -1
- package/dist/components/expression-editor/expression-editor.vue.d.ts +2 -1
- package/dist/components/expression-editor/style.js +0 -2
- package/dist/components/expression-editor/style2.css +145 -218
- package/dist/components/index.js +54 -54
- package/dist/components/layout/layout.js +12 -11
- package/dist/components/layout/layout.js.map +1 -1
- package/dist/components/multi-select/multi-select.vue.d.ts +1 -1
- package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +1 -1
- package/dist/components/number-input/number-input.js +4 -4
- package/dist/components/number-input/number-input.js.map +1 -1
- package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
- package/dist/components/rich-text-editor/rich-text-editor.js +1 -1
- package/dist/components/rich-text-editor/toolbar.js +2 -2
- package/dist/components/steps/steps.vue.d.ts +1 -1
- package/dist/components/table/use-table.js +16 -0
- package/dist/components/table/use-table.js.map +1 -1
- package/dist/components/tabs/tabs-horizontal.vue.d.ts +3 -3
- package/dist/components/tabs/tabs-vertical.vue.d.ts +2 -2
- package/dist/components/tabs/tabs.vue.d.ts +3 -3
- package/dist/components/tip/tip.js +4 -5
- package/dist/components/tip/tip.js.map +1 -1
- package/dist/components/tip/tip.vue.d.ts +1 -1
- package/dist/components/tree/style2.css +18 -12
- package/dist/components/tree/tree-node.js +7 -4
- package/dist/components/tree/tree-node.js.map +1 -1
- package/dist/components/tree-select/tree-select.vue.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +83 -83
- package/dist/style.js +3 -3
- package/dist/types/action.d.ts +38 -4
- package/dist/types/cascade.d.ts +2 -3
- package/dist/types/code-editor.d.ts +9 -2
- package/dist/types/expression-editor.d.ts +9 -1
- package/dist/types/index.d.ts +2 -2
- package/package.json +11 -12
- package/src/components/action/action-group.vue +126 -56
- package/src/components/action/action.vue +35 -12
- package/src/components/action/style.scss +46 -10
- package/src/components/action/style.ts +2 -1
- package/src/components/batch-edit/batch-edit-form.vue +121 -45
- package/src/components/batch-edit/batch-edit-list.vue +56 -39
- package/src/components/batch-edit/batch-edit.vue +57 -6
- package/src/components/batch-edit/style.scss +251 -37
- package/src/components/batch-edit/style.ts +5 -8
- package/src/components/batch-edit/use-edit.ts +58 -11
- package/src/components/cascade/cascade.vue +3 -1
- package/src/components/code-editor/code-editor.vue +198 -63
- package/src/components/code-editor/style.scss +110 -12
- package/src/components/dialog/dialog.vue +6 -2
- package/src/components/dialog/style.scss +4 -4
- package/src/components/expression-editor/__test__/chip.test.ts +86 -0
- package/src/components/expression-editor/__test__/editor.test.ts +47 -0
- package/src/components/expression-editor/__test__/mention.test.ts +122 -0
- package/src/components/expression-editor/__test__/model.test.ts +127 -0
- package/src/components/expression-editor/components/path-preview.vue +32 -0
- package/src/components/expression-editor/components/variable-picker.vue +225 -219
- package/src/components/expression-editor/core/chip.ts +101 -0
- package/src/components/expression-editor/core/editor.ts +514 -0
- package/src/components/expression-editor/core/mention.ts +121 -0
- package/src/components/expression-editor/core/model.ts +109 -0
- package/src/components/expression-editor/di.ts +4 -32
- package/src/components/expression-editor/expression-editor.vue +184 -151
- package/src/components/expression-editor/style.scss +148 -219
- package/src/components/expression-editor/style.ts +0 -2
- package/src/components/layout/layout.vue +17 -19
- package/src/components/number-input/number-input.vue +4 -4
- package/src/components/table/use-table.ts +16 -0
- package/src/components/tip/tip.vue +4 -3
- package/src/components/tree/style.scss +30 -33
- package/src/components/tree/tree-node.vue +4 -2
- package/src/types/action.ts +42 -4
- package/src/types/cascade.ts +4 -3
- package/src/types/code-editor.ts +9 -2
- package/src/types/expression-editor.ts +9 -0
- package/dist/components/action/action-group.vue_vue_type_script_setup_true_lang.js +0 -65
- package/dist/components/action/action-group.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/components/expression-editor/constants.js +0 -9
- package/dist/components/expression-editor/constants.js.map +0 -1
- package/dist/components/expression-editor/internal/editor-runtime/create-runtime.js +0 -47
- package/dist/components/expression-editor/internal/editor-runtime/create-runtime.js.map +0 -1
- package/dist/components/expression-editor/internal/editor-runtime/model-sync.js +0 -49
- package/dist/components/expression-editor/internal/editor-runtime/model-sync.js.map +0 -1
- package/dist/components/expression-editor/internal/features/commands/register-command-packs.js +0 -215
- package/dist/components/expression-editor/internal/features/commands/register-command-packs.js.map +0 -1
- package/dist/components/expression-editor/internal/features/drag-drop/drag-drop-service.js +0 -33
- package/dist/components/expression-editor/internal/features/drag-drop/drag-drop-service.js.map +0 -1
- package/dist/components/expression-editor/internal/features/insertion/insertion-service.js +0 -33
- package/dist/components/expression-editor/internal/features/insertion/insertion-service.js.map +0 -1
- package/dist/components/expression-editor/nodes/variable-node.js +0 -94
- package/dist/components/expression-editor/nodes/variable-node.js.map +0 -1
- package/dist/components/expression-editor/parser.js +0 -32
- package/dist/components/expression-editor/parser.js.map +0 -1
- package/dist/components/expression-editor/plain-text.js +0 -19
- package/dist/components/expression-editor/plain-text.js.map +0 -1
- package/dist/components/expression-editor/use-context.js +0 -69
- package/dist/components/expression-editor/use-context.js.map +0 -1
- package/dist/components/expression-editor/use-decorators.js +0 -27
- package/dist/components/expression-editor/use-decorators.js.map +0 -1
- package/dist/components/expression-editor/use-expression-drag-drop.js +0 -275
- package/dist/components/expression-editor/use-expression-drag-drop.js.map +0 -1
- package/src/components/batch-edit/batch-edit-new.vue +0 -168
- package/src/components/batch-edit/style-new.scss +0 -35
- package/src/components/batch-edit/use-edit-new.ts +0 -68
- package/src/components/batch-edit/use-tip.ts +0 -47
- package/src/components/expression-editor/__test__/architecture-boundaries.test.ts +0 -103
- package/src/components/expression-editor/__test__/architecture-compatibility.test.ts +0 -52
- package/src/components/expression-editor/__test__/drag-drop.test.ts +0 -171
- package/src/components/expression-editor/__test__/parser.test.ts +0 -62
- package/src/components/expression-editor/constants.ts +0 -6
- package/src/components/expression-editor/internal/contracts/editor-runtime.ts +0 -11
- package/src/components/expression-editor/internal/editor-runtime/create-runtime.ts +0 -64
- package/src/components/expression-editor/internal/editor-runtime/index.ts +0 -1
- package/src/components/expression-editor/internal/editor-runtime/model-sync.ts +0 -68
- package/src/components/expression-editor/internal/features/commands/register-command-packs.ts +0 -517
- package/src/components/expression-editor/internal/features/drag-drop/drag-drop-service.ts +0 -51
- package/src/components/expression-editor/internal/features/insertion/insertion-service.ts +0 -56
- package/src/components/expression-editor/nodes/variable-node.tsx +0 -118
- package/src/components/expression-editor/parser.ts +0 -46
- package/src/components/expression-editor/plain-text.ts +0 -25
- package/src/components/expression-editor/plan.md +0 -22
- package/src/components/expression-editor/use-context.ts +0 -134
- package/src/components/expression-editor/use-decorators.tsx +0 -29
- package/src/components/expression-editor/use-editor.ts +0 -12
- package/src/components/expression-editor/use-expression-drag-drop.ts +0 -396
|
@@ -50,6 +50,8 @@ export interface EditReturned {
|
|
|
50
50
|
handleSave: () => Promise<void>
|
|
51
51
|
handleClose: () => void
|
|
52
52
|
handleCreate: () => void
|
|
53
|
+
handleEdit: (row: TableRow) => void
|
|
54
|
+
handleCopy: (row: TableRow) => void
|
|
53
55
|
handleDelete: (row: TableRow) => Promise<void>
|
|
54
56
|
handleInsertToNext: (row: TableRow) => void
|
|
55
57
|
handleInsertToPrev: (row: TableRow) => void
|
|
@@ -79,8 +81,17 @@ export function useEdit(options: Options): EditReturned {
|
|
|
79
81
|
}
|
|
80
82
|
)
|
|
81
83
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
+
/**
|
|
85
|
+
* 「打开行 / 切换 type」时记录的基线快照
|
|
86
|
+
* @description
|
|
87
|
+
* 用于 changeCb 内部比对 — 若新值与基线相等(深比较)视为无改动,避免某些组件在 mount
|
|
88
|
+
* 阶段把规范化后的值再写回模型时被误判为「用户已修改」
|
|
89
|
+
*/
|
|
90
|
+
let baselineSnapshot: Record<string, any> | undefined
|
|
91
|
+
|
|
92
|
+
function snapshotModelData() {
|
|
93
|
+
return safeRun(() => JSON.parse(JSON.stringify(props.model?.data ?? {})), {})
|
|
94
|
+
}
|
|
84
95
|
|
|
85
96
|
watch(
|
|
86
97
|
() => state.row,
|
|
@@ -89,22 +100,40 @@ export function useEdit(options: Options): EditReturned {
|
|
|
89
100
|
if (row) {
|
|
90
101
|
state.type = 'update'
|
|
91
102
|
state.visible = true
|
|
92
|
-
changedByUserInput = false
|
|
93
103
|
props.model?.setData(row.data)
|
|
94
|
-
nextTick(() => {
|
|
95
|
-
changedByUserInput = true
|
|
96
|
-
})
|
|
97
104
|
} else {
|
|
98
105
|
state.visible = false
|
|
99
106
|
}
|
|
100
|
-
|
|
107
|
+
baselineSnapshot = snapshotModelData()
|
|
108
|
+
state.dataUpdated = false
|
|
109
|
+
},
|
|
110
|
+
// sync 保证 setData 完成后立刻取快照,避免 mount 期间组件回写值时丢基线
|
|
111
|
+
{ flush: 'sync' }
|
|
101
112
|
)
|
|
102
113
|
|
|
103
|
-
const changeCb = (
|
|
104
|
-
//
|
|
105
|
-
if (
|
|
106
|
-
|
|
114
|
+
const changeCb = (field: string, val: any) => {
|
|
115
|
+
// 与基线比较 — 同值(如组件 mount 时把当前值再写一次)不算"用户修改"
|
|
116
|
+
if (baselineSnapshot) {
|
|
117
|
+
const baseVal = o(baselineSnapshot).get(field)
|
|
118
|
+
if (isSameValue(baseVal, val)) {
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
state.dataUpdated = true
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function isSameValue(a: any, b: any) {
|
|
126
|
+
if (a === b) return true
|
|
127
|
+
if (a == null && b == null) return true
|
|
128
|
+
if (typeof a !== typeof b) return false
|
|
129
|
+
if (typeof a === 'object') {
|
|
130
|
+
try {
|
|
131
|
+
return JSON.stringify(a) === JSON.stringify(b)
|
|
132
|
+
} catch {
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
107
135
|
}
|
|
136
|
+
return false
|
|
108
137
|
}
|
|
109
138
|
|
|
110
139
|
watch(
|
|
@@ -216,6 +245,22 @@ export function useEdit(options: Options): EditReturned {
|
|
|
216
245
|
})
|
|
217
246
|
}
|
|
218
247
|
|
|
248
|
+
/** 编辑指定行(等同于点击行) */
|
|
249
|
+
function handleEdit(row: TableRow) {
|
|
250
|
+
state.row = row
|
|
251
|
+
state.depth = row.depth
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** 复制指定行:在其下方以 create 模式打开表单,并预填副本数据 */
|
|
255
|
+
function handleCopy(row: TableRow) {
|
|
256
|
+
runCreate(() => {
|
|
257
|
+
state.depth = row.depth
|
|
258
|
+
insertIndexes.value = [...row.indexes.slice(0, -1), row.index + 1]
|
|
259
|
+
const cloned = safeRun(() => JSON.parse(JSON.stringify(row.data ?? {})), {})
|
|
260
|
+
props.model?.setData(cloned)
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
|
|
219
264
|
function runWithLoading<Arg extends any[]>(fn: (...args: Arg) => Promise<void> | void) {
|
|
220
265
|
return async (...args: Arg) => {
|
|
221
266
|
state.loading = true
|
|
@@ -321,6 +366,8 @@ export function useEdit(options: Options): EditReturned {
|
|
|
321
366
|
handleInsertToNext,
|
|
322
367
|
handleInsertToPrev,
|
|
323
368
|
handleCreate,
|
|
369
|
+
handleEdit,
|
|
370
|
+
handleCopy,
|
|
324
371
|
handleClose,
|
|
325
372
|
handleSave
|
|
326
373
|
}
|
|
@@ -255,7 +255,9 @@ const qs = shallowRef<string>('')
|
|
|
255
255
|
watch([qs, forest], ([qs, forest]) => {
|
|
256
256
|
const { filterable } = props
|
|
257
257
|
if (!filterable || !qs) {
|
|
258
|
-
forest.dfs((node) =>
|
|
258
|
+
forest.dfs((node) => {
|
|
259
|
+
node.visible = true
|
|
260
|
+
})
|
|
259
261
|
getPanelItemList(forest.roots)
|
|
260
262
|
return
|
|
261
263
|
}
|
|
@@ -1,24 +1,35 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<u-scroll :class="className" @keyup.enter.stop>
|
|
2
|
+
<u-scroll :class="className" :style="rootStyle" @keyup.enter.stop>
|
|
3
3
|
<div ref="container"></div>
|
|
4
4
|
</u-scroll>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<script lang="ts" setup>
|
|
8
|
-
import { EditorState } from '@codemirror/state'
|
|
8
|
+
import { Compartment, EditorState, type Extension } from '@codemirror/state'
|
|
9
9
|
import { oneDark } from '@codemirror/theme-one-dark'
|
|
10
|
-
import { tooltips } from '@codemirror/view'
|
|
10
|
+
import { ViewPlugin, tooltips, type ViewUpdate } from '@codemirror/view'
|
|
11
11
|
import { useFormComponent, useFormFallbackProps } from '@veltra/compositions'
|
|
12
12
|
import { bem, zIndex } from '@veltra/utils'
|
|
13
13
|
import { EditorView, basicSetup } from 'codemirror'
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
computed,
|
|
16
|
+
onBeforeUnmount,
|
|
17
|
+
shallowRef,
|
|
18
|
+
useTemplateRef,
|
|
19
|
+
watch,
|
|
20
|
+
type CSSProperties
|
|
21
|
+
} from 'vue'
|
|
15
22
|
|
|
16
23
|
import type { CodeEditorLang, CodeEditorProps } from '../../types'
|
|
17
24
|
import { UScroll } from '../scroll'
|
|
18
25
|
|
|
26
|
+
defineOptions({ name: 'CodeEditor' })
|
|
27
|
+
|
|
19
28
|
const props = withDefaults(defineProps<CodeEditorProps>(), {
|
|
20
29
|
disabled: undefined,
|
|
21
|
-
readonly: undefined
|
|
30
|
+
readonly: undefined,
|
|
31
|
+
dark: false,
|
|
32
|
+
defaultLines: 8
|
|
22
33
|
})
|
|
23
34
|
|
|
24
35
|
const model = defineModel<string>()
|
|
@@ -27,101 +38,225 @@ const cls = bem('code-editor')
|
|
|
27
38
|
|
|
28
39
|
const { formProps } = useFormComponent()
|
|
29
40
|
|
|
30
|
-
const {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return [
|
|
34
|
-
cls.b,
|
|
35
|
-
cls.m(size.value),
|
|
36
|
-
bem.is('disabled', disabled.value),
|
|
37
|
-
bem.is('readonly', readonly.value)
|
|
38
|
-
]
|
|
41
|
+
const { disabled, readonly } = useFormFallbackProps([formProps ?? {}, props], {
|
|
42
|
+
disabled: false,
|
|
43
|
+
readonly: false
|
|
39
44
|
})
|
|
40
45
|
|
|
41
|
-
const
|
|
46
|
+
const className = computed<string[]>(() => [
|
|
47
|
+
cls.b,
|
|
48
|
+
bem.is('disabled', disabled.value),
|
|
49
|
+
bem.is('readonly', readonly.value),
|
|
50
|
+
bem.is('dark', props.dark)
|
|
51
|
+
])
|
|
42
52
|
|
|
53
|
+
const rootStyle = computed<CSSProperties>(() => ({
|
|
54
|
+
'--u-code-editor-default-lines': Math.max(1, props.defaultLines)
|
|
55
|
+
}))
|
|
56
|
+
|
|
57
|
+
const containerRef = useTemplateRef('container')
|
|
43
58
|
const editor = shallowRef<EditorView | null>(null)
|
|
44
59
|
|
|
45
|
-
const langLoaders: Record<CodeEditorLang, () => Promise<
|
|
60
|
+
const langLoaders: Record<CodeEditorLang, () => Promise<Extension>> = {
|
|
46
61
|
js: () => import('@codemirror/lang-javascript').then((m) => m.javascript({ typescript: true })),
|
|
47
62
|
sql: () => import('@codemirror/lang-sql').then((m) => m.sql()),
|
|
48
63
|
java: () => import('@codemirror/lang-java').then((m) => m.java()),
|
|
49
64
|
json: () => import('@codemirror/lang-json').then((m) => m.json())
|
|
50
65
|
}
|
|
51
66
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
const themeCompartment = new Compartment()
|
|
68
|
+
const editableCompartment = new Compartment()
|
|
69
|
+
const readOnlyCompartment = new Compartment()
|
|
70
|
+
const langCompartment = new Compartment()
|
|
55
71
|
|
|
56
|
-
|
|
72
|
+
let phantomGutter: HTMLElement | null = null
|
|
73
|
+
let phantomContainer: HTMLElement | null = null
|
|
74
|
+
let lastPhantomKey = ''
|
|
57
75
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
function resetPhantomCache() {
|
|
77
|
+
phantomGutter = null
|
|
78
|
+
phantomContainer = null
|
|
79
|
+
lastPhantomKey = ''
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 在 .cm-lineNumbers 末尾追加占位行号 DOM,使可视行号数量补齐到 defaultLines。
|
|
84
|
+
* CodeMirror 的 lineNumbers() 仅为真实文档行渲染行号,超出部分由本逻辑补全。
|
|
85
|
+
*/
|
|
86
|
+
function syncPhantomLineNumbers() {
|
|
87
|
+
const view = editor.value
|
|
88
|
+
if (!view) return
|
|
70
89
|
|
|
71
|
-
if (
|
|
72
|
-
|
|
90
|
+
if (!phantomGutter || !phantomGutter.isConnected) {
|
|
91
|
+
phantomGutter = view.dom.querySelector<HTMLElement>('.cm-gutter.cm-lineNumbers')
|
|
73
92
|
}
|
|
74
|
-
if (
|
|
75
|
-
|
|
93
|
+
if (!phantomGutter) return
|
|
94
|
+
|
|
95
|
+
if (!phantomContainer) {
|
|
96
|
+
phantomContainer = document.createElement('div')
|
|
97
|
+
phantomContainer.className = 'cm-phantom-lines'
|
|
98
|
+
}
|
|
99
|
+
if (
|
|
100
|
+
phantomContainer.parentElement !== phantomGutter ||
|
|
101
|
+
phantomContainer !== phantomGutter.lastElementChild
|
|
102
|
+
) {
|
|
103
|
+
phantomGutter.appendChild(phantomContainer)
|
|
76
104
|
}
|
|
77
|
-
if (language) {
|
|
78
|
-
const lang = await langLoaders[language]()
|
|
79
105
|
|
|
80
|
-
|
|
106
|
+
const realLines = view.state.doc.lines
|
|
107
|
+
const target = Math.max(0, Math.max(1, props.defaultLines) - realLines)
|
|
108
|
+
const lineHeight = view.defaultLineHeight
|
|
109
|
+
|
|
110
|
+
const key = `${realLines}|${target}|${lineHeight}`
|
|
111
|
+
if (key === lastPhantomKey) return
|
|
112
|
+
lastPhantomKey = key
|
|
113
|
+
|
|
114
|
+
while (phantomContainer.children.length > target) {
|
|
115
|
+
phantomContainer.lastElementChild!.remove()
|
|
116
|
+
}
|
|
117
|
+
while (phantomContainer.children.length < target) {
|
|
118
|
+
const el = document.createElement('div')
|
|
119
|
+
el.className = 'cm-gutterElement cm-gutterElement-phantom'
|
|
120
|
+
phantomContainer.appendChild(el)
|
|
81
121
|
}
|
|
122
|
+
for (let i = 0; i < target; i++) {
|
|
123
|
+
const el = phantomContainer.children[i] as HTMLElement
|
|
124
|
+
const num = String(realLines + i + 1)
|
|
125
|
+
if (el.textContent !== num) el.textContent = num
|
|
126
|
+
if (lineHeight && el.style.height !== `${lineHeight}px`) {
|
|
127
|
+
el.style.height = `${lineHeight}px`
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
82
131
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
132
|
+
const phantomLineNumbersPlugin = ViewPlugin.fromClass(
|
|
133
|
+
class {
|
|
134
|
+
update(update: ViewUpdate) {
|
|
135
|
+
if (update.docChanged || update.geometryChanged) {
|
|
136
|
+
syncPhantomLineNumbers()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
function buildExtensions(): Extension[] {
|
|
143
|
+
return [
|
|
144
|
+
basicSetup,
|
|
145
|
+
tooltips({ parent: document.body }),
|
|
146
|
+
EditorView.theme({
|
|
147
|
+
'.cm-tooltip': { zIndex: zIndex() }
|
|
148
|
+
}),
|
|
149
|
+
themeCompartment.of(props.dark ? oneDark : []),
|
|
150
|
+
editableCompartment.of(EditorView.editable.of(!disabled.value)),
|
|
151
|
+
readOnlyCompartment.of(EditorState.readOnly.of(readonly.value)),
|
|
152
|
+
langCompartment.of([]),
|
|
153
|
+
phantomLineNumbersPlugin
|
|
154
|
+
]
|
|
155
|
+
}
|
|
87
156
|
|
|
157
|
+
let editorToken = 0
|
|
158
|
+
let langToken = 0
|
|
159
|
+
|
|
160
|
+
async function applyLanguage() {
|
|
161
|
+
const view = editor.value
|
|
162
|
+
if (!view) return
|
|
163
|
+
const myToken = ++langToken
|
|
164
|
+
const { language } = props
|
|
165
|
+
const ext = language ? await langLoaders[language]() : []
|
|
166
|
+
if (myToken !== langToken || editor.value !== view) return
|
|
167
|
+
view.dispatch({ effects: langCompartment.reconfigure(ext) })
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function destroyEditor() {
|
|
171
|
+
++editorToken
|
|
172
|
+
++langToken
|
|
173
|
+
editor.value?.destroy()
|
|
174
|
+
editor.value = null
|
|
175
|
+
resetPhantomCache()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function createEditor() {
|
|
179
|
+
destroyEditor()
|
|
180
|
+
const myToken = ++editorToken
|
|
181
|
+
const parent = containerRef.value
|
|
182
|
+
if (!parent) return
|
|
183
|
+
|
|
184
|
+
const view = new EditorView({
|
|
185
|
+
doc: model.value,
|
|
186
|
+
extensions: buildExtensions(),
|
|
187
|
+
parent,
|
|
88
188
|
dispatch(tr) {
|
|
89
|
-
|
|
90
|
-
if (tr.
|
|
91
|
-
|
|
189
|
+
view.update([tr])
|
|
190
|
+
if (tr.docChanged) {
|
|
191
|
+
model.value = tr.state.doc.toString()
|
|
92
192
|
}
|
|
93
|
-
model.value = tr.state.doc.toString()
|
|
94
193
|
}
|
|
95
194
|
})
|
|
195
|
+
|
|
196
|
+
if (myToken !== editorToken) {
|
|
197
|
+
view.destroy()
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
editor.value = view
|
|
202
|
+
syncPhantomLineNumbers()
|
|
203
|
+
applyLanguage()
|
|
96
204
|
}
|
|
97
205
|
|
|
98
|
-
watch(
|
|
206
|
+
watch(
|
|
207
|
+
containerRef,
|
|
208
|
+
(el) => {
|
|
209
|
+
if (el) createEditor()
|
|
210
|
+
else destroyEditor()
|
|
211
|
+
},
|
|
212
|
+
{ immediate: true, flush: 'post' }
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
watch(() => props.language, applyLanguage)
|
|
216
|
+
|
|
217
|
+
watch(
|
|
218
|
+
() => props.dark,
|
|
219
|
+
(dark) => {
|
|
220
|
+
editor.value?.dispatch({
|
|
221
|
+
effects: themeCompartment.reconfigure(dark ? oneDark : [])
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
watch(disabled, (v) => {
|
|
227
|
+
editor.value?.dispatch({
|
|
228
|
+
effects: editableCompartment.reconfigure(EditorView.editable.of(!v))
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
watch(readonly, (v) => {
|
|
233
|
+
editor.value?.dispatch({
|
|
234
|
+
effects: readOnlyCompartment.reconfigure(EditorState.readOnly.of(v))
|
|
235
|
+
})
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
watch(() => props.defaultLines, syncPhantomLineNumbers)
|
|
99
239
|
|
|
100
240
|
watch(
|
|
101
241
|
[() => props.modelValue, editor],
|
|
102
242
|
([v, e]) => {
|
|
103
243
|
if (!e) return
|
|
244
|
+
if (e.composing) return
|
|
245
|
+
const next = v ?? ''
|
|
246
|
+
if (e.state.doc.toString() === next) return
|
|
104
247
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const isSelectionOutOfRange = !e?.state.selection.ranges.every(
|
|
109
|
-
(range) => range.anchor < (v?.length ?? 0) && range.head < (v?.length ?? 0)
|
|
248
|
+
const valueLength = next.length
|
|
249
|
+
const inRange = e.state.selection.ranges.every(
|
|
250
|
+
(range) => range.anchor <= valueLength && range.head <= valueLength
|
|
110
251
|
)
|
|
111
|
-
e
|
|
112
|
-
changes: {
|
|
113
|
-
|
|
114
|
-
to: e?.state.doc.length,
|
|
115
|
-
insert: v
|
|
116
|
-
},
|
|
117
|
-
selection: isSelectionOutOfRange ? { anchor: 0, head: 0 } : e?.state.selection,
|
|
252
|
+
e.dispatch({
|
|
253
|
+
changes: { from: 0, to: e.state.doc.length, insert: next },
|
|
254
|
+
selection: inRange ? e.state.selection : { anchor: 0, head: 0 },
|
|
118
255
|
scrollIntoView: true
|
|
119
256
|
})
|
|
120
257
|
},
|
|
121
258
|
{ immediate: true }
|
|
122
259
|
)
|
|
123
260
|
|
|
124
|
-
onBeforeUnmount(
|
|
125
|
-
editor.value?.destroy()
|
|
126
|
-
})
|
|
261
|
+
onBeforeUnmount(destroyEditor)
|
|
127
262
|
</script>
|
|
@@ -2,32 +2,130 @@
|
|
|
2
2
|
@use 'pkg:@veltra/styles/functions' as fn;
|
|
3
3
|
@use 'pkg:@veltra/styles/vars';
|
|
4
4
|
|
|
5
|
-
// 方便拼接
|
|
6
5
|
$root-name: code-editor;
|
|
7
6
|
|
|
8
7
|
@include m.b($root-name) {
|
|
9
|
-
|
|
8
|
+
--u-code-editor-default-lines: 8;
|
|
9
|
+
--u-code-editor-line-height: 1.6;
|
|
10
|
+
--u-code-editor-font-size: 13px;
|
|
11
|
+
--u-code-editor-padding-block: 6px;
|
|
12
|
+
--u-code-editor-bg: #{fn.use-var(bg, color, top)};
|
|
13
|
+
--u-code-editor-fg: #{fn.use-var(text-color, main)};
|
|
14
|
+
--u-code-editor-border: #{fn.use-var(border, color)};
|
|
15
|
+
--u-code-editor-gutter-fg: #{fn.use-var(text-color, placeholder)};
|
|
16
|
+
--u-code-editor-active-bg: rgba(30, 136, 229, 0.06);
|
|
17
|
+
--u-code-editor-selection-bg: rgba(30, 136, 229, 0.18);
|
|
18
|
+
|
|
19
|
+
position: relative;
|
|
10
20
|
width: 100%;
|
|
11
|
-
|
|
12
|
-
|
|
21
|
+
max-height: 480px;
|
|
22
|
+
border-radius: fn.use-var(radius, default);
|
|
23
|
+
background-color: var(--u-code-editor-bg);
|
|
24
|
+
box-shadow: inset 0 0 0 1px var(--u-code-editor-border);
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
transition: box-shadow 0.2s ease;
|
|
13
27
|
|
|
14
|
-
&:hover
|
|
15
|
-
|
|
28
|
+
&:hover,
|
|
29
|
+
&:focus-within {
|
|
30
|
+
box-shadow: inset 0 0 0 1px fn.use-var(color, primary);
|
|
16
31
|
}
|
|
17
32
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
33
|
+
.cm-editor {
|
|
34
|
+
background: transparent;
|
|
35
|
+
color: var(--u-code-editor-fg);
|
|
36
|
+
font-size: var(--u-code-editor-font-size);
|
|
37
|
+
line-height: var(--u-code-editor-line-height);
|
|
38
|
+
font-family: ui-monospace, 'JetBrains Mono', 'Fira Code', 'SFMono-Regular', Menlo, Consolas,
|
|
39
|
+
'Liberation Mono', monospace;
|
|
40
|
+
outline: none;
|
|
41
|
+
|
|
42
|
+
&.cm-focused {
|
|
43
|
+
outline: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.cm-scroller {
|
|
47
|
+
font-family: inherit;
|
|
48
|
+
line-height: inherit;
|
|
49
|
+
overflow: visible;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.cm-content {
|
|
53
|
+
padding: var(--u-code-editor-padding-block) 0;
|
|
54
|
+
min-height: calc(
|
|
55
|
+
var(--u-code-editor-default-lines) * var(--u-code-editor-line-height) *
|
|
56
|
+
var(--u-code-editor-font-size) + var(--u-code-editor-padding-block) * 2
|
|
57
|
+
);
|
|
58
|
+
caret-color: fn.use-var(color, primary);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.cm-line {
|
|
62
|
+
padding: 0 8px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.cm-gutters {
|
|
66
|
+
background-color: transparent;
|
|
67
|
+
color: var(--u-code-editor-gutter-fg);
|
|
68
|
+
border-right: 1px solid var(--u-code-editor-border);
|
|
69
|
+
user-select: none;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.cm-gutterElement-phantom {
|
|
73
|
+
color: var(--u-code-editor-gutter-fg);
|
|
74
|
+
opacity: 0.45;
|
|
75
|
+
padding: 0 3px 0 5px;
|
|
76
|
+
text-align: right;
|
|
77
|
+
white-space: nowrap;
|
|
78
|
+
min-width: 1ch;
|
|
79
|
+
box-sizing: border-box;
|
|
80
|
+
pointer-events: none;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.cm-activeLineGutter,
|
|
84
|
+
.cm-activeLine {
|
|
85
|
+
background-color: var(--u-code-editor-active-bg);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.cm-selectionBackground,
|
|
89
|
+
::selection {
|
|
90
|
+
background-color: var(--u-code-editor-selection-bg);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.cm-cursor {
|
|
94
|
+
border-left-color: fn.use-var(color, primary);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@include m.is(dark) {
|
|
99
|
+
--u-code-editor-bg: #282c34;
|
|
100
|
+
--u-code-editor-fg: #abb2bf;
|
|
101
|
+
--u-code-editor-border: rgba(255, 255, 255, 0.08);
|
|
102
|
+
--u-code-editor-gutter-fg: #5c6370;
|
|
103
|
+
--u-code-editor-active-bg: rgba(255, 255, 255, 0.04);
|
|
104
|
+
--u-code-editor-selection-bg: rgba(99, 162, 255, 0.25);
|
|
22
105
|
}
|
|
23
106
|
|
|
24
107
|
@include m.is(disabled) {
|
|
25
108
|
background-color: fn.use-var(color, disabled);
|
|
26
|
-
box-shadow: inset 0 0
|
|
109
|
+
box-shadow: inset 0 0 0 1px var(--u-code-editor-border);
|
|
27
110
|
cursor: not-allowed;
|
|
111
|
+
|
|
112
|
+
&:hover,
|
|
113
|
+
&:focus-within {
|
|
114
|
+
box-shadow: inset 0 0 0 1px var(--u-code-editor-border);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.cm-editor,
|
|
118
|
+
.cm-content {
|
|
119
|
+
cursor: not-allowed;
|
|
120
|
+
}
|
|
28
121
|
}
|
|
29
122
|
|
|
30
123
|
@include m.is(readonly) {
|
|
31
|
-
box-shadow:
|
|
124
|
+
box-shadow: inset 0 0 0 1px var(--u-code-editor-border);
|
|
125
|
+
|
|
126
|
+
&:hover,
|
|
127
|
+
&:focus-within {
|
|
128
|
+
box-shadow: inset 0 0 0 1px var(--u-code-editor-border);
|
|
129
|
+
}
|
|
32
130
|
}
|
|
33
131
|
}
|
|
@@ -193,12 +193,16 @@ const translated = {
|
|
|
193
193
|
y: 0
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
/**
|
|
196
|
+
/**
|
|
197
|
+
* 更新弹框位置
|
|
198
|
+
* 使用独立 `translate` 而非整体 `transform`, 与 spring 动画的 `scale` 解耦,
|
|
199
|
+
* 避免动画期间整体 transform 被关键帧覆盖、动画结束后又"啪"地跳回内联位置
|
|
200
|
+
*/
|
|
197
201
|
const updateDialogTransform = (x: number, y: number) => {
|
|
198
202
|
const dom = dialogRef.value
|
|
199
203
|
if (!dom) return
|
|
200
204
|
setStyles(dom, {
|
|
201
|
-
|
|
205
|
+
translate: `${x}px ${y}px`
|
|
202
206
|
})
|
|
203
207
|
}
|
|
204
208
|
|
|
@@ -23,7 +23,6 @@ $root-name: dialog;
|
|
|
23
23
|
@include m.b($root-name) {
|
|
24
24
|
pointer-events: auto;
|
|
25
25
|
border-radius: fn.use-var(radius, large);
|
|
26
|
-
will-change: transform, opacity;
|
|
27
26
|
display: flex;
|
|
28
27
|
flex-direction: column;
|
|
29
28
|
max-height: 90vh;
|
|
@@ -119,12 +118,13 @@ $root-name: dialog;
|
|
|
119
118
|
}
|
|
120
119
|
}
|
|
121
120
|
|
|
122
|
-
//
|
|
121
|
+
// 弹框最大化动画进入后的类
|
|
123
122
|
.#{$root-name}-maximize-enter-to {
|
|
124
123
|
width: 100vw !important;
|
|
125
124
|
height: 100vh !important;
|
|
126
125
|
max-height: 100vh;
|
|
127
|
-
|
|
126
|
+
// 重置拖拽偏移; 不动 scale 以免与 spring leave 的 scale: 0 冲突
|
|
127
|
+
translate: 0 0 !important;
|
|
128
128
|
@include m.bem($root-name, header) {
|
|
129
129
|
&:active {
|
|
130
130
|
cursor: default;
|
|
@@ -135,7 +135,7 @@ $root-name: dialog;
|
|
|
135
135
|
// 弹框最大化动画激活时的类
|
|
136
136
|
.#{$root-name}-maximize-enter-active,
|
|
137
137
|
.#{$root-name}-maximize-leave-active {
|
|
138
|
-
transition-property: width, height,
|
|
138
|
+
transition-property: width, height, translate;
|
|
139
139
|
transition-duration: 0.25s;
|
|
140
140
|
}
|
|
141
141
|
|