adtec-core-package 3.0.4 → 3.0.5
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/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/package.json +21 -4
- package/prebuilt/umo-editor/umo-editor.css +1 -1
- package/prebuilt/umo-editor/umo-editor.js +13739 -15052
- package/prebuilt/umo-editor/umo-editor.js.map +1 -1
- package/src/components/RichTextEditor/RichTextEditor.vue +1 -1
- package/src/components/editor-main/src/components/index.vue +2 -1
- package/src/components/editor-main/src/components/menus/button.vue +30 -3
- package/src/components/editor-main/src/components/menus/toolbar/base/color.vue +12 -5
- package/src/components/editor-main/src/components/menus/toolbar/base/font-family.vue +6 -4
- package/src/components/editor-main/src/components/menus/toolbar/base/font-size.vue +42 -25
- package/src/components/editor-main/src/components/menus/toolbar/base/heading.vue +6 -8
- package/src/components/editor-main/src/composables/toolbarSelection.js +233 -0
- package/src/components/editor-main/src/locales/bo.json +2 -2
- package/src/components/editor-main/src/locales/en-US.json +2 -2
- package/src/components/editor-main/src/locales/it-IT.json +2 -2
- package/src/components/editor-main/src/locales/ru-RU.json +2 -2
- package/src/components/editor-main/src/locales/zh-CN.json +2 -2
- package/src/components/editor-main/src/utils/editor-scroll.js +39 -15
- package/src/components/vxeGrid/index.vue +11 -4
- package/src/stores/dictStore.ts +72 -5
|
@@ -81,7 +81,7 @@ import {
|
|
|
81
81
|
} from '../utils/history-record'
|
|
82
82
|
import { getOpitons } from '../utils/options'
|
|
83
83
|
import { getSelectionNode, getSelectionText } from '../utils/selection'
|
|
84
|
-
import { syncCacheOnEditorSelectionUpdate } from '../composables/toolbarSelection.js'
|
|
84
|
+
import { syncCacheOnEditorSelectionUpdate, bumpToolbarEditorState } from '../composables/toolbarSelection.js'
|
|
85
85
|
import { shortId } from '../utils/short-id'
|
|
86
86
|
import { getCurrentInstance } from 'vue'
|
|
87
87
|
|
|
@@ -332,6 +332,7 @@ watch(
|
|
|
332
332
|
editor.value.on('update', ({ editor }) => {
|
|
333
333
|
emits('changed', { editor })
|
|
334
334
|
contentUpdated.value = true
|
|
335
|
+
bumpToolbarEditorState()
|
|
335
336
|
})
|
|
336
337
|
editor.value.on('selectionUpdate', ({ editor }) => {
|
|
337
338
|
syncCacheOnEditorSelectionUpdate(editor)
|
|
@@ -205,6 +205,7 @@
|
|
|
205
205
|
:disabled="
|
|
206
206
|
!forceEnabled && (disabled || editor?.isEditable === false)
|
|
207
207
|
"
|
|
208
|
+
@mousedown.prevent="cacheToolbarSelection"
|
|
208
209
|
>
|
|
209
210
|
<div class="umo-button-content" @click="menuClick">
|
|
210
211
|
<slot />
|
|
@@ -341,7 +342,7 @@ import { isString } from '@tool-belt/type-predicates'
|
|
|
341
342
|
import { getShortcut } from '../../utils/shortcut'
|
|
342
343
|
|
|
343
344
|
|
|
344
|
-
import { useAttrs, inject, watch, ref } from 'vue';
|
|
345
|
+
import { useAttrs, inject, watch, ref, onBeforeUnmount } from 'vue';
|
|
345
346
|
import { onClickOutside } from '@vueuse/core';
|
|
346
347
|
const { selectVisible } = useSelect()
|
|
347
348
|
|
|
@@ -442,24 +443,50 @@ const cacheToolbarSelection = () => {
|
|
|
442
443
|
}
|
|
443
444
|
}
|
|
444
445
|
|
|
446
|
+
/** 打开下拉时同一次点击可能落在首项上(幽灵 click),短暂忽略 @change */
|
|
447
|
+
let suppressSelectChange = false
|
|
448
|
+
let suppressSelectChangeTimer = null
|
|
449
|
+
|
|
450
|
+
const clearSuppressSelectChange = () => {
|
|
451
|
+
suppressSelectChange = false
|
|
452
|
+
if (suppressSelectChangeTimer != null) {
|
|
453
|
+
clearTimeout(suppressSelectChangeTimer)
|
|
454
|
+
suppressSelectChangeTimer = null
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const scheduleSuppressSelectChange = () => {
|
|
459
|
+
clearSuppressSelectChange()
|
|
460
|
+
suppressSelectChange = true
|
|
461
|
+
suppressSelectChangeTimer = setTimeout(clearSuppressSelectChange, 250)
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
onBeforeUnmount(clearSuppressSelectChange)
|
|
465
|
+
|
|
445
466
|
const onSelectTriggerPress = () => {
|
|
446
467
|
beginSelectMenuInteraction(editor?.value)
|
|
468
|
+
scheduleSuppressSelectChange()
|
|
447
469
|
}
|
|
448
470
|
|
|
449
471
|
const selectPopupVisibleChange = (visible) => {
|
|
450
472
|
popupVisileChange(visible)
|
|
451
473
|
if (visible) {
|
|
452
474
|
beginSelectMenuInteraction(editor?.value)
|
|
475
|
+
scheduleSuppressSelectChange()
|
|
453
476
|
} else {
|
|
454
477
|
endSelectMenuInteraction()
|
|
478
|
+
clearSuppressSelectChange()
|
|
455
479
|
}
|
|
456
480
|
}
|
|
457
481
|
|
|
458
|
-
const selectMenuClick = (...
|
|
482
|
+
const selectMenuClick = (value, context, ...rest) => {
|
|
483
|
+
if (suppressSelectChange || context?.trigger === 'default') {
|
|
484
|
+
return
|
|
485
|
+
}
|
|
459
486
|
if (editor?.value?.state.selection.empty) {
|
|
460
487
|
restorePendingSelectMenuRange(editor.value)
|
|
461
488
|
}
|
|
462
|
-
menuClick(...
|
|
489
|
+
menuClick(value, context, ...rest)
|
|
463
490
|
}
|
|
464
491
|
|
|
465
492
|
const tooltipVisible = _ref(false)
|
|
@@ -28,6 +28,10 @@ import { ref as _ref } from 'vue';
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
import { inject } from 'vue';
|
|
31
|
+
import {
|
|
32
|
+
FOCUS_WITHOUT_SCROLL,
|
|
33
|
+
runPreservingScroll,
|
|
34
|
+
} from '../../../../utils/editor-scroll.js';
|
|
31
35
|
const props = defineProps({
|
|
32
36
|
text: {
|
|
33
37
|
type: String,
|
|
@@ -46,6 +50,7 @@ const emits = defineEmits(['change'])
|
|
|
46
50
|
|
|
47
51
|
const { popupVisible, togglePopup } = usePopup()
|
|
48
52
|
const editor = inject('editor')
|
|
53
|
+
const container = inject('container')
|
|
49
54
|
|
|
50
55
|
let currentColor = _ref()
|
|
51
56
|
const colorChange = (color) => {
|
|
@@ -57,11 +62,13 @@ const colorChange = (color) => {
|
|
|
57
62
|
return
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
runPreservingScroll(container, () => {
|
|
66
|
+
if (color === '') {
|
|
67
|
+
editor.value?.chain().focus(undefined, FOCUS_WITHOUT_SCROLL).unsetColor().run()
|
|
68
|
+
} else {
|
|
69
|
+
editor.value?.chain().focus(undefined, FOCUS_WITHOUT_SCROLL).setColor(color).run()
|
|
70
|
+
}
|
|
71
|
+
})
|
|
65
72
|
}
|
|
66
73
|
</script>
|
|
67
74
|
|
|
@@ -57,6 +57,10 @@
|
|
|
57
57
|
|
|
58
58
|
<script setup>
|
|
59
59
|
import { t } from '../../../../composables/i18n.js';
|
|
60
|
+
import {
|
|
61
|
+
getToolbarFontFamily,
|
|
62
|
+
toolbarEditorTick,
|
|
63
|
+
} from '../../../../composables/toolbarSelection.js';
|
|
60
64
|
|
|
61
65
|
import { useState } from '../../../../composables/state.js';
|
|
62
66
|
import { ref as _ref } from 'vue';
|
|
@@ -81,13 +85,11 @@ let autoDownloadRunning = _ref(false)
|
|
|
81
85
|
let restoringDownloadedFonts = _ref(true)
|
|
82
86
|
|
|
83
87
|
const selectedFont = computed(() => {
|
|
88
|
+
toolbarEditorTick.value
|
|
84
89
|
if (!editor.value || typeWriterIsRunning.value) {
|
|
85
90
|
return null
|
|
86
91
|
}
|
|
87
|
-
|
|
88
|
-
return null
|
|
89
|
-
}
|
|
90
|
-
return editor.value.getAttributes('textStyle').fontFamily.replace(/"/g, '')
|
|
92
|
+
return getToolbarFontFamily(editor.value)
|
|
91
93
|
})
|
|
92
94
|
|
|
93
95
|
const ensureRecentState = () => {
|
|
@@ -6,11 +6,7 @@
|
|
|
6
6
|
hide-text
|
|
7
7
|
style="width: 80px"
|
|
8
8
|
:select-options="fontSizes"
|
|
9
|
-
:select-value="
|
|
10
|
-
typeWriterIsRunning
|
|
11
|
-
? null
|
|
12
|
-
: editor?.getAttributes('textStyle').fontSize || '14px'
|
|
13
|
-
"
|
|
9
|
+
:select-value="selectFontSize"
|
|
14
10
|
v-bind="$attrs"
|
|
15
11
|
:placeholder="t('base.fontSize.text')"
|
|
16
12
|
filterable
|
|
@@ -40,10 +36,16 @@
|
|
|
40
36
|
import { t } from '../../../../composables/i18n.js';
|
|
41
37
|
import {
|
|
42
38
|
applyCachedTextSelection,
|
|
39
|
+
getToolbarFontSize,
|
|
43
40
|
refreshCachedTextSelection,
|
|
44
41
|
restorePendingSelectMenuRange,
|
|
42
|
+
toolbarEditorTick,
|
|
45
43
|
} from '../../../../composables/toolbarSelection.js';
|
|
46
|
-
import { inject } from 'vue';
|
|
44
|
+
import { computed, inject } from 'vue';
|
|
45
|
+
import {
|
|
46
|
+
FOCUS_WITHOUT_SCROLL,
|
|
47
|
+
runPreservingScroll,
|
|
48
|
+
} from '../../../../utils/editor-scroll.js';
|
|
47
49
|
|
|
48
50
|
const props = defineProps({
|
|
49
51
|
select: {
|
|
@@ -53,6 +55,7 @@ const props = defineProps({
|
|
|
53
55
|
})
|
|
54
56
|
|
|
55
57
|
const editor = inject('editor')
|
|
58
|
+
const container = inject('container')
|
|
56
59
|
const options = inject('options')
|
|
57
60
|
const typeWriterIsRunning = inject('typeWriterIsRunning')
|
|
58
61
|
|
|
@@ -60,6 +63,12 @@ const disableMenu = (name) => {
|
|
|
60
63
|
return options.value.disableExtensions.includes(name)
|
|
61
64
|
}
|
|
62
65
|
|
|
66
|
+
const selectFontSize = computed(() => {
|
|
67
|
+
toolbarEditorTick.value
|
|
68
|
+
if (typeWriterIsRunning.value) return null
|
|
69
|
+
return getToolbarFontSize(editor.value)
|
|
70
|
+
})
|
|
71
|
+
|
|
63
72
|
const fontSizes = [
|
|
64
73
|
{ label: t('base.fontSize.default'), value: '14px', order: 4 },
|
|
65
74
|
{ label: t('base.fontSize.42pt'), value: '42pt', order: 20 }, // 56
|
|
@@ -94,36 +103,44 @@ const fontSizes = [
|
|
|
94
103
|
{ label: '96', value: '96px', order: 22 },
|
|
95
104
|
]
|
|
96
105
|
|
|
97
|
-
//
|
|
106
|
+
// 设置字体大小(连续改字号时保持选区,且不触发外层页面滚动)
|
|
98
107
|
const applyFontSize = (fontSize) => {
|
|
99
108
|
const ed = editor.value
|
|
100
109
|
if (!ed) return
|
|
101
110
|
|
|
102
|
-
|
|
103
|
-
|
|
111
|
+
runPreservingScroll(container, () => {
|
|
112
|
+
const { empty, from, to } = ed.state.selection
|
|
113
|
+
let chain = ed.chain().focus(undefined, FOCUS_WITHOUT_SCROLL)
|
|
104
114
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return
|
|
112
|
-
} else {
|
|
113
|
-
const sel = ed.state.selection
|
|
114
|
-
if (!sel.empty && sel.from !== sel.to) {
|
|
115
|
-
chain = ed.chain().focus().setTextSelection({ from: sel.from, to: sel.to })
|
|
116
|
-
} else {
|
|
115
|
+
if (!empty && from !== to) {
|
|
116
|
+
chain = chain.setTextSelection({ from, to })
|
|
117
|
+
} else if (
|
|
118
|
+
!restorePendingSelectMenuRange(ed) &&
|
|
119
|
+
!applyCachedTextSelection(ed)
|
|
120
|
+
) {
|
|
117
121
|
return
|
|
122
|
+
} else {
|
|
123
|
+
const sel = ed.state.selection
|
|
124
|
+
if (!sel.empty && sel.from !== sel.to) {
|
|
125
|
+
chain = ed
|
|
126
|
+
.chain()
|
|
127
|
+
.focus(undefined, FOCUS_WITHOUT_SCROLL)
|
|
128
|
+
.setTextSelection({ from: sel.from, to: sel.to })
|
|
129
|
+
} else {
|
|
130
|
+
return
|
|
131
|
+
}
|
|
118
132
|
}
|
|
119
|
-
}
|
|
120
133
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
134
|
+
if (chain.setFontSize(fontSize).run()) {
|
|
135
|
+
refreshCachedTextSelection(ed)
|
|
136
|
+
}
|
|
137
|
+
})
|
|
124
138
|
}
|
|
125
139
|
|
|
126
140
|
const setFontSize = (fontSize) => {
|
|
141
|
+
if (!fontSize) return
|
|
142
|
+
const current = getToolbarFontSize(editor.value)
|
|
143
|
+
if (current === fontSize) return
|
|
127
144
|
applyFontSize(fontSize)
|
|
128
145
|
}
|
|
129
146
|
|
|
@@ -84,8 +84,8 @@ import { inject, ref, computed } from 'vue';
|
|
|
84
84
|
import { onClickOutside } from '@vueuse/core';
|
|
85
85
|
import {
|
|
86
86
|
applyHeadingWithoutScroll,
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
FOCUS_WITHOUT_SCROLL,
|
|
88
|
+
runPreservingScroll,
|
|
89
89
|
} from '../../../../utils/editor-scroll.js';
|
|
90
90
|
const { popupVisible } = usePopup()
|
|
91
91
|
const container = inject('container')
|
|
@@ -133,16 +133,14 @@ const currentValue = computed(() => {
|
|
|
133
133
|
return ''
|
|
134
134
|
})
|
|
135
135
|
|
|
136
|
-
const FOCUS_WITHOUT_SCROLL = { scrollIntoView: false }
|
|
137
|
-
|
|
138
136
|
const setHeading = (value) => {
|
|
139
137
|
const ed = editor.value
|
|
140
138
|
if (!ed) return
|
|
141
139
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
140
|
+
runPreservingScroll(container, () => {
|
|
141
|
+
ed.commands.focus(undefined, FOCUS_WITHOUT_SCROLL)
|
|
142
|
+
applyHeadingWithoutScroll(ed, value)
|
|
143
|
+
})
|
|
146
144
|
popupVisible.value = false
|
|
147
145
|
}
|
|
148
146
|
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
|
|
1
3
|
/** 工具栏 t-select 等抢焦点时缓存的非空文本选区 */
|
|
2
4
|
let cachedRange = null
|
|
3
5
|
/** 当前 t-select 弹层会话内快照(打开下拉时锁定,避免误恢复旧选区) */
|
|
4
6
|
let pendingSelectRange = null
|
|
5
7
|
|
|
8
|
+
/** ProseMirror 选区/mark 变化时递增,供 Vue computed 订阅 */
|
|
9
|
+
export const toolbarEditorTick = ref(0)
|
|
10
|
+
|
|
11
|
+
export function bumpToolbarEditorState() {
|
|
12
|
+
toolbarEditorTick.value += 1
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
export function cacheEditorTextSelection(editor) {
|
|
7
16
|
if (!editor?.state) return
|
|
8
17
|
const { from, to, empty } = editor.state.selection
|
|
@@ -17,11 +26,13 @@ export function syncCacheOnEditorSelectionUpdate(editor) {
|
|
|
17
26
|
if (!empty && from !== to) {
|
|
18
27
|
cachedRange = { from, to }
|
|
19
28
|
}
|
|
29
|
+
bumpToolbarEditorState()
|
|
20
30
|
}
|
|
21
31
|
|
|
22
32
|
export function beginSelectMenuInteraction(editor) {
|
|
23
33
|
if (!editor?.state) {
|
|
24
34
|
pendingSelectRange = cachedRange ? { ...cachedRange } : null
|
|
35
|
+
bumpToolbarEditorState()
|
|
25
36
|
return
|
|
26
37
|
}
|
|
27
38
|
const { from, to, empty } = editor.state.selection
|
|
@@ -33,10 +44,12 @@ export function beginSelectMenuInteraction(editor) {
|
|
|
33
44
|
} else {
|
|
34
45
|
pendingSelectRange = null
|
|
35
46
|
}
|
|
47
|
+
bumpToolbarEditorState()
|
|
36
48
|
}
|
|
37
49
|
|
|
38
50
|
export function endSelectMenuInteraction() {
|
|
39
51
|
pendingSelectRange = null
|
|
52
|
+
bumpToolbarEditorState()
|
|
40
53
|
}
|
|
41
54
|
|
|
42
55
|
function applyTextRange(editor, range) {
|
|
@@ -65,4 +78,224 @@ export function refreshCachedTextSelection(editor) {
|
|
|
65
78
|
if (!empty && from !== to) {
|
|
66
79
|
cachedRange = { from, to }
|
|
67
80
|
}
|
|
81
|
+
bumpToolbarEditorState()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function clampRange(editor, range) {
|
|
85
|
+
if (!editor?.state || !range) return null
|
|
86
|
+
const docSize = editor.state.doc.content.size
|
|
87
|
+
const from = Math.max(0, Math.min(range.from, docSize))
|
|
88
|
+
const to = Math.max(from, Math.min(range.to, docSize))
|
|
89
|
+
if (to <= from) return null
|
|
90
|
+
return { from, to }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Word 中文字号 pt/px 对照(与 richTextHtmlUtils 一致) */
|
|
94
|
+
const CHINESE_FONT_SIZE_PT_PX = [
|
|
95
|
+
{ pt: 42, px: 49 },
|
|
96
|
+
{ pt: 36, px: 42 },
|
|
97
|
+
{ pt: 26, px: 30 },
|
|
98
|
+
{ pt: 24, px: 28 },
|
|
99
|
+
{ pt: 22, px: 26 },
|
|
100
|
+
{ pt: 18, px: 21 },
|
|
101
|
+
{ pt: 16, px: 19 },
|
|
102
|
+
{ pt: 15, px: 18 },
|
|
103
|
+
{ pt: 14, px: 16 },
|
|
104
|
+
{ pt: 12, px: 14 },
|
|
105
|
+
{ pt: 10.5, px: 12 },
|
|
106
|
+
{ pt: 9, px: 11 },
|
|
107
|
+
{ pt: 7.5, px: 9 },
|
|
108
|
+
{ pt: 6.5, px: 8 },
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
function normalizeFontSizeValue(raw) {
|
|
112
|
+
if (!raw) return null
|
|
113
|
+
const value = String(raw).trim()
|
|
114
|
+
const pxMatch = value.match(/^(\d+(?:\.\d+)?)px$/i)
|
|
115
|
+
if (pxMatch) {
|
|
116
|
+
return `${Math.round(parseFloat(pxMatch[1]))}px`
|
|
117
|
+
}
|
|
118
|
+
const ptMatch = value.match(/^(\d+(?:\.\d+)?)pt$/i)
|
|
119
|
+
if (ptMatch) {
|
|
120
|
+
return `${parseFloat(ptMatch[1])}pt`
|
|
121
|
+
}
|
|
122
|
+
return value
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** 将 DOM/浏览器读到的 px 映射回 pt 选项值,供 t-select 显示中文标签 */
|
|
126
|
+
function resolveFontSizeForToolbarDisplay(raw) {
|
|
127
|
+
const normalized = normalizeFontSizeValue(raw)
|
|
128
|
+
if (!normalized) return null
|
|
129
|
+
if (/pt$/i.test(normalized)) return normalized
|
|
130
|
+
|
|
131
|
+
const pxMatch = normalized.match(/^(\d+(?:\.\d+)?)px$/i)
|
|
132
|
+
if (!pxMatch) return normalized
|
|
133
|
+
|
|
134
|
+
const px = Math.round(parseFloat(pxMatch[1]))
|
|
135
|
+
// 14px 为编辑器「默认」,勿映射为 12pt(小四)
|
|
136
|
+
if (px === 14) return '14px'
|
|
137
|
+
|
|
138
|
+
const byWordPx = CHINESE_FONT_SIZE_PT_PX.find((entry) => entry.px === px)
|
|
139
|
+
if (byWordPx) return `${byWordPx.pt}pt`
|
|
140
|
+
|
|
141
|
+
// 浏览器按 CSS 换算的 px(如 36pt → 48px)
|
|
142
|
+
const cssPt = (px * 72) / 96
|
|
143
|
+
const byCssPt = CHINESE_FONT_SIZE_PT_PX.find(
|
|
144
|
+
(entry) => Math.abs(entry.pt - cssPt) < 0.6,
|
|
145
|
+
)
|
|
146
|
+
if (byCssPt) return `${byCssPt.pt}pt`
|
|
147
|
+
|
|
148
|
+
return normalized
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function readTextStyleMarkAtPos(editor, pos, attr) {
|
|
152
|
+
const $pos = editor.state.doc.resolve(pos)
|
|
153
|
+
const activeMarks = $pos.marks()
|
|
154
|
+
const storedMarks = editor.state.storedMarks ?? []
|
|
155
|
+
const marks = activeMarks.length ? activeMarks : storedMarks
|
|
156
|
+
const textStyle = marks.find((mark) => mark.type.name === 'textStyle')
|
|
157
|
+
return textStyle?.attrs?.[attr] ?? null
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function readFontSizeFromDom(editor, pos) {
|
|
161
|
+
if (typeof window === 'undefined') return null
|
|
162
|
+
const view = editor.view
|
|
163
|
+
if (!view?.dom) return null
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const domPos = view.domAtPos(pos)
|
|
167
|
+
let node = domPos.node
|
|
168
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
169
|
+
node = node.parentElement
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
while (node && node !== view.dom) {
|
|
173
|
+
const inlineSize = node.style?.fontSize
|
|
174
|
+
const computedSize =
|
|
175
|
+
inlineSize && inlineSize !== 'inherit'
|
|
176
|
+
? inlineSize
|
|
177
|
+
: window.getComputedStyle(node).fontSize
|
|
178
|
+
const normalized = normalizeFontSizeValue(computedSize)
|
|
179
|
+
if (normalized) {
|
|
180
|
+
return normalized
|
|
181
|
+
}
|
|
182
|
+
node = node.parentElement
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
return null
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return null
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function readFontSizeAtRange(editor, range) {
|
|
192
|
+
const clamped = clampRange(editor, range)
|
|
193
|
+
if (!clamped) return null
|
|
194
|
+
|
|
195
|
+
const { from, to } = clamped
|
|
196
|
+
let fontSize = null
|
|
197
|
+
|
|
198
|
+
editor.state.doc.nodesBetween(from, to, (node, pos) => {
|
|
199
|
+
if (fontSize || !node.isText) return
|
|
200
|
+
const mark = node.marks.find(
|
|
201
|
+
(item) => item.type.name === 'textStyle' && item.attrs?.fontSize,
|
|
202
|
+
)
|
|
203
|
+
if (mark) {
|
|
204
|
+
fontSize = mark.attrs.fontSize
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
if (!fontSize) {
|
|
209
|
+
const readPos = Math.min(from + 1, to - 1, editor.state.doc.content.size - 1)
|
|
210
|
+
fontSize = readTextStyleMarkAtPos(editor, Math.max(from, readPos), 'fontSize')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!fontSize) {
|
|
214
|
+
fontSize = readFontSizeFromDom(editor, from)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return resolveFontSizeForToolbarDisplay(fontSize)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function readFontFamilyAtRange(editor, range) {
|
|
221
|
+
const clamped = clampRange(editor, range)
|
|
222
|
+
if (!clamped) return null
|
|
223
|
+
|
|
224
|
+
const { from, to } = clamped
|
|
225
|
+
let fontFamily = null
|
|
226
|
+
|
|
227
|
+
editor.state.doc.nodesBetween(from, to, (node) => {
|
|
228
|
+
if (fontFamily || !node.isText) return
|
|
229
|
+
const mark = node.marks.find(
|
|
230
|
+
(item) => item.type.name === 'textStyle' && item.attrs?.fontFamily,
|
|
231
|
+
)
|
|
232
|
+
if (mark) {
|
|
233
|
+
fontFamily = mark.attrs.fontFamily
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
if (!fontFamily) {
|
|
238
|
+
const readPos = Math.min(from + 1, to - 1, editor.state.doc.content.size - 1)
|
|
239
|
+
fontFamily = readTextStyleMarkAtPos(
|
|
240
|
+
editor,
|
|
241
|
+
Math.max(from, readPos),
|
|
242
|
+
'fontFamily',
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return fontFamily ? fontFamily.replace(/"/g, '') : null
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function getActiveTextRange(editor) {
|
|
250
|
+
if (!editor?.state) return null
|
|
251
|
+
const { from, to, empty } = editor.state.selection
|
|
252
|
+
if (!empty && from !== to) {
|
|
253
|
+
return { from, to }
|
|
254
|
+
}
|
|
255
|
+
return pendingSelectRange ?? cachedRange
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** t-select 抢焦点导致选区折叠时,仍从缓存选区读取字号供下拉框展示 */
|
|
259
|
+
export function getToolbarFontSize(editor) {
|
|
260
|
+
if (!editor?.state) return null
|
|
261
|
+
|
|
262
|
+
const { from, to, empty } = editor.state.selection
|
|
263
|
+
if (!empty && from !== to) {
|
|
264
|
+
return (
|
|
265
|
+
readFontSizeAtRange(editor, { from, to }) ??
|
|
266
|
+
resolveFontSizeForToolbarDisplay(
|
|
267
|
+
editor.getAttributes('textStyle').fontSize,
|
|
268
|
+
) ??
|
|
269
|
+
resolveFontSizeForToolbarDisplay(readFontSizeFromDom(editor, from))
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const range = getActiveTextRange(editor)
|
|
274
|
+
return (
|
|
275
|
+
readFontSizeAtRange(editor, range) ??
|
|
276
|
+
resolveFontSizeForToolbarDisplay(
|
|
277
|
+
editor.getAttributes('textStyle').fontSize,
|
|
278
|
+
) ??
|
|
279
|
+
(range
|
|
280
|
+
? resolveFontSizeForToolbarDisplay(readFontSizeFromDom(editor, range.from))
|
|
281
|
+
: null)
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** t-select 抢焦点导致选区折叠时,仍从缓存选区读取字体供下拉框展示 */
|
|
286
|
+
export function getToolbarFontFamily(editor) {
|
|
287
|
+
if (!editor?.state) return null
|
|
288
|
+
|
|
289
|
+
const { from, to, empty } = editor.state.selection
|
|
290
|
+
if (!empty && from !== to) {
|
|
291
|
+
return readFontFamilyAtRange(editor, { from, to })
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const range = getActiveTextRange(editor)
|
|
295
|
+
return (
|
|
296
|
+
readFontFamilyAtRange(editor, range) ??
|
|
297
|
+
(editor.getAttributes('textStyle').fontFamily
|
|
298
|
+
? editor.getAttributes('textStyle').fontFamily.replace(/"/g, '')
|
|
299
|
+
: null)
|
|
300
|
+
)
|
|
68
301
|
}
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"12pt": "Sotto livello quattro",
|
|
52
52
|
"10_5pt": "Quinto livello",
|
|
53
53
|
"9pt": "Sotto livello cinque",
|
|
54
|
-
"7_5pt": "
|
|
55
|
-
"6_5pt": "
|
|
54
|
+
"7_5pt": "Sesto livello",
|
|
55
|
+
"6_5pt": "Sotto livello sei"
|
|
56
56
|
},
|
|
57
57
|
"formatPainter": {
|
|
58
58
|
"text": "Copia formato",
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"12pt": "Под-четвертый уровень",
|
|
58
58
|
"10_5pt": "Пятый уровень",
|
|
59
59
|
"9pt": "Под-пятый уровень",
|
|
60
|
-
"7_5pt": "
|
|
61
|
-
"6_5pt": "
|
|
60
|
+
"7_5pt": "Шестой уровень",
|
|
61
|
+
"6_5pt": "Под-шестой уровень"
|
|
62
62
|
},
|
|
63
63
|
"formatPainter": {
|
|
64
64
|
"text": "Формат по образцу",
|