dragon-editor 2.0.0-beta.1.4 → 2.0.0-beta.2.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.
Files changed (31) hide show
  1. package/README.md +94 -147
  2. package/README_en.md +14 -62
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +8 -0
  5. package/dist/runtime/core/components/SvgIcon.vue +30 -21
  6. package/dist/runtime/core/components/editor/ImageBlock.vue +175 -0
  7. package/dist/runtime/core/components/editor/OlBlock.vue +135 -0
  8. package/dist/runtime/core/components/editor/TextBlock.vue +77 -31
  9. package/dist/runtime/core/components/icon/Accept.vue +5 -0
  10. package/dist/runtime/core/components/icon/ArrowDown.vue +3 -0
  11. package/dist/runtime/core/components/icon/ArrowUp.vue +3 -0
  12. package/dist/runtime/core/components/icon/Cancel.vue +5 -0
  13. package/dist/runtime/core/components/icon/Delete.vue +3 -0
  14. package/dist/runtime/core/style/common.css +320 -31
  15. package/dist/runtime/core/style/viewer.css +191 -0
  16. package/dist/runtime/core/utils/cursor.d.ts +1 -1
  17. package/dist/runtime/core/utils/cursor.mjs +16 -4
  18. package/dist/runtime/core/utils/element.d.ts +2 -1
  19. package/dist/runtime/core/utils/element.mjs +19 -4
  20. package/dist/runtime/core/utils/index.d.ts +2 -3
  21. package/dist/runtime/core/utils/index.mjs +62 -5
  22. package/dist/runtime/core/utils/keyboard.d.ts +1 -1
  23. package/dist/runtime/core/utils/keyboard.mjs +500 -41
  24. package/dist/runtime/core/utils/style.d.ts +6 -2
  25. package/dist/runtime/core/utils/style.mjs +140 -30
  26. package/dist/runtime/shared/components/DragonEditor.vue +488 -159
  27. package/dist/runtime/shared/components/DragonEditorComment.vue +42 -32
  28. package/dist/runtime/shared/components/DragonEditorViewer.vue +30 -2
  29. package/package.json +1 -1
  30. package/dist/runtime/core/style/main.d.ts +0 -1
  31. package/dist/runtime/core/style/main.mjs +0 -24
