dragon-editor 3.4.4 → 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.
Files changed (70) hide show
  1. package/README.md +9 -15
  2. package/dist/module.d.mts +5 -0
  3. package/dist/module.json +8 -1
  4. package/dist/module.mjs +5 -3
  5. package/dist/runtime/components/DragonEditor.vue +252 -720
  6. package/dist/runtime/components/DragonEditorViewer.vue +59 -45
  7. package/dist/runtime/scss/editor.css +83 -34
  8. package/dist/runtime/scss/viewer.css +31 -4
  9. package/dist/runtime/type.d.ts +78 -23
  10. package/dist/runtime/utils/event/block.d.ts +0 -0
  11. package/dist/runtime/utils/event/block.js +78 -0
  12. package/dist/runtime/utils/event/cursor.d.ts +0 -0
  13. package/dist/runtime/utils/{cursor.mjs → event/cursor.js} +4 -16
  14. package/dist/runtime/utils/event/data.d.ts +0 -0
  15. package/dist/runtime/utils/event/data.js +342 -0
  16. package/dist/runtime/utils/event/index.d.ts +8 -0
  17. package/dist/runtime/utils/event/index.js +8 -0
  18. package/dist/runtime/utils/event/keyboard.d.ts +0 -0
  19. package/dist/runtime/utils/event/keyboard.js +1368 -0
  20. package/dist/runtime/utils/event/mouse.d.ts +0 -0
  21. package/dist/runtime/utils/event/mouse.js +70 -0
  22. package/dist/runtime/utils/event/scroll.d.ts +0 -0
  23. package/dist/runtime/utils/event/scroll.js +29 -0
  24. package/dist/runtime/utils/event/touch.d.ts +0 -0
  25. package/dist/runtime/utils/event/touch.js +10 -0
  26. package/dist/runtime/utils/event/window.d.ts +0 -0
  27. package/dist/runtime/utils/event/window.js +32 -0
  28. package/dist/runtime/utils/layout/block.d.ts +0 -0
  29. package/dist/runtime/utils/layout/block.js +105 -0
  30. package/dist/runtime/utils/layout/body.d.ts +0 -0
  31. package/dist/runtime/utils/layout/body.js +22 -0
  32. package/dist/runtime/utils/layout/controlbar.d.ts +0 -0
  33. package/dist/runtime/utils/layout/controlbar.js +99 -0
  34. package/dist/runtime/utils/layout/icon.d.ts +0 -0
  35. package/dist/runtime/utils/layout/icon.js +156 -0
  36. package/dist/runtime/utils/layout/index.d.ts +5 -0
  37. package/dist/runtime/utils/layout/index.js +5 -0
  38. package/dist/runtime/utils/layout/menuBar.d.ts +0 -0
  39. package/dist/runtime/utils/layout/menuBar.js +358 -0
  40. package/dist/runtime/utils/node/block.d.ts +0 -0
  41. package/dist/runtime/utils/node/block.js +235 -0
  42. package/dist/runtime/utils/{element.d.ts → node/element.d.ts} +1 -0
  43. package/dist/runtime/utils/{element.mjs → node/element.js} +19 -4
  44. package/dist/runtime/utils/node/index.d.ts +2 -0
  45. package/dist/runtime/utils/node/index.js +2 -0
  46. package/dist/runtime/utils/style/anchor.d.ts +0 -0
  47. package/dist/runtime/utils/style/anchor.js +240 -0
  48. package/dist/runtime/utils/style/decoration.d.ts +0 -0
  49. package/dist/runtime/utils/style/decoration.js +378 -0
  50. package/dist/runtime/utils/style/index.d.ts +2 -0
  51. package/dist/runtime/utils/style/index.js +2 -0
  52. package/dist/types.d.mts +7 -0
  53. package/dist/types.d.ts +3 -3
  54. package/package.json +15 -16
  55. package/dist/runtime/store.d.ts +0 -11
  56. package/dist/runtime/store.mjs +0 -51
  57. package/dist/runtime/utils/block.d.ts +0 -13
  58. package/dist/runtime/utils/block.mjs +0 -144
  59. package/dist/runtime/utils/content.d.ts +0 -2
  60. package/dist/runtime/utils/content.mjs +0 -19
  61. package/dist/runtime/utils/controlBar.d.ts +0 -9
  62. package/dist/runtime/utils/controlBar.mjs +0 -172
  63. package/dist/runtime/utils/convertor.d.ts +0 -3
  64. package/dist/runtime/utils/convertor.mjs +0 -138
  65. package/dist/runtime/utils/cursor.d.ts +0 -6
  66. package/dist/runtime/utils/keyboardEvent.d.ts +0 -10
  67. package/dist/runtime/utils/keyboardEvent.mjs +0 -978
  68. package/dist/runtime/utils/style.d.ts +0 -5
  69. package/dist/runtime/utils/style.mjs +0 -617
  70. /package/dist/runtime/{plugin.mjs → plugin.js} +0 -0
