vue-editify 0.1.47 → 0.1.48

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.
@@ -0,0 +1,238 @@
1
+ import { common as DapCommon, color as DapColor } from 'dap-util'
2
+ import { PluginType } from '../../core/tool'
3
+ import { ComponentInternalInstance, h } from 'vue'
4
+ import { AlexEditor, AlexElement, AlexElementsRangeType } from 'alex-editor'
5
+ import Icon from '../../components/icon/icon.vue'
6
+ import { elementToParagraph, hasPreInRange, hasTableInRange } from '../../core/function'
7
+ import { hasPanelInRange } from '../panel'
8
+
9
+ export type InfoBlockOptionsType = {
10
+ //排序
11
+ sequence?: number
12
+ //工具提示内容
13
+ title?: string
14
+ //按钮是否显示左侧边框
15
+ leftBorder?: boolean
16
+ //按钮是否显示右侧边框
17
+ rightBorder?: boolean
18
+ //按钮是否禁用
19
+ disabled?: boolean
20
+ }
21
+
22
+ /**
23
+ * 是否信息元素
24
+ * @param el
25
+ * @returns
26
+ */
27
+ export const isInfoBlock = (el: AlexElement) => {
28
+ return el.parsedom == 'div' && el.hasMarks() && el.marks!['data-editify-info']
29
+ }
30
+
31
+ /**
32
+ * 判断某个元素是否在信息元素内
33
+ * @param el
34
+ * @returns
35
+ */
36
+ export const isUnderInfoBlock = (el: AlexElement): boolean => {
37
+ if (isInfoBlock(el)) {
38
+ return true
39
+ }
40
+ if (el.parent) {
41
+ return isUnderInfoBlock(el.parent)
42
+ }
43
+ return false
44
+ }
45
+
46
+ /**
47
+ * 根据某个元素获取所在的信息元素,如果不在信息元素内则返回null
48
+ * @param el
49
+ * @returns
50
+ */
51
+ export const getInfoBlockElement = (el: AlexElement): AlexElement | null => {
52
+ if (isInfoBlock(el)) {
53
+ return el
54
+ }
55
+ if (el.parent) {
56
+ return getInfoBlockElement(el.parent)
57
+ }
58
+ return null
59
+ }
60
+
61
+ /**
62
+ * 选区是否含有信息元素
63
+ * @param editor
64
+ * @param dataRangeCaches
65
+ * @returns
66
+ */
67
+ export const hasInfoBlockInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
68
+ if (!editor.range) {
69
+ return false
70
+ }
71
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
72
+ return isUnderInfoBlock(editor.range.anchor.element)
73
+ }
74
+ return dataRangeCaches.flatList.some(item => {
75
+ return isUnderInfoBlock(item.element)
76
+ })
77
+ }
78
+
79
+ /**
80
+ * 选区是否都在信息块内
81
+ * @param editor
82
+ * @param dataRangeCaches
83
+ * @returns
84
+ */
85
+ export const isRangeInInfoBlock = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
86
+ if (!editor.range) {
87
+ return false
88
+ }
89
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
90
+ return isUnderInfoBlock(editor.range.anchor.element)
91
+ }
92
+ return dataRangeCaches.flatList.every(item => {
93
+ return isUnderInfoBlock(item.element)
94
+ })
95
+ }
96
+
97
+ /**
98
+ * 选区是否在某个信息元素下,如果是返回该信息元素否则返回null
99
+ * @param editor
100
+ * @param dataRangeCaches
101
+ * @returns
102
+ */
103
+ export const getInfoBlockElementByRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
104
+ if (!editor.range) {
105
+ return null
106
+ }
107
+ if (editor.range.anchor.element.isEqual(editor.range.focus.element)) {
108
+ return getInfoBlockElement(editor.range.anchor.element)
109
+ }
110
+ const arr = dataRangeCaches.list.map(item => {
111
+ return getInfoBlockElement(item.element)
112
+ })
113
+ let hasNull = arr.some(el => {
114
+ return el == null
115
+ })
116
+ //如果存在null,则表示有的选区元素不在公式元素下,返回null
117
+ if (hasNull) {
118
+ return null
119
+ }
120
+ //如果只有一个元素,则返回该元素
121
+ if (arr.length == 1) {
122
+ return arr[0]!
123
+ }
124
+ //默认数组中的元素都相等
125
+ let flag = true
126
+ for (let i = 1; i < arr.length; i++) {
127
+ if (!arr[i]!.isEqual(arr[0]!)) {
128
+ flag = false
129
+ break
130
+ }
131
+ }
132
+ //如果相等,则返回该元素
133
+ if (flag) {
134
+ return arr[0]
135
+ }
136
+ return null
137
+ }
138
+
139
+ /**
140
+ * 信息插件
141
+ * @param options
142
+ * @returns
143
+ */
144
+ export const infoBlock = (options?: InfoBlockOptionsType) => {
145
+ if (!DapCommon.isObject(options)) {
146
+ options = {}
147
+ }
148
+ const plugin: PluginType = (editifyInstance: ComponentInternalInstance, editTrans: (key: string) => any) => {
149
+ let isDisabled: boolean = false
150
+ //光标在表格、代码块和面板中则禁用
151
+ if (editifyInstance.exposed!.editor.value) {
152
+ isDisabled = hasTableInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value) || hasPreInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value) || hasPanelInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value)
153
+ }
154
+ return {
155
+ //插件名称
156
+ name: 'infoBlock',
157
+ //菜单项配置
158
+ menu: {
159
+ sequence: options!.sequence || 103,
160
+ extend: {
161
+ type: 'default',
162
+ title: options!.title || editTrans('insertInfoBlock'),
163
+ leftBorder: options!.leftBorder,
164
+ rightBorder: options!.rightBorder,
165
+ hideScroll: true,
166
+ active: editifyInstance.exposed!.editor.value ? isRangeInInfoBlock(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value) : false,
167
+ disabled: isDisabled || options!.disabled,
168
+ default: () => h(Icon, { value: 'info' }),
169
+ onOperate: () => {
170
+ const editor = <AlexEditor>editifyInstance.exposed!.editor.value
171
+ const dataRangeCaches = <AlexElementsRangeType>editifyInstance.exposed!.dataRangeCaches.value
172
+ //是否都在引用里
173
+ const flag = isRangeInInfoBlock(editor, dataRangeCaches)
174
+ //起点和终点在一起
175
+ if (editor.range!.anchor.isEqual(editor.range!.focus)) {
176
+ const block = editor.range!.anchor.element.getBlock()
177
+ elementToParagraph(block)
178
+ if (!flag) {
179
+ block.parsedom = 'div'
180
+ block.marks = {
181
+ 'data-editify-info': 'true'
182
+ }
183
+ }
184
+ }
185
+ //起点和终点不在一起
186
+ else {
187
+ let blocks: AlexElement[] = []
188
+ dataRangeCaches.list.forEach(item => {
189
+ const block = item.element.getBlock()
190
+ const exist = blocks.some(el => block.isEqual(el))
191
+ if (!exist) {
192
+ blocks.push(block)
193
+ }
194
+ })
195
+ blocks.forEach(block => {
196
+ elementToParagraph(block)
197
+ if (!flag) {
198
+ block.parsedom = 'div'
199
+ block.marks = {
200
+ 'data-editify-info': 'true'
201
+ }
202
+ }
203
+ })
204
+ }
205
+ //渲染
206
+ editor.formatElementStack()
207
+ editor.domRender()
208
+ editor.rangeRender()
209
+ }
210
+ }
211
+ },
212
+ //粘贴保留的属性
213
+ pasteKeepMarks: (element: AlexElement) => {
214
+ if (isInfoBlock(element)) {
215
+ return {
216
+ 'data-editify-info': 'true'
217
+ }
218
+ }
219
+ return {}
220
+ },
221
+ renderRule: (el: AlexElement) => {
222
+ if (isInfoBlock(el)) {
223
+ const color = DapColor.hex2rgb(editifyInstance.props.color as string)
224
+ if (el.hasStyles()) {
225
+ el.styles!['background-color'] = `rgba(${color[0]},${color[1]},${color[2]},0.15)`
226
+ el.styles!['color'] = editifyInstance.props.color
227
+ } else {
228
+ el.styles = {
229
+ 'background-color': `rgba(${color[0]},${color[1]},${color[2]},0.15)`,
230
+ color: editifyInstance.props.color
231
+ }
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
237
+ return plugin
238
+ }
@@ -133,7 +133,6 @@ export const mathformula = (options?: MathformulaOptionsType) => {
133
133
  options = {}
134
134
  }
135
135
  const plugin: PluginType = (editifyInstance: ComponentInternalInstance, editTrans: (key: string) => any) => {
136
- //是否禁用该插件按钮
137
136
  let isDisabled: boolean = false
138
137
  //如果光标范围内有链接、代码块则禁用
139
138
  if (editifyInstance.exposed!.editor.value) {
@@ -141,7 +140,6 @@ export const mathformula = (options?: MathformulaOptionsType) => {
141
140
  }
142
141
  //数学公式文本框内置LaTex文本内容
143
142
  let defaultLaTexContent: string = ''
144
-
145
143
  return {
146
144
  //插件名称
147
145
  name: 'mathformula',
@@ -161,7 +159,7 @@ export const mathformula = (options?: MathformulaOptionsType) => {
161
159
  leftBorder: options!.leftBorder,
162
160
  rightBorder: options!.rightBorder,
163
161
  hideScroll: true,
164
- active: editifyInstance.exposed!.editor.value ? hasMathformulaInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value) : false,
162
+ active: editifyInstance.exposed!.editor.value ? !!getMathformulaElementByRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value) : false,
165
163
  disabled: isDisabled || options!.disabled,
166
164
  //浮层展开时触发的事件
167
165
  onLayerShow() {
@@ -0,0 +1,228 @@
1
+ import { common as DapCommon } from 'dap-util'
2
+ import { PluginType } from '../../core/tool'
3
+ import { ComponentInternalInstance, h } from 'vue'
4
+ import { AlexEditor, AlexElement, AlexElementsRangeType } from 'alex-editor'
5
+ import Icon from '../../components/icon/icon.vue'
6
+ import { hasTableInRange } from '../../core/function'
7
+ import { hasMathformulaInRange } from '../mathformula'
8
+
9
+ export type PanelOptionsType = {
10
+ //排序
11
+ sequence?: number
12
+ //工具提示内容
13
+ title?: string
14
+ //按钮是否显示左侧边框
15
+ leftBorder?: boolean
16
+ //按钮是否显示右侧边框
17
+ rightBorder?: boolean
18
+ //按钮是否禁用
19
+ disabled?: boolean
20
+ }
21
+
22
+ /**
23
+ * 是否面板元素
24
+ * @param el
25
+ * @returns
26
+ */
27
+ export const isPanel = (el: AlexElement) => {
28
+ return el.parsedom == 'div' && el.hasMarks() && el.marks!['data-editify-panel']
29
+ }
30
+
31
+ /**
32
+ * 判断某个元素是否在面板元素内
33
+ * @param el
34
+ * @returns
35
+ */
36
+ export const isUnderPanel = (el: AlexElement): boolean => {
37
+ if (isPanel(el)) {
38
+ return true
39
+ }
40
+ if (el.parent) {
41
+ return isUnderPanel(el.parent)
42
+ }
43
+ return false
44
+ }
45
+
46
+ /**
47
+ * 根据某个元素获取所在的面板元素,如果不在面板元素内则返回null
48
+ * @param el
49
+ * @returns
50
+ */
51
+ export const getPanelElement = (el: AlexElement): AlexElement | null => {
52
+ if (isPanel(el)) {
53
+ return el
54
+ }
55
+ if (el.parent) {
56
+ return getPanelElement(el.parent)
57
+ }
58
+ return null
59
+ }
60
+
61
+ /**
62
+ * 选区是否含有面板元素
63
+ * @param editor
64
+ * @param dataRangeCaches
65
+ * @returns
66
+ */
67
+ export const hasPanelInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
68
+ if (!editor.range) {
69
+ return false
70
+ }
71
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
72
+ return isUnderPanel(editor.range.anchor.element)
73
+ }
74
+ return dataRangeCaches.flatList.some(item => {
75
+ return isUnderPanel(item.element)
76
+ })
77
+ }
78
+
79
+ /**
80
+ * 选区是否在某个面板元素下,如果是返回该面板元素否则返回null
81
+ * @param editor
82
+ * @param dataRangeCaches
83
+ * @returns
84
+ */
85
+ export const getPanelElementByRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
86
+ if (!editor.range) {
87
+ return null
88
+ }
89
+ if (editor.range.anchor.element.isEqual(editor.range.focus.element)) {
90
+ return getPanelElement(editor.range.anchor.element)
91
+ }
92
+ const arr = dataRangeCaches.list.map(item => {
93
+ return getPanelElement(item.element)
94
+ })
95
+ let hasNull = arr.some(el => {
96
+ return el == null
97
+ })
98
+ //如果存在null,则表示有的选区元素不在公式元素下,返回null
99
+ if (hasNull) {
100
+ return null
101
+ }
102
+ //如果只有一个元素,则返回该元素
103
+ if (arr.length == 1) {
104
+ return arr[0]!
105
+ }
106
+ //默认数组中的元素都相等
107
+ let flag = true
108
+ for (let i = 1; i < arr.length; i++) {
109
+ if (!arr[i]!.isEqual(arr[0]!)) {
110
+ flag = false
111
+ break
112
+ }
113
+ }
114
+ //如果相等,则返回该元素
115
+ if (flag) {
116
+ return arr[0]
117
+ }
118
+ return null
119
+ }
120
+
121
+ /**
122
+ * 面板插件
123
+ * @param options
124
+ * @returns
125
+ */
126
+ export const panel = (options?: PanelOptionsType) => {
127
+ if (!DapCommon.isObject(options)) {
128
+ options = {}
129
+ }
130
+ const plugin: PluginType = (editifyInstance: ComponentInternalInstance, editTrans: (key: string) => any) => {
131
+ let isDisabled: boolean = false
132
+ //光标在表格、面板和数学公式下则禁用
133
+ if (editifyInstance.exposed!.editor.value) {
134
+ isDisabled = hasPanelInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value) || hasTableInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value) || hasMathformulaInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value)
135
+ }
136
+ return {
137
+ //插件名称
138
+ name: 'panel',
139
+ //菜单项配置
140
+ menu: {
141
+ sequence: options!.sequence || 102,
142
+ extraDisabled: (name: string) => {
143
+ //如果光标选区内有面板,则禁用有序列表、无需列表、任务列表、引用、代码块、表格和标题菜单
144
+ if (name == 'orderList' || name == 'unorderList' || name == 'task' || name == 'quote' || name == 'codeBlock' || name == 'table' || name == 'heading') {
145
+ return hasPanelInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value)
146
+ }
147
+ return false
148
+ },
149
+ extend: {
150
+ type: 'default',
151
+ title: options!.title || editTrans('insertPanel'),
152
+ leftBorder: options!.leftBorder,
153
+ rightBorder: options!.rightBorder,
154
+ hideScroll: true,
155
+ active: false,
156
+ disabled: isDisabled || options!.disabled,
157
+ default: () => h(Icon, { value: 'panel' }),
158
+ onOperate: () => {
159
+ const panelElement = AlexElement.create({
160
+ type: 'block',
161
+ parsedom: 'div',
162
+ marks: {
163
+ 'data-editify-panel': 'true'
164
+ },
165
+ children: [
166
+ {
167
+ type: 'inblock',
168
+ parsedom: 'div',
169
+ behavior: 'block',
170
+ children: [
171
+ {
172
+ type: 'text',
173
+ textcontent: editTrans('panelTitle')
174
+ }
175
+ ]
176
+ },
177
+ {
178
+ type: 'inblock',
179
+ parsedom: 'div',
180
+ behavior: 'block',
181
+ children: [
182
+ {
183
+ type: 'text',
184
+ textcontent: editTrans('panelContent')
185
+ }
186
+ ]
187
+ }
188
+ ]
189
+ })
190
+ //获取编辑器对象
191
+ const editor = <AlexEditor>editifyInstance.exposed!.editor.value
192
+ //插入编辑器
193
+ editor.insertElement(panelElement)
194
+ //面板后面插入段落
195
+ const paragraph = AlexElement.create({
196
+ type: 'block',
197
+ parsedom: AlexElement.BLOCK_NODE,
198
+ children: [
199
+ {
200
+ type: 'closed',
201
+ parsedom: 'br'
202
+ }
203
+ ]
204
+ })
205
+ editor.addElementAfter(paragraph, panelElement)
206
+ //移动光标到新插入的元素
207
+ editor.range!.anchor.moveToEnd(panelElement.children![0])
208
+ editor.range!.focus.moveToEnd(panelElement.children![0])
209
+ //渲染
210
+ editor.formatElementStack()
211
+ editor.domRender()
212
+ editor.rangeRender()
213
+ }
214
+ }
215
+ },
216
+ //粘贴保留的属性
217
+ pasteKeepMarks: (element: AlexElement) => {
218
+ if (isPanel(element)) {
219
+ return {
220
+ 'data-editify-panel': 'true'
221
+ }
222
+ }
223
+ return {}
224
+ }
225
+ }
226
+ }
227
+ return plugin
228
+ }