dragon-editor 2.0.0-beta.2 → 2.0.0-beta.2.1.1

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 CHANGED
@@ -1,14 +1,35 @@
1
- [![Github stars](https://img.shields.io/github/stars/lovefields/dragonEditor)](https://github.com/lovefields/dragonEditor/stargazers)
2
- [![Github issues](https://img.shields.io/github/issues/lovefields/dragonEditor)](https://github.com/lovefields/dragonEditor/issues)
3
- [![Github forks](https://img.shields.io/github/forks/lovefields/dragonEditor)](https://github.com/lovefields/dragonEditor/network/members)
4
- [![Github top language](https://img.shields.io/github/languages/top/lovefields/dragonEditor)](https://github.com/lovefields/dragonEditor/)
5
- [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Flovefields%2FdragonEditor&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com)
1
+ [stars-src]: https://img.shields.io/github/stars/lovefields/dragonEditor
2
+ [stars-href]: https://github.com/lovefields/dragonEditor/stargazers
3
+ [issues-src]: https://img.shields.io/github/issues/lovefields/dragonEditor
4
+ [issues-href]: https://github.com/lovefields/dragonEditor/issues
5
+ [forks-src]: https://img.shields.io/github/forks/lovefields/dragonEditor
6
+ [forks-href]: https://github.com/lovefields/dragonEditor/network/members
7
+ [language-src]: https://img.shields.io/github/languages/top/lovefields/dragonEditor
8
+ [language-href]: https://github.com/lovefields/dragonEditor/
9
+ [hits-src]: https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Flovefields%2FdragonEditor&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false
10
+ [hits-href]: https://hits.seeyoufarm.com
11
+ [npm-version-src]: https://img.shields.io/npm/v/my-module/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
12
+ [npm-version-href]: https://www.npmjs.com/package/dragon-editor
13
+ [npm-downloads-src]: https://img.shields.io/npm/dm/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
14
+ [npm-downloads-href]: https://www.npmjs.com/package/dragon-editor
15
+ [license-src]: https://img.shields.io/npm/l/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
16
+ [license-href]: https://www.npmjs.com/package/dragon-editor
17
+ [nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
18
+ [nuxt-href]: https://nuxt.com
19
+ [readme-ko]: https://github.com/lovefields/dragonEditor/blob/main/README.md
20
+ [readme-en]: https://github.com/lovefields/dragonEditor/blob/main/README_en.md
21
+
22
+ [![Github stars](stars-src)](stars-href)
23
+ [![Github issues](issues-src)](issues-href)
24
+ [![Github forks](forks-src)](forks-href)
25
+ [![Github top language](language-src)](language-href)
26
+ [![Hits](hits-src)](hits-href)
6
27
  [![npm version][npm-version-src]][npm-version-href]
7
28
  [![npm downloads][npm-downloads-src]][npm-downloads-href]
8
29
  [![License][license-src]][license-href]
9
30
  [![Nuxt][nuxt-src]][nuxt-href]
10
31
 
11
- [KO](https://github.com/lovefields/dragonEditor/blob/main/README.md) / [EN](https://github.com/lovefields/dragonEditor/blob/main/README_en.md)
32
+ [KO](readme-ko) / [EN](readme-en)
12
33
 
13
34
  # DragonEditor
14
35
 
@@ -102,14 +123,3 @@ const contentData = ref([]); // 에디터로 저장한 데이터
102
123
  ## 문서
103
124
 
104
125
  - [DragonEditor Document](https://lovefields.github.io/dragonEditor-doc/) -->
105
-
106
- <!-- Badges -->
107
-
108
- [npm-version-src]: https://img.shields.io/npm/v/my-module/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
109
- [npm-version-href]: https://www.npmjs.com/package/dragon-edito
110
- [npm-downloads-src]: https://img.shields.io/npm/dm/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
111
- [npm-downloads-href]: https://www.npmjs.com/package/dragon-edito
112
- [license-src]: https://img.shields.io/npm/l/my-module.svg?style=flat&colorA=18181B&colorB=28CF8D
113
- [license-href]: https://www.npmjs.com/package/dragon-edito
114
- [nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
115
- [nuxt-href]: https://nuxt.com
package/dist/module.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "dragon-editor",
3
3
  "configKey": "dragon-editor",
4
- "version": "2.0.0-beta.2"
4
+ "version": "2.0.0-beta.2.1.1"
5
5
  }
@@ -6,13 +6,13 @@
6
6
 
7
7
  <template v-if="data.webp">
8
8
  <picture>
9
- <source :srcset="data.src.replace(/\.(jpg|png)/g, '.webp')">
10
- <img class="d-img" :src="data.src" :width="data.width" :height="data.height" :alt="data.caption" loading="lazy">
9
+ <source :srcset="data.src.replace(/\.(jpg|png|jpeg|apng|gif)/g, '.webp')" />
10
+ <img class="d-img" :src="data.src" :width="data.width" :height="data.height" :alt="data.caption" loading="lazy" />
11
11
  </picture>
12
12
  </template>
13
13
 
14
14
  <template v-else>
15
- <img class="d-img" :src="data.src" :width="data.width" :height="data.height" :alt="data.caption" loading="lazy">
15
+ <img class="d-img" :src="data.src" :width="data.width" :height="data.height" :alt="data.caption" loading="lazy" />
16
16
  </template>
17
17
  </div>
18
18
  <p class="d-caption" v-html="data.caption" @keydown="textKeyboardEvent" ref="$caption" contenteditable></p>
@@ -20,6 +20,7 @@
20
20
  </template>
21
21
 
22
22
  <script setup lang="ts">
23
+ // @ts-ignore
23
24
  import { ref, unref } from "#imports";
24
25
  import { keyboardEvent, pasteText, styleSettings } from "../../utils/index";
25
26
  import { imageBlock, cursorSelection, styleFunctionArgument } from "../../../../types/index";
@@ -0,0 +1,135 @@
1
+ <template>
2
+ <ol class="d-ol-block" :class="data.classList" @keydown="textKeyboardEvent">
3
+ <li class="d-li-item" contenteditable ref="$olItem"></li>
4
+ </ol>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ // @ts-ignore
9
+ import { ref, unref } from "#imports";
10
+ import { cursorSelection, listBlock, styleFunctionArgument } from "../../../../types";
11
+ import { getArrangementCursorData, setCursor, pasteText, styleSettings, keyboardEvent } from "../../utils";
12
+
13
+ const $olItem = ref();
14
+ const data = ref<listBlock>({
15
+ type: "",
16
+ id: "",
17
+ classList: [],
18
+ childList: [],
19
+ });
20
+ const props = defineProps<{ modelValue: listBlock; cursorData: cursorSelection }>();
21
+ const emit = defineEmits<{
22
+ (e: "update:modelValue", modelValue: listBlock): void;
23
+ (e: "addBlock", {}: { name: string; value: object }): void;
24
+ (e: "deleteBlockLocal", index?: number): void;
25
+ }>();
26
+ data.value = unref(props.modelValue) as listBlock;
27
+
28
+ if(data.value.childList.length === 0){
29
+
30
+ }
31
+
32
+ // 키보드 이벤트 할당
33
+ function textKeyboardEvent(e: KeyboardEvent) {
34
+ keyboardEvent("ol", e, emit, updateBlockData);
35
+ }
36
+
37
+ /**
38
+ * 외부용 함수
39
+ */
40
+
41
+ // 데이터 정규화 및 검수
42
+ function updateBlockData() {
43
+ // const blockClassList = [...$block.value.classList];
44
+ // blockClassList.splice(0, 1);
45
+ // const pushList = blockClassList.filter((item) => data.value.classList.indexOf(item) === -1);
46
+ // data.value.classList = data.value.classList.concat(pushList);
47
+ // // 클레스 검수
48
+ // const checkClassIdx = data.value.classList.indexOf("d-text-block");
49
+ // if (checkClassIdx > -1) {
50
+ // data.value.classList.splice(checkClassIdx, 1);
51
+ // }
52
+ // // 커서위치 재지정
53
+ // if ($block.value.innerHTML.length > 0) {
54
+ // const cursorData = getArrangementCursorData(props.cursorData);
55
+ // data.value.content = $block.value.innerHTML;
56
+ // emit("update:modelValue", data.value);
57
+ // setTimeout(() => {
58
+ // if ($block.value) {
59
+ // setCursor($block.value.childNodes[cursorData.childCount], cursorData.length);
60
+ // // 구조 검수
61
+ // $block.value.childNodes.forEach((child: ChildNode) => {
62
+ // const $child = child as HTMLElement;
63
+ // if (child.constructor.name !== "Text") {
64
+ // // 텍스트가 아닐경우
65
+ // if (child.constructor.name !== "HTMLBRElement") {
66
+ // // br 태그 유지
67
+ // if (child.textContent === "") {
68
+ // // 빈 태그 삭제
69
+ // child.remove();
70
+ // } else if ($child.classList.length === 0) {
71
+ // // 클레스 없는 엘리먼트 처리
72
+ // $child.insertAdjacentHTML("afterend", $child.innerHTML);
73
+ // child.remove();
74
+ // }
75
+ // } else {
76
+ // $child.removeAttribute("class");
77
+ // }
78
+ // }
79
+ // });
80
+ // }
81
+ // }, 100);
82
+ // } else {
83
+ // emit("update:modelValue", data.value);
84
+ // }
85
+ }
86
+
87
+ // 포커스
88
+ function focus(type: string = "first") {
89
+ // if (type === "first") {
90
+ // setCursor($block.value, 0);
91
+ // } else {
92
+ // const childCount = $block.value.childNodes.length;
93
+ // const targetChild = $block.value.childNodes[childCount - 1];
94
+ // setCursor(targetChild, 0);
95
+ // }
96
+ }
97
+
98
+ // 블럭 위치 주기
99
+ function getBoundingClientRect() {
100
+ // return $block.value.parentNode.getBoundingClientRect();
101
+ }
102
+
103
+ // 타입 전달
104
+ function getType() {
105
+ return data.value.type;
106
+ }
107
+
108
+ // 붙여넣기 이벤트
109
+ function pasteEvent(text: string) {
110
+ pasteText("text", text);
111
+ }
112
+
113
+ // 텍스트 스타일 지정
114
+ function setStyles({ type, url }: styleFunctionArgument) {
115
+ data.value = styleSettings({
116
+ kind: type,
117
+ blockData: data.value,
118
+ $target: undefined,
119
+ url: url,
120
+ cursorData: props.cursorData,
121
+ });
122
+ setTimeout(() => {
123
+ updateBlockData();
124
+ }, 250);
125
+ }
126
+
127
+ defineExpose({
128
+ updateBlockData,
129
+ focus,
130
+ getType,
131
+ pasteEvent,
132
+ setStyles,
133
+ getBoundingClientRect,
134
+ });
135
+ </script>
@@ -3,6 +3,7 @@
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
+ // @ts-ignore
6
7
  import { ref, unref } from "#imports";
7
8
  import { keyboardEvent, setCursor, pasteText, styleSettings, getArrangementCursorData } from "../../utils/index";
8
9
  import { textBlock, styleFunctionArgument, cursorSelection } from "../../../../types/index";
@@ -48,16 +49,41 @@ function updateBlockData() {
48
49
 
49
50
  // 커서위치 재지정
50
51
  if ($block.value.innerHTML.length > 0) {
51
- const cursorData = getArrangementCursorData(props.cursorData);
52
-
53
- data.value.content = $block.value.innerHTML;
54
- emit("update:modelValue", data.value);
55
-
56
- setTimeout(() => {
52
+ if (props.cursorData.endOffset !== null) {
53
+ const cursorData = getArrangementCursorData(props.cursorData);
54
+
55
+ data.value.content = $block.value.innerHTML;
56
+ emit("update:modelValue", data.value);
57
+
58
+ setTimeout(() => {
59
+ if ($block.value) {
60
+ setCursor($block.value.childNodes[cursorData.childCount], cursorData.length);
61
+
62
+ // 구조 검수
63
+ $block.value.childNodes.forEach((child: ChildNode) => {
64
+ const $child = child as HTMLElement;
65
+
66
+ if (child.constructor.name !== "Text") {
67
+ // 텍스트가 아닐경우
68
+ if (child.constructor.name !== "HTMLBRElement") {
69
+ // br 태그 유지
70
+ if (child.textContent === "") {
71
+ // 빈 태그 삭제
72
+ child.remove();
73
+ } else if ($child.classList.length === 0) {
74
+ // 클레스 없는 엘리먼트 처리
75
+ $child.insertAdjacentHTML("afterend", $child.innerHTML);
76
+ child.remove();
77
+ }
78
+ } else {
79
+ $child.removeAttribute("class");
80
+ }
81
+ }
82
+ });
83
+ }
84
+ }, 100);
85
+ } else {
57
86
  if ($block.value) {
58
- setCursor($block.value.childNodes[cursorData.childCount], cursorData.length);
59
-
60
- // 구조 검수
61
87
  $block.value.childNodes.forEach((child: ChildNode) => {
62
88
  const $child = child as HTMLElement;
63
89
 
@@ -78,8 +104,11 @@ function updateBlockData() {
78
104
  }
79
105
  }
80
106
  });
107
+
108
+ data.value.content = $block.value.innerHTML;
109
+ emit("update:modelValue", data.value);
81
110
  }
82
- }, 100);
111
+ }
83
112
  } else {
84
113
  emit("update:modelValue", data.value);
85
114
  }
@@ -2,7 +2,10 @@
2
2
  position: relative;
3
3
  width: 100%;
4
4
  padding: 10px 10px 50px;
5
+ background: #fff;
6
+ border-radius: 10px;
5
7
  line-height: 1.6;
8
+ box-sizing: border-box;
6
9
  }
7
10
  .dragon-editor.--comment {
8
11
  padding: 10px;
@@ -109,6 +112,24 @@
109
112
  .dragon-editor .d-deco-link {
110
113
  color: inherit;
111
114
  }
115
+ .dragon-editor .d-h1 {
116
+ font-size: 2em;
117
+ }
118
+ .dragon-editor .d-h2 {
119
+ font-size: 1.5em;
120
+ }
121
+ .dragon-editor .d-h3 {
122
+ font-size: 1.17em;
123
+ }
124
+ .dragon-editor .d-h4 {
125
+ font-size: 1em;
126
+ }
127
+ .dragon-editor .d-h5 {
128
+ font-size: 0.83em;
129
+ }
130
+ .dragon-editor .d-h6 {
131
+ font-size: 0.67em;
132
+ }
112
133
  .dragon-editor .d-row-block {
113
134
  padding-left: 50px;
114
135
  border-radius: 5px;
@@ -130,6 +151,23 @@
130
151
  content: "Write text";
131
152
  color: #ccc;
132
153
  }
154
+ .dragon-editor .d-ol-block {
155
+ padding-left: 30px;
156
+ cursor: text;
157
+ list-style: decimal;
158
+ }
159
+ .dragon-editor .d-ol-block .d-li-item {
160
+ list-style: inherit;
161
+ outline: 0;
162
+ }
163
+ .dragon-editor .d-ol-block .d-li-item:empty {
164
+ min-height: 1.6em;
165
+ }
166
+ .dragon-editor .d-ol-block .d-li-item:empty::after {
167
+ display: inline;
168
+ content: "Write text";
169
+ color: #ccc;
170
+ }
133
171
  .dragon-editor .d-image-block {
134
172
  display: flex;
135
173
  flex-direction: column;
@@ -362,6 +400,7 @@
362
400
  .dragon-editor .d-style-menu .d-column {
363
401
  display: flex;
364
402
  column-gap: 2px;
403
+ position: relative;
365
404
  padding: 5px;
366
405
  border-right: 1px solid #eee;
367
406
  box-sizing: border-box;
@@ -380,6 +419,27 @@
380
419
  .dragon-editor .d-style-menu .d-btn.--active {
381
420
  fill: blue;
382
421
  }
422
+ .dragon-editor .d-style-menu .d-btn.--hug {
423
+ width: auto;
424
+ min-width: 60px;
425
+ }
426
+ .dragon-editor .d-style-menu .d-child-list {
427
+ display: none;
428
+ flex-direction: column;
429
+ row-gap: 4px;
430
+ position: absolute;
431
+ top: 100%;
432
+ left: 0;
433
+ right: 0;
434
+ padding: 4px 0;
435
+ background: #fff;
436
+ border: 1px solid #f1f1f1;
437
+ border-top: 0;
438
+ border-radius: 0 0 5px 5px;
439
+ }
440
+ .dragon-editor .d-style-menu .d-child-list.--active {
441
+ display: flex;
442
+ }
383
443
  .dragon-editor .d-link-box {
384
444
  display: flex;
385
445
  justify-content: space-between;
@@ -1,2 +1,3 @@
1
- export declare function findEditableElement(node: Node): (HTMLElement | null);
1
+ export declare function findEditableElement(node: Node): HTMLElement | null;
2
+ export declare function findLiElement(node: Node): HTMLElement | null;
2
3
  export declare function findChildNumber(parent: HTMLElement, child: Node): number;
@@ -18,6 +18,17 @@ export function findEditableElement(node) {
18
18
  return null;
19
19
  }
20
20
  }
21
+ export function findLiElement(node) {
22
+ if (node) {
23
+ if (node.constructor.name !== "HTMLLIElement") {
24
+ return findLiElement(node.parentNode);
25
+ } else {
26
+ return node;
27
+ }
28
+ } else {
29
+ return null;
30
+ }
31
+ }
21
32
  export function findChildNumber(parent, child) {
22
33
  let idx = 0;
23
34
  parent.childNodes.forEach((item, count) => {
@@ -50,9 +50,25 @@ function createImageBlock(data) {
50
50
  caption: data.caption
51
51
  };
52
52
  }
53
+ function createlistBlock(type = "ul") {
54
+ return {
55
+ type,
56
+ id: generateId(),
57
+ classList: [],
58
+ childList: [
59
+ {
60
+ classList: [],
61
+ content: ""
62
+ }
63
+ ]
64
+ };
65
+ }
53
66
  export function createBlock(name, value) {
54
67
  let blockData;
55
68
  switch (name) {
69
+ case "ol":
70
+ blockData = createlistBlock("ol");
71
+ break;
56
72
  case "image":
57
73
  blockData = createImageBlock(value);
58
74
  break;
@@ -1,5 +1,5 @@
1
1
  import { getCursor, setCursor } from "./cursor.mjs";
2
- import { findEditableElement, findChildNumber } from "./element.mjs";
2
+ import { findEditableElement, findChildNumber, findLiElement } from "./element.mjs";
3
3
  import { getTagName } from "./style.mjs";
4
4
  let enterCount = 0;
5
5
  function enterEvent(type, event, action) {
@@ -7,6 +7,15 @@ function enterEvent(type, event, action) {
7
7
  event.preventDefault();
8
8
  const useShift = event.shiftKey;
9
9
  switch (type) {
10
+ case "ol":
11
+ if (useShift === false) {
12
+ if (enterCount == 0) {
13
+ listEnterEvent(event, action);
14
+ }
15
+ } else {
16
+ addBrEvent();
17
+ }
18
+ break;
10
19
  case "image":
11
20
  action("addBlock", {
12
21
  name: "text"
@@ -30,6 +39,99 @@ function enterEvent(type, event, action) {
30
39
  }, 150);
31
40
  }
32
41
  }
42
+ function listEnterEvent(event, action) {
43
+ const cursorData = getCursor();
44
+ if (cursorData.startNode) {
45
+ const editableElement = findEditableElement(cursorData.startNode);
46
+ const editableElementClassList = [...editableElement.classList].slice(0, 1);
47
+ const startNode = cursorData.startNode;
48
+ const endNode = cursorData.endNode;
49
+ let startChild = startNode;
50
+ let endChild = endNode;
51
+ if (startNode.parentNode !== editableElement) {
52
+ startChild = startNode.parentNode;
53
+ }
54
+ if (endNode.parentNode !== editableElement) {
55
+ endChild = endNode.parentNode;
56
+ }
57
+ const startChildIdx = findChildNumber(editableElement, startChild);
58
+ const endChildIdx = findChildNumber(editableElement, endChild);
59
+ let startIdx = 0;
60
+ let endIdx = 0;
61
+ let startOffset = 0;
62
+ let endOffset = 0;
63
+ let preHTMLStructor = "";
64
+ let nextHTMLStructor = "";
65
+ if (startChildIdx > endChildIdx) {
66
+ startIdx = endChildIdx;
67
+ endIdx = startChildIdx;
68
+ startOffset = cursorData.endOffset;
69
+ endOffset = cursorData.startOffset;
70
+ } else {
71
+ startIdx = startChildIdx;
72
+ endIdx = endChildIdx;
73
+ startOffset = cursorData.startOffset;
74
+ endOffset = cursorData.endOffset;
75
+ }
76
+ if (editableElement.childNodes.length === 0 || endChildIdx === editableElement.childNodes.length - 1 && editableElement.childNodes[endChildIdx].textContent.length === endOffset) {
77
+ action("addBlock", {
78
+ name: "text"
79
+ });
80
+ } else {
81
+ editableElement.childNodes.forEach((child, count) => {
82
+ const text = child.textContent;
83
+ if (count < startChildIdx) {
84
+ if (child.constructor.name === "Text") {
85
+ preHTMLStructor += child.textContent;
86
+ } else {
87
+ preHTMLStructor += child.outerHTML;
88
+ }
89
+ } else if (count > endChildIdx) {
90
+ if (child.constructor.name === "Text") {
91
+ nextHTMLStructor += child.textContent;
92
+ } else {
93
+ nextHTMLStructor += child.outerHTML;
94
+ }
95
+ } else {
96
+ if (startChildIdx === endChildIdx) {
97
+ if (child.constructor.name === "Text") {
98
+ preHTMLStructor += text.substring(0, startOffset);
99
+ nextHTMLStructor += text.substring(endOffset, text.length);
100
+ } else {
101
+ const childClassList = [...child.classList];
102
+ const originTag = getTagName(child);
103
+ preHTMLStructor += `<${originTag.name} ${originTag.href ? `href="${originTag.href}" rel="nofollow"` : ""} class="${childClassList.join(" ")}">${text.substring(0, startOffset)}</${originTag.name}>`;
104
+ nextHTMLStructor += `<${originTag.name} ${originTag.href ? `href="${originTag.href}" rel="nofollow"` : ""} class="${childClassList.join(" ")}">${text.substring(endOffset, text.length)}</${originTag.name}>`;
105
+ }
106
+ } else {
107
+ if (count === startChildIdx) {
108
+ if (child.constructor.name === "Text") {
109
+ preHTMLStructor += text.substring(0, startOffset);
110
+ } else {
111
+ const childClassList = [...child.classList];
112
+ const originTag = getTagName(child);
113
+ preHTMLStructor += `<${originTag.name} ${originTag.href ? `href="${originTag.href}" rel="nofollow"` : ""} class="${childClassList.join(" ")}">${text.substring(0, startOffset)}</${originTag.name}>`;
114
+ }
115
+ } else if (count === endChildIdx) {
116
+ if (child.constructor.name === "Text") {
117
+ nextHTMLStructor += text.substring(endOffset, text.length);
118
+ } else {
119
+ const childClassList = [...child.classList];
120
+ const originTag = getTagName(child);
121
+ nextHTMLStructor += `<${originTag.name} ${originTag.href ? `href="${originTag.href}" rel="nofollow"` : ""} class="${childClassList.join(" ")}">${text.substring(endOffset, text.length)}</${originTag.name}>`;
122
+ }
123
+ }
124
+ }
125
+ }
126
+ });
127
+ editableElement.innerHTML = preHTMLStructor;
128
+ action("addBlock", {
129
+ name: "text",
130
+ value: { classList: editableElementClassList, content: nextHTMLStructor }
131
+ });
132
+ }
133
+ }
134
+ }
33
135
  function textEnterEvent(event, action) {
34
136
  const cursorData = getCursor();
35
137
  if (cursorData.startNode) {
@@ -320,3 +422,136 @@ function addBrEvent() {
320
422
  }
321
423
  }
322
424
  }
425
+ function addBrEventWithList() {
426
+ const cursorData = getCursor();
427
+ if (cursorData.startNode) {
428
+ let $target = cursorData.startNode;
429
+ const preEditableElement = findLiElement($target);
430
+ if ($target.constructor.name === "Text") {
431
+ $target = $target.parentNode;
432
+ }
433
+ if (preEditableElement !== $target && $target.constructor.name !== "HTMLBRElement") {
434
+ let startNode = cursorData.startNode;
435
+ let endNode = cursorData.endNode;
436
+ let startChild = startNode;
437
+ let endChild = endNode;
438
+ if (startNode.parentNode !== preEditableElement) {
439
+ startChild = startNode.parentNode;
440
+ }
441
+ if (endNode.parentNode !== preEditableElement) {
442
+ endChild = endNode.parentNode;
443
+ }
444
+ const startChildIdx = findChildNumber(preEditableElement, startChild);
445
+ const endChildIdx = findChildNumber(preEditableElement, endChild);
446
+ let startIdx = 0;
447
+ let endIdx = 0;
448
+ let startOffset = 0;
449
+ let endOffset = 0;
450
+ let htmlStructure = "";
451
+ if (startChildIdx > endChildIdx) {
452
+ startIdx = endChildIdx;
453
+ endIdx = startChildIdx;
454
+ startOffset = cursorData.endOffset;
455
+ endOffset = cursorData.startOffset;
456
+ } else {
457
+ startIdx = startChildIdx;
458
+ endIdx = endChildIdx;
459
+ startOffset = cursorData.startOffset;
460
+ endOffset = cursorData.endOffset;
461
+ }
462
+ if (enterCount === 1) {
463
+ const text = startChild.textContent;
464
+ startChild.textContent = text.substring(1, text.length);
465
+ setCursor(startChild, 0);
466
+ } else {
467
+ if (startNode === endNode) {
468
+ const text = startChild.textContent;
469
+ const childClassList = [...startChild.classList];
470
+ const tagData = getTagName(startChild);
471
+ htmlStructure += `<${tagData.name} ${tagData.href ? `href="${tagData.href}" rel="nofollow"` : ""} class="${childClassList.join(" ")}">${text.substring(0, startOffset)}</${tagData.name}>`;
472
+ htmlStructure += `<br>`;
473
+ htmlStructure += `<${tagData.name} ${tagData.href ? `href="${tagData.href}" rel="nofollow"` : ""} class="${childClassList.join(" ")}">${text.substring(endOffset, text.length)}</${tagData.name}>`;
474
+ startChild.insertAdjacentHTML("beforebegin", htmlStructure);
475
+ setCursor(preEditableElement.childNodes[startChildIdx + 2], 0);
476
+ startChild.remove();
477
+ } else {
478
+ preEditableElement.childNodes.forEach((child, count) => {
479
+ const type = child.constructor.name;
480
+ const text = child.textContent;
481
+ if (count > startIdx && count < endIdx) {
482
+ } else if (count === startIdx) {
483
+ if (type === "Text") {
484
+ htmlStructure += text.substring(0, startOffset);
485
+ } else {
486
+ const childClassList = [...child.classList];
487
+ const tagData = getTagName(child);
488
+ htmlStructure += `<${tagData.name} ${tagData.href ? `href="${tagData.href}" rel="nofollow"` : ""} class="${childClassList.join(" ")}">${text.substring(0, startOffset)}</${tagData.name}><br>`;
489
+ }
490
+ } else if (count === endIdx) {
491
+ if (type === "Text") {
492
+ htmlStructure += text.substring(endOffset, text.length);
493
+ } else {
494
+ const childClassList = [...child.classList];
495
+ const tagData = getTagName(child);
496
+ htmlStructure += `<${tagData.name} ${tagData.href ? `href="${tagData.href}" rel="nofollow"` : ""} class="${childClassList.join(" ")}">${text.substring(endOffset, text.length)}</${tagData.name}><br>`;
497
+ }
498
+ } else {
499
+ if (type === "Text") {
500
+ htmlStructure += child.textContent;
501
+ } else {
502
+ htmlStructure += child.outerHTML;
503
+ }
504
+ }
505
+ });
506
+ preEditableElement.innerHTML = htmlStructure;
507
+ }
508
+ }
509
+ } else {
510
+ const brTag = document.createElement("br");
511
+ const selection = window.getSelection();
512
+ const range = document.createRange();
513
+ console.log("br");
514
+ if (enterCount === 1) {
515
+ const nextCursorData = getCursor();
516
+ const editableElement = findEditableElement(nextCursorData.startNode);
517
+ const childList = editableElement.childNodes;
518
+ const childIdx = findChildNumber(editableElement, nextCursorData.startNode);
519
+ setCursor(childList[childIdx + 2], 0);
520
+ } else {
521
+ selection.deleteFromDocument();
522
+ selection.getRangeAt(0).insertNode(brTag);
523
+ range.setStart(brTag, 0);
524
+ range.collapse(true);
525
+ selection.removeAllRanges();
526
+ selection.addRange(range);
527
+ const nextCursorData = getCursor();
528
+ const editableElement = findEditableElement(nextCursorData.startNode);
529
+ const childList = editableElement.childNodes;
530
+ const childIdx = findChildNumber(editableElement, nextCursorData.startNode);
531
+ let hasText = false;
532
+ childList.forEach((row) => {
533
+ if (row.constructor.name === "Text") {
534
+ hasText = true;
535
+ }
536
+ });
537
+ if (hasText) {
538
+ if (childList[childIdx + 1].textContent?.length === 0) {
539
+ if (childList[childIdx + 2]) {
540
+ if (childList[childIdx + 2].constructor.name == "HTMLBRElement") {
541
+ setCursor(childList[childIdx + 1], 0);
542
+ } else {
543
+ childList[childIdx].insertAdjacentHTML("beforebegin", "<br>");
544
+ }
545
+ } else {
546
+ childList[childIdx].insertAdjacentHTML("beforebegin", "<br>");
547
+ }
548
+ } else {
549
+ setCursor(childList[childIdx + 1], 0);
550
+ }
551
+ } else {
552
+ childList[childIdx].insertAdjacentHTML("beforebegin", "<br>");
553
+ }
554
+ }
555
+ }
556
+ }
557
+ }
@@ -13,6 +13,19 @@ function arrangementAlignClass(originList, className) {
13
13
  }
14
14
  return array;
15
15
  }
16
+ const headingClassList = ["d-h1", "d-h2", "d-h3", "d-h4", "d-h5", "d-h6"];
17
+ function arrangementHeadingClass(originList, className) {
18
+ const hasClass = originList.indexOf(className) > -1;
19
+ let array = originList;
20
+ if (hasClass) {
21
+ originList.splice(originList.indexOf(className), 1);
22
+ array = originList;
23
+ } else {
24
+ array = originList.filter((item) => headingClassList.indexOf(item) === -1);
25
+ array.push(className);
26
+ }
27
+ return array;
28
+ }
16
29
  function getNextNode($target, node) {
17
30
  const childNode = $target.childNodes;
18
31
  let idx = -1;
@@ -252,13 +265,7 @@ function defaultDecorationMake({ originData, $target, className, tagName = "span
252
265
  }
253
266
  return originData;
254
267
  }
255
- export function styleSettings({
256
- kind,
257
- blockData,
258
- $target,
259
- url,
260
- cursorData
261
- }) {
268
+ export function styleSettings({ kind, blockData, $target, url, cursorData }) {
262
269
  let rawData = blockData;
263
270
  switch (kind) {
264
271
  case "alignLeft":
@@ -271,44 +278,36 @@ export function styleSettings({
271
278
  rawData.classList = arrangementAlignClass(rawData.classList, "d-align-right");
272
279
  break;
273
280
  case "decorationBold":
274
- rawData = defaultDecorationMake(
275
- {
276
- originData: rawData,
277
- $target,
278
- className: "d-deco-bold",
279
- parentCursorData: cursorData
280
- }
281
- );
281
+ rawData = defaultDecorationMake({
282
+ originData: rawData,
283
+ $target,
284
+ className: "d-deco-bold",
285
+ parentCursorData: cursorData
286
+ });
282
287
  break;
283
288
  case "decorationItalic":
284
- rawData = defaultDecorationMake(
285
- {
286
- originData: rawData,
287
- $target,
288
- className: "d-deco-italic",
289
- parentCursorData: cursorData
290
- }
291
- );
289
+ rawData = defaultDecorationMake({
290
+ originData: rawData,
291
+ $target,
292
+ className: "d-deco-italic",
293
+ parentCursorData: cursorData
294
+ });
292
295
  break;
293
296
  case "decorationUnderline":
294
- rawData = defaultDecorationMake(
295
- {
296
- originData: rawData,
297
- $target,
298
- className: "d-deco-underline",
299
- parentCursorData: cursorData
300
- }
301
- );
297
+ rawData = defaultDecorationMake({
298
+ originData: rawData,
299
+ $target,
300
+ className: "d-deco-underline",
301
+ parentCursorData: cursorData
302
+ });
302
303
  break;
303
304
  case "decorationStrikethrough":
304
- rawData = defaultDecorationMake(
305
- {
306
- originData: rawData,
307
- $target,
308
- className: "d-deco-through",
309
- parentCursorData: cursorData
310
- }
311
- );
305
+ rawData = defaultDecorationMake({
306
+ originData: rawData,
307
+ $target,
308
+ className: "d-deco-through",
309
+ parentCursorData: cursorData
310
+ });
312
311
  break;
313
312
  case "decorationCode":
314
313
  rawData = defaultDecorationMake({
@@ -329,15 +328,31 @@ export function styleSettings({
329
328
  parentCursorData: cursorData
330
329
  });
331
330
  break;
331
+ case "heading-1":
332
+ rawData.classList = arrangementHeadingClass(rawData.classList, "d-h1");
333
+ break;
334
+ case "heading-2":
335
+ rawData.classList = arrangementHeadingClass(rawData.classList, "d-h2");
336
+ break;
337
+ case "heading-3":
338
+ rawData.classList = arrangementHeadingClass(rawData.classList, "d-h3");
339
+ break;
340
+ case "heading-4":
341
+ rawData.classList = arrangementHeadingClass(rawData.classList, "d-h4");
342
+ break;
343
+ case "heading-5":
344
+ rawData.classList = arrangementHeadingClass(rawData.classList, "d-h5");
345
+ break;
346
+ case "heading-6":
347
+ rawData.classList = arrangementHeadingClass(rawData.classList, "d-h6");
348
+ break;
332
349
  default:
333
- rawData = defaultDecorationMake(
334
- {
335
- originData: rawData,
336
- $target,
337
- className: kind,
338
- parentCursorData: cursorData
339
- }
340
- );
350
+ rawData = defaultDecorationMake({
351
+ originData: rawData,
352
+ $target,
353
+ className: kind,
354
+ parentCursorData: cursorData
355
+ });
341
356
  }
342
357
  return rawData;
343
358
  }
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="dragon-editor" @paste="pasteEvent" ref="$wrap" @mouseleave="deactiveMenuEvent" @keydown="deactiveMenuEvent" :key="totalKey">
2
+ <div class="dragon-editor" @paste="pasteEvent" ref="$wrap" @mouseleave="deactiveMenuEvent" @keydown="deactiveMenuEvent">
3
3
  <div class="d-left-menu" :class="{ '--active': activeMenu }" :style="{ top: `${leftMenuPosition}px` }">
4
4
  <div class="d-add-block">
5
5
  <button class="d-btn-menu-pop" @click="toggleBlockAddMenu"></button>
@@ -17,23 +17,21 @@
17
17
  <button class="d-btn-block-pop" @click="toggleBlockControlMenu"></button>
18
18
 
19
19
  <div class="d-block-list" :class="{ '--active': activeBlockColtrolMenu }">
20
- <button class="d-btn-block" @click="moveBlock('up')">
21
- <SvgIcon kind="arrowUp" />Up
22
- </button>
23
- <button class="d-btn-block" @click="moveBlock('down')">
24
- <SvgIcon kind="arrowDown" />Down
25
- </button>
26
- <button class="d-btn-block">
27
- <SvgIcon kind="delete" />Delete
28
- </button>
20
+ <button class="d-btn-block" @click="moveBlock('up')"> <SvgIcon kind="arrowUp" />Up </button>
21
+ <button class="d-btn-block" @click="moveBlock('down')"> <SvgIcon kind="arrowDown" />Down </button>
22
+ <button class="d-btn-block"> <SvgIcon kind="delete" />Delete </button>
29
23
  </div>
30
24
  </div>
31
25
  </div>
32
26
 
33
- <div class="d-link-box" :class="{ '--active': activeLinkBox }" :style="{
34
- top: `${linkBoxPosition.top}px`,
35
- left: `${linkBoxPosition.left}px`,
36
- }">
27
+ <div
28
+ class="d-link-box"
29
+ :class="{ '--active': activeLinkBox }"
30
+ :style="{
31
+ top: `${linkBoxPosition.top}px`,
32
+ left: `${linkBoxPosition.left}px`,
33
+ }"
34
+ >
37
35
  <template v-if="styleButtonList[2][0].active">
38
36
  <p class="d-input">{{ linkValue }}</p>
39
37
  <button class="d-btn-link" @click="decoLinkControl">
@@ -51,23 +49,32 @@
51
49
  <div class="d-style-menu" :class="{ '--active': activeMenu }" :style="{ top: `${styleMenuPosition}px` }">
52
50
  <div v-for="(column, count) in styleButtonList" :key="count" class="d-column">
53
51
  <template v-for="(item, j) in column">
54
- <button v-if="item.target.indexOf(content[activeIdx].type) > -1" class="d-btn" :class="{ '--active': item.active }" @click="item.action">
55
- <SvgIcon :kind="item.icon" />
56
- </button>
52
+ <template v-if="item.type === 'single'">
53
+ <button v-if="item.target.indexOf(content[activeIdx].type) > -1" class="d-btn" :class="{ '--active': item.active }" @click="item.action">
54
+ <SvgIcon :kind="item.icon" />
55
+ </button>
56
+ </template>
57
+
58
+ <template v-else>
59
+ <button v-if="item.target.indexOf(content[activeIdx].type) > -1" class="d-btn --hug" @click="item.action(count, j)">{{ `${item.name} : ${item.value}` }}</button>
60
+
61
+ <div class="d-child-list" :class="{ '--active': item.active }">
62
+ <button class="d-child-btn" v-for="(child, k) in item.childList" :key="k" @click="child.action(count, j)">{{ child.name }}</button>
63
+ </div>
64
+ </template>
57
65
  </template>
58
66
  </div>
59
- <div v-if="customStyleMenu.length > 0" class="d-column">
60
- <!-- customStyleMenu-->
61
- </div>
67
+ <div v-if="customStyleMenu.length > 0" class="d-column"></div>
62
68
  </div>
63
69
 
64
- <div class="d-row-block" v-for="(row, count) in content" :key="count" @click="activeIdx = count" @mouseenter="activeMenuEvent(count, $event)" @mousemove="activeMenuEvent(count, $event)" @mouseup="activeMenuEvent(count, $event)">
65
- <component ref="$child" v-model="content[count]" :key="`${row.type}-count`" :is="setComponentKind(row.type)" :cursorData="cursorData" @addBlock="addBlockLocal" @deleteBlockLocal="deleteBlockLocal" />
70
+ <div class="d-row-block" v-for="(row, count) in content" :key="`${row.id}-wrap`" @click="activeIdx = count" @mouseenter="activeMenuEvent(count, $event)" @mousemove="activeMenuEvent(count, $event)" @mouseup="activeMenuEvent(count, $event)">
71
+ <component ref="$child" v-model="content[count]" :key="row.id" :is="setComponentKind(row.type)" :cursorData="cursorData" @addBlock="addBlockLocal" @deleteBlockLocal="deleteBlockLocal" />
66
72
  </div>
67
73
  </div>
68
74
  </template>
69
75
 
70
76
  <script setup lang="ts">
77
+ // @ts-ignore
71
78
  import { ref, unref, onMounted } from "#imports";
72
79
  import { createBlock, getClipboardData, getCursor } from "../../core/utils";
73
80
  import type { editorOptions, editorMenu, editorContentType, userCustomMenu, userStyleMenu, cursorSelection } from "../../../types/index";
@@ -76,6 +83,7 @@ import type { editorOptions, editorMenu, editorContentType, userCustomMenu, user
76
83
  import SvgIcon from "../../core/components/SvgIcon.vue";
77
84
  import textBlock from "../../core/components/editor/TextBlock.vue";
78
85
  import imageBlock from "../../core/components/editor/ImageBlock.vue";
86
+ import olBlock from "../../core/components/editor/OlBlock.vue";
79
87
 
80
88
  // 기본 정보
81
89
  const props = defineProps<{
@@ -130,6 +138,7 @@ const cursorData = ref<cursorSelection>({
130
138
  const styleButtonList = ref([
131
139
  [
132
140
  {
141
+ type: "single",
133
142
  name: "Align Left",
134
143
  icon: "alignLeft",
135
144
  active: false,
@@ -139,6 +148,7 @@ const styleButtonList = ref([
139
148
  },
140
149
  },
141
150
  {
151
+ type: "single",
142
152
  name: "Align Center",
143
153
  icon: "alignCenter",
144
154
  target: ["text", "image", "table", "ul", "ol"],
@@ -148,6 +158,7 @@ const styleButtonList = ref([
148
158
  },
149
159
  },
150
160
  {
161
+ type: "single",
151
162
  name: "Align right",
152
163
  icon: "alignRight",
153
164
  target: ["text", "image", "table", "ul", "ol"],
@@ -159,6 +170,7 @@ const styleButtonList = ref([
159
170
  ],
160
171
  [
161
172
  {
173
+ type: "single",
162
174
  name: "Decoration Bold",
163
175
  icon: "decorationBold",
164
176
  target: ["text", "table", "ul", "ol"],
@@ -168,6 +180,7 @@ const styleButtonList = ref([
168
180
  },
169
181
  },
170
182
  {
183
+ type: "single",
171
184
  name: "Decoration Italic",
172
185
  icon: "decorationItalic",
173
186
  target: ["text", "table", "ul", "ol"],
@@ -177,6 +190,7 @@ const styleButtonList = ref([
177
190
  },
178
191
  },
179
192
  {
193
+ type: "single",
180
194
  name: "Decoration Underline",
181
195
  icon: "decorationUnderline",
182
196
  target: ["text", "table", "ul", "ol"],
@@ -186,6 +200,7 @@ const styleButtonList = ref([
186
200
  },
187
201
  },
188
202
  {
203
+ type: "single",
189
204
  name: "Decoration Strikethrough",
190
205
  icon: "decorationStrikethrough",
191
206
  target: ["text", "table", "ul", "ol"],
@@ -197,6 +212,7 @@ const styleButtonList = ref([
197
212
  ],
198
213
  [
199
214
  {
215
+ type: "single",
200
216
  name: "Link",
201
217
  icon: "link",
202
218
  active: false,
@@ -208,6 +224,7 @@ const styleButtonList = ref([
208
224
  ],
209
225
  [
210
226
  {
227
+ type: "single",
211
228
  name: "Decoration Code",
212
229
  icon: "codeBlock",
213
230
  target: ["text", "table", "ul", "ol"],
@@ -217,8 +234,69 @@ const styleButtonList = ref([
217
234
  },
218
235
  },
219
236
  ],
237
+ [
238
+ {
239
+ type: "list",
240
+ name: "Font Size",
241
+ value: "default",
242
+ target: ["text"],
243
+ active: false,
244
+ action: (count, j) => {
245
+ styleButtonList.value[count][j].active = !styleButtonList.value[count][j].active;
246
+ },
247
+ childList: [
248
+ {
249
+ name: "default",
250
+ action: (count, j) => {
251
+ setBlockDecoEvent("heading-4");
252
+ styleButtonList.value[count][j].value = "default";
253
+ styleButtonList.value[count][j].active = false;
254
+ },
255
+ },
256
+ {
257
+ name: "h1",
258
+ action: (count, j) => {
259
+ setBlockDecoEvent("heading-1");
260
+ styleButtonList.value[count][j].value = "h1";
261
+ styleButtonList.value[count][j].active = false;
262
+ },
263
+ },
264
+ {
265
+ name: "h2",
266
+ action: (count, j) => {
267
+ setBlockDecoEvent("heading-2");
268
+ styleButtonList.value[count][j].value = "h2";
269
+ styleButtonList.value[count][j].active = false;
270
+ },
271
+ },
272
+ {
273
+ name: "h3",
274
+ action: (count, j) => {
275
+ setBlockDecoEvent("heading-3");
276
+ styleButtonList.value[count][j].value = "h3";
277
+ styleButtonList.value[count][j].active = false;
278
+ },
279
+ },
280
+ {
281
+ name: "h5",
282
+ action: (count, j) => {
283
+ setBlockDecoEvent("heading-5");
284
+ styleButtonList.value[count][j].value = "h5";
285
+ styleButtonList.value[count][j].active = false;
286
+ },
287
+ },
288
+ {
289
+ name: "h6",
290
+ action: (count, j) => {
291
+ setBlockDecoEvent("heading-6");
292
+ styleButtonList.value[count][j].value = "h6";
293
+ styleButtonList.value[count][j].active = false;
294
+ },
295
+ },
296
+ ],
297
+ },
298
+ ],
220
299
  ]);
221
- const totalKey = ref<number>(1);
222
300
 
223
301
  // 블럭 추가 메뉴 설정
224
302
  blockMenu.value = setEditorMenu(option.value.blockMenu as string[], unref(option.value.customBlockMenu) as userCustomMenu[]);
@@ -253,6 +331,7 @@ function checkAlignActive(className: string) {
253
331
  return value;
254
332
  }
255
333
 
334
+ // 스타일 메뉴 엑티브 상황
256
335
  function checkDecoActive() {
257
336
  styleButtonList.value[0][0].active = checkAlignActive("d-align-left");
258
337
  styleButtonList.value[0][1].active = checkAlignActive("d-align-center");
@@ -263,8 +342,10 @@ function checkDecoActive() {
263
342
  styleButtonList.value[1][3].active = hasClassNameCheckLogic("d-deco-through");
264
343
  styleButtonList.value[2][0].active = hasClassNameCheckLogic("d-deco-link");
265
344
  styleButtonList.value[3][0].active = hasClassNameCheckLogic("d-deco-code");
345
+ styleButtonList.value[4][0].value = checkHeadingClass();
266
346
  }
267
347
 
348
+ // 텍스트 스타일 확인용 함수
268
349
  function hasClassNameCheckLogic(className: string) {
269
350
  const cursorData = getCursor();
270
351
  let value = false;
@@ -297,6 +378,33 @@ function hasClassNameCheckLogic(className: string) {
297
378
  return value;
298
379
  }
299
380
 
381
+ // 글자크기 클레스 확인
382
+ function checkHeadingClass(): string {
383
+ let value: string = "";
384
+
385
+ switch (true) {
386
+ case hasClassNameCheckLogic("d-h1"):
387
+ value = "h1";
388
+ break;
389
+ case hasClassNameCheckLogic("d-h2"):
390
+ value = "h2";
391
+ break;
392
+ case hasClassNameCheckLogic("d-h3"):
393
+ value = "h3";
394
+ break;
395
+ case hasClassNameCheckLogic("d-h5"):
396
+ value = "h5";
397
+ break;
398
+ case hasClassNameCheckLogic("d-h6"):
399
+ value = "h6";
400
+ break;
401
+ default:
402
+ value = "default";
403
+ }
404
+
405
+ return value;
406
+ }
407
+
300
408
  function setEditorMenu(vanillaData: string[], customData?: userCustomMenu[]) {
301
409
  const dataList: editorMenu[] = [];
302
410
 
@@ -338,6 +446,22 @@ function activeMenuEvent(count: number, e?: MouseEvent) {
338
446
 
339
447
  if (e) {
340
448
  $target = e.currentTarget as HTMLElement;
449
+
450
+ if (e.type === "mouseup") {
451
+ const wrap = $target.parentElement as HTMLElement;
452
+ const child = wrap.querySelectorAll(".d-row-block");
453
+ let idx: number = -1;
454
+
455
+ [...child].filter((item, count) => {
456
+ if (item === $target) {
457
+ idx = count;
458
+ }
459
+ });
460
+
461
+ if (idx > -1) {
462
+ activeIdx.value = idx;
463
+ }
464
+ }
341
465
  } else {
342
466
  $target = $child.value[activeIdx.value];
343
467
  }
@@ -345,13 +469,15 @@ function activeMenuEvent(count: number, e?: MouseEvent) {
345
469
  setMenuPosition($target);
346
470
  checkDecoActive();
347
471
  activeMenu.value = true;
472
+ styleButtonList.value[4][0].active = false;
348
473
  }
349
474
 
350
475
  // 관련 메뉴 닫기
351
- function deactiveMenuEvent(e?: (MouseEvent | KeyboardEvent)) {
476
+ function deactiveMenuEvent(e?: MouseEvent | KeyboardEvent) {
352
477
  activeMenu.value = false;
353
478
  activeBlockAddMenu.value = false;
354
479
  activeBlockColtrolMenu.value = false;
480
+ styleButtonList.value[4][0].active = false;
355
481
 
356
482
  if (e && e.type === "mouseleave") {
357
483
  activeLinkBox.value = false;
@@ -418,7 +544,9 @@ function pasteEvent(e: ClipboardEvent) {
418
544
  const targetComponent = $child.value[activeIdx.value];
419
545
  const componentType = targetComponent.getType();
420
546
 
421
- if (componentType !== "other") {
547
+ if (componentType === "other") {
548
+ // TODO : add block
549
+ } else {
422
550
  targetComponent.pasteEvent(data.value);
423
551
  }
424
552
  }
@@ -427,7 +555,11 @@ function pasteEvent(e: ClipboardEvent) {
427
555
  // 블럭 종류 정의
428
556
  function setComponentKind(kind: string) {
429
557
  let componentData: any;
558
+
430
559
  switch (kind) {
560
+ case "ol":
561
+ componentData = olBlock;
562
+ break;
431
563
  case "image":
432
564
  componentData = imageBlock;
433
565
  break;
@@ -504,8 +636,6 @@ async function moveBlock(type: string) {
504
636
  content.value.splice(targetIdx, 1, thisData);
505
637
  content.value.splice(activeIdx.value, 1, targetData);
506
638
  activeIdx.value = targetIdx;
507
-
508
- totalKey.value += 1;
509
639
  deactiveMenuEvent();
510
640
  }
511
641
  }
@@ -1,22 +1,13 @@
1
1
  <template>
2
2
  <div class="dragon-editor --comment">
3
- <p class="d-text-block" :class="data.classList" contenteditable v-html="data.content" @keydown="textKeyboardEvent"
4
- @paste="pasteEvent" ref="$block"></p>
3
+ <p class="d-text-block" :class="data.classList" contenteditable v-html="data.content" @keydown="textKeyboardEvent" @paste="pasteEvent" ref="$block"></p>
5
4
  </div>
6
5
  </template>
7
6
 
8
7
  <script setup lang="ts">
8
+ // @ts-ignore
9
9
  import { ref, unref } from "#imports";
10
- import {
11
- keyboardEvent,
12
- setCursor,
13
- pasteText,
14
- styleSettings,
15
- getArrangementCursorData,
16
- getClipboardData,
17
- getCursor,
18
- findEditableElement
19
- } from "../../core/utils/index";
10
+ import { keyboardEvent, setCursor, pasteText, styleSettings, getArrangementCursorData, getClipboardData, getCursor, findEditableElement } from "../../core/utils/index";
20
11
  import { commentBlock, cursorSelection } from "../../../types/index";
21
12
 
22
13
  const $block = ref();
@@ -40,7 +31,7 @@ const blockCursorData = ref<cursorSelection>({
40
31
  data.value = unref(props.modelValue) as commentBlock;
41
32
 
42
33
  function textKeyboardEvent(e: KeyboardEvent) {
43
- keyboardEvent("comment", e, emit);
34
+ keyboardEvent("comment", e, emit, updateBlockData);
44
35
  }
45
36
 
46
37
  function pasteEvent(e: ClipboardEvent) {
@@ -57,9 +48,7 @@ function updateBlockData() {
57
48
  // 데이터 정규화 및 검수
58
49
  const blockClassList = [...$block.value.classList];
59
50
  blockClassList.splice(0, 1);
60
- const pushList = blockClassList.filter(
61
- (item) => data.value.classList.indexOf(item) === -1
62
- );
51
+ const pushList = blockClassList.filter((item) => data.value.classList.indexOf(item) === -1);
63
52
 
64
53
  data.value.classList = data.value.classList.concat(pushList);
65
54
 
@@ -71,18 +60,19 @@ function updateBlockData() {
71
60
  emit("update:modelValue", data.value);
72
61
 
73
62
  setTimeout(() => {
74
- setCursor(
75
- $block.value.childNodes[cursorData.childCount],
76
- cursorData.length
77
- );
63
+ setCursor($block.value.childNodes[cursorData.childCount], cursorData.length);
78
64
 
79
65
  // 구조 검수
80
66
  $block.value.childNodes.forEach((child: ChildNode) => {
81
- if (child.constructor.name !== "Text") { // 텍스트가 아닐경우
82
- if (child.constructor.name !== "HTMLBRElement") { // br 태그 유지
83
- if (child.textContent === "") { // 빈 태그 삭제
67
+ if (child.constructor.name !== "Text") {
68
+ // 텍스트가 아닐경우
69
+ if (child.constructor.name !== "HTMLBRElement") {
70
+ // br 태그 유지
71
+ if (child.textContent === "") {
72
+ // 빈 태그 삭제
84
73
  child.remove();
85
- } else if ((child as HTMLElement).classList.length === 0) { // 클레스 없는 엘리먼트 처리
74
+ } else if ((child as HTMLElement).classList.length === 0) {
75
+ // 클레스 없는 엘리먼트 처리
86
76
  (child as HTMLElement).insertAdjacentHTML("afterend", child.textContent as string);
87
77
  child.remove();
88
78
  }
@@ -103,15 +93,13 @@ function focus() {
103
93
  }
104
94
 
105
95
  function setStyles(kind: string, url?: string) {
106
- data.value = styleSettings(
107
- {
108
- kind: kind,
109
- blockData: data.value,
110
- $target: $block.value,
111
- url: url,
112
- cursorData: blockCursorData.value
113
- }
114
- );
96
+ data.value = styleSettings({
97
+ kind: kind,
98
+ blockData: data.value,
99
+ $target: $block.value,
100
+ url: url,
101
+ cursorData: blockCursorData.value,
102
+ });
115
103
  setTimeout(() => {
116
104
  updateBlockData();
117
105
  }, 250);
@@ -142,7 +130,7 @@ defineExpose({
142
130
  updateBlockData,
143
131
  focus,
144
132
  setStyles,
145
- getCursorClassList
133
+ getCursorClassList,
146
134
  });
147
135
  </script>
148
136
 
@@ -8,7 +8,7 @@
8
8
  <template v-else>
9
9
  <div class="d-image-block" :class="row.classList">
10
10
  <div class="d-image-area">
11
- <img class="d-img" :src="row.src" :width="row.width" :height="row.height" :alt="row.caption" loading="lazy">
11
+ <img class="d-img" :src="row.src" :width="row.width" :height="row.height" :alt="row.caption" loading="lazy" />
12
12
  </div>
13
13
  <p class="d-caption" v-if="row.caption" v-html="row.caption"></p>
14
14
  </div>
@@ -19,8 +19,10 @@
19
19
  </template>
20
20
 
21
21
  <script setup lang="ts">
22
+ import { editorContentType } from "../../../types";
23
+
22
24
  const props = defineProps<{
23
- content: any[];
25
+ content: editorContentType;
24
26
  }>();
25
27
  </script>
26
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dragon-editor",
3
- "version": "2.0.0-beta.2",
3
+ "version": "2.0.0-beta.2.1.1",
4
4
  "description": "WYSIWYG editor on Nuxt.js",
5
5
  "repository": {
6
6
  "type": "git",