@@ -1,727 +1,210 @@
1
1
  <template>
2
- <div class="dragon-editor" :class="{ '--hasMenu': props.useMenuBar === true }" @mousemove="resizeEvent" @touchmove="resizeEvent" @mouseup="resizeEventEnd" @touchend="resizeEventEnd" @mouseleave="resizeEventEnd" ref="$editor">
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, onUnmounted } from "vue";
184
- import { useEditorStore } from "../store";
185
- import { _getCodeBlockTheme, _getCodeBlockLanguage, _setCodeBlockTheme, _setCodeBlockLanguage, _updateCodeBlockStyle, _setListBlockStyle, _updateListBlockStyle } from "../utils/controlBar";
186
- import { _findScrollingElement, _findContentEditableElement } from "../utils/element";
187
- import { _elementKeyEvent, _hotKeyEvent, _copyEvent, _pasteEvent } from "../utils/keyboardEvent";
188
- import { _getBlockType, _createTextBlock, _createHeadingBlock, _createListBlock, _createImageBlock, _createCustomBlock, _createCodeBlock } from "../utils/block";
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
- const props = defineProps({
196
- useMenuBar: {
197
- type: Boolean,
198
- requiard: false,
199
- default: () => true,
200
- },
201
- imageHostURL: {
202
- type: String,
203
- requiard: false,
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: "uploadImageEvent", file: File): DEImage;
25
+ (e: "update:modelValue", data: DEContentData): void;
26
+ (e: "uploadImageEvent", file: File): void;
209
27
  }>();
210
- const editorStore = useEditorStore();
211
- const isActiveAddBlockMenu = ref<boolean>(false);
212
- const menuBarTop = ref<number>(0);
213
- const curruntType = ref<string>("");
214
- const codeBlockTheme = ref<string>("github");
215
- const codeblockLanguage = ref<string>("text");
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[]>([]);
221
- const anchorTagValue = ref<string>("");
222
- const $editor = ref<HTMLDivElement>();
223
- const $content = ref<HTMLDivElement>();
224
- const $controlBar = ref<HTMLDivElement>();
225
- const $linkInput = ref<HTMLInputElement>();
226
- let resizeEventActive: boolean = false;
227
- let resizeStartX: number = 0;
228
- let resizeType: string = "right";
229
- let resizeEndX: number = 0;
230
- let resizeCurruntWidth: number = 0;
231
-
232
- /**
233
- * 이벤트 관련 영역 시작
234
- */
235
-
236
- function contentKeyboardEvent(e: KeyboardEvent) {
237
- _elementKeyEvent(e, editorStore);
238
- _hotKeyEvent(e, editorStore);
239
- }
240
-
241
- function updateCursorData(e: MouseEvent) {
242
- const originalCursorData = editorStore.cursorData;
243
-
244
- _clenupCursor(editorStore);
245
-
246
- if (editorStore.cursorData !== null && _findContentEditableElement(editorStore.cursorData.startNode) === null) {
247
- // 비정상 커서 값일 경우 초기화
248
- editorStore.cursorData = originalCursorData;
249
- }
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
+ });
250
130
 