@@ -0,0 +1,175 @@
1
+ <template>
2
+ <div class="d-image-block" :class="data.classList" ref="$block" @mousemove="resizeMouseEvent" @touchmove="resizeTouchEvent" @mouseup="doneResizeStatus" @touchend="doneResizeStatus">
3
+ <div class="d-image-area">
4
+ <button class="d-btn-size-left" @mousedown="startResizeEvent" @touchstart="startResizeEvent"></button>
5
+ <button class="d-btn-size-right" @mousedown="startResizeEvent" @touchstart="startResizeEvent"></button>
6
+
7
+ <template v-if="data.webp">
8
+ <picture>
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
+ </picture>
12
+ </template>
13
+
14
+ <template v-else>
15
+ <img class="d-img" :src="data.src" :width="data.width" :height="data.height" :alt="data.caption" loading="lazy" />
16
+ </template>
17
+ </div>
18
+ <p class="d-caption" v-html="data.caption" @keydown="textKeyboardEvent" ref="$caption" contenteditable></p>
19
+ </div>
20
+ </template>
21
+
22
+ <script setup lang="ts">
23
+ // @ts-ignore
24
+ import { ref, unref } from "#imports";
25
+ import { keyboardEvent, pasteText, styleSettings } from "../../utils/index";
26
+ import { imageBlock, cursorSelection, styleFunctionArgument } from "../../../../types/index";
27
+
28
+ const $block = ref();
29
+ const $caption = ref();
30
+ const data = ref<imageBlock>({
31
+ type: "",
32
+ id: "",
33
+ classList: [],
34
+ src: "",
35
+ width: 0,
36
+ height: 0,
37
+ webp: false,
38
+ caption: "",
39
+ });
40
+ const props = defineProps<{ modelValue: imageBlock; cursorData: cursorSelection }>();
41
+ const emit = defineEmits<{
42
+ (e: "update:modelValue", modelValue: imageBlock): void;
43
+ (e: "addBlock", name: string): void;
44
+ (e: "deleteBlockLocal", index?: number): void;
45
+ }>();
46
+
47
+ data.value = unref(props.modelValue) as imageBlock;
48
+
49
+ /**
50
+ * 내부 상수
51
+ */
52
+ let activeResize = false;
53
+ let startX = 0;
54
+
55
+ /**
56
+ * 내부 이벤트
57
+ */
58
+ // 키보드 이벤트 할당
59
+ function textKeyboardEvent(e: KeyboardEvent) {
60
+ keyboardEvent("image", e, emit, updateBlockData);
61
+ }
62
+
63
+ // 리사이즈 시작
64
+ const startResizeStatus = (value) => {
65
+ activeResize = true;
66
+ startX = value;
67
+ };
68
+
69
+ function startResizeEvent() {
70
+ // startResizeStatus(e.clientX);
71
+ activeResize = true;
72
+ }
73
+
74
+ function activeResizeTouchEvent(e: TouchEvent) {
75
+ // startResizeStatus(Math.floor(e.touches[0].clientX));
76
+ activeResize = true;
77
+ }
78
+
79
+ // 리사이즈 이벤트
80
+ const resizeEvent = (value) => {
81
+ if (activeResize) {
82
+ const blockRect = $block.value.getBoundingClientRect();
83
+ const bodyRect = document.body.getBoundingClientRect();
84
+ const blockLeft = blockRect.left - bodyRect.left;
85
+ const centerPoint: number = blockRect.width / 2 + blockLeft;
86
+ const blockRight = window.innerWidth - (centerPoint + blockRect.width / 2);
87
+ let percent: number = 0;
88
+
89
+ // console.log(value);
90
+
91
+ if (centerPoint > value) {
92
+ // 왼쪽
93
+ const leftPercent = (100 / (centerPoint - blockLeft)) * (value - blockLeft);
94
+
95
+ percent = Math.floor(100 - leftPercent) + 2;
96
+ } else {
97
+ // 오른쪽
98
+ const right = window.innerWidth - (centerPoint + blockRight);
99
+ const valueData = value - centerPoint;
100
+ const rightPercent = (100 / right) * valueData;
101
+
102
+ percent = Math.floor(rightPercent);
103
+ }
104
+
105
+ percent = percent - (percent % 5);
106
+ percent = percent / 5;
107
+
108
+ const classList = data.value.classList.filter((item) => /--\d{1,2}/g.test(item) === false);
109
+ classList.push(`--${percent}`);
110
+ data.value.classList = classList;
111
+ }
112
+ };
113
+
114
+ function resizeMouseEvent(e: MouseEvent) {
115
+ resizeEvent(e.clientX);
116
+ }
117
+
118
+ function resizeTouchEvent(e: TouchEvent) {
119
+ resizeEvent(Math.floor(e.touches[0].clientX));
120
+ }
121
+
122
+ // 리사이즈 종료
123
+ function doneResizeStatus() {
124
+ activeResize = false;
125
+ }
126
+
127
+ /**
128
+ * 외부용 함수
129
+ */
130
+ function focus() {
131
+ $caption.value.focus();
132
+ }
133
+
134
+ // 블럭 위치 주기
135
+ function getBoundingClientRect() {
136
+ return $block.value.parentNode.getBoundingClientRect();
137
+ }
138
+
139
+ // 타입 전달
140
+ function getType() {
141
+ return data.value.type;
142
+ }
143
+
144
+ // 붙여넣기 이벤트
145
+ function pasteEvent(text: string) {
146
+ pasteText("text", text);
147
+ }
148
+
149
+ // 데이터 정규화 및 검수
150
+ function updateBlockData() {
151
+ emit("update:modelValue", data.value);
152
+ }
153
+
154
+ // 텍스트 스타일 지정
155
+ function setStyles({ type, url }: styleFunctionArgument) {
156
+ data.value = styleSettings({
157
+ kind: type,
158
+ blockData: data.value,
159
+ $target: $block.value,
160
+ cursorData: props.cursorData,
161
+ });
162
+ setTimeout(() => {
163
+ updateBlockData();
164
+ }, 250);
165
+ }
166
+
167
+ defineExpose({
168
+ updateBlockData,
169
+ getType,
170
+ focus,
171
+ pasteEvent,
172
+ setStyles,
173
+ getBoundingClientRect,
174
+ });
175
+ </script>
@@ -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>
@@ -1,13 +1,12 @@
1
1
  <template>
