@ebl-vue/editor-full 2.31.16 → 2.31.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ebl-vue/editor-full",
3
- "version": "2.31.16",
3
+ "version": "2.31.18",
4
4
  "type": "module",
5
5
  "author": "lrj525@sina.com",
6
6
  "description": "结构化编辑器",
@@ -1,14 +1,5 @@
1
1
  <template>
2
- <div class="ebl-editor" id="holder">
3
- <!-- <div class="ce-block__content ebl-editor-title-wrap">
4
- <input type="text" v-model="title" placeholder="请输入标题" class="ebl-editor-title-input"></input>
5
- </div> -->
6
- <!-- <div class="ce-block__content ebl-editor-time-wrap">
7
- <span class="ebl-editor-time">最后修改时间:{{ lastUpdateTime }}</span>
8
- </div> -->
9
- <!-- <button @click="validate">保存</button> -->
10
-
11
- </div>
2
+ <div class="ebl-editor" id="holder"></div>
12
3
  </template>
13
4
  <script lang="ts" setup>
14
5
 
@@ -83,7 +74,7 @@ const tunes = ['indent', 'blockAlignment'];
83
74
  onMounted(() => {
84
75
 
85
76
  editor = new EditorJS({
86
- minHeight: 100,
77
+ minHeight: 300,
87
78
  holder: 'holder',
88
79
  autofocus: true,
89
80
  defaultBlock: 'paragraph',
@@ -0,0 +1,276 @@
1
+ <template>
2
+ <div class="ebl-editor" id="holder"></div>
3
+ </template>
4
+ <script lang="ts" setup>
5
+
6
+ import EditorJS from '@ebl-vue/editorjs';
7
+ // import EditorJS from '../../../../editorjs/src/codex';
8
+ // window.VERSION="2.31.0"; //如果是用源码模式时,需要设置全局变量
9
+
10
+ import { onMounted, onUnmounted, watch, inject } from 'vue';
11
+ import { toRaw } from 'vue';
12
+ import { IEblEditorSettings } from '../../types';
13
+
14
+ /**
15
+ * 插件
16
+ */
17
+
18
+ import DragDrop from '../../plugins/drag-drop';
19
+ import { H1, H2, H3, H4, H5, H6 } from '../../plugins/header';
20
+ import BlockAlignment from '../../plugins/block-alignment';
21
+ import Paragraph from '../../plugins/paragraph';
22
+ import Code from '../../plugins/code';
23
+ import type { OutputData } from '@ebl-vue/editorjs';
24
+ import Quote from "../../plugins/quote";
25
+ import Delimiter from '../../plugins/delimiter';
26
+ import List from '../../plugins/list';
27
+ import Undo from '../../plugins/undo';
28
+ import Alert from '../../plugins/alert';
29
+ import IndentTune from '../../plugins/indent';
30
+ import Marker from "../../plugins/marker";
31
+ import { ColorPickerWithoutSanitize } from "../../plugins/color-picker";
32
+ import Underline from "../../plugins/underline";
33
+ import InlineCode from "../../plugins/inline-code";
34
+ import Table from '../../plugins/table';
35
+ import ImageTool from "../../plugins/imageTool";
36
+ import { ImageToolTune } from "../../plugins/imageResizeCrop";
37
+ import Outline from '../../plugins/outline';
38
+
39
+ const EblEditorSettings: IEblEditorSettings | undefined = inject("EblEditorSettings");
40
+ defineOptions({
41
+ name: 'EblEditorRender',
42
+ inheritAttrs: false
43
+ })
44
+
45
+
46
+ let emits = defineEmits(["onReady", "onChange"]);
47
+
48
+
49
+ interface Props {
50
+ placeholder: string,
51
+ data: OutputData,
52
+ locale: any,
53
+ userStore: any,
54
+ showOutline?: boolean,
55
+ }
56
+
57
+ const props = withDefaults(defineProps<Props>(), {
58
+
59
+ placeholder: "Enter something",
60
+ data: ()=>({
61
+ blocks: []
62
+ }),
63
+ locale: {},
64
+ showOutline:false
65
+ });
66
+ let editorData=toRaw(props.data);
67
+ let editorIsReady = false;
68
+
69
+ let editor: EditorJS | null = null;
70
+ let undo: Undo | null = null;
71
+ let outline: Outline | null = null;
72
+ const tunes = ['indent', 'blockAlignment'];
73
+ onMounted(() => {
74
+
75
+ editor = new EditorJS({
76
+ minHeight: 50,
77
+ holder: 'holder',
78
+ autofocus: true,
79
+ defaultBlock: 'paragraph',
80
+ placeholder: props.placeholder,
81
+ tunes: tunes,
82
+ tools: {
83
+ inlineCode: InlineCode,
84
+ underline: Underline,
85
+ Color: {
86
+ class: ColorPickerWithoutSanitize
87
+ },
88
+ marker: {
89
+ class: Marker
90
+ },
91
+ indent: {
92
+ class: IndentTune,
93
+ },
94
+ alert: {
95
+ class: Alert,
96
+ inlineToolbar: true
97
+ },
98
+ List: {
99
+ class: List,
100
+ inlineToolbar: true,
101
+ config: {
102
+ defaultStyle: 'checklist',
103
+
104
+ maxLevel: 4,
105
+ }
106
+ },
107
+ h1: {
108
+ class: H1,
109
+ inlineToolbar: true
110
+ },
111
+ h2: {
112
+ class: H2,
113
+ inlineToolbar: true,
114
+
115
+ },
116
+ h3: {
117
+ class: H3,
118
+ inlineToolbar: true
119
+ },
120
+ h4: {
121
+ class: H4,
122
+ inlineToolbar: true
123
+ },
124
+ h5: {
125
+ class: H5,
126
+ inlineToolbar: true
127
+ },
128
+ h6: {
129
+ class: H6,
130
+ inlineToolbar: true
131
+ },
132
+ paragraph: {
133
+ class: Paragraph,
134
+ inlineToolbar: true
135
+ },
136
+ blockAlignment: {
137
+ class: BlockAlignment,
138
+ inlineToolbar: true
139
+ },
140
+ code: {
141
+ class: Code,
142
+ config: {
143
+ lang: "javascript",
144
+ theme: "github-dark-dimmed"//"catppuccin-latte"
145
+ }
146
+ },
147
+ quote: {
148
+ class: Quote,
149
+ inlineToolbar: true
150
+ },
151
+ delimiter: Delimiter,
152
+ table: {
153
+ class: Table,
154
+ inlineToolbar: true,
155
+ config: {
156
+ rows: 2,
157
+ cols: 3,
158
+ }
159
+ },
160
+ image: {
161
+ class: ImageTool,
162
+ inlineToolbar: true,
163
+ tunes: tunes.concat(['imageResize']),
164
+ config: {
165
+ types: ".png,.jpeg,.jpg,.gif,.webp",
166
+ userStore: props.userStore,
167
+ endpoints: {
168
+ byFile: EblEditorSettings?.fileUploadEndpoint,
169
+ //byUrl: EblEditorSettings?.urlUploadEndpoint
170
+ },
171
+ features: {
172
+ caption: false,
173
+ stretch: false,
174
+ border: false,
175
+ background: false,
176
+ }
177
+
178
+
179
+ }
180
+ },
181
+ imageResize: {
182
+ class: ImageToolTune,
183
+ config: {
184
+ resize: true,
185
+ crop: false
186
+ }
187
+ },
188
+
189
+
190
+
191
+ },
192
+
193
+ data: editorData,
194
+ readOnly: true,
195
+ i18n: props.locale,
196
+ onChange: (api, event) => {
197
+ emits("onChange", api, event);
198
+ },
199
+ onReady: () => {
200
+ editorIsReady = true;
201
+ editorData = toRaw(props.data);
202
+ if (props.data && props.data.blocks && props.data.blocks.length > 0) {
203
+ editor!.render(editorData);
204
+ }
205
+ //文档大纲
206
+ if(props.showOutline){
207
+ outline = new Outline("holder",editor!);
208
+ outline.render(editorData);
209
+ }
210
+
211
+ emits("onReady");
212
+ },
213
+
214
+ })
215
+ });
216
+ onUnmounted(() => {
217
+ if (editor !== null) {
218
+ editor.destroy();
219
+ editor = null;
220
+ }
221
+ });
222
+
223
+ watch(() => props.data, (newVal, oldVal) => {
224
+ editorData = toRaw(newVal);
225
+ if (editor != null && editorIsReady) {
226
+ editor.render(editorData).then(() => {
227
+ if (outline != null && props.showOutline) {
228
+ outline.render(editorData);
229
+ }
230
+ });
231
+ }
232
+
233
+
234
+
235
+ },{
236
+ deep:true
237
+ });
238
+
239
+
240
+ /**
241
+ * 验证是否为空 */
242
+ function validate() {
243
+ return new Promise((resolve, reject) => {
244
+ if (editor !== null) {
245
+ editor.save().then((outputData: OutputData) => {
246
+ if (outputData.blocks.length > 0) {
247
+ return resolve(true);
248
+
249
+ }
250
+ return resolve(false);
251
+ });
252
+ } else {
253
+ return resolve(false);
254
+ }
255
+ });
256
+ }
257
+ function getData() {
258
+ return new Promise((resolve, reject) => {
259
+ if (editor !== null) {
260
+ editor.save().then((outputData: OutputData) => {
261
+ return resolve(outputData);
262
+ });
263
+ } else {
264
+ return resolve(null);
265
+ }
266
+ });
267
+ }
268
+
269
+ defineExpose({
270
+ validate,
271
+ getData,
272
+ });
273
+ </script>
274
+ <style scoped>
275
+
276
+ </style>
@@ -5,12 +5,13 @@ import type { SFCWithInstall } from 'types'
5
5
 
6
6
 
7
7
  import Editor from "./Editor/Editor.vue"
8
-
8
+ import EditorRender from "./Editor/EditorRender.vue"
9
9
 
10
10
 
11
11
 
12
12
  const items = [
13
- Editor
13
+ Editor,
14
+ EditorRender
14
15
  ];
15
16
 
16
17
  let PluginList: Plugin[] = [];
@@ -23,5 +24,6 @@ for (let key in items) {
23
24
 
24
25
  export default PluginList;
25
26
  export {
26
- Editor as EblEditor
27
+ Editor as EblEditor,
28
+ EditorRender as EblEditorRender
27
29
  };
@@ -51,8 +51,11 @@
51
51
  font-size: 13px;
52
52
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
53
53
  }
54
-
54
+ .ce-editorjs-x-shiki__span code{
55
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
56
+ }
55
57
  .ce-editorjs-x-shiki__textarea {
58
+ all:unset;
56
59
  white-space: pre;
57
60
  overflow: hidden;
58
61
  background-color: transparent;
@@ -72,6 +75,12 @@
72
75
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
73
76
  }
74
77
 
78
+ .ce-editorjs-x-shiki__textarea::selection {
79
+ background-color: #083ca9; /* 选中文本的背景色 */
80
+ color: #ffffff; /* 选中文本的颜色 */
81
+
82
+ }
83
+
75
84
  .ce-editorjs-x-shiki__lang {
76
85
  /* position: absolute;
77
86
  line-height: 14px;
@@ -365,7 +365,7 @@ export default class CodeTool implements BlockTool {
365
365
 
366
366
  wrapperLang.innerHTML = this.data.lang
367
367
  wrapperCopy.innerHTML = IconCopy;
368
- wrapperCopyTip.innerText=this.api.i18n.t('Copied');
368
+ wrapperCopyTip.innerText = this.api.i18n.t('Copied');
369
369
 
370
370
 
371
371
 
@@ -398,7 +398,7 @@ export default class CodeTool implements BlockTool {
398
398
  wrapperHolder.appendChild(span);
399
399
  wrapperHolder.appendChild(textarea);
400
400
 
401
- if(!this.readOnly) {
401
+ if (!this.readOnly) {
402
402
  wrapperSelectorHolder.appendChild(selectorLanguage);
403
403
  //wrapperSelectorHolder.appendChild(selectorTheme);
404
404
 
@@ -467,7 +467,7 @@ export default class CodeTool implements BlockTool {
467
467
  }
468
468
  });
469
469
  wrapperCopy.addEventListener('click', (event: MouseEvent) => {
470
- this.copyCode(this.data.code,event);
470
+ this.copyCode(this.data.code, event);
471
471
  });
472
472
 
473
473
  this.nodes.textarea = textarea;
@@ -486,7 +486,7 @@ export default class CodeTool implements BlockTool {
486
486
  {
487
487
  preprocess(code) {
488
488
  // if (code.endsWith('\n'))
489
- return `${code}\n`
489
+ return `${code}\n`
490
490
  },
491
491
  pre(node) {
492
492
  this.addClassToHast(node, 'ce-editorjs-x-shiki__span')
@@ -509,30 +509,60 @@ export default class CodeTool implements BlockTool {
509
509
 
510
510
  }
511
511
 
512
- private copyCode(code: string, event: MouseEvent) {
512
+ private copyCode(code: string, event: MouseEvent) {
513
513
  if (this.data.code) {
514
- navigator.clipboard.writeText(code)
515
- .then(() => {
516
- if (event.target) {
517
- const parentEle = (event.target as HTMLElement).parentElement;
518
- if (parentEle) {
519
- const tooltip = parentEle.lastChild as HTMLElement;
520
- if (tooltip) {
521
- tooltip.classList.add('visible');
522
- setTimeout(() => {
523
- tooltip.classList.remove('visible');
524
-
525
- }, 2000);
514
+ if (navigator.clipboard && window.isSecureContext) {
515
+ try {
516
+ navigator.clipboard.writeText(code).then(() => {
517
+ if (event.target) {
518
+ const parentEle = (event.target as HTMLElement).parentElement;
519
+ if (parentEle) {
520
+ const tooltip = parentEle.lastChild as HTMLElement;
521
+ if (tooltip) {
522
+ tooltip.classList.add('visible');
523
+ setTimeout(() => {
524
+ tooltip.classList.remove('visible');
525
+
526
+ }, 2000);
527
+ }
526
528
  }
527
529
  }
530
+ }).catch((err) => {
531
+ console.error(err);
532
+ alert("复制失败");
533
+ });
534
+
535
+ } catch (err) {
536
+ alert("复制失败");
537
+ }
538
+ } else {
539
+ // 传统方法:使用 textarea 和 execCommand
540
+
541
+ const textArea = document.createElement('textarea');
542
+ textArea.value = code;
543
+
544
+ // 避免滚动到底部
545
+ textArea.style.top = '0';
546
+ textArea.style.left = '0';
547
+ textArea.style.position = 'fixed';
548
+ textArea.style.opacity = '0';
549
+ textArea.style.pointerEvents = 'none';
550
+ textArea.style.zIndex = '-1000';
551
+
552
+ document.body.appendChild(textArea);
553
+ textArea.focus();
554
+ textArea.select();
555
+ try {
556
+ document.execCommand('copy');
557
+ document.body.removeChild(textArea);
558
+ } catch (err) {
559
+ alert("复制失败");
560
+ document.body.removeChild(textArea);
561
+
528
562
  }
529
-
530
563
 
531
- })
532
- .catch((err) => {
533
- console.error(err);
534
- alert("Unable to copy");
535
- });
564
+ }
536
565
  }
566
+
537
567
  }
538
568
  }