dragon-editor 3.2.3 → 3.4.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/module.json +1 -1
- package/dist/runtime/components/DragonEditor.vue +347 -78
- package/dist/runtime/components/DragonEditorViewer.vue +6 -1
- package/dist/runtime/scss/editor.css +77 -2
- package/dist/runtime/scss/viewer.css +4 -0
- package/dist/runtime/type.d.ts +5 -0
- package/dist/runtime/utils/convertor.d.ts +1 -1
- package/dist/runtime/utils/convertor.mjs +6 -6
- package/dist/runtime/utils/keyboardEvent.mjs +1 -1
- package/dist/runtime/utils/style.d.ts +3 -0
- package/dist/runtime/utils/style.mjs +261 -9
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -1,74 +1,112 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="dragon-editor" :class="{ '--hasMenu': props.useMenuBar === true }" @mousemove="resizeEvent" @touchmove="resizeEvent" @mouseup="resizeEventEnd" @touchend="resizeEventEnd" @mouseleave="resizeEventEnd" ref="$editor">
|
|
3
3
|
<div v-if="props.useMenuBar === true" class="de-menu-bar" :style="{ top: `${menuBarTop}px` }">
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
class="de-path"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
<
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
4
|
+
<div class="de-menu-wrap">
|
|
5
|
+
<button class="de-menu de-menu-add" @click="isActiveAddBlockMenu = !isActiveAddBlockMenu">
|
|
6
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
7
|
+
<path class="de-path" d="M50.6667 34.6668H34.6667V50.6668H29.3334V34.6668H13.3334V29.3335H29.3334V13.3335H34.6667V29.3335H50.6667V34.6668Z"></path>
|
|
8
|
+
</svg>
|
|
9
|
+
</button>
|
|
10
|
+
|
|
11
|
+
<button class="de-menu" @click="setDecoration('bold')">
|
|
12
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
13
|
+
<path class="de-path" d="M40.6 31.4402C43.1866 29.6535 45 26.7202 45 24.0002C45 17.9735 40.3333 13.3335 34.3333 13.3335H17.6666V50.6668H36.44C42.0133 50.6668 46.3333 46.1335 46.3333 40.5602C46.3333 36.5068 44.04 33.0402 40.6 31.4402ZM25.6666 20.0002H33.6666C35.88 20.0002 37.6666 21.7868 37.6666 24.0002C37.6666 26.2135 35.88 28.0002 33.6666 28.0002H25.6666V20.0002ZM35 44.0002H25.6666V36.0002H35C37.2133 36.0002 39 37.7868 39 40.0002C39 42.2135 37.2133 44.0002 35 44.0002Z"></path>
|
|
14
|
+
</svg>
|
|
15
|
+
</button>
|
|
16
|
+
|
|
17
|
+
<button class="de-menu" @click="setDecoration('italic')">
|
|
18
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
19
|
+
<path class="de-path" d="M26.6667 13.3335V21.3335H32.56L23.44 42.6668H16V50.6668H37.3333V42.6668H31.44L40.56 21.3335H48V13.3335H26.6667Z"></path>
|
|
20
|
+
</svg>
|
|
21
|
+
</button>
|
|
22
|
+
|
|
23
|
+
<button class="de-menu" @click="setDecoration('underline')">
|
|
24
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
25
|
+
<path class="de-path" d="M32 45.3333C40.8267 45.3333 48 38.16 48 29.3333V8H41.3334V29.3333C41.3334 34.48 37.1467 38.6667 32 38.6667C26.8534 38.6667 22.6667 34.48 22.6667 29.3333V8H16V29.3333C16 38.16 23.1734 45.3333 32 45.3333ZM13.3334 50.6667V56H50.6667V50.6667H13.3334Z"></path>
|
|
26
|
+
</svg>
|
|
27
|
+
</button>
|
|
28
|
+
|
|
29
|
+
<button class="de-menu" @click="setDecoration('strikethrough')">
|
|
30
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
31
|
+
<path class="de-path" d="M26.6667 52H37.3333V44H26.6667V52ZM13.3333 12V20H26.6667V28H37.3333V20H50.6667V12H13.3333ZM8 38.6667H56V33.3333H8V38.6667Z"></path>
|
|
32
|
+
</svg>
|
|
33
|
+
</button>
|
|
34
|
+
|
|
35
|
+
<button class="de-menu" @click="setDecoration('code')">
|
|
36
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
37
|
+
<path class="de-path" d="M25.0667 44.2667L12.8 32L25.0667 19.7333L21.3334 16L5.33337 32L21.3334 48L25.0667 44.2667ZM38.9334 44.2667L51.2 32L38.9334 19.7333L42.6667 16L58.6667 32L42.6667 48L38.9334 44.2667Z"></path>
|
|
38
|
+
</svg>
|
|
39
|
+
</button>
|
|
40
|
+
|
|
41
|
+
<button
|
|
42
|
+
class="de-menu de-link-add"
|
|
43
|
+
@click="
|
|
44
|
+
() => {
|
|
45
|
+
isActiveLinkArea = !isActiveLinkArea;
|
|
46
|
+
openLinkArea();
|
|
47
|
+
}
|
|
48
|
+
"
|
|
49
|
+
>
|
|
50
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
51
|
+
<path class="de-path" d="M45.3334 18.6665H34.6667V23.9998H45.3334C49.7334 23.9998 53.3334 27.5998 53.3334 31.9998C53.3334 36.3998 49.7334 39.9998 45.3334 39.9998H34.6667V45.3332H45.3334C52.6934 45.3332 58.6667 39.3598 58.6667 31.9998C58.6667 24.6398 52.6934 18.6665 45.3334 18.6665ZM29.3334 39.9998H18.6667C14.2667 39.9998 10.6667 36.3998 10.6667 31.9998C10.6667 27.5998 14.2667 23.9998 18.6667 23.9998H29.3334V18.6665H18.6667C11.3067 18.6665 5.33337 24.6398 5.33337 31.9998C5.33337 39.3598 11.3067 45.3332 18.6667 45.3332H29.3334V39.9998ZM21.3334 29.3332H42.6667V34.6665H21.3334V29.3332Z"></path>
|
|
52
|
+
</svg>
|
|
53
|
+
</button>
|
|
54
|
+
|
|
55
|
+
<button class="de-menu" @click="removeLink">
|
|
56
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
57
|
+
<path class="de-path" d="M38.3734 29.5065L42.6667 33.7998V29.5065H38.3734ZM45.3334 18.8398H34.6667V23.9065H45.3334C49.8934 23.9065 53.6 27.6131 53.6 32.1731C53.6 35.5598 51.5467 38.4931 48.6134 39.7465L52.3467 43.4798C56.1334 41.1331 58.6667 36.9465 58.6667 32.1731C58.6667 24.8131 52.6934 18.8398 45.3334 18.8398ZM5.33337 11.5598L13.6267 19.8531C8.77337 21.8265 5.33337 26.5998 5.33337 32.1731C5.33337 39.5331 11.3067 45.5065 18.6667 45.5065H29.3334V40.4398H18.6667C14.1067 40.4398 10.4 36.7331 10.4 32.1731C10.4 27.9331 13.6267 24.4398 17.76 23.9865L23.28 29.5065H21.3334V34.8398H28.6134L34.6667 40.8931V45.5065H39.28L49.9734 56.1998L53.7334 52.4398L9.09337 7.7998L5.33337 11.5598Z"></path>
|
|
58
|
+
</svg>
|
|
59
|
+
</button>
|
|
60
|
+
|
|
61
|
+
<label class="de-menu">
|
|
62
|
+
<input type="file" hidden accept=".jpg,.jpeg,.png,.webp,.gif" @change="chooseMediaEvent" />
|
|
63
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
64
|
+
<path d="M50.6667 13.3333V50.6667H13.3333V13.3333H50.6667ZM50.6667 8H13.3333C10.4 8 8 10.4 8 13.3333V50.6667C8 53.6 10.4 56 13.3333 56H50.6667C53.6 56 56 53.6 56 50.6667V13.3333C56 10.4 53.6 8 50.6667 8ZM37.7067 31.6267L29.7067 41.9467L24 35.04L16 45.3333H48L37.7067 31.6267Z"></path>
|
|
65
|
+
</svg>
|
|
66
|
+
</label>
|
|
67
|
+
|
|
68
|
+
<button class="de-menu" @click="setTextAlign('left')">
|
|
69
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
70
|
+
<path class="de-path" d="M40 40H8V45.3333H40V40ZM40 18.6667H8V24H40V18.6667ZM8 34.6667H56V29.3333H8V34.6667ZM8 56H56V50.6667H8V56ZM8 8V13.3333H56V8H8Z"></path>
|
|
71
|
+
</svg>
|
|
72
|
+
</button>
|
|
73
|
+
|
|
74
|
+
<button class="de-menu" @click="setTextAlign('center')">
|
|
75
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
76
|
+
<path class="de-path" d="M18.6667 40V45.3333H45.3333V40H18.6667ZM8 56H56V50.6667H8V56ZM8 34.6667H56V29.3333H8V34.6667ZM18.6667 18.6667V24H45.3333V18.6667H18.6667ZM8 8V13.3333H56V8H8Z"></path>
|
|
77
|
+
</svg>
|
|
78
|
+
</button>
|
|
79
|
+
|
|
80
|
+
<button class="de-menu" @click="setTextAlign('right')">
|
|
81
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
82
|
+
<path class="de-path" d="M8 56H56V50.6667H8V56ZM24 45.3333H56V40H24V45.3333ZM8 34.6667H56V29.3333H8V34.6667ZM24 24H56V18.6667H24V24ZM8 8V13.3333H56V8H8Z"></path>
|
|
83
|
+
</svg>
|
|
84
|
+
</button>
|
|
85
|
+
|
|
86
|
+
<button class="de-menu" @click="setTextAlign('justify')">
|
|
87
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
88
|
+
<path class="de-path" d="M8 56H56V50.6667H8V56ZM8 45.3333H56V40H8V45.3333ZM8 34.6667H56V29.3333H8V34.6667ZM8 24H56V18.6667H8V24ZM8 8V13.3333H56V8H8Z"></path>
|
|
89
|
+
</svg>
|
|
90
|
+
</button>
|
|
91
|
+
|
|
92
|
+
<button class="de-menu" @click="moveBlock('up')">
|
|
93
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
94
|
+
<path d="M9.33333 35.9999C9.33333 29.4666 14.0267 24.0799 20.2133 22.9066L16.24 26.9066L20 30.6666L30.6667 19.9733L20 9.33325L16.24 13.0933L20.4533 17.3066V17.4666C11.2 18.5599 4 26.4533 4 35.9999C4 46.3199 12.3467 54.6666 22.6667 54.6666H30.6667V49.3333H22.6667C15.3067 49.3333 9.33333 43.3599 9.33333 35.9999Z M36 35.9999V54.6666H60V35.9999H36ZM54.6667 49.3333H41.3333V41.3333H54.6667V49.3333Z M60 11.9999H36V30.6666H60V11.9999Z"></path>
|
|
95
|
+
</svg>
|
|
96
|
+
</button>
|
|
97
|
+
|
|
98
|
+
<button class="de-menu" @click="moveBlock('down')">
|
|
99
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
100
|
+
<path d="M9.33333 27.9999C9.33333 34.5333 14.0267 39.9199 20.2133 41.0933L16.24 37.1199L20 33.3333L30.6667 44.0266L20 54.6666L16.24 50.9066L20.4533 46.6933V46.5333C11.2 45.4399 4 37.5466 4 27.9999C4 17.6799 12.3467 9.33325 22.6667 9.33325H30.6667V14.6666H22.6667C15.3067 14.6666 9.33333 20.6399 9.33333 27.9999Z M60 27.9999V9.33325H36V27.9999H60ZM54.6667 22.6666H41.3333V14.6666H54.6667V22.6666Z M60 33.3333H36V51.9999H60V33.3333Z"></path>
|
|
101
|
+
</svg>
|
|
102
|
+
</button>
|
|
103
|
+
|
|
104
|
+
<button class="de-menu" @click="deleteBlock">
|
|
105
|
+
<svg class="de-icon" viewBox="0 0 64 64">
|
|
106
|
+
<path class="de-path --red" fill-rule="evenodd" clip-rule="evenodd" d="M42.6667 24V50.6667H21.3334V24H42.6667ZM38.6667 8H25.3334L22.6667 10.6667H13.3334V16H50.6667V10.6667H41.3334L38.6667 8ZM48 18.6667H16V50.6667C16 53.6 18.4 56 21.3334 56H42.6667C45.6 56 48 53.6 48 50.6667V18.6667Z"></path>
|
|
107
|
+
</svg>
|
|
108
|
+
</button>
|
|
109
|
+
</div>
|
|
72
110
|
|
|
73
111
|
<div class="de-block-menu-area" :class="{ '--active': isActiveAddBlockMenu }">
|
|
74
112
|
<div class="de-list">
|
|
@@ -83,6 +121,22 @@
|
|
|
83
121
|
<!-- <button class="de-add-block" @click="addBlock('video')">Video</button> youtube | vimeo -->
|
|
84
122
|
</div>
|
|
85
123
|
</div>
|
|
124
|
+
|
|
125
|
+
<div class="de-link-exit-area" :class="{ '--active': isActiveLinkArea }">
|
|
126
|
+
<div class="de-btn-area">
|
|
127
|
+
<button class="de-btn" :class="{ '--active': activeLinkTabType === 'url' }" @click="openLinkArea"> Text </button>
|
|
128
|
+
<button class="de-btn" :class="{ '--active': activeLinkTabType === 'heading' }" @click="listUpHeading">Heading</button>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<div v-if="activeLinkTabType === 'url'" class="de-link-text-area">
|
|
132
|
+
<input v-model="anchorTagValue" class="de-input" :class="{ '--red': anchorValueError }" type="url" ref="$linkInput" />
|
|
133
|
+
<button class="de-btn" @click="setLink">Add</button>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<div v-if="activeLinkTabType === 'heading'" class="de-link-heading-area">
|
|
137
|
+
<button v-for="item in anchorHeadingList" class="de-btn" @click="setHeadingLink(item.id)">{{ item.name }}</button>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
86
140
|
</div>
|
|
87
141
|
|
|
88
142
|
<div v-if="editorStore.$currentBlock !== null" class="de-control-bar" :class="{ '--active': editorStore.controlBar.active === true }" :style="{ top: `${editorStore.controlBar.y}px`, left: `${editorStore.controlBar.x}px` }" ref="$controlBar">
|
|
@@ -132,7 +186,7 @@ import { _getCodeBlockTheme, _getCodeBlockLanguage, _setCodeBlockTheme, _setCode
|
|
|
132
186
|
import { _findScrollingElement, _findContentEditableElement } from "../utils/element";
|
|
133
187
|
import { _elementKeyEvent, _hotKeyEvent, _copyEvent, _pasteEvent } from "../utils/keyboardEvent";
|
|
134
188
|
import { _getBlockType, _createTextBlock, _createHeadingBlock, _createListBlock, _createImageBlock, _createCustomBlock, _createCodeBlock } from "../utils/block";
|
|
135
|
-
import { _setNodeStyle, _setTextAlign } from "../utils/style";
|
|
189
|
+
import { _setNodeStyle, _setTextAlign, _setAnchorTag, _unsetAnchorTag, _getAnchorTagValue } from "../utils/style";
|
|
136
190
|
import { _setCursor, _setCursorData, _clenupCursor } from "../utils/cursor";
|
|
137
191
|
import { _getContentData, _setContentData } from "../utils/convertor";
|
|
138
192
|
import { _addBlockToContent } from "../utils/content";
|
|
@@ -144,9 +198,14 @@ const props = defineProps({
|
|
|
144
198
|
requiard: false,
|
|
145
199
|
default: () => true,
|
|
146
200
|
},
|
|
201
|
+
imageHostURL: {
|
|
202
|
+
type: String,
|
|
203
|
+
requiard: false,
|
|
204
|
+
default: () => "",
|
|
205
|
+
},
|
|
147
206
|
});
|
|
148
207
|
const emit = defineEmits<{
|
|
149
|
-
(e: "
|
|
208
|
+
(e: "uploadImageEvent", file: File): DEImage;
|
|
150
209
|
}>();
|
|
151
210
|
const editorStore = useEditorStore();
|
|
152
211
|
const isActiveAddBlockMenu = ref<boolean>(false);
|
|
@@ -155,10 +214,15 @@ const curruntType = ref<string>("");
|
|
|
155
214
|
const codeBlockTheme = ref<string>("github");
|
|
156
215
|
const codeblockLanguage = ref<string>("text");
|
|
157
216
|
const listBlockStyle = ref<DEListStyle>("disc");
|
|
217
|
+
const isActiveLinkArea = ref<boolean>(false);
|
|
218
|
+
const anchorValueError = ref<boolean>(false);
|
|
219
|
+
const activeLinkTabType = ref<"url" | "heading">("url");
|
|
220
|
+
const anchorHeadingList = ref<DEHeadingItem[]>([]);
|
|
158
221
|
const anchorTagValue = ref<string>("");
|
|
159
222
|
const $editor = ref<HTMLDivElement>();
|
|
160
223
|
const $content = ref<HTMLDivElement>();
|
|
161
224
|
const $controlBar = ref<HTMLDivElement>();
|
|
225
|
+
const $linkInput = ref<HTMLInputElement>();
|
|
162
226
|
let resizeEventActive: boolean = false;
|
|
163
227
|
let resizeStartX: number = 0;
|
|
164
228
|
let resizeType: string = "right";
|
|
@@ -177,7 +241,7 @@ function contentKeyboardEvent(e: KeyboardEvent) {
|
|
|
177
241
|
function updateCursorData(e: MouseEvent) {
|
|
178
242
|
const originalCursorData = editorStore.cursorData;
|
|
179
243
|
|
|
180
|
-
|
|
244
|
+
_clenupCursor(editorStore);
|
|
181
245
|
|
|
182
246
|
if (editorStore.cursorData !== null && _findContentEditableElement(editorStore.cursorData.startNode) === null) {
|
|
183
247
|
// 비정상 커서 값일 경우 초기화
|
|
@@ -194,7 +258,9 @@ function updateCursorData(e: MouseEvent) {
|
|
|
194
258
|
}
|
|
195
259
|
|
|
196
260
|
controlBarStatusUpdate();
|
|
261
|
+
anchorTagValueUpdate();
|
|
197
262
|
}
|
|
263
|
+
|
|
198
264
|
// 컨트롤 바 상태 업데이트
|
|
199
265
|
function controlBarStatusUpdate() {
|
|
200
266
|
if (editorStore.$currentBlock !== null) {
|
|
@@ -296,10 +362,34 @@ function resizeEventEnd() {
|
|
|
296
362
|
function checkOthersideClick(event: MouseEvent) {
|
|
297
363
|
if (event.target !== null) {
|
|
298
364
|
const $controlBar = (event.target as HTMLElement).closest(".de-menu-bar");
|
|
365
|
+
const $btnMenu = (event.target as HTMLElement).closest(".de-menu-add");
|
|
366
|
+
const $menuArea = (event.target as HTMLElement).closest(".de-block-menu-area");
|
|
367
|
+
const $btnLink = (event.target as HTMLElement).closest(".de-link-add");
|
|
368
|
+
const $linkArea = (event.target as HTMLElement).closest(".de-link-exit-area");
|
|
369
|
+
let closeMenu: boolean = false;
|
|
370
|
+
let closeLink: boolean = false;
|
|
299
371
|
|
|
300
372
|
if ($controlBar === null) {
|
|
373
|
+
closeMenu = true;
|
|
374
|
+
closeLink = true;
|
|
375
|
+
} else {
|
|
376
|
+
if ($btnMenu === null && $menuArea === null) {
|
|
377
|
+
closeMenu = true;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if ($btnLink === null && $linkArea === null) {
|
|
381
|
+
closeLink = true;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (closeMenu === true) {
|
|
301
386
|
isActiveAddBlockMenu.value = false;
|
|
302
387
|
}
|
|
388
|
+
|
|
389
|
+
if (closeLink === true) {
|
|
390
|
+
isActiveLinkArea.value = false;
|
|
391
|
+
anchorTagValue.value = "";
|
|
392
|
+
}
|
|
303
393
|
}
|
|
304
394
|
}
|
|
305
395
|
|
|
@@ -323,8 +413,6 @@ function deleteBlock() {
|
|
|
323
413
|
|
|
324
414
|
// 부모 요소 스크롤 이벤트 발생시 컨트롤 바 고정
|
|
325
415
|
function parentWrapScollEvent() {
|
|
326
|
-
editorStore.setParentWrapElement(_findScrollingElement($editor.value as HTMLElement));
|
|
327
|
-
|
|
328
416
|
if (props.useMenuBar === true && editorStore.$parentWrap !== null && editorStore.$editor !== null) {
|
|
329
417
|
// 메뉴바를 사용하는 경우만
|
|
330
418
|
|
|
@@ -345,11 +433,19 @@ function parentWrapScollEvent() {
|
|
|
345
433
|
realElementY -= parentRect.y;
|
|
346
434
|
}
|
|
347
435
|
|
|
436
|
+
let value: number = 0;
|
|
437
|
+
|
|
348
438
|
if (scrollY > realElementY) {
|
|
349
|
-
|
|
439
|
+
value = scrollY - realElementY - 1;
|
|
350
440
|
} else {
|
|
351
|
-
|
|
441
|
+
value = 0;
|
|
352
442
|
}
|
|
443
|
+
|
|
444
|
+
if (value > editorReac.height - 39) {
|
|
445
|
+
value = editorReac.height - 39;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
menuBarTop.value = Math.floor(value);
|
|
353
449
|
}
|
|
354
450
|
}
|
|
355
451
|
|
|
@@ -358,6 +454,13 @@ function contentPasteEvent(event: ClipboardEvent) {
|
|
|
358
454
|
_pasteEvent(event, editorStore, emit);
|
|
359
455
|
}
|
|
360
456
|
|
|
457
|
+
function anchorTagValueUpdate() {
|
|
458
|
+
// 다른 이벤트 순서에 의한 딜레이
|
|
459
|
+
setTimeout(() => {
|
|
460
|
+
anchorTagValue.value = _getAnchorTagValue(editorStore);
|
|
461
|
+
}, 500);
|
|
462
|
+
}
|
|
463
|
+
|
|
361
464
|
/**
|
|
362
465
|
* 이벤트 관련 영역 종료
|
|
363
466
|
*/
|
|
@@ -385,6 +488,9 @@ function listBlockStyleChangeEvent() {
|
|
|
385
488
|
* 컨트롤 바 이벤트 관련 영역 종료
|
|
386
489
|
*/
|
|
387
490
|
|
|
491
|
+
/**
|
|
492
|
+
* 메뉴 이벤트 관련 영역
|
|
493
|
+
*/
|
|
388
494
|
function addBlock(type: string) {
|
|
389
495
|
isActiveAddBlockMenu.value = false;
|
|
390
496
|
|
|
@@ -466,6 +572,10 @@ function addCustomBlock(HTML: string, classList: string[] = []) {
|
|
|
466
572
|
}
|
|
467
573
|
|
|
468
574
|
function addImageBlock(data: DEImage) {
|
|
575
|
+
if (props.imageHostURL !== "") {
|
|
576
|
+
data.src = props.imageHostURL + data.src;
|
|
577
|
+
}
|
|
578
|
+
|
|
469
579
|
const blockStructure = _createImageBlock({
|
|
470
580
|
...data,
|
|
471
581
|
type: "image",
|
|
@@ -486,7 +596,7 @@ function setTextAlign(type: DETextalign) {
|
|
|
486
596
|
|
|
487
597
|
function getContentData(): DEContentData {
|
|
488
598
|
if (editorStore.$content !== null) {
|
|
489
|
-
return _getContentData(editorStore.$content);
|
|
599
|
+
return _getContentData(editorStore.$content, props.imageHostURL);
|
|
490
600
|
} else {
|
|
491
601
|
console.error("[DragonEditor] Con't find content Element.");
|
|
492
602
|
return [];
|
|
@@ -497,6 +607,90 @@ function setContentData(data: DEContentData) {
|
|
|
497
607
|
_setContentData(data, editorStore);
|
|
498
608
|
}
|
|
499
609
|
|
|
610
|
+
function moveBlock(type: "up" | "down") {
|
|
611
|
+
if (editorStore.$currentBlock !== null) {
|
|
612
|
+
let $target: Element | null;
|
|
613
|
+
|
|
614
|
+
if (type === "up") {
|
|
615
|
+
$target = editorStore.$currentBlock.previousElementSibling;
|
|
616
|
+
} else {
|
|
617
|
+
$target = editorStore.$currentBlock.nextElementSibling;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if ($target !== null) {
|
|
621
|
+
($target as HTMLElement).insertAdjacentHTML(type === "up" ? "beforebegin" : "afterend", editorStore.$currentBlock.outerHTML);
|
|
622
|
+
editorStore.$currentBlock.remove();
|
|
623
|
+
|
|
624
|
+
if (type === "up") {
|
|
625
|
+
editorStore.setCurrentBlock(($target as HTMLElement).previousElementSibling as HTMLElement | null);
|
|
626
|
+
} else {
|
|
627
|
+
editorStore.setCurrentBlock(($target as HTMLElement).nextElementSibling as HTMLElement | null);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function openLinkArea() {
|
|
634
|
+
activeLinkTabType.value = "url";
|
|
635
|
+
anchorValueError.value = false;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function chooseMediaEvent(event: Event) {
|
|
639
|
+
const $target = event.target as HTMLInputElement;
|
|
640
|
+
const file = $target.files![0];
|
|
641
|
+
|
|
642
|
+
emit("uploadImageEvent", file);
|
|
643
|
+
$target.value = "";
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// 링크 삽입
|
|
647
|
+
function setLink() {
|
|
648
|
+
if ($linkInput.value !== null && $linkInput.value?.checkValidity() === true && anchorTagValue.value !== "") {
|
|
649
|
+
_setAnchorTag(anchorTagValue.value, true, editorStore);
|
|
650
|
+
isActiveLinkArea.value = false;
|
|
651
|
+
anchorValueError.value = false;
|
|
652
|
+
anchorTagValue.value = "";
|
|
653
|
+
} else {
|
|
654
|
+
anchorValueError.value = true;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function setHeadingLink(id: string) {
|
|
659
|
+
_setAnchorTag(id, false, editorStore);
|
|
660
|
+
isActiveLinkArea.value = false;
|
|
661
|
+
anchorValueError.value = false;
|
|
662
|
+
anchorTagValue.value = "";
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// 헤딩 리스트 업데이트
|
|
666
|
+
function listUpHeading() {
|
|
667
|
+
activeLinkTabType.value = "heading";
|
|
668
|
+
|
|
669
|
+
if (editorStore.$content !== null) {
|
|
670
|
+
const $blockList = editorStore.$content.querySelectorAll(".de-heading-block");
|
|
671
|
+
let headingList: DEHeadingItem[] = [];
|
|
672
|
+
|
|
673
|
+
$blockList.forEach(($headingTag) => {
|
|
674
|
+
if ($headingTag.textContent !== null) {
|
|
675
|
+
headingList.push({
|
|
676
|
+
name: $headingTag.textContent,
|
|
677
|
+
id: $headingTag.id,
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
anchorHeadingList.value = headingList;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function removeLink() {
|
|
687
|
+
_unsetAnchorTag(editorStore);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* 메뉴 이벤트 관련 영역 종료
|
|
692
|
+
*/
|
|
693
|
+
|
|
500
694
|
onMounted(() => {
|
|
501
695
|
if ($editor.value !== undefined) {
|
|
502
696
|
editorStore.setWrapElement($editor.value);
|
|
@@ -674,7 +868,8 @@ defineExpose({
|
|
|
674
868
|
padding-top: 38px;
|
|
675
869
|
}
|
|
676
870
|
.dragon-editor .de-body {
|
|
677
|
-
display:
|
|
871
|
+
display: flex;
|
|
872
|
+
flex-direction: column;
|
|
678
873
|
gap: 4px;
|
|
679
874
|
padding: 20px;
|
|
680
875
|
line-height: 1.6;
|
|
@@ -689,12 +884,23 @@ defineExpose({
|
|
|
689
884
|
border-bottom: 1px solid #ccc;
|
|
690
885
|
z-index: 10;
|
|
691
886
|
}
|
|
887
|
+
.dragon-editor .de-menu-bar .de-menu-wrap {
|
|
888
|
+
display: flex;
|
|
889
|
+
overflow-x: auto;
|
|
890
|
+
}
|
|
692
891
|
.dragon-editor .de-menu-bar .de-menu {
|
|
892
|
+
display: flex;
|
|
893
|
+
justify-content: center;
|
|
894
|
+
align-items: center;
|
|
693
895
|
min-width: 38px;
|
|
694
896
|
height: 38px;
|
|
695
|
-
padding: 0 8px;
|
|
696
897
|
border-right: 1px solid #ccc;
|
|
697
898
|
box-sizing: border-box;
|
|
899
|
+
cursor: pointer;
|
|
900
|
+
}
|
|
901
|
+
.dragon-editor .de-menu-bar .de-menu .de-icon {
|
|
902
|
+
width: 24px;
|
|
903
|
+
height: 24px;
|
|
698
904
|
}
|
|
699
905
|
.dragon-editor .de-menu-bar .de-menu.--lastchild {
|
|
700
906
|
border-right: 0;
|
|
@@ -724,6 +930,65 @@ defineExpose({
|
|
|
724
930
|
.dragon-editor .de-menu-bar .de-block-menu-area .de-add-block {
|
|
725
931
|
line-height: 1.6;
|
|
726
932
|
}
|
|
933
|
+
.dragon-editor .de-menu-bar .de-link-exit-area {
|
|
934
|
+
display: none;
|
|
935
|
+
position: absolute;
|
|
936
|
+
top: 39px;
|
|
937
|
+
left: 228px;
|
|
938
|
+
width: 200px;
|
|
939
|
+
background: #fff;
|
|
940
|
+
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.25);
|
|
941
|
+
z-index: 1000;
|
|
942
|
+
}
|
|
943
|
+
.dragon-editor .de-menu-bar .de-link-exit-area.--active {
|
|
944
|
+
display: block;
|
|
945
|
+
}
|
|
946
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-btn-area {
|
|
947
|
+
display: flex;
|
|
948
|
+
border-bottom: 1px solid #ccc;
|
|
949
|
+
}
|
|
950
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-btn-area .de-btn {
|
|
951
|
+
flex: 1;
|
|
952
|
+
height: 24px;
|
|
953
|
+
}
|
|
954
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-btn-area .de-btn.--active {
|
|
955
|
+
background: #f1f1f1;
|
|
956
|
+
}
|
|
957
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-text-area {
|
|
958
|
+
display: flex;
|
|
959
|
+
column-gap: 10px;
|
|
960
|
+
padding: 4px;
|
|
961
|
+
}
|
|
962
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-text-area .de-input {
|
|
963
|
+
flex: 1;
|
|
964
|
+
min-width: 0;
|
|
965
|
+
height: 20px;
|
|
966
|
+
padding: 0 8px;
|
|
967
|
+
border: 1px solid #ccc;
|
|
968
|
+
box-sizing: border-box;
|
|
969
|
+
}
|
|
970
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-text-area .de-input.--red {
|
|
971
|
+
border-color: red;
|
|
972
|
+
}
|
|
973
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-text-area .de-btn {
|
|
974
|
+
min-width: 50px;
|
|
975
|
+
height: 20px;
|
|
976
|
+
}
|
|
977
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-heading-area {
|
|
978
|
+
display: flex;
|
|
979
|
+
flex-direction: column;
|
|
980
|
+
max-height: 150px;
|
|
981
|
+
padding: 8px;
|
|
982
|
+
box-sizing: border-box;
|
|
983
|
+
overflow-y: auto;
|
|
984
|
+
}
|
|
985
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-heading-area .de-btn {
|
|
986
|
+
min-height: 20px;
|
|
987
|
+
text-align: left;
|
|
988
|
+
}
|
|
989
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-heading-area .de-btn:hover {
|
|
990
|
+
background: #f1f1f1;
|
|
991
|
+
}
|
|
727
992
|
.dragon-editor .de-control-bar {
|
|
728
993
|
display: none;
|
|
729
994
|
position: fixed;
|
|
@@ -1399,4 +1664,8 @@ defineExpose({
|
|
|
1399
1664
|
.dragon-editor .de-align-justify {
|
|
1400
1665
|
text-align: justify;
|
|
1401
1666
|
}
|
|
1667
|
+
.dragon-editor .de-link {
|
|
1668
|
+
color: #2c95fe;
|
|
1669
|
+
text-decoration: none;
|
|
1670
|
+
}
|
|
1402
1671
|
</style>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
<div v-if="item.type === 'image'" class="de-block de-image-block" :class="item.classList">
|
|
23
23
|
<div class="de-image-area" :data-maxwidth="item.maxWidth">
|
|
24
|
-
<img :src="item.src" alt="" class="de-img" :width="item.width" :height="item.height" loading="lazy" />
|
|
24
|
+
<img :src="props.imageHostURL === undefined ? item.src : props.imageHostURL + item.src" alt="" class="de-img" :width="item.width" :height="item.height" loading="lazy" />
|
|
25
25
|
</div>
|
|
26
26
|
|
|
27
27
|
<p v-if="item.caption" class="de-caption">{{ item.caption }}</p>
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
<script setup lang="ts">
|
|
42
42
|
const props = defineProps<{
|
|
43
43
|
content: DEContentData;
|
|
44
|
+
imageHostURL?: string;
|
|
44
45
|
}>();
|
|
45
46
|
</script>
|
|
46
47
|
|
|
@@ -782,4 +783,8 @@ const props = defineProps<{
|
|
|
782
783
|
.dragon-editor-viewer .de-align-justify {
|
|
783
784
|
text-align: justify;
|
|
784
785
|
}
|
|
786
|
+
.dragon-editor-viewer .de-link {
|
|
787
|
+
color: #2c95fe;
|
|
788
|
+
text-decoration: none;
|
|
789
|
+
}
|
|
785
790
|
</style>
|
|
@@ -140,7 +140,8 @@
|
|
|
140
140
|
padding-top: 38px;
|
|
141
141
|
}
|
|
142
142
|
.dragon-editor .de-body {
|
|
143
|
-
display:
|
|
143
|
+
display: flex;
|
|
144
|
+
flex-direction: column;
|
|
144
145
|
gap: 4px;
|
|
145
146
|
padding: 20px;
|
|
146
147
|
line-height: 1.6;
|
|
@@ -155,12 +156,23 @@
|
|
|
155
156
|
border-bottom: 1px solid #ccc;
|
|
156
157
|
z-index: 10;
|
|
157
158
|
}
|
|
159
|
+
.dragon-editor .de-menu-bar .de-menu-wrap {
|
|
160
|
+
display: flex;
|
|
161
|
+
overflow-x: auto;
|
|
162
|
+
}
|
|
158
163
|
.dragon-editor .de-menu-bar .de-menu {
|
|
164
|
+
display: flex;
|
|
165
|
+
justify-content: center;
|
|
166
|
+
align-items: center;
|
|
159
167
|
min-width: 38px;
|
|
160
168
|
height: 38px;
|
|
161
|
-
padding: 0 8px;
|
|
162
169
|
border-right: 1px solid #ccc;
|
|
163
170
|
box-sizing: border-box;
|
|
171
|
+
cursor: pointer;
|
|
172
|
+
}
|
|
173
|
+
.dragon-editor .de-menu-bar .de-menu .de-icon {
|
|
174
|
+
width: 24px;
|
|
175
|
+
height: 24px;
|
|
164
176
|
}
|
|
165
177
|
.dragon-editor .de-menu-bar .de-menu.--lastchild {
|
|
166
178
|
border-right: 0;
|
|
@@ -190,6 +202,65 @@
|
|
|
190
202
|
.dragon-editor .de-menu-bar .de-block-menu-area .de-add-block {
|
|
191
203
|
line-height: 1.6;
|
|
192
204
|
}
|
|
205
|
+
.dragon-editor .de-menu-bar .de-link-exit-area {
|
|
206
|
+
display: none;
|
|
207
|
+
position: absolute;
|
|
208
|
+
top: 39px;
|
|
209
|
+
left: 228px;
|
|
210
|
+
width: 200px;
|
|
211
|
+
background: #fff;
|
|
212
|
+
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.25);
|
|
213
|
+
z-index: 1000;
|
|
214
|
+
}
|
|
215
|
+
.dragon-editor .de-menu-bar .de-link-exit-area.--active {
|
|
216
|
+
display: block;
|
|
217
|
+
}
|
|
218
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-btn-area {
|
|
219
|
+
display: flex;
|
|
220
|
+
border-bottom: 1px solid #ccc;
|
|
221
|
+
}
|
|
222
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-btn-area .de-btn {
|
|
223
|
+
flex: 1;
|
|
224
|
+
height: 24px;
|
|
225
|
+
}
|
|
226
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-btn-area .de-btn.--active {
|
|
227
|
+
background: #f1f1f1;
|
|
228
|
+
}
|
|
229
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-text-area {
|
|
230
|
+
display: flex;
|
|
231
|
+
column-gap: 10px;
|
|
232
|
+
padding: 4px;
|
|
233
|
+
}
|
|
234
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-text-area .de-input {
|
|
235
|
+
flex: 1;
|
|
236
|
+
min-width: 0;
|
|
237
|
+
height: 20px;
|
|
238
|
+
padding: 0 8px;
|
|
239
|
+
border: 1px solid #ccc;
|
|
240
|
+
box-sizing: border-box;
|
|
241
|
+
}
|
|
242
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-text-area .de-input.--red {
|
|
243
|
+
border-color: red;
|
|
244
|
+
}
|
|
245
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-text-area .de-btn {
|
|
246
|
+
min-width: 50px;
|
|
247
|
+
height: 20px;
|
|
248
|
+
}
|
|
249
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-heading-area {
|
|
250
|
+
display: flex;
|
|
251
|
+
flex-direction: column;
|
|
252
|
+
max-height: 150px;
|
|
253
|
+
padding: 8px;
|
|
254
|
+
box-sizing: border-box;
|
|
255
|
+
overflow-y: auto;
|
|
256
|
+
}
|
|
257
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-heading-area .de-btn {
|
|
258
|
+
min-height: 20px;
|
|
259
|
+
text-align: left;
|
|
260
|
+
}
|
|
261
|
+
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-heading-area .de-btn:hover {
|
|
262
|
+
background: #f1f1f1;
|
|
263
|
+
}
|
|
193
264
|
.dragon-editor .de-control-bar {
|
|
194
265
|
display: none;
|
|
195
266
|
position: fixed;
|
|
@@ -864,4 +935,8 @@
|
|
|
864
935
|
}
|
|
865
936
|
.dragon-editor .de-align-justify {
|
|
866
937
|
text-align: justify;
|
|
938
|
+
}
|
|
939
|
+
.dragon-editor .de-link {
|
|
940
|
+
color: #2c95fe;
|
|
941
|
+
text-decoration: none;
|
|
867
942
|
}
|
package/dist/runtime/type.d.ts
CHANGED
|
@@ -43,6 +43,11 @@ interface DECodeItem {
|
|
|
43
43
|
code: string;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
interface DEHeadingItem {
|
|
47
|
+
name: string;
|
|
48
|
+
id: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
46
51
|
type DEDecoration = "bold" | "italic" | "underline" | "strikethrough" | "code";
|
|
47
52
|
|
|
48
53
|
type DETextalign = "left" | "right" | "center" | "justify";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import "../type.d.ts";
|
|
2
|
-
export declare function _getContentData($content: HTMLDivElement): DEContentData;
|
|
2
|
+
export declare function _getContentData($content: HTMLDivElement, imageHostURL: string): DEContentData;
|
|
3
3
|
export declare function _setContentData(data: DEContentData, store: any): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _createTextBlock, _createHeadingBlock, _createListBlock, _createImageBlock, _createCodeBlock, _createCustomBlock } from "./block.mjs";
|
|
2
2
|
import "../type.d.ts";
|
|
3
|
-
export function _getContentData($content) {
|
|
3
|
+
export function _getContentData($content, imageHostURL) {
|
|
4
4
|
const childList = $content.children;
|
|
5
5
|
const data = [];
|
|
6
6
|
[...childList].forEach(($child) => {
|
|
@@ -23,7 +23,7 @@ export function _getContentData($content) {
|
|
|
23
23
|
data.push(converteListToData($child));
|
|
24
24
|
break;
|
|
25
25
|
case "DIV":
|
|
26
|
-
data.push(converteDivToData($child));
|
|
26
|
+
data.push(converteDivToData($child, imageHostURL));
|
|
27
27
|
break;
|
|
28
28
|
}
|
|
29
29
|
});
|
|
@@ -84,11 +84,11 @@ function converteListToData($child) {
|
|
|
84
84
|
})
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
|
-
function converteDivToData($child) {
|
|
87
|
+
function converteDivToData($child, imageHostURL) {
|
|
88
88
|
let data;
|
|
89
89
|
switch (true) {
|
|
90
90
|
case $child.classList.contains("de-image-block"):
|
|
91
|
-
data = convertImageBlock($child);
|
|
91
|
+
data = convertImageBlock($child, imageHostURL);
|
|
92
92
|
break;
|
|
93
93
|
case $child.classList.contains("de-code-block"):
|
|
94
94
|
data = convertCodeBlock($child);
|
|
@@ -99,13 +99,13 @@ function converteDivToData($child) {
|
|
|
99
99
|
}
|
|
100
100
|
return data;
|
|
101
101
|
}
|
|
102
|
-
function convertImageBlock($imageBlock) {
|
|
102
|
+
function convertImageBlock($imageBlock, imageHostURL) {
|
|
103
103
|
const $imgArea = $imageBlock.querySelector(".de-image-area");
|
|
104
104
|
const $img = $imageBlock.querySelector(".de-img");
|
|
105
105
|
const $caption = $imageBlock.querySelector(".de-caption");
|
|
106
106
|
return {
|
|
107
107
|
type: "image",
|
|
108
|
-
src: $img.src,
|
|
108
|
+
src: imageHostURL === "" ? $img.src : $img.src.replace(imageHostURL, ""),
|
|
109
109
|
maxWidth: parseInt($imgArea.dataset["maxwidth"]),
|
|
110
110
|
width: $img.width,
|
|
111
111
|
height: $img.height,
|
|
@@ -965,7 +965,7 @@ export async function _pasteEvent(event, store, emit) {
|
|
|
965
965
|
if (imageItem !== void 0) {
|
|
966
966
|
const blob = await clipboardItems[0].getType(imageItem);
|
|
967
967
|
const file = new File([blob], `${_generateId()}.${imageItem.split("/")[1]}`);
|
|
968
|
-
emit("
|
|
968
|
+
emit("uploadImageEvent", file);
|
|
969
969
|
}
|
|
970
970
|
} else {
|
|
971
971
|
const selection = window.getSelection();
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
export declare function _setNodeStyle(className: string, store: any): void;
|
|
2
2
|
export declare function _setTextAlign(type: DETextalign, store: any): void;
|
|
3
|
+
export declare function _setAnchorTag(url: string, isOutsideLink: boolean, store: any): void;
|
|
4
|
+
export declare function _unsetAnchorTag(store: any): false | undefined;
|
|
5
|
+
export declare function _getAnchorTagValue(store: any): string;
|
|
@@ -14,20 +14,21 @@ export function _setNodeStyle(className, store) {
|
|
|
14
14
|
if ($target.constructor.name === "Text") {
|
|
15
15
|
const $parentElement = $target.parentElement;
|
|
16
16
|
if ($parentElement === $element) {
|
|
17
|
-
|
|
17
|
+
let childList = $element.childNodes;
|
|
18
18
|
let targetIdx = -1;
|
|
19
19
|
let structure = "";
|
|
20
|
-
let cursorOffset = 0;
|
|
21
20
|
for (let i = 0; childList.length > i; i += 1) {
|
|
22
21
|
if ($target === childList[i]) {
|
|
23
22
|
targetIdx = i;
|
|
24
23
|
break;
|
|
25
24
|
}
|
|
26
25
|
}
|
|
26
|
+
targetIdx = findPoverTextNode(childList[targetIdx], targetIdx);
|
|
27
|
+
$element.innerHTML = $element.innerHTML;
|
|
28
|
+
childList = $element.childNodes;
|
|
27
29
|
childList.forEach((node, i) => {
|
|
28
30
|
if (i === targetIdx) {
|
|
29
31
|
structure += `<span class="${className}">${node.textContent}</span>`;
|
|
30
|
-
cursorOffset = node.textContent.length;
|
|
31
32
|
} else {
|
|
32
33
|
if (node.constructor.name === "Text") {
|
|
33
34
|
structure += node.textContent;
|
|
@@ -37,22 +38,26 @@ export function _setNodeStyle(className, store) {
|
|
|
37
38
|
}
|
|
38
39
|
});
|
|
39
40
|
$element.innerHTML = structure;
|
|
40
|
-
|
|
41
|
+
const targetElement = $element.childNodes[targetIdx];
|
|
42
|
+
_setRangeCursor(targetElement, targetElement, 0, targetElement.textContent?.length ?? 0);
|
|
41
43
|
} else {
|
|
42
44
|
if ($parentElement.tagName === "SPAN") {
|
|
43
45
|
const classList = $parentElement.classList.value.split(" ");
|
|
44
46
|
const classIdx = classList.indexOf(className);
|
|
45
47
|
if (classIdx === -1) {
|
|
46
48
|
$parentElement.classList.add(className);
|
|
47
|
-
|
|
49
|
+
const targetNode = $parentElement.childNodes[0];
|
|
50
|
+
_setRangeCursor(targetNode, targetNode, 0, targetNode.textContent?.length ?? 0);
|
|
48
51
|
} else {
|
|
49
52
|
if (classList.length === 1) {
|
|
50
53
|
$parentElement.insertAdjacentText("afterend", $parentElement.textContent);
|
|
51
|
-
|
|
54
|
+
const targetNode = $parentElement.nextSibling;
|
|
52
55
|
$parentElement.remove();
|
|
56
|
+
_setRangeCursor(targetNode, targetNode, 0, targetNode.textContent?.length ?? 0);
|
|
53
57
|
} else {
|
|
54
58
|
$parentElement.classList.remove(className);
|
|
55
|
-
|
|
59
|
+
const targetNode = $parentElement.childNodes[0];
|
|
60
|
+
_setRangeCursor(targetNode, targetNode, 0, targetNode.textContent?.length ?? 0);
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
}
|
|
@@ -88,7 +93,8 @@ export function _setNodeStyle(className, store) {
|
|
|
88
93
|
childNumber += 1;
|
|
89
94
|
}
|
|
90
95
|
$element.innerHTML = structure;
|
|
91
|
-
|
|
96
|
+
const targetNode = $element.childNodes[childNumber];
|
|
97
|
+
_setRangeCursor(targetNode, targetNode, 0, targetNode.textContent?.length ?? 0);
|
|
92
98
|
} else {
|
|
93
99
|
const $target = cursorData.startNode;
|
|
94
100
|
if ($target.tagName !== "A") {
|
|
@@ -125,7 +131,8 @@ export function _setNodeStyle(className, store) {
|
|
|
125
131
|
$nextElement = $nextElement.nextSibling;
|
|
126
132
|
}
|
|
127
133
|
$target.remove();
|
|
128
|
-
|
|
134
|
+
const targetNode = $nextElement;
|
|
135
|
+
_setRangeCursor(targetNode, targetNode, 0, targetNode.textContent?.length ?? 0);
|
|
129
136
|
}
|
|
130
137
|
}
|
|
131
138
|
} else {
|
|
@@ -363,3 +370,248 @@ export function _setTextAlign(type, store) {
|
|
|
363
370
|
}
|
|
364
371
|
}
|
|
365
372
|
}
|
|
373
|
+
export function _setAnchorTag(url, isOutsideLink, store) {
|
|
374
|
+
if (store.cursorData !== null) {
|
|
375
|
+
const { type } = _getBlockType(store.$currentBlock);
|
|
376
|
+
const typeIgnoreList = ["image", "code", "other"];
|
|
377
|
+
const hrefValue = isOutsideLink === true ? url : `#${url}`;
|
|
378
|
+
if (typeIgnoreList.includes(type) === false) {
|
|
379
|
+
const $element = _findContentEditableElement(store.cursorData.startNode);
|
|
380
|
+
if ($element !== null) {
|
|
381
|
+
if (store.cursorData.type === "Caret") {
|
|
382
|
+
if ($element.hasChildNodes() === true) {
|
|
383
|
+
let $target = store.cursorData.startNode;
|
|
384
|
+
if ($target.constructor.name === "Text") {
|
|
385
|
+
const $parentElement = $target.parentElement;
|
|
386
|
+
if ($parentElement === $element) {
|
|
387
|
+
let childList = $element.childNodes;
|
|
388
|
+
let targetIdx = -1;
|
|
389
|
+
let structure = "";
|
|
390
|
+
for (let i = 0; childList.length > i; i += 1) {
|
|
391
|
+
if ($target === childList[i]) {
|
|
392
|
+
targetIdx = i;
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
targetIdx = findPoverTextNode(childList[targetIdx], targetIdx);
|
|
397
|
+
$element.innerHTML = $element.innerHTML;
|
|
398
|
+
childList = $element.childNodes;
|
|
399
|
+
childList.forEach((node, i) => {
|
|
400
|
+
if (i === targetIdx) {
|
|
401
|
+
structure += `<a class="de-link"`;
|
|
402
|
+
structure += `href="${hrefValue}"`;
|
|
403
|
+
if (isOutsideLink === true) {
|
|
404
|
+
structure += `target="_blank"`;
|
|
405
|
+
}
|
|
406
|
+
structure += `>`;
|
|
407
|
+
structure += `${node.textContent}</a>`;
|
|
408
|
+
} else {
|
|
409
|
+
if (node.constructor.name === "Text") {
|
|
410
|
+
structure += node.textContent;
|
|
411
|
+
} else {
|
|
412
|
+
structure += node.outerHTML;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
$element.innerHTML = structure;
|
|
417
|
+
const targetElement = $element.childNodes[targetIdx];
|
|
418
|
+
_setRangeCursor(targetElement, targetElement, 0, targetElement.textContent?.length ?? 0);
|
|
419
|
+
} else {
|
|
420
|
+
if ($parentElement.tagName === "A") {
|
|
421
|
+
$parentElement.href = hrefValue;
|
|
422
|
+
if (isOutsideLink === true) {
|
|
423
|
+
$parentElement.target = "_blank";
|
|
424
|
+
} else {
|
|
425
|
+
$parentElement.removeAttribute("target");
|
|
426
|
+
}
|
|
427
|
+
_setRangeCursor($parentElement, $parentElement, 0, $parentElement.textContent?.length ?? 0);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
const cursorData = _soltingCursorDataOnElement(store.cursorData, $element);
|
|
434
|
+
let structure = "";
|
|
435
|
+
let isDuble = false;
|
|
436
|
+
if (cursorData.startNodeIdx === cursorData.endNodeIdx) {
|
|
437
|
+
if (cursorData.startNode.constructor.name === "Text") {
|
|
438
|
+
$element.childNodes.forEach((childNode, i) => {
|
|
439
|
+
if (cursorData.startNodeIdx === i) {
|
|
440
|
+
if (cursorData.startOffset !== 0) {
|
|
441
|
+
structure += childNode.textContent.slice(0, cursorData.startOffset);
|
|
442
|
+
isDuble = true;
|
|
443
|
+
}
|
|
444
|
+
structure += `<a class="de-link"`;
|
|
445
|
+
structure += `href="${hrefValue}"`;
|
|
446
|
+
if (isOutsideLink === true) {
|
|
447
|
+
structure += `target="_blank"`;
|
|
448
|
+
}
|
|
449
|
+
structure += `>`;
|
|
450
|
+
structure += `${childNode.textContent.slice(cursorData.startOffset, cursorData.endOffset)}</a>`;
|
|
451
|
+
if (cursorData.endOffset !== childNode.textContent.length) {
|
|
452
|
+
structure += childNode.textContent.slice(cursorData.endOffset);
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
if (childNode.constructor.name === "Text") {
|
|
456
|
+
structure += childNode.textContent;
|
|
457
|
+
} else {
|
|
458
|
+
structure += childNode.outerHTML;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
let childNumber = cursorData.startNodeIdx;
|
|
463
|
+
if (isDuble === true) {
|
|
464
|
+
childNumber += 1;
|
|
465
|
+
}
|
|
466
|
+
$element.innerHTML = structure;
|
|
467
|
+
const targetNode = $element.childNodes[childNumber];
|
|
468
|
+
_setRangeCursor(targetNode, targetNode, 0, targetNode.textContent?.length ?? 0);
|
|
469
|
+
} else {
|
|
470
|
+
const $target = cursorData.startNode;
|
|
471
|
+
if ($target.tagName !== "A") {
|
|
472
|
+
const classList = $target.classList.value.split(" ");
|
|
473
|
+
if (cursorData.startOffset !== 0) {
|
|
474
|
+
structure += `<span class="${classList.join(" ")}">${$target.textContent.slice(0, cursorData.startOffset)}</span>`;
|
|
475
|
+
isDuble = true;
|
|
476
|
+
}
|
|
477
|
+
structure += `<a class="de-link"`;
|
|
478
|
+
structure += `href="${hrefValue}"`;
|
|
479
|
+
if (isOutsideLink === true) {
|
|
480
|
+
structure += `target="_blank"`;
|
|
481
|
+
}
|
|
482
|
+
structure += `>`;
|
|
483
|
+
structure += `${$target.textContent.slice(cursorData.startOffset, cursorData.endOffset)}</a>`;
|
|
484
|
+
if (cursorData.endOffset !== $target.textContent.length) {
|
|
485
|
+
structure += `<span class="${classList.join(" ")}">${$target.textContent.slice(cursorData.endOffset)}</span>`;
|
|
486
|
+
}
|
|
487
|
+
$target.insertAdjacentHTML("afterend", structure);
|
|
488
|
+
let $nextElement = $target.nextSibling;
|
|
489
|
+
if (isDuble === true) {
|
|
490
|
+
$nextElement = $nextElement.nextSibling;
|
|
491
|
+
}
|
|
492
|
+
$target.remove();
|
|
493
|
+
const targetNode = $nextElement;
|
|
494
|
+
_setRangeCursor(targetNode, targetNode, 0, targetNode.textContent?.length ?? 0);
|
|
495
|
+
} else {
|
|
496
|
+
$target.href = hrefValue;
|
|
497
|
+
if (isOutsideLink === true) {
|
|
498
|
+
$target.target = "_blank";
|
|
499
|
+
} else {
|
|
500
|
+
$target.removeAttribute("target");
|
|
501
|
+
}
|
|
502
|
+
_setRangeCursor($target, $target, 0, $target.textContent?.length ?? 0);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
} else {
|
|
506
|
+
let preStructure = "";
|
|
507
|
+
let anchorTextContent = "";
|
|
508
|
+
let nextStructure = "";
|
|
509
|
+
$element.childNodes.forEach((childNode, i) => {
|
|
510
|
+
const $elementNode = childNode;
|
|
511
|
+
let isText = childNode.constructor.name === "Text";
|
|
512
|
+
if (cursorData.startNodeIdx > i) {
|
|
513
|
+
if (isText === true) {
|
|
514
|
+
preStructure += childNode.textContent;
|
|
515
|
+
} else {
|
|
516
|
+
preStructure += $elementNode.outerHTML;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (cursorData.startNodeIdx === i) {
|
|
520
|
+
if (isText === true) {
|
|
521
|
+
preStructure += childNode.textContent.slice(0, cursorData.startOffset);
|
|
522
|
+
} else {
|
|
523
|
+
preStructure += `<span class="${childNode.classList.value}">${childNode.textContent.slice(0, cursorData.startOffset)}</span>`;
|
|
524
|
+
}
|
|
525
|
+
anchorTextContent += childNode.textContent.slice(cursorData.startOffset);
|
|
526
|
+
}
|
|
527
|
+
if (cursorData.startNodeIdx < i && cursorData.endNodeIdx > i) {
|
|
528
|
+
anchorTextContent += childNode.textContent;
|
|
529
|
+
}
|
|
530
|
+
if (cursorData.endNodeIdx === i) {
|
|
531
|
+
anchorTextContent += childNode.textContent.slice(0, cursorData.endOffset);
|
|
532
|
+
if (isText === true) {
|
|
533
|
+
nextStructure += childNode.textContent.slice(cursorData.startOffset);
|
|
534
|
+
} else {
|
|
535
|
+
nextStructure += `<span class="${childNode.classList.value}">${childNode.textContent.slice(cursorData.startOffset)}</span>`;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
if (cursorData.endNodeIdx < i) {
|
|
539
|
+
if (isText === true) {
|
|
540
|
+
nextStructure += childNode.textContent;
|
|
541
|
+
} else {
|
|
542
|
+
nextStructure += $elementNode.outerHTML;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
let anchorTag = "";
|
|
547
|
+
anchorTag += `<a class="de-link"`;
|
|
548
|
+
anchorTag += `href="${hrefValue}"`;
|
|
549
|
+
if (isOutsideLink === true) {
|
|
550
|
+
anchorTag += `target="_blank"`;
|
|
551
|
+
}
|
|
552
|
+
anchorTag += `>`;
|
|
553
|
+
anchorTag += anchorTextContent;
|
|
554
|
+
anchorTag += `</a>`;
|
|
555
|
+
$element.innerHTML = preStructure + anchorTag + nextStructure;
|
|
556
|
+
let targetNode = $element.childNodes[cursorData.startNodeIdx];
|
|
557
|
+
if (cursorData.startOffset !== 0) {
|
|
558
|
+
targetNode = $element.childNodes[cursorData.startNodeIdx + 1];
|
|
559
|
+
}
|
|
560
|
+
_setRangeCursor(targetNode, targetNode, 0, targetNode.textContent?.length ?? 0);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
_clenupCursor(store);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
export function _unsetAnchorTag(store) {
|
|
569
|
+
if (store.cursorData !== null) {
|
|
570
|
+
const $element = _findContentEditableElement(store.cursorData.startNode);
|
|
571
|
+
if ($element !== null) {
|
|
572
|
+
const cursorData = _soltingCursorDataOnElement(store.cursorData, $element);
|
|
573
|
+
if (cursorData.startNode.constructor.name === "HTMLAnchorElement") {
|
|
574
|
+
if (store.cursorData.type === "Range") {
|
|
575
|
+
if (cursorData.startNodeIdx !== cursorData.endNodeIdx) {
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
const $anchorTag = cursorData.startNode;
|
|
580
|
+
$anchorTag.insertAdjacentText("afterend", cursorData.startNode.textContent ?? "");
|
|
581
|
+
const $targetNode = $anchorTag.nextSibling;
|
|
582
|
+
$anchorTag.remove();
|
|
583
|
+
_setRangeCursor($targetNode, $targetNode, 0, $targetNode.textContent?.length ?? 0);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
export function _getAnchorTagValue(store) {
|
|
589
|
+
let href = "";
|
|
590
|
+
if (store.cursorData !== null) {
|
|
591
|
+
const $element = _findContentEditableElement(store.cursorData.startNode);
|
|
592
|
+
if ($element !== null) {
|
|
593
|
+
const cursorData = _soltingCursorDataOnElement(store.cursorData, $element);
|
|
594
|
+
if (cursorData.startNode.constructor.name === "HTMLAnchorElement") {
|
|
595
|
+
if (store.cursorData.type === "Range") {
|
|
596
|
+
if (cursorData.startNodeIdx !== cursorData.endNodeIdx) {
|
|
597
|
+
return href;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
const $anchorTag = cursorData.startNode;
|
|
601
|
+
href = $anchorTag.href;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return href;
|
|
606
|
+
}
|
|
607
|
+
function findPoverTextNode(node, idx) {
|
|
608
|
+
if (node.previousSibling !== null) {
|
|
609
|
+
if (node.previousSibling.constructor.name === "Text") {
|
|
610
|
+
return findPoverTextNode(node.previousSibling, idx -= 1);
|
|
611
|
+
} else {
|
|
612
|
+
return idx;
|
|
613
|
+
}
|
|
614
|
+
} else {
|
|
615
|
+
return idx;
|
|
616
|
+
}
|
|
617
|
+
}
|