2
- <p class="d-text-block" :class="data.classList" contenteditable v-html="data.content" @keydown="textKeyboardEvent"
3
- ref="$block"
4
- ></p>
2
+ <p class="d-text-block" :class="data.classList" contenteditable v-html="data.content" @keydown="textKeyboardEvent" ref="$block"></p>
5
3
  </template>
6
4
 
7
5
  <script setup lang="ts">
8
- import {ref, unref} from "#imports";
9
- import {keyboardEvent, setCursor, pasteText, styleSettings, getArrangementCursorData} from "../../utils/index";
10
- import {textBlock} from "../../../../types/index";
6
+ // @ts-ignore
7
+ import { ref, unref } from "#imports";
8
+ import { keyboardEvent, setCursor, pasteText, styleSettings, getArrangementCursorData } from "../../utils/index";
9
+ import { textBlock, styleFunctionArgument, cursorSelection } from "../../../../types/index";
11
10
 
12
11
  const $block = ref();
13
12
  const data = ref<textBlock>({
@@ -16,67 +15,113 @@ const data = ref<textBlock>({
16
15
  classList: [],
17
16
  content: "",
18
17
  });
19
- const props = defineProps<{ modelValue: textBlock }>();
18
+ const props = defineProps<{ modelValue: textBlock; cursorData: cursorSelection }>();
20
19
  const emit = defineEmits<{
21
20
  (e: "update:modelValue", modelValue: textBlock): void;
22
- (e: "addBlock", name: string): void;
21
+ (e: "addBlock", {}: { name: string; value: object }): void;
22
+ (e: "deleteBlockLocal", index?: number): void;
23
23
  }>();
24
24
 
25
25
  data.value = unref(props.modelValue) as textBlock;
26
26
 
27
+ // 키보드 이벤트 할당
27
28
  function textKeyboardEvent(e: KeyboardEvent) {
28
- keyboardEvent("text", e, emit);
29
+ keyboardEvent("text", e, emit, updateBlockData);
29
30
  }
30
31
 
32
+ /**
33
+ * 외부용 함수
34
+ */
31
35
 
32
- // export event
33
- function updateBlockData() { // 데이터 정규화 및 검수
36
+ // 데이터 정규화 및 검수
37
+ function updateBlockData() {
34
38
  const blockClassList = [...$block.value.classList];
35
39
  blockClassList.splice(0, 1);
36
- const pushList = blockClassList.filter(item => data.value.classList.indexOf(item) === -1);
40
+ const pushList = blockClassList.filter((item) => data.value.classList.indexOf(item) === -1);
37
41
 
38
42
  data.value.classList = data.value.classList.concat(pushList);
39
43
 
44
+ // 클레스 검수
45
+ const checkClassIdx = data.value.classList.indexOf("d-text-block");
46
+ if (checkClassIdx > -1) {
47
+ data.value.classList.splice(checkClassIdx, 1);
48
+ }
49
+
40
50
  // 커서위치 재지정
41
51
  if ($block.value.innerHTML.length > 0) {
42
- const cursorData = getArrangementCursorData();
43
- const preChildCount = $block.value.childNodes.length;
52
+ const cursorData = getArrangementCursorData(props.cursorData);
44
53
 
45
54
  data.value.content = $block.value.innerHTML;
46
55
  emit("update:modelValue", data.value);
47
56
 
48
57
  setTimeout(() => {
49
- const $target = $block.value.childNodes[cursorData.childCount];
50
- const childCount = $block.value.childNodes.length;
51
-
52
- setCursor($block.value.childNodes[cursorData.childCount], cursorData.length);
53
-
54
- // 태그 삭제
55
- $block.value.childNodes.forEach((child: ChildNode) => {
56
- if (child.constructor.name !== "Text" && child.textContent === "") {
57
- child.remove();
58
- }
59
- });
60
- }, 100)
58
+ if ($block.value) {
59
+ setCursor($block.value.childNodes[cursorData.childCount], cursorData.length);
60
+
61
+ // 구조 검수
62
+ $block.value.childNodes.forEach((child: ChildNode) => {
63
+ const $child = child as HTMLElement;
64
+
65
+ if (child.constructor.name !== "Text") {
66
+ // 텍스트가 아닐경우
67
+ if (child.constructor.name !== "HTMLBRElement") {
68
+ // br 태그 유지
69
+ if (child.textContent === "") {
70
+ // 빈 태그 삭제
71
+ child.remove();
72
+ } else if ($child.classList.length === 0) {
73
+ // 클레스 없는 엘리먼트 처리
74
+ $child.insertAdjacentHTML("afterend", $child.innerHTML);
75
+ child.remove();
76
+ }
77
+ } else {
78
+ $child.removeAttribute("class");
79
+ }
80
+ }
81
+ });
82
+ }
83
+ }, 100);
61
84
  } else {
62
85
  emit("update:modelValue", data.value);
63
86
  }
64
87
  }
65
88
 
66
- function focus() {
67
- setCursor($block.value, 0);
89
+ // 포커스
90
+ function focus(type: string = "first") {
91
+ if (type === "first") {
92
+ setCursor($block.value, 0);
93
+ } else {
94
+ const childCount = $block.value.childNodes.length;
95
+ const targetChild = $block.value.childNodes[childCount - 1];
96
+
97
+ setCursor(targetChild, 0);
98
+ }
99
+ }
100
+
101
+ // 블럭 위치 주기
102
+ function getBoundingClientRect() {
103
+ return $block.value.parentNode.getBoundingClientRect();
68
104
  }
69
105
 
106
+ // 타입 전달
70
107
  function getType() {
71
108
  return data.value.type;
72
109
  }
73
110
 
111
+ // 붙여넣기 이벤트
74
112
  function pasteEvent(text: string) {
75
113
  pasteText("text", text);
76
114
  }
77
115
 
78
- function setStyles(kind: string) {
79
- data.value = styleSettings(kind, data.value, $block.value);
116
+ // 텍스트 스타일 지정
117
+ function setStyles({ type, url }: styleFunctionArgument) {
118
+ data.value = styleSettings({
119
+ kind: type,
120
+ blockData: data.value,
121
+ $target: $block.value,
122
+ url: url,
123
+ cursorData: props.cursorData,
124
+ });
80
125
  setTimeout(() => {
81
126
  updateBlockData();
82
127
  }, 250);
@@ -87,6 +132,7 @@ defineExpose({
87
132
  focus,
88
133
  getType,
89
134
  pasteEvent,
90
- setStyles
135
+ setStyles,
136
+ getBoundingClientRect,
91
137
  });
92
138
  </script>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <path class="path"
3
+ d="M51.3456 17.6735C49.1181 15.4422 45.5066 15.4422 43.2792 17.6735L26.0616 34.9213L20.741 29.5914C18.5126 27.3591 14.8997 27.3591 12.6713 29.5914C10.4429 31.8236 10.4429 35.4429 12.6713 37.6752L21.9937 47.0139C24.222 49.2462 27.835 49.2462 30.0633 47.0139C30.1078 46.9693 30.1514 46.9243 30.194 46.8787C30.3822 46.7324 30.5633 46.5728 30.736 46.3997L51.3456 25.7541C53.573 23.5227 53.573 19.9049 51.3456 17.6735Z">
4
+ </path>
5
+ </template>
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <path class="path" d="M32 9C30.8954 9 30 9.89543 30 11V48.0208L18.4142 36.435C17.6332 35.654 16.3668 35.654 15.5858 36.435C14.8047 37.2161 14.8047 38.4824 15.5858 39.2635L30.6611 54.3388C31.4422 55.1199 32.7086 55.1199 33.4896 54.3388L48.5649 39.2635C49.346 38.4824 49.346 37.2161 48.5649 36.435C47.7839 35.654 46.5176 35.654 45.7365 36.435L34 48.1716V11C34 9.89543 33.1046 9 32 9Z"></path>
3
+ </template>
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <path class="path" d="M45.7366 27.4896C46.5176 28.2706 47.7839 28.2706 48.565 27.4896C49.346 26.7085 49.346 25.4422 48.565 24.6612L33.4896 9.58579C32.7085 8.80473 31.4422 8.80474 30.6612 9.58579L15.5858 24.6612C14.8047 25.4422 14.8047 26.7085 15.5858 27.4896C16.3668 28.2706 17.6332 28.2706 18.4142 27.4896L30 15.9038V53C30 54.1046 30.8954 55 32 55C33.1045 55 34 54.1046 34 53V15.753L45.7366 27.4896Z"></path>
3
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <path class="path"
3
+ d="M13.2497 13.0226C11.032 15.2442 11.032 18.8461 13.2497 21.0677L24.0457 31.8826L13.0153 42.9323C10.7976 45.1539 10.7976 48.7558 13.0153 50.9774C15.2331 53.199 18.8287 53.199 21.0464 50.9774L32.0768 39.9278L42.9532 50.8233C45.1709 53.0449 48.7666 53.0449 50.9843 50.8233C53.202 48.6017 53.202 44.9997 50.9843 42.7781L40.1078 31.8826L50.7499 21.2219C52.9677 19.0003 52.9677 15.3983 50.7499 13.1767C48.5322 10.9551 44.9366 10.9551 42.7189 13.1767L32.0768 23.8375L21.2808 13.0226C19.063 10.801 15.4674 10.801 13.2497 13.0226Z">
4
+ </path>
5
+ </template>
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <path class="path" fill-rule="evenodd" clip-rule="evenodd" d="M32 51C42.4934 51 51 42.4934 51 32C51 21.5066 42.4934 13 32 13C21.5066 13 13 21.5066 13 32C13 42.4934 21.5066 51 32 51ZM32 55C44.7026 55 55 44.7026 55 32C55 19.2974 44.7026 9 32 9C19.2974 9 9 19.2974 9 32C9 44.7026 19.2974 55 32 55ZM21.4142 42.6274C20.6332 41.8462 20.6332 40.5801 21.4142 39.7988L29.1715 32.0415L21.5858 24.4558C20.8047 23.6748 20.8047 22.4084 21.5858 21.6274C22.3668 20.8462 23.6332 20.8462 24.4142 21.6274L31.9999 29.2131L39.799 21.4141C40.58 20.6331 41.8464 20.6331 42.6274 21.4141C43.4084 22.1953 43.4084 23.4614 42.6274 24.2427L34.8284 32.0417L42.799 40.0122C43.58 40.7932 43.58 42.0596 42.799 42.8406C42.0179 43.6216 40.7516 43.6216 39.9706 42.8406L32.0001 34.8701L24.2426 42.6274C23.4616 43.4084 22.1953 43.4084 21.4142 42.6274Z"></path>
3
+ </template>