dragon-editor 2.0.0-beta.2.1.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -66,6 +66,13 @@ editor.value.addImageBlock({
66
66
  });
67
67
  ```
68
68
 
69
+ 2. 저장
70
+
71
+ ```typescript
72
+ editor.value.updateBlockData();
73
+ // do somthing
74
+ ```
75
+
69
76
  ### 코멘트
70
77
 
71
78
  ```vue
@@ -104,6 +111,19 @@ editor.value.setStyles("decorationBold");
104
111
  - `decorationUnderline` : 밑줄
105
112
  - `decorationStrikethrough` : 취소선
106
113
 
114
+ 2. 저장
115
+
116
+ ```typescript
117
+ editor.value.updateBlockData();
118
+ // do somthing
119
+ ```
120
+
121
+ 3. 포커스
122
+
123
+ ```typescript
124
+ editor.value.focus();
125
+ ```
126
+
107
127
  ### 뷰어
108
128
 
109
129
  ```vue
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.1.2"
4
+ "version": "2.0.0"
5
5
  }
@@ -1,21 +1,28 @@
1
1
  <template>
2
- <ol class="d-ol-block" :class="data.classList" @keydown="textKeyboardEvent">
3
- <li class="d-li-item" contenteditable ref="$olItem"></li>
2
+ <ol class="d-ol-block" @keydown="textKeyboardEvent" ref="$ol" :key="updateCount">
3
+ <li class="d-li-item" v-for="(row, i) in data.childList" :key="i" :class="row.classList" contenteditable ref="$item" v-html="row.content"></li>
4
4
  </ol>
5
5
  </template>
6
6
 
7
7
  <script setup lang="ts">
8
8
  // @ts-ignore
9
9
  import { ref, unref } from "#imports";
10
- import { cursorSelection, listBlock, styleFunctionArgument } from "../../../../types";
11
- import { getArrangementCursorData, setCursor, pasteText, styleSettings, keyboardEvent } from "../../utils";
10
+ import { cursorSelection, liItem, listBlock, styleFunctionArgument } from "../../../../types";
11
+ import { getArrangementCursorData, setCursor, pasteText, styleSettings, keyboardEvent, getCursor, findEditableElement } from "../../utils";
12
12
 
13
- const $olItem = ref();
13
+ const updateCount = ref<number>(0);
14
+ const $ol = ref();
15
+ const $item = ref();
16
+ const itemIdx = ref<number>(0);
14
17
  const data = ref<listBlock>({
15
18
  type: "",
16
19
  id: "",
17
- classList: [],
18
- childList: [],
20
+ childList: [
21
+ {
22
+ classList: [],
23
+ content: "",
24
+ },
25
+ ],
19
26
  });
20
27
  const props = defineProps<{ modelValue: listBlock; cursorData: cursorSelection }>();
21
28
  const emit = defineEmits<{
@@ -25,13 +32,12 @@ const emit = defineEmits<{
25
32
  }>();
26
33
  data.value = unref(props.modelValue) as listBlock;
27
34
 
28
- if(data.value.childList.length === 0){
29
-
35
+ if (data.value.childList.length === 0) {
30
36
  }
31
37
 
32
38
  // 키보드 이벤트 할당
33
39
  function textKeyboardEvent(e: KeyboardEvent) {
34
- keyboardEvent("ol", e, emit, updateBlockData);
40
+ keyboardEvent("list", e, emit, updateBlockData);
35
41
  }
36
42
 
37
43
  /**
@@ -40,64 +46,85 @@ function textKeyboardEvent(e: KeyboardEvent) {
40
46
 
41
47
  // 데이터 정규화 및 검수
42
48
  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
- // }
49
+ const $block = $ol.value;
50
+ const $childList = $block.querySelectorAll("li");
51
+ const childData: liItem[] = [];
52
+ const cursorData = getCursor();
53
+
54
+ $childList.forEach((row) => {
55
+ row.childNodes.forEach((child: ChildNode) => {
56
+ const $child = child as HTMLElement;
57
+
58
+ if (child.constructor.name !== "Text") {
59
+ // 텍스트가 아닐경우
60
+ if (child.constructor.name !== "HTMLBRElement") {
61
+ // br 태그 유지
62
+ if (child.textContent === "") {
63
+ // 태그 삭제
64
+ child.remove();
65
+ } else if ($child.classList.length === 0) {
66
+ // 클레스 없는 엘리먼트 처리
67
+ $child.insertAdjacentHTML("afterend", $child.innerHTML);
68
+ child.remove();
69
+ }
70
+ } else {
71
+ $child.removeAttribute("class");
72
+ }
73
+ }
74
+ });
75
+
76
+ childData.push({
77
+ classList: [...row.classList].splice(1),
78
+ content: row.innerHTML,
79
+ });
80
+ });
81
+
82
+ data.value.childList = childData;
83
+ emit("update:modelValue", data.value);
84
+ updateCount.value += 1;
85
+
86
+ if (cursorData.startNode) {
87
+ const editableNode = findEditableElement(cursorData.startNode);
88
+ let childIdx = -1;
89
+
90
+ $childList.forEach((row, count) => {
91
+ if (row === editableNode) {
92
+ childIdx = count;
93
+ }
94
+ });
95
+
96
+ if (childIdx > -1) {
97
+ // 기본 로직
98
+ itemIdx.value = childIdx;
99
+ setTimeout(() => {
100
+ const afterChildList = $ol.value.querySelectorAll("li");
101
+ setCursor(afterChildList[childIdx], cursorData.startOffset as number);
102
+ }, 100);
103
+ } else {
104
+ // 중간 엔터
105
+ $childList.forEach((row, count) => {
106
+ if (row === (cursorData.startNode as Node).parentNode) {
107
+ childIdx = count;
108
+ }
109
+ });
110
+
111
+ setTimeout(() => {
112
+ const afterChildList = $ol.value.querySelectorAll("li");
113
+ setCursor(afterChildList[childIdx], cursorData.startOffset as number);
114
+ }, 100);
115
+ }
116
+ }
85
117
  }
86
118
 
87
119
  // 포커스
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
- // }
120
+ function focus() {
121
+ const childList = $ol.value.querySelectorAll(".d-li-item");
122
+ setCursor(childList[itemIdx.value], 0);
96
123
  }
97
124
 
98
125
  // 블럭 위치 주기
99
126
  function getBoundingClientRect() {
100
- // return $block.value.parentNode.getBoundingClientRect();
127
+ return $ol.value.parentNode.getBoundingClientRect();
101
128
  }
102
129
 
103
130
  // 타입 전달
@@ -115,7 +142,7 @@ function setStyles({ type, url }: styleFunctionArgument) {
115
142
  data.value = styleSettings({
116
143
  kind: type,
117
144
  blockData: data.value,
118
- $target: undefined,
145
+ $target: $item[itemIdx.value],
119
146
  url: url,
120
147
  cursorData: props.cursorData,
121
148
  });
@@ -117,7 +117,13 @@ function updateBlockData() {
117
117
  // 포커스
118
118
  function focus(type: string = "first") {
119
119
  if (type === "first") {
120
- setCursor($block.value, 0);
120
+ if ($block.value.childNodes.length > 0) {
121
+ setTimeout(() => {
122
+ setCursor($block.value.childNodes[0], 0);
123
+ }, 100);
124
+ } else {
125
+ setCursor($block.value, 0);
126
+ }
121
127
  } else {
122
128
  const childCount = $block.value.childNodes.length;
123
129
  const targetChild = $block.value.childNodes[childCount - 1];
@@ -0,0 +1,162 @@
1
+ <template>
2
+ <ul class="d-ul-block" @keydown="textKeyboardEvent" ref="$ul" :key="updateCount">
3
+ <li class="d-li-item" v-for="(row, i) in data.childList" :key="i" :class="row.classList" contenteditable ref="$item" v-html="row.content"></li>
4
+ </ul>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ // @ts-ignore
9
+ import { ref, unref } from "#imports";
10
+ import { cursorSelection, liItem, listBlock, styleFunctionArgument } from "../../../../types";
11
+ import { getArrangementCursorData, setCursor, pasteText, styleSettings, keyboardEvent, getCursor, findEditableElement } from "../../utils";
12
+
13
+ const updateCount = ref<number>(0);
14
+ const $ul = ref();
15
+ const $item = ref();
16
+ const itemIdx = ref<number>(0);
17
+ const data = ref<listBlock>({
18
+ type: "",
19
+ id: "",
20
+ childList: [
21
+ {
22
+ classList: [],
23
+ content: "",
24
+ },
25
+ ],
26
+ });
27
+ const props = defineProps<{ modelValue: listBlock; cursorData: cursorSelection }>();
28
+ const emit = defineEmits<{
29
+ (e: "update:modelValue", modelValue: listBlock): void;
30
+ (e: "addBlock", {}: { name: string; value: object }): void;
31
+ (e: "deleteBlockLocal", index?: number): void;
32
+ }>();
33
+ data.value = unref(props.modelValue) as listBlock;
34
+
35
+ if (data.value.childList.length === 0) {
36
+ }
37
+
38
+ // 키보드 이벤트 할당
39
+ function textKeyboardEvent(e: KeyboardEvent) {
40
+ keyboardEvent("list", e, emit, updateBlockData);
41
+ }
42
+
43
+ /**
44
+ * 외부용 함수
45
+ */
46
+
47
+ // 데이터 정규화 및 검수
48
+ function updateBlockData() {
49
+ const $block = $ul.value;
50
+ const $childList = $block.querySelectorAll("li");
51
+ const childData: liItem[] = [];
52
+ const cursorData = getCursor();
53
+
54
+ $childList.forEach((row) => {
55
+ row.childNodes.forEach((child: ChildNode) => {
56
+ const $child = child as HTMLElement;
57
+
58
+ if (child.constructor.name !== "Text") {
59
+ // 텍스트가 아닐경우
60
+ if (child.constructor.name !== "HTMLBRElement") {
61
+ // br 태그 유지
62
+ if (child.textContent === "") {
63
+ // 빈 태그 삭제
64
+ child.remove();
65
+ } else if ($child.classList.length === 0) {
66
+ // 클레스 없는 엘리먼트 처리
67
+ $child.insertAdjacentHTML("afterend", $child.innerHTML);
68
+ child.remove();
69
+ }
70
+ } else {
71
+ $child.removeAttribute("class");
72
+ }
73
+ }
74
+ });
75
+
76
+ childData.push({
77
+ classList: [...row.classList].splice(1),
78
+ content: row.innerHTML,
79
+ });
80
+ });
81
+
82
+ data.value.childList = childData;
83
+ emit("update:modelValue", data.value);
84
+ updateCount.value += 1;
85
+
86
+ if (cursorData.startNode) {
87
+ const editableNode = findEditableElement(cursorData.startNode);
88
+ let childIdx = -1;
89
+
90
+ $childList.forEach((row, count) => {
91
+ if (row === editableNode) {
92
+ childIdx = count;
93
+ }
94
+ });
95
+
96
+ if (childIdx > -1) {
97
+ // 기본 로직
98
+ itemIdx.value = childIdx;
99
+ setTimeout(() => {
100
+ const afterChildList = $ul.value.querySelectorAll("li");
101
+ setCursor(afterChildList[childIdx], cursorData.startOffset as number);
102
+ }, 100);
103
+ } else {
104
+ // 중간 엔터
105
+ $childList.forEach((row, count) => {
106
+ if (row === (cursorData.startNode as Node).parentNode) {
107
+ childIdx = count;
108
+ }
109
+ });
110
+
111
+ setTimeout(() => {
112
+ const afterChildList = $ul.value.querySelectorAll("li");
113
+ setCursor(afterChildList[childIdx], cursorData.startOffset as number);
114
+ }, 100);
115
+ }
116
+ }
117
+ }
118
+
119
+ // 포커스
120
+ function focus() {
121
+ const childList = $ul.value.querySelectorAll(".d-li-item");
122
+ setCursor(childList[itemIdx.value], 0);
123
+ }
124
+
125
+ // 블럭 위치 주기
126
+ function getBoundingClientRect() {
127
+ return $ul.value.parentNode.getBoundingClientRect();
128
+ }
129
+
130
+ // 타입 전달
131
+ function getType() {
132
+ return data.value.type;
133
+ }
134
+
135
+ // 붙여넣기 이벤트
136
+ function pasteEvent(text: string) {
137
+ pasteText("text", text);
138
+ }
139
+
140
+ // 텍스트 스타일 지정
141
+ function setStyles({ type, url }: styleFunctionArgument) {
142
+ data.value = styleSettings({
143
+ kind: type,
144
+ blockData: data.value,
145
+ $target: $item[itemIdx.value],
146
+ url: url,
147
+ cursorData: props.cursorData,
148
+ });
149
+ setTimeout(() => {
150
+ updateBlockData();
151
+ }, 250);
152
+ }
153
+
154
+ defineExpose({
155
+ updateBlockData,
156
+ focus,
157
+ getType,
158
+ pasteEvent,
159
+ setStyles,
160
+ getBoundingClientRect,
161
+ });
162
+ </script>
@@ -168,6 +168,23 @@
168
168
  content: "Write text";
169
169
  color: #ccc;
170
170
  }
171
+ .dragon-editor .d-ul-block {
172
+ padding-left: 30px;
173
+ cursor: text;
174
+ list-style: disc;
175
+ }
176
+ .dragon-editor .d-ul-block .d-li-item {
177
+ list-style: inherit;
178
+ outline: 0;
179
+ }
180
+ .dragon-editor .d-ul-block .d-li-item:empty {
181
+ min-height: 1.6em;
182
+ }
183
+ .dragon-editor .d-ul-block .d-li-item:empty::after {
184
+ display: inline;
185
+ content: "Write text";
186
+ color: #ccc;
187
+ }
171
188
  .dragon-editor .d-image-block {
172
189
  display: flex;
173
190
  flex-direction: column;
@@ -189,3 +189,17 @@
189
189
  color: #ccc;
190
190
  font-size: 1rem;
191
191
  }
192
+ .dragon-editor-viewer .d-ol-block {
193
+ padding-left: 30px;
194
+ list-style: decimal;
195
+ }
196
+ .dragon-editor-viewer .d-ol-block .d-li-item {
197
+ list-style: inherit;
198
+ }
199
+ .dragon-editor-viewer .d-ul-block {
200
+ padding-left: 30px;
201
+ list-style: disc;
202
+ }
203
+ .dragon-editor-viewer .d-ul-block .d-li-item {
204
+ list-style: inherit;
205
+ }
@@ -0,0 +1,2 @@
1
+ import { editorContentType } from "../../../types";
2
+ export declare function convertToHTML(data: editorContentType): string;
@@ -0,0 +1,39 @@
1
+ export function convertToHTML(data) {
2
+ let htmlStructure = "";
3
+ data.forEach((row) => {
4
+ switch (row.type) {
5
+ case "text":
6
+ htmlStructure += `<p class="d-text-block ${row.classList.join(" ")}">${row.content}</p>`;
7
+ break;
8
+ case "image":
9
+ htmlStructure += `
10
+ <div class="d-image-block ${row.classList.join(" ")}">
11
+ <div class="d-image-area">
12
+ <img class="d-img" src="${row.src}" width="${row.width}" height="${row.height}" alt="${row.caption}" loading="lazy">
13
+ </div>
14
+ `;
15
+ if (row.caption) {
16
+ htmlStructure += `<p class="d-caption">${row.caption}</p>`;
17
+ }
18
+ htmlStructure += `</div>`;
19
+ break;
20
+ case "ol":
21
+ htmlStructure += `<ol class="d-ol-block ${row.classList.join(" ")}">`;
22
+ row.childList.forEach((child) => {
23
+ htmlStructure += `<li class="d-li-item ${child.classList.join(" ")}">${child.content}</li>`;
24
+ });
25
+ htmlStructure += `</ol>`;
26
+ break;
27
+ case "ul":
28
+ htmlStructure += `<ul class="d-ul-block ${row.classList.join(" ")}">`;
29
+ row.childList.forEach((child) => {
30
+ htmlStructure += `<li class="d-li-item ${child.classList.join(" ")}">${child.content}</li>`;
31
+ });
32
+ htmlStructure += `</ul>`;
33
+ break;
34
+ default:
35
+ htmlStructure += row.innerHTML;
36
+ }
37
+ });
38
+ return htmlStructure;
39
+ }
@@ -4,3 +4,4 @@ export * from "./keyboard";
4
4
  export * from "./cursor";
5
5
  export * from "./style";
6
6
  export * from "./element";
7
+ export * from "./convertor";
@@ -46,7 +46,6 @@ function createImageBlock(data) {
46
46
  src: data.src,
47
47
  width: data.width,
48
48
  height: data.height,
49
- webp: data.webp,
50
49
  caption: data.caption
51
50
  };
52
51
  }
@@ -66,6 +65,9 @@ function createlistBlock(type = "ul") {
66
65
  export function createBlock(name, value) {
67
66
  let blockData;
68
67
  switch (name) {
68
+ case "ul":
69
+ blockData = createlistBlock();
70
+ break;
69
71
  case "ol":
70
72
  blockData = createlistBlock("ol");
71
73
  break;
@@ -81,3 +83,4 @@ export * from "./keyboard.mjs";
81
83
  export * from "./cursor.mjs";
82
84
  export * from "./style.mjs";
83
85
  export * from "./element.mjs";
86
+ export * from "./convertor.mjs";
@@ -2,15 +2,15 @@ import { getCursor, setCursor } from "./cursor.mjs";
2
2
  import { findEditableElement, findChildNumber, findLiElement } from "./element.mjs";
3
3
  import { getTagName } from "./style.mjs";
4
4
  let enterCount = 0;
5
- function enterEvent(type, event, action) {
5
+ function enterEvent(type, event, action, update) {
6
6
  if (event.code === "Enter") {
7
7
  event.preventDefault();
8
8
  const useShift = event.shiftKey;
9
9
  switch (type) {
10
- case "ol":
10
+ case "list":
11
11
  if (useShift === false) {
12
12
  if (enterCount == 0) {
13
- listEnterEvent(event, action);
13
+ listEnterEvent(action, update);
14
14
  }
15
15
  } else {
16
16
  addBrEvent();
@@ -27,7 +27,7 @@ function enterEvent(type, event, action) {
27
27
  default:
28
28
  if (useShift === false) {
29
29
  if (enterCount == 0) {
30
- textEnterEvent(event, action);
30
+ textEnterEvent(action);
31
31
  }
32
32
  } else {
33
33
  addBrEvent();
@@ -39,7 +39,7 @@ function enterEvent(type, event, action) {
39
39
  }, 150);
40
40
  }
41
41
  }
42
- function listEnterEvent(event, action) {
42
+ function listEnterEvent(action, update) {
43
43
  const cursorData = getCursor();
44
44
  if (cursorData.startNode) {
45
45
  const editableElement = findEditableElement(cursorData.startNode);
@@ -74,9 +74,16 @@ function listEnterEvent(event, action) {
74
74
  endOffset = cursorData.endOffset;
75
75
  }
76
76
  if (editableElement.childNodes.length === 0 || endChildIdx === editableElement.childNodes.length - 1 && editableElement.childNodes[endChildIdx].textContent.length === endOffset) {
77
- action("addBlock", {
78
- name: "text"
79
- });
77
+ if (editableElement.childNodes.length === 0) {
78
+ editableElement.remove();
79
+ action("addBlock", {
80
+ name: "text"
81
+ });
82
+ } else {
83
+ editableElement.insertAdjacentHTML("afterend", `<li class="d-li-item" contenteditable></li>`);
84
+ setCursor(editableElement.nextSibling, 0);
85
+ update();
86
+ }
80
87
  } else {
81
88
  editableElement.childNodes.forEach((child, count) => {
82
89
  const text = child.textContent;
@@ -125,14 +132,15 @@ function listEnterEvent(event, action) {
125
132
  }
126
133
  });
127
134
  editableElement.innerHTML = preHTMLStructor;
128
- action("addBlock", {
129
- name: "text",
130
- value: { classList: editableElementClassList, content: nextHTMLStructor }
131
- });
135
+ editableElement.insertAdjacentHTML("afterend", `<li class="d-li-item ${editableElementClassList.join(" ")}">${nextHTMLStructor}</li>`);
136
+ setTimeout(() => {
137
+ setCursor(editableElement.nextSibling.childNodes[0], 0);
138
+ update();
139
+ }, 100);
132
140
  }
133
141
  }
134
142
  }
135
- function textEnterEvent(event, action) {
143
+ function textEnterEvent(action) {
136
144
  const cursorData = getCursor();
137
145
  if (cursorData.startNode) {
138
146
  const editableElement = findEditableElement(cursorData.startNode);
@@ -245,7 +253,7 @@ function backspaceEvent(type, event, action, update) {
245
253
  }
246
254
  }
247
255
  export function keyboardEvent(type, event, action, update) {
248
- enterEvent(type, event, action);
256
+ enterEvent(type, event, action, update);
249
257
  backspaceEvent(type, event, action, update);
250
258
  }
251
259
  export function getClipboardData(data) {
@@ -58,7 +58,7 @@
58
58
  <template v-else>
59
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
60
 
61
- <div class="d-child-list" :class="{ '--active': item.active }">
61
+ <div v-if="item.target.indexOf(content[activeIdx].type) > -1" class="d-child-list" :class="{ '--active': item.active }">
62
62
  <button class="d-child-btn" v-for="(child, k) in item.childList" :key="k" @click="child.action(count, j)">{{ child.name }}</button>
63
63
  </div>
64
64
  </template>
@@ -84,6 +84,7 @@ import SvgIcon from "../../core/components/SvgIcon.vue";
84
84
  import textBlock from "../../core/components/editor/TextBlock.vue";
85
85
  import imageBlock from "../../core/components/editor/ImageBlock.vue";
86
86
  import olBlock from "../../core/components/editor/OlBlock.vue";
87
+ import ulBlock from "../../core/components/editor/UlBlock.vue";
87
88
 
88
89
  // 기본 정보
89
90
  const props = defineProps<{
@@ -92,7 +93,7 @@ const props = defineProps<{
92
93
  }>();
93
94
  const modelValue = ref<editorContentType>([]);
94
95
  const option = ref<editorOptions>({
95
- blockMenu: ["text"],
96
+ blockMenu: ["text", "ol", "ul"],
96
97
  // blockMenu: ["text", "ol", "ul", "table", "quotation"], // TODO : 다른 블럭 만들기
97
98
  });
98
99
 
@@ -557,6 +558,9 @@ function setComponentKind(kind: string) {
557
558
  let componentData: any;
558
559
 
559
560
  switch (kind) {
561
+ case "ul":
562
+ componentData = ulBlock;
563
+ break;
560
564
  case "ol":
561
565
  componentData = olBlock;
562
566
  break;
@@ -675,7 +679,7 @@ function addImageBlock({ src, width, height, webp, caption }: { src: string; wid
675
679
  // 함수 내보내기
676
680
  defineExpose({
677
681
  addImageBlock,
678
- dataUpdateAction
682
+ dataUpdateAction,
679
683
  });
680
684
 
681
685
  /**
@@ -12,7 +12,6 @@ import { commentBlock, cursorSelection } from "../../../types/index";
12
12
 
13
13
  const $block = ref();
14
14
  const data = ref<commentBlock>({
15
- type: "comment",
16
15
  classList: [],
17
16
  content: "",
18
17
  });
@@ -54,34 +53,68 @@ function updateBlockData() {
54
53
 
55
54
  // 커서위치 재지정
56
55
  if ($block.value.innerHTML.length > 0) {
57
- const cursorData = getArrangementCursorData(blockCursorData.value);
58
-
59
- data.value.content = $block.value.innerHTML;
60
- emit("update:modelValue", data.value);
61
-
62
- setTimeout(() => {
63
- setCursor($block.value.childNodes[cursorData.childCount], cursorData.length);
64
-
65
- // 구조 검수
66
- $block.value.childNodes.forEach((child: ChildNode) => {
67
- if (child.constructor.name !== "Text") {
68
- // 텍스트가 아닐경우
69
- if (child.constructor.name !== "HTMLBRElement") {
70
- // br 태그 유지
71
- if (child.textContent === "") {
72
- // 빈 태그 삭제
73
- child.remove();
74
- } else if ((child as HTMLElement).classList.length === 0) {
75
- // 클레스 없는 엘리먼트 처리
76
- (child as HTMLElement).insertAdjacentHTML("afterend", child.textContent as string);
77
- child.remove();
56
+ const preCursorData = getCursor();
57
+
58
+ if (preCursorData.startNode !== null) {
59
+ const cursorData = getArrangementCursorData(blockCursorData.value);
60
+
61
+ data.value.content = $block.value.innerHTML;
62
+ emit("update:modelValue", data.value);
63
+
64
+ setTimeout(() => {
65
+ if ($block.value) {
66
+ setCursor($block.value.childNodes[cursorData.childCount], cursorData.length);
67
+
68
+ // 구조 검수
69
+ $block.value.childNodes.forEach((child: ChildNode) => {
70
+ const $child = child as HTMLElement;
71
+
72
+ if (child.constructor.name !== "Text") {
73
+ // 텍스트가 아닐경우
74
+ if (child.constructor.name !== "HTMLBRElement") {
75
+ // br 태그 유지
76
+ if (child.textContent === "") {
77
+ // 빈 태그 삭제
78
+ child.remove();
79
+ } else if ($child.classList.length === 0) {
80
+ // 클레스 없는 엘리먼트 처리
81
+ $child.insertAdjacentHTML("afterend", $child.innerHTML);
82
+ child.remove();
83
+ }
84
+ } else {
85
+ $child.removeAttribute("class");
86
+ }
78
87
  }
79
- } else {
80
- (child as HTMLElement).removeAttribute("class");
81
- }
88
+ });
82
89
  }
83
- });
84
- }, 100);
90
+ }, 100);
91
+ } else {
92
+ if ($block.value) {
93
+ $block.value.childNodes.forEach((child: ChildNode) => {
94
+ const $child = child as HTMLElement;
95
+
96
+ if (child.constructor.name !== "Text") {
97
+ // 텍스트가 아닐경우
98
+ if (child.constructor.name !== "HTMLBRElement") {
99
+ // br 태그 유지
100
+ if (child.textContent === "") {
101
+ // 빈 태그 삭제
102
+ child.remove();
103
+ } else if ($child.classList.length === 0) {
104
+ // 클레스 없는 엘리먼트 처리
105
+ $child.insertAdjacentHTML("afterend", $child.innerHTML);
106
+ child.remove();
107
+ }
108
+ } else {
109
+ $child.removeAttribute("class");
110
+ }
111
+ }
112
+ });
113
+
114
+ data.value.content = $block.value.innerHTML;
115
+ emit("update:modelValue", data.value);
116
+ }
117
+ }
85
118
  } else {
86
119
  emit("update:modelValue", data.value);
87
120
  }
@@ -1,25 +1,10 @@
1
1
  <template>
2
- <div class="dragon-editor-viewer">
3
- <template v-for="(row, count) in props.content">
4
- <p class="d-text-block" v-if="row.type === 'text'" :class="row.classList" v-html="row.content"></p>
5
-
6
- <template v-if="row.type === 'image'">
7
- <template v-if="row.webp"> </template>
8
- <template v-else>
9
- <div class="d-image-block" :class="row.classList">
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" />
12
- </div>
13
- <p class="d-caption" v-if="row.caption" v-html="row.caption"></p>
14
- </div>
15
- </template>
16
- </template>
17
- </template>
18
- </div>
2
+ <div class="dragon-editor-viewer" v-html="convertToHTML(props.content)"></div>
19
3
  </template>
20
4
 
21
5
  <script setup lang="ts">
22
6
  import { editorContentType } from "../../../types";
7
+ import { convertToHTML } from "../../core/utils";
23
8
 
24
9
  const props = defineProps<{
25
10
  content: editorContentType;
package/package.json CHANGED
@@ -1,58 +1,58 @@
1
1
  {
2
- "name": "dragon-editor",
3
- "version": "2.0.0-beta.2.1.2",
4
- "description": "WYSIWYG editor on Nuxt.js",
5
- "repository": {
6
- "type": "git",
7
- "url": "git+https://github.com/lovefields/dragonEditor.git"
8
- },
9
- "license": "MIT",
10
- "type": "module",
11
- "exports": {
12
- ".": {
13
- "types": "./dist/types.d.ts",
14
- "import": "./dist/module.mjs",
15
- "require": "./dist/module.cjs"
16
- }
17
- },
18
- "main": "./dist/module.cjs",
19
- "types": "./dist/types.d.ts",
20
- "files": [
21
- "dist"
22
- ],
23
- "scripts": {
24
- "prepack": "nuxt-module-build",
25
- "dev": "nuxi dev playground",
26
- "dev:build": "nuxi build playground",
27
- "dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
28
- "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
29
- "lint": "eslint .",
30
- "test": "vitest run",
31
- "test:watch": "vitest watch"
32
- },
33
- "dependencies": {
34
- "@nuxt/kit": "^3.5.0"
35
- },
36
- "devDependencies": {
37
- "@nuxt/eslint-config": "^0.1.1",
38
- "@nuxt/module-builder": "^0.3.1",
39
- "@nuxt/schema": "^3.5.0",
40
- "@nuxt/test-utils": "^3.5.0",
41
- "@types/node": "^18",
42
- "changelogen": "^0.5.3",
43
- "eslint": "^8.40.0",
44
- "nuxt": "^3.5.0",
45
- "vitest": "^0.31.0"
46
- },
47
- "keywords": [
48
- "editor",
49
- "wysiwyg-editor",
50
- "korean",
51
- "language"
52
- ],
53
- "author": "Lovefields <lovefield@dico.me> (https://dico.me)",
54
- "bugs": {
55
- "url": "https://github.com/lovefields/dragonEditor/issues"
56
- },
57
- "homepage": "https://github.com/lovefields/dragonEditor#readme"
58
- }
2
+ "name": "dragon-editor",
3
+ "version": "2.0.0",
4
+ "description": "WYSIWYG editor on Nuxt.js",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/lovefields/dragonEditor.git"
8
+ },
9
+ "license": "MIT",
10
+ "type": "module",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/types.d.ts",
14
+ "import": "./dist/module.mjs",
15
+ "require": "./dist/module.cjs"
16
+ }
17
+ },
18
+ "main": "./dist/module.cjs",
19
+ "types": "./dist/types.d.ts",
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "prepack": "nuxt-module-build",
25
+ "dev": "nuxi dev playground",
26
+ "dev:build": "nuxi build playground",
27
+ "dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
28
+ "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
29
+ "lint": "eslint .",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest watch"
32
+ },
33
+ "dependencies": {
34
+ "@nuxt/kit": "^3.5.0"
35
+ },
36
+ "devDependencies": {
37
+ "@nuxt/eslint-config": "^0.1.1",
38
+ "@nuxt/module-builder": "^0.3.1",
39
+ "@nuxt/schema": "^3.5.0",
40
+ "@nuxt/test-utils": "^3.5.0",
41
+ "@types/node": "^18",
42
+ "changelogen": "^0.5.3",
43
+ "eslint": "^8.40.0",
44
+ "nuxt": "^3.5.0",
45
+ "vitest": "^0.31.0"
46
+ },
47
+ "keywords": [
48
+ "editor",
49
+ "wysiwyg-editor",
50
+ "korean",
51
+ "language"
52
+ ],
53
+ "author": "Lovefields <lovefield@dico.me> (https://dico.me)",
54
+ "bugs": {
55
+ "url": "https://github.com/lovefields/dragonEditor/issues"
56
+ },
57
+ "homepage": "https://github.com/lovefields/dragonEditor#readme"
58
+ }