251
- // 선택 블럭 업데이트
252
- if (e.target !== null) {
253
- const $block = (e.target as HTMLElement).closest(".de-block");
131
+ function mainStrucutre(): VNode {
132
+ const childList: VNode[] = [];
254
133
 
255
- if ($block !== null) {
256
- editorStore.setCurrentBlock($block as HTMLElement);
257
- }
134
+ if (editorStore.value.useMenuBar === true) {
135
+ childList.push(_getMenuBarVNodeStructure(editorStore));
258
136
  }
259
137
 
260
- controlBarStatusUpdate();
261
- anchorTagValueUpdate();
262
- }
263
-
264
- // 컨트롤 바 상태 업데이트
265
- function controlBarStatusUpdate() {
266
- if (editorStore.$currentBlock !== null) {
267
- const { type } = _getBlockType(editorStore.$currentBlock);
268
- const activeList = ["code", "list"];
138
+ childList.push(_getBodyVNodeStructure(editorStore));
269
139
 
270
- curruntType.value = type;
271
-
272
- if (activeList.includes(curruntType.value) === true) {
273
- editorStore.controlBarActive();
274
-
275
- switch (type) {
276
- case "code":
277
- _updateCodeBlockStyle(editorStore, codeBlockTheme, codeblockLanguage);
278
- break;
279
- case "list":
280
- _updateListBlockStyle(editorStore, listBlockStyle);
281
- break;
282
- }
283
- } else {
284
- editorStore.controlBarDeactive();
285
- }
140
+ if (editorStore.value.controlBar.active === true) {
141
+ childList.push(_getControlbarVNodeStructure(editorStore));
286
142
  }
287
- }
288
-
289
- // 사이즈 조정 이벤트 시작
290
- function resizeEventStart(event: Event) {
291
- const $target = event.target as HTMLElement;
292
-
293
- if ($target !== null) {
294
- const $block = $target.closest(".de-block");
295
143
 
296
- if ($block?.classList.contains("de-image-block") === true && $target.classList.contains("de-btn") === true) {
297
- editorStore.setCurrentBlock($block as HTMLElement);
298
- resizeEventActive = true;
299
-
300
- if (event.type === "touchstart") {
301
- resizeStartX = (event as TouchEvent).touches[0].clientX;
302
- } else {
303
- resizeStartX = (event as MouseEvent).clientX;
304
- }
305
-
306
- if ($target.classList.contains("de-btn-left") === true) {
307
- resizeType = "left";
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";
308
169
  } else {
309
- resizeType = "right";
170
+ type = "heading3";
310
171
  }
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
- }
349
-
350
- $imgArea.dataset["maxwidth"] = String(value);
351
- }
352
- }
353
-
354
- // 사이즈 조정 이벤트 종료
355
- function resizeEventEnd() {
356
- if (resizeEventActive === true) {
357
- resizeEventActive = false;
358
- }
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
-
385
- if (closeMenu === true) {
386
- isActiveAddBlockMenu.value = false;
387
- }
388
-
389
- if (closeLink === true) {
390
- isActiveLinkArea.value = false;
391
- anchorTagValue.value = "";
392
- }
393
- }
394
- }
395
-
396
- // 블럭 삭제
397
- function deleteBlock() {
398
- if (editorStore.$currentBlock !== null) {
399
- const childCount: number = editorStore.$content?.childElementCount ?? 1;
400
-
401
- editorStore.$currentBlock.remove();
402
- editorStore.setCurrentBlock(null);
403
-
404
- if (childCount < 2) {
405
- // 모든 엘리먼트를 지우려는 경우
406
- const $block = _createTextBlock();
407
-
408
- editorStore.$content?.insertAdjacentElement("beforeend", $block);
409
- _setCursor($block, 0);
410
- }
411
- }
412
- }
413
-
414
- // 부모 요소 스크롤 이벤트 발생시 컨트롤 바 고정
415
- function parentWrapScollEvent() {
416
- if (props.useMenuBar === true && editorStore.$parentWrap !== null && editorStore.$editor !== null) {
417
- // 메뉴바를 사용하는 경우만
418
-
419
- const editorReac = editorStore.$editor.getBoundingClientRect();
420
- let scrollY: number = 0;
421
-
422
- if (editorStore.$parentWrap.constructor.name === "Window") {
423
- scrollY = (editorStore.$parentWrap as Window).scrollY;
424
- } else {
425
- scrollY = (editorStore.$parentWrap as HTMLElement).scrollTop;
426
- }
427
-
428
- let realElementY = editorReac.y + scrollY;
429
-
430
- if (editorStore.$parentWrap.constructor.name !== "Window") {
431
- const parentRect = (editorStore.$parentWrap as HTMLElement).getBoundingClientRect();
432
-
433
- realElementY -= parentRect.y;
434
- }
435
-
436
- let value: number = 0;
437
-
438
- if (scrollY > realElementY) {
439
- value = scrollY - realElementY - 1;
440
- } else {
441
- value = 0;
442
- }
443
-
444
- if (value > editorReac.height - 39) {
445
- value = editorReac.height - 39;
446
- }
447
-
448
- menuBarTop.value = Math.floor(value);
449
- }
450
- }
451
-
452
- // 붙여넣기 이벤트
453
- function contentPasteEvent(event: ClipboardEvent) {
454
- _pasteEvent(event, editorStore, emit);
455
- }
456
-
457
- function anchorTagValueUpdate() {
458
- // 다른 이벤트 순서에 의한 딜레이
459
- setTimeout(() => {
460
- anchorTagValue.value = _getAnchorTagValue(editorStore);
461
- }, 500);
462
- }
463
-
464
- /**
465
- * 이벤트 관련 영역 종료
466
- */
467
-
468
- /**
469
- * 컨트롤 바 이벤트 관련 영역 시작
470
- */
471
-
472
- // 코드 블럭 테마 적용
473
- function codeBlockThemeChangeEvent() {
474
- _setCodeBlockTheme(editorStore, codeBlockTheme.value);
475
- }
476
-
477
- // 코드 블럭 언어 적용
478
- function codeblockLanguageChangeEvent() {
479
- _setCodeBlockLanguage(editorStore, codeblockLanguage.value);
480
- }
481
-
482
- // 리스트 스타일 적용
483
- function listBlockStyleChangeEvent() {
484
- _setListBlockStyle(editorStore, listBlockStyle.value);
485
- }
486
-
487
- /**
488
- * 컨트롤 바 이벤트 관련 영역 종료
489
- */
490
-
491
- /**
492
- * 메뉴 이벤트 관련 영역
493
- */
494
- function addBlock(type: string) {
495
- isActiveAddBlockMenu.value = false;
496
-
497
- let blockStructure: HTMLElement | null = null;
498
-
499
- switch (type) {
500
- case "text":
501
- blockStructure = _createTextBlock();
502
172
  break;
503
- case "heading1":
504
- case "heading2":
505
- case "heading3":
506
- const level: number = parseInt(type.replace("heading", ""));
507
-
508
- blockStructure = _createHeadingBlock({
509
- type: "heading",
510
- classList: [],
511
- id: "",
512
- level: level,
513
- textContent: "",
514
- });
515
- break;
516
- case "ul":
517
- case "ol":
518
- blockStructure = _createListBlock({
519
- type: "list",
520
- element: type,
521
- style: type === "ul" ? "disc" : "decimal",
522
- child: [
523
- {
524
- classList: [],
525
- textContent: "",
526
- },
527
- ],
528
- });
529
- break;
530
- case "table":
531
- // TODO : table block
532
- break;
533
- case "code":
534
- blockStructure = _createCodeBlock({
535
- type: "code",
536
- theme: "github",
537
- filename: "",
538
- language: "Plain Text",
539
- textContent: "",
540
- });
541
- break;
542
- }
543
-
544
- if (blockStructure !== null) {
545
- _addBlockToContent(blockStructure, editorStore);
546
-
547
- switch (type) {
548
- case "ul":
549
- case "ol":
550
- (blockStructure.childNodes[0] as HTMLElement).focus();
551
- break;
552
- case "codeblock":
553
- blockStructure.querySelector("code")?.focus();
554
- break;
555
- default:
556
- blockStructure.focus();
557
- }
558
-
559
- editorStore.setCurrentBlock(blockStructure as HTMLElement);
560
- controlBarStatusUpdate();
561
- }
562
- }
563
-
564
- function addCustomBlock(HTML: string, classList: string[] = []) {
565
- const blockStructure = _createCustomBlock({
566
- type: "custom",
567
- classList: classList,
568
- textContent: HTML,
569
- });
570
-
571
- _addBlockToContent(blockStructure, editorStore);
572
- }
573
-
574
- function addImageBlock(data: DEImage) {
575
- if (props.imageHostURL !== "") {
576
- data.src = props.imageHostURL + data.src;
577
- }
578
-
579
- const blockStructure = _createImageBlock({
580
- ...data,
581
- type: "image",
582
- maxWidth: 100,
583
- classList: [],
584
- } as DEImageBlock);
585
-
586
- _addBlockToContent(blockStructure, editorStore);
587
- }
588
-
589
- function setDecoration(type: DEDecoration) {
590
- _setNodeStyle(`de-${type}`, editorStore);
591
- }
592
-
593
- function setTextAlign(type: DETextalign) {
594
- _setTextAlign(type, editorStore);
595
- }
596
173
 
597
- function getContentData(): DEContentData {
598
- if (editorStore.$content !== null) {
599
- return _getContentData(editorStore.$content, props.imageHostURL);
600
- } else {
601
- console.error("[DragonEditor] Con't find content Element.");
602
- return [];
603
- }
604
- }
605
-
606
- function setContentData(data: DEContentData) {
607
- _setContentData(data, editorStore, props.imageHostURL);
608
- }
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);
174
+ case "list":
175
+ if (data.element === "ol") {
176
+ type = "ol";
626
177
  } else {
627
- editorStore.setCurrentBlock(($target as HTMLElement).nextElementSibling as HTMLElement | null);
178
+ type = "ul";
628
179
  }
