dragon-editor 3.4.5 → 3.5.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/README.md +9 -15
- package/dist/module.d.mts +5 -0
- package/dist/module.json +8 -1
- package/dist/module.mjs +5 -3
- package/dist/runtime/components/DragonEditor.vue +252 -734
- package/dist/runtime/components/DragonEditorViewer.vue +59 -45
- package/dist/runtime/scss/editor.css +83 -34
- package/dist/runtime/scss/viewer.css +31 -4
- package/dist/runtime/type.d.ts +78 -23
- package/dist/runtime/utils/event/block.d.ts +0 -0
- package/dist/runtime/utils/event/block.js +78 -0
- package/dist/runtime/utils/event/cursor.d.ts +0 -0
- package/dist/runtime/utils/{cursor.mjs → event/cursor.js} +4 -16
- package/dist/runtime/utils/event/data.d.ts +0 -0
- package/dist/runtime/utils/event/data.js +342 -0
- package/dist/runtime/utils/event/index.d.ts +8 -0
- package/dist/runtime/utils/event/index.js +8 -0
- package/dist/runtime/utils/event/keyboard.d.ts +0 -0
- package/dist/runtime/utils/event/keyboard.js +1368 -0
- package/dist/runtime/utils/event/mouse.d.ts +0 -0
- package/dist/runtime/utils/event/mouse.js +70 -0
- package/dist/runtime/utils/event/scroll.d.ts +0 -0
- package/dist/runtime/utils/event/scroll.js +29 -0
- package/dist/runtime/utils/event/touch.d.ts +0 -0
- package/dist/runtime/utils/event/touch.js +10 -0
- package/dist/runtime/utils/event/window.d.ts +0 -0
- package/dist/runtime/utils/event/window.js +32 -0
- package/dist/runtime/utils/layout/block.d.ts +0 -0
- package/dist/runtime/utils/layout/block.js +105 -0
- package/dist/runtime/utils/layout/body.d.ts +0 -0
- package/dist/runtime/utils/layout/body.js +22 -0
- package/dist/runtime/utils/layout/controlbar.d.ts +0 -0
- package/dist/runtime/utils/layout/controlbar.js +99 -0
- package/dist/runtime/utils/layout/icon.d.ts +0 -0
- package/dist/runtime/utils/layout/icon.js +156 -0
- package/dist/runtime/utils/layout/index.d.ts +5 -0
- package/dist/runtime/utils/layout/index.js +5 -0
- package/dist/runtime/utils/layout/menuBar.d.ts +0 -0
- package/dist/runtime/utils/layout/menuBar.js +358 -0
- package/dist/runtime/utils/node/block.d.ts +0 -0
- package/dist/runtime/utils/node/block.js +235 -0
- package/dist/runtime/utils/{element.d.ts → node/element.d.ts} +1 -0
- package/dist/runtime/utils/{element.mjs → node/element.js} +11 -0
- package/dist/runtime/utils/node/index.d.ts +2 -0
- package/dist/runtime/utils/node/index.js +2 -0
- package/dist/runtime/utils/style/anchor.d.ts +0 -0
- package/dist/runtime/utils/style/anchor.js +240 -0
- package/dist/runtime/utils/style/decoration.d.ts +0 -0
- package/dist/runtime/utils/style/decoration.js +378 -0
- package/dist/runtime/utils/style/index.d.ts +2 -0
- package/dist/runtime/utils/style/index.js +2 -0
- package/dist/types.d.mts +7 -0
- package/dist/types.d.ts +3 -3
- package/package.json +15 -16
- package/dist/runtime/store.d.ts +0 -11
- package/dist/runtime/store.mjs +0 -51
- package/dist/runtime/utils/block.d.ts +0 -13
- package/dist/runtime/utils/block.mjs +0 -144
- package/dist/runtime/utils/content.d.ts +0 -2
- package/dist/runtime/utils/content.mjs +0 -19
- package/dist/runtime/utils/controlBar.d.ts +0 -9
- package/dist/runtime/utils/controlBar.mjs +0 -172
- package/dist/runtime/utils/convertor.d.ts +0 -3
- package/dist/runtime/utils/convertor.mjs +0 -138
- package/dist/runtime/utils/cursor.d.ts +0 -6
- package/dist/runtime/utils/keyboardEvent.d.ts +0 -10
- package/dist/runtime/utils/keyboardEvent.mjs +0 -979
- package/dist/runtime/utils/style.d.ts +0 -5
- package/dist/runtime/utils/style.mjs +0 -617
- /package/dist/runtime/{plugin.mjs → plugin.js} +0 -0
|
@@ -1,741 +1,210 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<div v-if="props.useMenuBar === true" class="de-menu-bar" :style="{ top: `${menuBarTop}px` }">
|
|
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>
|
|
110
|
-
|
|
111
|
-
<div class="de-block-menu-area" :class="{ '--active': isActiveAddBlockMenu }">
|
|
112
|
-
<div class="de-list">
|
|
113
|
-
<button class="de-add-block" @click="addBlock('text')">Text</button>
|
|
114
|
-
<button class="de-add-block" @click="addBlock('heading1')">Heading-1</button>
|
|
115
|
-
<button class="de-add-block" @click="addBlock('heading2')">Heading-2</button>
|
|
116
|
-
<button class="de-add-block" @click="addBlock('heading3')">Heading-3</button>
|
|
117
|
-
<button class="de-add-block" @click="addBlock('ul')">Unodered List</button>
|
|
118
|
-
<button class="de-add-block" @click="addBlock('ol')">Odered List</button>
|
|
119
|
-
<button class="de-add-block" @click="addBlock('code')">Code Block</button>
|
|
120
|
-
<!-- <button class="de-add-block" @click="addBlock('table')">Table Block</button> -->
|
|
121
|
-
<!-- <button class="de-add-block" @click="addBlock('video')">Video</button> youtube | vimeo -->
|
|
122
|
-
</div>
|
|
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>
|
|
140
|
-
</div>
|
|
141
|
-
|
|
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">
|
|
143
|
-
<div v-if="['code'].includes(curruntType) === true" class="de-col">
|
|
144
|
-
<p class="de-name">Theme :</p>
|
|
145
|
-
<select class="de-selector" v-model="codeBlockTheme" @change="codeBlockThemeChangeEvent">
|
|
146
|
-
<option v-for="(item, i) in _getCodeBlockTheme()" :value="item.code" :key="`codeBlockTheme-${i}`">{{ item.text }}</option>
|
|
147
|
-
</select>
|
|
148
|
-
</div>
|
|
149
|
-
|
|
150
|
-
<div v-if="['code'].includes(curruntType) === true" class="de-col">
|
|
151
|
-
<p class="de-name">Language :</p>
|
|
152
|
-
<select class="de-selector" v-model="codeblockLanguage" @change="codeblockLanguageChangeEvent">
|
|
153
|
-
<option v-for="(item, i) in _getCodeBlockLanguage()" :value="item.code" :key="`codeBlockLanuage-${i}`">{{ item.text }}</option>
|
|
154
|
-
</select>
|
|
155
|
-
</div>
|
|
156
|
-
|
|
157
|
-
<div v-if="['list'].includes(curruntType) === true" class="de-col">
|
|
158
|
-
<p class="de-name">List Style :</p>
|
|
159
|
-
<select class="de-selector" v-model="listBlockStyle" @change="listBlockStyleChangeEvent">
|
|
160
|
-
<template v-if="editorStore.$currentBlock.tagName === 'UL'">
|
|
161
|
-
<option value="disc">Disc</option>
|
|
162
|
-
<option value="square">Square</option>
|
|
163
|
-
</template>
|
|
164
|
-
|
|
165
|
-
<template v-else>
|
|
166
|
-
<option value="decimal">Decimal</option>
|
|
167
|
-
<option value="lower-alpha">Lower-Alpha</option>
|
|
168
|
-
<option value="upper-alpha">Upper-Alpha</option>
|
|
169
|
-
<option value="lower-roman">Lower-Roman</option>
|
|
170
|
-
<option value="upper-roman">Upper-Roman</option>
|
|
171
|
-
</template>
|
|
172
|
-
</select>
|
|
173
|
-
</div>
|
|
174
|
-
</div>
|
|
175
|
-
|
|
176
|
-
<div class="de-body" ref="$content" @keydown="contentKeyboardEvent" @mouseup="updateCursorData" @mousedown="resizeEventStart" @touchstart="resizeEventStart" @paste="contentPasteEvent">
|
|
177
|
-
<p class="de-block de-text-block" contenteditable="true"></p>
|
|
178
|
-
</div>
|
|
179
|
-
</div>
|
|
2
|
+
<component :is="mainStrucutre()"></component>
|
|
180
3
|
</template>
|
|
181
4
|
|
|
182
5
|
<script setup lang="ts">
|
|
183
|
-
import { ref, onMounted,
|
|
184
|
-
import {
|
|
185
|
-
import {
|
|
186
|
-
import {
|
|
187
|
-
import {
|
|
188
|
-
import {
|
|
189
|
-
import { _setNodeStyle, _setTextAlign, _setAnchorTag, _unsetAnchorTag, _getAnchorTagValue } from "../utils/style";
|
|
190
|
-
import { _setCursor, _setCursorData, _clenupCursor } from "../utils/cursor";
|
|
191
|
-
import { _getContentData, _setContentData } from "../utils/convertor";
|
|
192
|
-
import { _addBlockToContent } from "../utils/content";
|
|
6
|
+
import { ref, h, onMounted, onBeforeUnmount } from "vue";
|
|
7
|
+
import { _getBodyVNodeStructure, _getMenuBarVNodeStructure, _getControlbarVNodeStructure } from "../utils/layout";
|
|
8
|
+
import { _eidtorMountEvent, _eidtorUnmountEvent, _editorMousemoveEvent, _editorMouseupEvent, _editorMouseleaveEvent, _editorTouchmoveEvent, _editorTouchendEvent, _checkOthersideClick, _parentWrapScollEvent, _editorContextMenuEvent, _windowResizeEvent } from "../utils/event";
|
|
9
|
+
import { _addBlock } from "../utils/node";
|
|
10
|
+
import { _setDecoration, _setTextAlign } from "../utils/style";
|
|
11
|
+
import type { VNode } from "vue";
|
|
193
12
|
import "../type.d.ts";
|
|
194
13
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
default: () => "",
|
|
205
|
-
},
|
|
14
|
+
interface DEOption {
|
|
15
|
+
modelValue: DEContentData;
|
|
16
|
+
useMenuBar?: boolean;
|
|
17
|
+
imageHostURL?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const props = withDefaults(defineProps<DEOption>(), {
|
|
21
|
+
useMenuBar: true,
|
|
22
|
+
imageHostURL: "",
|
|
206
23
|
});
|
|
207
24
|
const emit = defineEmits<{
|
|
208
|
-
(e: "
|
|
25
|
+
(e: "update:modelValue", data: DEContentData): void;
|
|
26
|
+
(e: "uploadImageEvent", file: File): void;
|
|
209
27
|
}>();
|
|
210
|
-
const editorStore =
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
resizeEndX = resizeStartX;
|
|
313
|
-
|
|
314
|
-
const $imgArea = (editorStore.$currentBlock as HTMLElement).querySelector(".de-image-area") as HTMLDivElement;
|
|
315
|
-
resizeCurruntWidth = parseInt($imgArea.dataset["maxwidth"] ?? "25");
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// 사이즈 조정 이벤트
|
|
321
|
-
function resizeEvent(event: Event) {
|
|
322
|
-
if (resizeEventActive === true) {
|
|
323
|
-
const $imgArea = (editorStore.$currentBlock as HTMLElement).querySelector(".de-image-area") as HTMLDivElement;
|
|
324
|
-
const contentWidth = (editorStore.$content?.offsetWidth ?? 0) / 2;
|
|
325
|
-
let gap: number = 0;
|
|
326
|
-
|
|
327
|
-
if (event.type === "touchmove") {
|
|
328
|
-
resizeEndX = (event as TouchEvent).touches[0].clientX;
|
|
329
|
-
} else {
|
|
330
|
-
resizeEndX = (event as MouseEvent).clientX;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if (resizeType === "right") {
|
|
334
|
-
gap = Math.floor(resizeStartX - resizeEndX);
|
|
335
|
-
} else {
|
|
336
|
-
gap = Math.floor(resizeEndX - resizeStartX);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const percent = (100 / contentWidth) * gap;
|
|
340
|
-
let value = Math.floor(resizeCurruntWidth - percent);
|
|
341
|
-
|
|
342
|
-
if (value < 25) {
|
|
343
|
-
value = 25;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (value > 100) {
|
|
347
|
-
value = 100;
|
|
348
|
-
}
|
|
28
|
+
const editorStore = ref<DragonEditorStore>({
|
|
29
|
+
cursorData: null,
|
|
30
|
+
message: {
|
|
31
|
+
linkTextNoStyle: "Link text can't set any style.",
|
|
32
|
+
},
|
|
33
|
+
controlBar: {
|
|
34
|
+
active: false,
|
|
35
|
+
x: 0,
|
|
36
|
+
y: 0,
|
|
37
|
+
$element: null,
|
|
38
|
+
},
|
|
39
|
+
useMenuBar: props.useMenuBar,
|
|
40
|
+
imageHostURL: props.imageHostURL,
|
|
41
|
+
firstData: props.modelValue,
|
|
42
|
+
menuBarTop: 0,
|
|
43
|
+
activeStatus: {
|
|
44
|
+
addBlockMenu: false,
|
|
45
|
+
anchorInputArea: false,
|
|
46
|
+
imageResizeEvent: false,
|
|
47
|
+
},
|
|
48
|
+
eventStatus: {
|
|
49
|
+
preComposing: false,
|
|
50
|
+
imageResizeEventStartX: 0,
|
|
51
|
+
imageResizeEventType: "left",
|
|
52
|
+
imageResizeEventEndX: 0,
|
|
53
|
+
imageResizeCurrentWidth: 0,
|
|
54
|
+
keyboardEnterCount: 0,
|
|
55
|
+
},
|
|
56
|
+
controlStatus: {
|
|
57
|
+
isMobile: false,
|
|
58
|
+
currentBlockType: "text",
|
|
59
|
+
codeBlockTheme: "github",
|
|
60
|
+
codeBlockLang: "text",
|
|
61
|
+
listBlockStyle: "disc",
|
|
62
|
+
anchorTabType: "url",
|
|
63
|
+
anchorHeadingList: [],
|
|
64
|
+
anchorHref: "",
|
|
65
|
+
anchorValidation: false,
|
|
66
|
+
previousCorsorData: null,
|
|
67
|
+
$anchorInput: null,
|
|
68
|
+
$currentBlock: null,
|
|
69
|
+
},
|
|
70
|
+
codeBlockTheme: [
|
|
71
|
+
{
|
|
72
|
+
text: "GitHub",
|
|
73
|
+
code: "github",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
text: "GitHub Dark Dimmed",
|
|
77
|
+
code: "github-dark-dimmed",
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
listUlType: [
|
|
81
|
+
{
|
|
82
|
+
text: "Disc",
|
|
83
|
+
code: "disc",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
text: "Square",
|
|
87
|
+
code: "square",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
listOlType: [
|
|
91
|
+
{
|
|
92
|
+
text: "Decimal",
|
|
93
|
+
code: "decimal",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
text: "Lower-Alpha",
|
|
97
|
+
code: "lower-alpha",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
text: "Upper-Alpha",
|
|
101
|
+
code: "upper-alpha",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
text: "Lower-Roman",
|
|
105
|
+
code: "lower-roman",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
text: "Upper-Roman",
|
|
109
|
+
code: "upper-roman",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
$editor: null,
|
|
113
|
+
$body: null,
|
|
114
|
+
$controlBar: null,
|
|
115
|
+
$parentWrap: null,
|
|
116
|
+
emit: emit,
|
|
117
|
+
windowClickEvent: function (event: MouseEvent) {
|
|
118
|
+
_checkOthersideClick(event, editorStore);
|
|
119
|
+
},
|
|
120
|
+
windowResizeEvent: function (event: Event) {
|
|
121
|
+
_windowResizeEvent(event, editorStore);
|
|
122
|
+
},
|
|
123
|
+
windowMouseUpEvent: function (event: MouseEvent) {
|
|
124
|
+
_editorMouseupEvent(event, editorStore);
|
|
125
|
+
},
|
|
126
|
+
parentWrapScollEvent: function (event: Event) {
|
|
127
|
+
_parentWrapScollEvent(event, editorStore);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
349
130
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
}
|
|
131
|
+
function mainStrucutre(): VNode {
|
|
132
|
+
const childList: VNode[] = [];
|
|
353
133
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
if (resizeEventActive === true) {
|
|
357
|
-
resizeEventActive = false;
|
|
134
|
+
if (editorStore.value.useMenuBar === true) {
|
|
135
|
+
childList.push(_getMenuBarVNodeStructure(editorStore));
|
|
358
136
|
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// 메뉴 외부 클릭시 닫기
|
|
362
|
-
function checkOthersideClick(event: MouseEvent) {
|
|
363
|
-
if (event.target !== null) {
|
|
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;
|
|
371
|
-
|
|
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
137
|
|
|
385
|
-
|
|
386
|
-
isActiveAddBlockMenu.value = false;
|
|
387
|
-
}
|
|
138
|
+
childList.push(_getBodyVNodeStructure(editorStore));
|
|
388
139
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
anchorTagValue.value = "";
|
|
392
|
-
}
|
|
140
|
+
if (editorStore.value.controlBar.active === true) {
|
|
141
|
+
childList.push(_getControlbarVNodeStructure(editorStore));
|
|
393
142
|
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// 블럭 삭제
|
|
397
|
-
function deleteBlock() {
|
|
398
|
-
if (editorStore.$currentBlock !== null) {
|
|
399
|
-
const childCount: number = editorStore.$content?.childElementCount ?? 1;
|
|
400
|
-
const preElement = editorStore.$currentBlock.previousElementSibling;
|
|
401
|
-
|
|
402
|
-
editorStore.$currentBlock.remove();
|
|
403
|
-
|
|
404
|
-
if (preElement === null) {
|
|
405
|
-
editorStore.setCurrentBlock(null);
|
|
406
|
-
} else {
|
|
407
|
-
const { type } = _getBlockType(editorStore.$currentBlock);
|
|
408
|
-
const activeList = ["text", "heading"];
|
|
409
143
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
144
|
+
return h(
|
|
145
|
+
"div",
|
|
146
|
+
{
|
|
147
|
+
class: ["dragon-editor", "js-dragon-editor", { "--has-menu": editorStore.value.useMenuBar === true }, { "--mobile": editorStore.value.controlStatus.isMobile === true }],
|
|
148
|
+
onMousemove: (event: MouseEvent) => _editorMousemoveEvent(event, editorStore),
|
|
149
|
+
onMouseup: (event: MouseEvent) => _editorMouseupEvent(event, editorStore),
|
|
150
|
+
onMouseleave: (event: MouseEvent) => _editorMouseleaveEvent(event, editorStore),
|
|
151
|
+
onTouchmove: (event: TouchEvent) => _editorTouchmoveEvent(event, editorStore),
|
|
152
|
+
onTouchend: (event: TouchEvent) => _editorTouchendEvent(event, editorStore),
|
|
153
|
+
onContextmenu: (event: MouseEvent) => _editorContextMenuEvent(event, editorStore),
|
|
154
|
+
},
|
|
155
|
+
childList
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 외부용 블럭 추가 함수
|
|
160
|
+
function addBlock(data: DEBlockData): void {
|
|
161
|
+
let type: DEBlockMenutype = "text";
|
|
162
|
+
|
|
163
|
+
switch (data.type) {
|
|
164
|
+
case "heading":
|
|
165
|
+
if (data.level === 1) {
|
|
166
|
+
type = "heading1";
|
|
167
|
+
} else if (data.level === 2) {
|
|
168
|
+
type = "heading2";
|
|
413
169
|
} else {
|
|
414
|
-
|
|
170
|
+
type = "heading3";
|
|
415
171
|
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
if (childCount < 2) {
|
|
419
|
-
// 모든 엘리먼트를 지우려는 경우
|
|
420
|
-
const $block = _createTextBlock();
|
|
421
|
-
|
|
422
|
-
editorStore.$content?.insertAdjacentElement("beforeend", $block);
|
|
423
|
-
_setCursor($block, 0);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// 부모 요소 스크롤 이벤트 발생시 컨트롤 바 고정
|
|
429
|
-
function parentWrapScollEvent() {
|
|
430
|
-
if (props.useMenuBar === true && editorStore.$parentWrap !== null && editorStore.$editor !== null) {
|
|
431
|
-
// 메뉴바를 사용하는 경우만
|
|
432
|
-
|
|
433
|
-
const editorReac = editorStore.$editor.getBoundingClientRect();
|
|
434
|
-
let scrollY: number = 0;
|
|
435
|
-
|
|
436
|
-
if (editorStore.$parentWrap.constructor.name === "Window") {
|
|
437
|
-
scrollY = (editorStore.$parentWrap as Window).scrollY;
|
|
438
|
-
} else {
|
|
439
|
-
scrollY = (editorStore.$parentWrap as HTMLElement).scrollTop;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
let realElementY = editorReac.y + scrollY;
|
|
443
|
-
|
|
444
|
-
if (editorStore.$parentWrap.constructor.name !== "Window") {
|
|
445
|
-
const parentRect = (editorStore.$parentWrap as HTMLElement).getBoundingClientRect();
|
|
446
|
-
|
|
447
|
-
realElementY -= parentRect.y;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
let value: number = 0;
|
|
451
|
-
|
|
452
|
-
if (scrollY > realElementY) {
|
|
453
|
-
value = scrollY - realElementY - 1;
|
|
454
|
-
} else {
|
|
455
|
-
value = 0;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
if (value > editorReac.height - 39) {
|
|
459
|
-
value = editorReac.height - 39;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
menuBarTop.value = Math.floor(value);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// 붙여넣기 이벤트
|
|
467
|
-
function contentPasteEvent(event: ClipboardEvent) {
|
|
468
|
-
_pasteEvent(event, editorStore, emit);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
function anchorTagValueUpdate() {
|
|
472
|
-
// 다른 이벤트 순서에 의한 딜레이
|
|
473
|
-
setTimeout(() => {
|
|
474
|
-
anchorTagValue.value = _getAnchorTagValue(editorStore);
|
|
475
|
-
}, 500);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* 이벤트 관련 영역 종료
|
|
480
|
-
*/
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* 컨트롤 바 이벤트 관련 영역 시작
|
|
484
|
-
*/
|
|
485
|
-
|
|
486
|
-
// 코드 블럭 테마 적용
|
|
487
|
-
function codeBlockThemeChangeEvent() {
|
|
488
|
-
_setCodeBlockTheme(editorStore, codeBlockTheme.value);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// 코드 블럭 언어 적용
|
|
492
|
-
function codeblockLanguageChangeEvent() {
|
|
493
|
-
_setCodeBlockLanguage(editorStore, codeblockLanguage.value);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
// 리스트 스타일 적용
|
|
497
|
-
function listBlockStyleChangeEvent() {
|
|
498
|
-
_setListBlockStyle(editorStore, listBlockStyle.value);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* 컨트롤 바 이벤트 관련 영역 종료
|
|
503
|
-
*/
|
|
504
|
-
|
|
505
|
-
/**
|
|
506
|
-
* 메뉴 이벤트 관련 영역
|
|
507
|
-
*/
|
|
508
|
-
function addBlock(type: string) {
|
|
509
|
-
isActiveAddBlockMenu.value = false;
|
|
510
|
-
|
|
511
|
-
let blockStructure: HTMLElement | null = null;
|
|
512
|
-
|
|
513
|
-
switch (type) {
|
|
514
|
-
case "text":
|
|
515
|
-
blockStructure = _createTextBlock();
|
|
516
|
-
break;
|
|
517
|
-
case "heading1":
|
|
518
|
-
case "heading2":
|
|
519
|
-
case "heading3":
|
|
520
|
-
const level: number = parseInt(type.replace("heading", ""));
|
|
521
|
-
|
|
522
|
-
blockStructure = _createHeadingBlock({
|
|
523
|
-
type: "heading",
|
|
524
|
-
classList: [],
|
|
525
|
-
id: "",
|
|
526
|
-
level: level,
|
|
527
|
-
textContent: "",
|
|
528
|
-
});
|
|
529
|
-
break;
|
|
530
|
-
case "ul":
|
|
531
|
-
case "ol":
|
|
532
|
-
blockStructure = _createListBlock({
|
|
533
|
-
type: "list",
|
|
534
|
-
element: type,
|
|
535
|
-
style: type === "ul" ? "disc" : "decimal",
|
|
536
|
-
child: [
|
|
537
|
-
{
|
|
538
|
-
classList: [],
|
|
539
|
-
textContent: "",
|
|
540
|
-
},
|
|
541
|
-
],
|
|
542
|
-
});
|
|
543
|
-
break;
|
|
544
|
-
case "table":
|
|
545
|
-
// TODO : table block
|
|
546
|
-
break;
|
|
547
|
-
case "code":
|
|
548
|
-
blockStructure = _createCodeBlock({
|
|
549
|
-
type: "code",
|
|
550
|
-
theme: "github",
|
|
551
|
-
filename: "",
|
|
552
|
-
language: "Plain Text",
|
|
553
|
-
textContent: "",
|
|
554
|
-
});
|
|
555
172
|
break;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
if (blockStructure !== null) {
|
|
559
|
-
_addBlockToContent(blockStructure, editorStore);
|
|
560
|
-
|
|
561
|
-
switch (type) {
|
|
562
|
-
case "ul":
|
|
563
|
-
case "ol":
|
|
564
|
-
(blockStructure.childNodes[0] as HTMLElement).focus();
|
|
565
|
-
break;
|
|
566
|
-
case "codeblock":
|
|
567
|
-
blockStructure.querySelector("code")?.focus();
|
|
568
|
-
break;
|
|
569
|
-
default:
|
|
570
|
-
blockStructure.focus();
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
editorStore.setCurrentBlock(blockStructure as HTMLElement);
|
|
574
|
-
controlBarStatusUpdate();
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function addCustomBlock(HTML: string, classList: string[] = []) {
|
|
579
|
-
const blockStructure = _createCustomBlock({
|
|
580
|
-
type: "custom",
|
|
581
|
-
classList: classList,
|
|
582
|
-
textContent: HTML,
|
|
583
|
-
});
|
|
584
173
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
function addImageBlock(data: DEImage) {
|
|
589
|
-
if (props.imageHostURL !== "") {
|
|
590
|
-
data.src = props.imageHostURL + data.src;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
const blockStructure = _createImageBlock({
|
|
594
|
-
...data,
|
|
595
|
-
type: "image",
|
|
596
|
-
maxWidth: 100,
|
|
597
|
-
classList: [],
|
|
598
|
-
} as DEImageBlock);
|
|
599
|
-
|
|
600
|
-
_addBlockToContent(blockStructure, editorStore);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
function setDecoration(type: DEDecoration) {
|
|
604
|
-
_setNodeStyle(`de-${type}`, editorStore);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
function setTextAlign(type: DETextalign) {
|
|
608
|
-
_setTextAlign(type, editorStore);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
function getContentData(): DEContentData {
|
|
612
|
-
if (editorStore.$content !== null) {
|
|
613
|
-
return _getContentData(editorStore.$content, props.imageHostURL);
|
|
614
|
-
} else {
|
|
615
|
-
console.error("[DragonEditor] Con't find content Element.");
|
|
616
|
-
return [];
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
function setContentData(data: DEContentData) {
|
|
621
|
-
_setContentData(data, editorStore, props.imageHostURL);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
function moveBlock(type: "up" | "down") {
|
|
625
|
-
if (editorStore.$currentBlock !== null) {
|
|
626
|
-
let $target: Element | null;
|
|
627
|
-
|
|
628
|
-
if (type === "up") {
|
|
629
|
-
$target = editorStore.$currentBlock.previousElementSibling;
|
|
630
|
-
} else {
|
|
631
|
-
$target = editorStore.$currentBlock.nextElementSibling;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
if ($target !== null) {
|
|
635
|
-
($target as HTMLElement).insertAdjacentHTML(type === "up" ? "beforebegin" : "afterend", editorStore.$currentBlock.outerHTML);
|
|
636
|
-
editorStore.$currentBlock.remove();
|
|
637
|
-
|
|
638
|
-
if (type === "up") {
|
|
639
|
-
editorStore.setCurrentBlock(($target as HTMLElement).previousElementSibling as HTMLElement | null);
|
|
174
|
+
case "list":
|
|
175
|
+
if (data.element === "ol") {
|
|
176
|
+
type = "ol";
|
|
640
177
|
} else {
|
|
641
|
-
|
|
178
|
+
type = "ul";
|
|
642
179
|
}
|
|
643
|
-
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
function openLinkArea() {
|
|
648
|
-
activeLinkTabType.value = "url";
|
|
649
|
-
anchorValueError.value = false;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
function chooseMediaEvent(event: Event) {
|
|
653
|
-
const $target = event.target as HTMLInputElement;
|
|
654
|
-
const file = $target.files![0];
|
|
655
|
-
|
|
656
|
-
emit("uploadImageEvent", file);
|
|
657
|
-
$target.value = "";
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
// 링크 삽입
|
|
661
|
-
function setLink() {
|
|
662
|
-
if ($linkInput.value !== null && $linkInput.value?.checkValidity() === true && anchorTagValue.value !== "") {
|
|
663
|
-
_setAnchorTag(anchorTagValue.value, true, editorStore);
|
|
664
|
-
isActiveLinkArea.value = false;
|
|
665
|
-
anchorValueError.value = false;
|
|
666
|
-
anchorTagValue.value = "";
|
|
667
|
-
} else {
|
|
668
|
-
anchorValueError.value = true;
|
|
180
|
+
break;
|
|
669
181
|
}
|
|
670
|
-
}
|
|
671
182
|
|
|
672
|
-
|
|
673
|
-
_setAnchorTag(id, false, editorStore);
|
|
674
|
-
isActiveLinkArea.value = false;
|
|
675
|
-
anchorValueError.value = false;
|
|
676
|
-
anchorTagValue.value = "";
|
|
183
|
+
_addBlock(type, editorStore, data);
|
|
677
184
|
}
|
|
678
185
|
|
|
679
|
-
//
|
|
680
|
-
function
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
if (editorStore.$content !== null) {
|
|
684
|
-
const $blockList = editorStore.$content.querySelectorAll(".de-heading-block");
|
|
685
|
-
let headingList: DEHeadingItem[] = [];
|
|
686
|
-
|
|
687
|
-
$blockList.forEach(($headingTag) => {
|
|
688
|
-
if ($headingTag.textContent !== null) {
|
|
689
|
-
headingList.push({
|
|
690
|
-
name: $headingTag.textContent,
|
|
691
|
-
id: $headingTag.id,
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
anchorHeadingList.value = headingList;
|
|
697
|
-
}
|
|
186
|
+
// 외부용 스타일 적용 함수
|
|
187
|
+
function setDecoration(style: DEDecoration): void {
|
|
188
|
+
_setDecoration(`de-${style}`, editorStore);
|
|
698
189
|
}
|
|
699
190
|
|
|
700
|
-
|
|
701
|
-
|
|
191
|
+
// 외부용 정렬 함수
|
|
192
|
+
function setAlign(align: DETextalign): void {
|
|
193
|
+
_setTextAlign(align, editorStore);
|
|
702
194
|
}
|
|
703
195
|
|
|
704
|
-
/**
|
|
705
|
-
* 메뉴 이벤트 관련 영역 종료
|
|
706
|
-
*/
|
|
707
|
-
|
|
708
196
|
onMounted(() => {
|
|
709
|
-
|
|
710
|
-
editorStore.setWrapElement($editor.value);
|
|
711
|
-
editorStore.setParentWrapElement(_findScrollingElement($editor.value));
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
if ($content.value !== undefined) {
|
|
715
|
-
editorStore.setContentElement($content.value);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
if ($controlBar.value !== undefined) {
|
|
719
|
-
editorStore.setContrulBar($controlBar.value);
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
window.addEventListener("click", checkOthersideClick, true);
|
|
723
|
-
editorStore.$parentWrap?.addEventListener("scroll", parentWrapScollEvent, true);
|
|
197
|
+
_eidtorMountEvent(editorStore);
|
|
724
198
|
});
|
|
725
199
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
editorStore.$parentWrap?.removeEventListener("scroll", parentWrapScollEvent, true);
|
|
200
|
+
onBeforeUnmount(() => {
|
|
201
|
+
_eidtorUnmountEvent(editorStore);
|
|
729
202
|
});
|
|
730
203
|
|
|
731
204
|
defineExpose({
|
|
732
205
|
addBlock,
|
|
733
|
-
addImageBlock,
|
|
734
206
|
setDecoration,
|
|
735
|
-
|
|
736
|
-
getContentData,
|
|
737
|
-
setContentData,
|
|
738
|
-
addCustomBlock,
|
|
207
|
+
setAlign,
|
|
739
208
|
});
|
|
740
209
|
</script>
|
|
741
210
|
|
|
@@ -743,6 +212,7 @@ defineExpose({
|
|
|
743
212
|
@charset "UTF-8";
|
|
744
213
|
.dragon-editor,
|
|
745
214
|
.dragon-editor-viewer {
|
|
215
|
+
--radius-default: 4px;
|
|
746
216
|
/**
|
|
747
217
|
* Reset style start
|
|
748
218
|
*/
|
|
@@ -829,6 +299,7 @@ defineExpose({
|
|
|
829
299
|
padding: 0;
|
|
830
300
|
border-radius: 0;
|
|
831
301
|
outline: 0;
|
|
302
|
+
font-size: inherit;
|
|
832
303
|
vertical-align: middle;
|
|
833
304
|
}
|
|
834
305
|
.dragon-editor a,
|
|
@@ -843,6 +314,7 @@ defineExpose({
|
|
|
843
314
|
.dragon-editor-viewer input[type=reset] {
|
|
844
315
|
border: 0;
|
|
845
316
|
background: transparent;
|
|
317
|
+
font-size: inherit;
|
|
846
318
|
cursor: pointer;
|
|
847
319
|
}
|
|
848
320
|
.dragon-editor img,
|
|
@@ -870,6 +342,26 @@ defineExpose({
|
|
|
870
342
|
.dragon-editor-viewer .de-block[data-depth="5"] {
|
|
871
343
|
padding-left: 150px;
|
|
872
344
|
}
|
|
345
|
+
.dragon-editor .de-block.de-list-block[data-depth="1"],
|
|
346
|
+
.dragon-editor-viewer .de-block.de-list-block[data-depth="1"] {
|
|
347
|
+
padding-left: 54px;
|
|
348
|
+
}
|
|
349
|
+
.dragon-editor .de-block.de-list-block[data-depth="2"],
|
|
350
|
+
.dragon-editor-viewer .de-block.de-list-block[data-depth="2"] {
|
|
351
|
+
padding-left: 84px;
|
|
352
|
+
}
|
|
353
|
+
.dragon-editor .de-block.de-list-block[data-depth="3"],
|
|
354
|
+
.dragon-editor-viewer .de-block.de-list-block[data-depth="3"] {
|
|
355
|
+
padding-left: 114px;
|
|
356
|
+
}
|
|
357
|
+
.dragon-editor .de-block.de-list-block[data-depth="4"],
|
|
358
|
+
.dragon-editor-viewer .de-block.de-list-block[data-depth="4"] {
|
|
359
|
+
padding-left: 144px;
|
|
360
|
+
}
|
|
361
|
+
.dragon-editor .de-block.de-list-block[data-depth="5"],
|
|
362
|
+
.dragon-editor-viewer .de-block.de-list-block[data-depth="5"] {
|
|
363
|
+
padding-left: 174px;
|
|
364
|
+
}
|
|
873
365
|
|
|
874
366
|
/**
|
|
875
367
|
* 노드 스타일
|
|
@@ -877,14 +369,19 @@ defineExpose({
|
|
|
877
369
|
.dragon-editor {
|
|
878
370
|
position: relative;
|
|
879
371
|
border: 1px solid #ccc;
|
|
372
|
+
border-radius: var(--radius-default);
|
|
880
373
|
}
|
|
881
|
-
.dragon-editor.--
|
|
882
|
-
padding-top:
|
|
374
|
+
.dragon-editor.--has-menu {
|
|
375
|
+
padding-top: 35px;
|
|
376
|
+
}
|
|
377
|
+
.dragon-editor.--mobile .de-menu-bar .de-menu-wrap {
|
|
378
|
+
overflow-x: auto;
|
|
883
379
|
}
|
|
884
380
|
.dragon-editor .de-body {
|
|
885
381
|
display: flex;
|
|
886
382
|
flex-direction: column;
|
|
887
383
|
gap: 4px;
|
|
384
|
+
min-height: 500px;
|
|
888
385
|
padding: 20px;
|
|
889
386
|
line-height: 1.6;
|
|
890
387
|
}
|
|
@@ -893,43 +390,58 @@ defineExpose({
|
|
|
893
390
|
top: 0;
|
|
894
391
|
left: 0;
|
|
895
392
|
right: 0;
|
|
896
|
-
height:
|
|
393
|
+
height: 34px;
|
|
897
394
|
background: #fff;
|
|
898
395
|
border-bottom: 1px solid #ccc;
|
|
396
|
+
border-radius: var(--radius-default) var(--radius-default) 0 0;
|
|
899
397
|
z-index: 10;
|
|
900
398
|
}
|
|
901
399
|
.dragon-editor .de-menu-bar .de-menu-wrap {
|
|
902
400
|
display: flex;
|
|
903
|
-
|
|
401
|
+
}
|
|
402
|
+
.dragon-editor .de-menu-bar .de-col {
|
|
403
|
+
display: flex;
|
|
404
|
+
position: relative;
|
|
405
|
+
border-right: 1px solid #ccc;
|
|
406
|
+
}
|
|
407
|
+
.dragon-editor .de-menu-bar .de-col:last-child {
|
|
408
|
+
border-right: 0;
|
|
904
409
|
}
|
|
905
410
|
.dragon-editor .de-menu-bar .de-menu {
|
|
906
411
|
display: flex;
|
|
907
412
|
justify-content: center;
|
|
908
413
|
align-items: center;
|
|
909
|
-
min-width:
|
|
910
|
-
height:
|
|
911
|
-
border-right: 1px solid #ccc;
|
|
912
|
-
box-sizing: border-box;
|
|
414
|
+
min-width: 34px;
|
|
415
|
+
height: 34px;
|
|
913
416
|
cursor: pointer;
|
|
914
417
|
}
|
|
418
|
+
.dragon-editor .de-menu-bar .de-menu.--disabled .de-path {
|
|
419
|
+
fill: #ccc;
|
|
420
|
+
}
|
|
915
421
|
.dragon-editor .de-menu-bar .de-menu .de-icon {
|
|
916
|
-
width:
|
|
917
|
-
height:
|
|
422
|
+
width: 16px;
|
|
423
|
+
height: 16px;
|
|
918
424
|
}
|
|
919
425
|
.dragon-editor .de-menu-bar .de-menu.--lastchild {
|
|
920
426
|
border-right: 0;
|
|
921
427
|
}
|
|
428
|
+
.dragon-editor .de-menu-bar .de-menu .de-path {
|
|
429
|
+
fill: #333;
|
|
430
|
+
}
|
|
922
431
|
.dragon-editor .de-menu-bar .de-menu .de-path.--red {
|
|
923
432
|
fill: #dd0000;
|
|
924
433
|
}
|
|
925
434
|
.dragon-editor .de-menu-bar .de-block-menu-area {
|
|
926
435
|
display: none;
|
|
927
436
|
position: absolute;
|
|
928
|
-
top:
|
|
437
|
+
top: 35px;
|
|
929
438
|
left: 0;
|
|
930
439
|
width: 120px;
|
|
931
440
|
background: #fff;
|
|
932
|
-
|
|
441
|
+
border-width: 0 1px 1px 0;
|
|
442
|
+
border-style: solid;
|
|
443
|
+
border-color: #ccc;
|
|
444
|
+
border-bottom-right-radius: var(--radius-default);
|
|
933
445
|
z-index: 1000;
|
|
934
446
|
}
|
|
935
447
|
.dragon-editor .de-menu-bar .de-block-menu-area.--active {
|
|
@@ -947,12 +459,15 @@ defineExpose({
|
|
|
947
459
|
.dragon-editor .de-menu-bar .de-link-exit-area {
|
|
948
460
|
display: none;
|
|
949
461
|
position: absolute;
|
|
950
|
-
top:
|
|
951
|
-
left:
|
|
462
|
+
top: calc(100% + 1px);
|
|
463
|
+
left: 50%;
|
|
952
464
|
width: 200px;
|
|
953
465
|
background: #fff;
|
|
954
|
-
|
|
466
|
+
border: 1px solid #ccc;
|
|
467
|
+
border-top: 0;
|
|
468
|
+
border-radius: 0 0 var(--radius-default) var(--radius-default);
|
|
955
469
|
z-index: 1000;
|
|
470
|
+
transform: translateX(-50%);
|
|
956
471
|
}
|
|
957
472
|
.dragon-editor .de-menu-bar .de-link-exit-area.--active {
|
|
958
473
|
display: block;
|
|
@@ -964,9 +479,10 @@ defineExpose({
|
|
|
964
479
|
.dragon-editor .de-menu-bar .de-link-exit-area .de-btn-area .de-btn {
|
|
965
480
|
flex: 1;
|
|
966
481
|
height: 24px;
|
|
482
|
+
color: #999;
|
|
967
483
|
}
|
|
968
484
|
.dragon-editor .de-menu-bar .de-link-exit-area .de-btn-area .de-btn.--active {
|
|
969
|
-
|
|
485
|
+
color: #333;
|
|
970
486
|
}
|
|
971
487
|
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-text-area {
|
|
972
488
|
display: flex;
|
|
@@ -997,42 +513,40 @@ defineExpose({
|
|
|
997
513
|
overflow-y: auto;
|
|
998
514
|
}
|
|
999
515
|
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-heading-area .de-btn {
|
|
1000
|
-
|
|
516
|
+
padding: 2px 4px;
|
|
1001
517
|
text-align: left;
|
|
518
|
+
border-radius: var(--radius-default);
|
|
1002
519
|
}
|
|
1003
520
|
.dragon-editor .de-menu-bar .de-link-exit-area .de-link-heading-area .de-btn:hover {
|
|
1004
521
|
background: #f1f1f1;
|
|
1005
522
|
}
|
|
1006
|
-
.dragon-editor .de-
|
|
1007
|
-
display:
|
|
523
|
+
.dragon-editor .de-controlbar {
|
|
524
|
+
display: flex;
|
|
1008
525
|
position: fixed;
|
|
1009
|
-
height:
|
|
526
|
+
height: 34px;
|
|
1010
527
|
background: #fff;
|
|
1011
528
|
border: 1px solid #ccc;
|
|
1012
|
-
border-
|
|
529
|
+
border-radius: var(--radius-default);
|
|
1013
530
|
transform: translateX(-50%);
|
|
1014
531
|
z-index: 20;
|
|
1015
532
|
}
|
|
1016
|
-
.dragon-editor .de-
|
|
1017
|
-
display: flex;
|
|
1018
|
-
}
|
|
1019
|
-
.dragon-editor .de-control-bar:empty {
|
|
1020
|
-
display: none;
|
|
1021
|
-
}
|
|
1022
|
-
.dragon-editor .de-control-bar .de-col {
|
|
533
|
+
.dragon-editor .de-controlbar .de-col {
|
|
1023
534
|
display: flex;
|
|
1024
535
|
align-items: center;
|
|
1025
536
|
column-gap: 6px;
|
|
1026
537
|
padding: 0 10px;
|
|
1027
|
-
border: 1px solid #ccc;
|
|
1028
|
-
border-width: 0 1px 1px 0;
|
|
538
|
+
border-right: 1px solid #ccc;
|
|
1029
539
|
}
|
|
1030
|
-
.dragon-editor .de-
|
|
540
|
+
.dragon-editor .de-controlbar .de-col:last-child {
|
|
541
|
+
border-right: 0;
|
|
542
|
+
}
|
|
543
|
+
.dragon-editor .de-controlbar .de-col .de-selector {
|
|
1031
544
|
height: 100%;
|
|
1032
545
|
border: 0;
|
|
1033
546
|
}
|
|
1034
547
|
.dragon-editor .de-block {
|
|
1035
548
|
width: 100%;
|
|
549
|
+
box-sizing: border-box;
|
|
1036
550
|
}
|
|
1037
551
|
.dragon-editor .de-text-block {
|
|
1038
552
|
min-height: 1.6em;
|
|
@@ -1354,14 +868,16 @@ defineExpose({
|
|
|
1354
868
|
.dragon-editor .de-image-block .de-image-area .de-img {
|
|
1355
869
|
width: 100%;
|
|
1356
870
|
height: auto;
|
|
871
|
+
border-radius: var(--radius-default);
|
|
1357
872
|
}
|
|
1358
873
|
.dragon-editor .de-image-block .de-image-area .de-btn {
|
|
1359
874
|
position: absolute;
|
|
1360
875
|
top: 50%;
|
|
1361
876
|
width: 8px;
|
|
1362
|
-
height:
|
|
1363
|
-
background: #
|
|
1364
|
-
border: 1px solid #
|
|
877
|
+
height: 15%;
|
|
878
|
+
background: #f1f1f1;
|
|
879
|
+
border: 1px solid #ccc;
|
|
880
|
+
border-radius: var(--radius-default);
|
|
1365
881
|
transform: translate(-50%, -50%);
|
|
1366
882
|
cursor: col-resize;
|
|
1367
883
|
user-select: none;
|
|
@@ -1389,6 +905,8 @@ defineExpose({
|
|
|
1389
905
|
.dragon-editor .de-code-block {
|
|
1390
906
|
display: flex;
|
|
1391
907
|
flex-wrap: wrap;
|
|
908
|
+
border-radius: var(--radius-default);
|
|
909
|
+
overflow: hidden;
|
|
1392
910
|
}
|
|
1393
911
|
.dragon-editor .de-code-block .de-filename {
|
|
1394
912
|
flex: 1;
|