629
- }
180
+ break;
630
181
  }
631
- }
632
182
 
633
- function openLinkArea() {
634
- activeLinkTabType.value = "url";
635
- anchorValueError.value = false;
183
+ _addBlock(type, editorStore, data);
636
184
  }
637
185
 
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 = "";
186
+ // 외부용 스타일 적용 함수
187
+ function setDecoration(style: DEDecoration): void {
188
+ _setDecoration(`de-${style}`, editorStore);
644
189
  }
645
190
 
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 = "";
191
+ // 외부용 정렬 함수
192
+ function setAlign(align: DETextalign): void {
193
+ _setTextAlign(align, editorStore);
663
194
  }
664
195
 
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
-
694
196
  onMounted(() => {
695
- if ($editor.value !== undefined) {
696
- editorStore.setWrapElement($editor.value);
697
- editorStore.setParentWrapElement(_findScrollingElement($editor.value));
698
- }
699
-
700
- if ($content.value !== undefined) {
701
- editorStore.setContentElement($content.value);
702
- }
703
-
704
- if ($controlBar.value !== undefined) {
705
- editorStore.setContrulBar($controlBar.value);
706
- }
707
-
708
- window.addEventListener("click", checkOthersideClick, true);
709
- editorStore.$parentWrap?.addEventListener("scroll", parentWrapScollEvent, true);
197
+ _eidtorMountEvent(editorStore);
710
198
  });
711
199
 
712
- onUnmounted(() => {
713
- window.removeEventListener("click", checkOthersideClick, true);
714
- editorStore.$parentWrap?.removeEventListener("scroll", parentWrapScollEvent, true);
200
+ onBeforeUnmount(() => {
201
+ _eidtorUnmountEvent(editorStore);
715
202
  });
716
203
 
717
204
  defineExpose({
718
205
  addBlock,
719
- addImageBlock,
720
206
  setDecoration,
721
- setTextAlign,
722
- getContentData,
723
- setContentData,
724
- addCustomBlock,
207
+ setAlign,
725
208
  });
726
209
  </script>
727
210
 
@@ -729,6 +212,7 @@ defineExpose({
729
212
  @charset "UTF-8";
730
213
  .dragon-editor,
731
214
  .dragon-editor-viewer {
215
+ --radius-default: 4px;
732
216
  /**
733
217
  * Reset style start
734
218
  */
@@ -815,6 +299,7 @@ defineExpose({
815
299
  padding: 0;
816
300
  border-radius: 0;
817
301
  outline: 0;
302
+ font-size: inherit;
818
303
  vertical-align: middle;
819
304
  }
820
305
  .dragon-editor a,
@@ -829,6 +314,7 @@ defineExpose({
829
314
  .dragon-editor-viewer input[type=reset] {
830
315
  border: 0;
831
316
  background: transparent;
317
+ font-size: inherit;
832
318
  cursor: pointer;
833
319
  }
834
320
  .dragon-editor img,
@@ -856,6 +342,26 @@ defineExpose({
856
342
  .dragon-editor-viewer .de-block[data-depth="5"] {
857
343
  padding-left: 150px;
858
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
+ }
859
365
 
860
366
  /**
861
367
  * 노드 스타일
@@ -863,14 +369,19 @@ defineExpose({
863
369
  .dragon-editor {
864
370
  position: relative;
865
371
  border: 1px solid #ccc;
372
+ border-radius: var(--radius-default);
373
+ }
374
+ .dragon-editor.--has-menu {
375
+ padding-top: 35px;
866
376
  }
867
- .dragon-editor.--hasMenu {
868
- padding-top: 38px;
377
+ .dragon-editor.--mobile .de-menu-bar .de-menu-wrap {
378
+ overflow-x: auto;
869
379
  }
870
380
  .dragon-editor .de-body {
871
381
  display: flex;
872
382
  flex-direction: column;
873
383
  gap: 4px;
384
+ min-height: 500px;
874
385
  padding: 20px;
875
386
  line-height: 1.6;
876
387
  }
@@ -879,43 +390,58 @@ defineExpose({
879
390
  top: 0;
880
391
  left: 0;
881
392
  right: 0;
882
- height: 38px;
393
+ height: 34px;
883
394
  background: #fff;
884
395
  border-bottom: 1px solid #ccc;
396
+ border-radius: var(--radius-default) var(--radius-default) 0 0;
885
397
  z-index: 10;
886
398
  }
887
399
  .dragon-editor .de-menu-bar .de-menu-wrap {
888
400
  display: flex;
889
- overflow-x: auto;
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;
890
409
  }
891
410
  .dragon-editor .de-menu-bar .de-menu {
892
411
  display: flex;
893
412
  justify-content: center;
894
413
  align-items: center;
895
- min-width: 38px;
896
- height: 38px;
897
- border-right: 1px solid #ccc;
898
- box-sizing: border-box;
414
+ min-width: 34px;
415
+ height: 34px;
899
416
  cursor: pointer;
900
417
  }
418
+ .dragon-editor .de-menu-bar .de-menu.--disabled .de-path {
419
+ fill: #ccc;
420
+ }
901
421
  .dragon-editor .de-menu-bar .de-menu .de-icon {
902
- width: 24px;
903
- height: 24px;
422
+ width: 16px;
423
+ height: 16px;
904
424
  }
905
425
  .dragon-editor .de-menu-bar .de-menu.--lastchild {
906
426
  border-right: 0;
907
427
  }
428
+ .dragon-editor .de-menu-bar .de-menu .de-path {
429
+ fill: #333;
430
+ }
908
431
  .dragon-editor .de-menu-bar .de-menu .de-path.--red {
909
432
  fill: #dd0000;
910
433
  }
911
434
  .dragon-editor .de-menu-bar .de-block-menu-area {
912
435
  display: none;
913
436
  position: absolute;
914
- top: 39px;
437
+ top: 35px;
915
438
  left: 0;
916
439
  width: 120px;
917
440
  background: #fff;
918
- box-shadow: 0 2px 2px rgba(0, 0, 0, 0.25);
441
+ border-width: 0 1px 1px 0;
442
+ border-style: solid;
443
+ border-color: #ccc;
444
+ border-bottom-right-radius: var(--radius-default);
919
445
  z-index: 1000;
920
446
  }
921
447
  .dragon-editor .de-menu-bar .de-block-menu-area.--active {
@@ -933,12 +459,15 @@ defineExpose({
933
459
  .dragon-editor .de-menu-bar .de-link-exit-area {
934
460
  display: none;
935
461
  position: absolute;
936
- top: 39px;
937
- left: 228px;
462
+ top: calc(100% + 1px);
463
+ left: 50%;
938
464
  width: 200px;
939
465
  background: #fff;
940
- box-shadow: 0 2px 2px rgba(0, 0, 0, 0.25);
466
+ border: 1px solid #ccc;
467
+ border-top: 0;
468
+ border-radius: 0 0 var(--radius-default) var(--radius-default);
941
469
  z-index: 1000;
470
+ transform: translateX(-50%);
942
471
  }
943
472
  .dragon-editor .de-menu-bar .de-link-exit-area.--active {
944
473
  display: block;
@@ -950,9 +479,10 @@ defineExpose({
950
479
  .dragon-editor .de-menu-bar .de-link-exit-area .de-btn-area .de-btn {
951
480
  flex: 1;
952
481
  height: 24px;
482
+ color: #999;
953
483
  }
954
484
  .dragon-editor .de-menu-bar .de-link-exit-area .de-btn-area .de-btn.--active {
955
- background: #f1f1f1;
485
+ color: #333;
956
486
  }
957
487
  .dragon-editor .de-menu-bar .de-link-exit-area .de-link-text-area {
958
488
  display: flex;
@@ -983,42 +513,40 @@ defineExpose({
983
513
  overflow-y: auto;
984
514
  }
985
515
  .dragon-editor .de-menu-bar .de-link-exit-area .de-link-heading-area .de-btn {
986
- min-height: 20px;
516
+ padding: 2px 4px;
987
517
  text-align: left;
518
+ border-radius: var(--radius-default);
988
519
  }
989
520
  .dragon-editor .de-menu-bar .de-link-exit-area .de-link-heading-area .de-btn:hover {
990
521
  background: #f1f1f1;
991
522
  }
992
- .dragon-editor .de-control-bar {
993
- display: none;
523
+ .dragon-editor .de-controlbar {
524
+ display: flex;
994
525
  position: fixed;
995
- height: 38px;
526
+ height: 34px;
996
527
  background: #fff;
997
528
  border: 1px solid #ccc;
998
- border-width: 1px 0 0 1px;
529
+ border-radius: var(--radius-default);
999
530
  transform: translateX(-50%);
1000
531
  z-index: 20;
1001
532
  }
1002
- .dragon-editor .de-control-bar.--active {
1003
- display: flex;
1004
- }
1005
- .dragon-editor .de-control-bar:empty {
1006
- display: none;
1007
- }
1008
- .dragon-editor .de-control-bar .de-col {
533
+ .dragon-editor .de-controlbar .de-col {
1009
534
  display: flex;
1010
535
  align-items: center;
1011
536
  column-gap: 6px;
1012
537
  padding: 0 10px;
1013
- border: 1px solid #ccc;
1014
- border-width: 0 1px 1px 0;
538
+ border-right: 1px solid #ccc;
1015
539
  }
1016
- .dragon-editor .de-control-bar .de-col .de-selector {
540
+ .dragon-editor .de-controlbar .de-col:last-child {
541
+ border-right: 0;
542
+ }
543
+ .dragon-editor .de-controlbar .de-col .de-selector {
1017
544
  height: 100%;
1018
545
  border: 0;
1019
546
  }
1020
547
  .dragon-editor .de-block {
1021
548
  width: 100%;
549
+ box-sizing: border-box;
1022
550
  }
1023
551
  .dragon-editor .de-text-block {
1024
552
  min-height: 1.6em;
@@ -1340,14 +868,16 @@ defineExpose({
1340
868
  .dragon-editor .de-image-block .de-image-area .de-img {
1341
869
  width: 100%;
1342
870
  height: auto;
871
+ border-radius: var(--radius-default);
1343
872
  }
1344
873
  .dragon-editor .de-image-block .de-image-area .de-btn {
1345
874
  position: absolute;
1346
875
  top: 50%;
1347
876
  width: 8px;
1348
- height: 100px;
1349
- background: #ccc;
1350
- border: 1px solid #333;
877
+ height: 15%;
878
+ background: #f1f1f1;
879
+ border: 1px solid #ccc;
880
+ border-radius: var(--radius-default);
1351
881
  transform: translate(-50%, -50%);
1352
882
  cursor: col-resize;
1353
883
  user-select: none;
@@ -1375,6 +905,8 @@ defineExpose({
1375
905
  .dragon-editor .de-code-block {
1376
906
  display: flex;
1377
907
  flex-wrap: wrap;
908
+ border-radius: var(--radius-default);
909
+ overflow: hidden;
1378
910
  }
1379
911
  .dragon-editor .de-code-block .de-filename {
1380
912
  flex: 1;