Skip to content

Commit

Permalink
feat: add list block
Browse files Browse the repository at this point in the history
  • Loading branch information
lovefields committed Jul 11, 2023
1 parent 9d2a677 commit 06761cc
Show file tree
Hide file tree
Showing 17 changed files with 426 additions and 88 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dragon-editor",
"version": "2.0.0-beta",
"version": "2.0.0",
"description": "WYSIWYG editor on Nuxt.js",
"repository": {
"type": "git",
Expand Down
15 changes: 13 additions & 2 deletions playground/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,18 @@
import { ref, onMounted } from "#imports";
const editor = ref();
const contentData = ref([]);
const contentData = ref([
{
type: "ol",
id: "ksadgjkl3",
childList: [
{
classList: [],
content: "123<span>3333</span>",
},
],
},
]);
const option = {
// blockMenu: ["text", "ol", "ul"],
customBlockMenu: [
Expand Down Expand Up @@ -48,7 +59,7 @@ function addImage2() {
});
}
function test(){
function test() {
editor.value.dataUpdateAction();
}
Expand Down
14 changes: 14 additions & 0 deletions playground/pages/viewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ const contentData = ref([
{ type: "text", id: "D3ubnFyQIjYfI9YWs4x6", classList: [], content: "" },
{ type: "image", id: "FgCpjlXNZVx7KgHKjEzO", classList: ["d-align-center", "--10"], src: "https://c.pxhere.com/images/37/e4/22c0adf08932049eb1b8af36cbd7-1622414.jpg!d", width: 1200, height: 1739, webp: false, caption: undefined },
{ type: "text", id: "0ygjWGTB4V6O2dAOgkPJ", classList: [], content: 'sadfasdfsa<a href="sdafsadfsadf" rel="nofollow" class="d-deco-link">dfsdfsadfsafsafsa</a>fsafsafsafasd' },
{ type: "ol", id: "ksadgjkl3", classList: [], childList: [{ classList: [], content: "1233333" }] },
{
type: "ul",
id: "YyocAhurpA4eQ5fAxlTe",
classList: [],
childList: [
{ classList: [], content: "sdafsfksdfjkaslfj" },
{ classList: [], content: "saklfjsaldfjsklafs" },
{ classList: [], content: "skalfjsaklfjsadklfsafj" },
{ classList: [], content: "afkjsladf" },
{ classList: [], content: "jsadklfjsakldf" },
],
},
{ type: "text", id: "B1akIdmWYW7vshudsRMN", classList: [], content: "" },
]);
</script>

Expand Down
105 changes: 57 additions & 48 deletions src/runtime/core/components/editor/OlBlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// @ts-ignore
import { ref, unref } from "#imports";
import { cursorSelection, liItem, listBlock, styleFunctionArgument } from "../../../../types";
import { getArrangementCursorData, setCursor, pasteText, styleSettings, keyboardEvent } from "../../utils";
import { getArrangementCursorData, setCursor, pasteText, styleSettings, keyboardEvent, getCursor, findEditableElement } from "../../utils";
const updateCount = ref<number>(0);
const $ol = ref();
Expand Down Expand Up @@ -37,7 +37,7 @@ if (data.value.childList.length === 0) {
// 키보드 이벤트 할당
function textKeyboardEvent(e: KeyboardEvent) {
keyboardEvent("ol", e, emit, updateBlockData);
keyboardEvent("list", e, emit, updateBlockData);
}
/**
Expand All @@ -49,62 +49,71 @@ function updateBlockData() {
const $block = $ol.value;
const $childList = $block.querySelectorAll("li");
const childData: liItem[] = [];
const cursorData = getCursor();
$childList.forEach((row) => {
console.log(row);
row.childNodes.forEach((child: ChildNode) => {
const $child = child as HTMLElement;
if (child.constructor.name !== "Text") {
// 텍스트가 아닐경우
if (child.constructor.name !== "HTMLBRElement") {
// br 태그 유지
if (child.textContent === "") {
// 빈 태그 삭제
child.remove();
} else if ($child.classList.length === 0) {
// 클레스 없는 엘리먼트 처리
$child.insertAdjacentHTML("afterend", $child.innerHTML);
child.remove();
}
} else {
$child.removeAttribute("class");
}
}
});
childData.push({
classList: [],
classList: [...row.classList].splice(1),
content: row.innerHTML,
});
});
// console.log($block);
// console.log("$childList", $childList);
data.value.childList = childData;
emit("update:modelValue", data.value);
updateCount.value += 1;
// const blockClassList = [...$block.value.classList];
// blockClassList.splice(0, 1);
// const pushList = blockClassList.filter((item) => data.value.classList.indexOf(item) === -1);
// data.value.classList = data.value.classList.concat(pushList);
// // 클레스 검수
// const checkClassIdx = data.value.classList.indexOf("d-text-block");
// if (checkClassIdx > -1) {
// data.value.classList.splice(checkClassIdx, 1);
// }
// // 커서위치 재지정
// if ($block.value.innerHTML.length > 0) {
// const cursorData = getArrangementCursorData(props.cursorData);
// data.value.content = $block.value.innerHTML;
// emit("update:modelValue", data.value);
// setTimeout(() => {
// if ($block.value) {
// setCursor($block.value.childNodes[cursorData.childCount], cursorData.length);
// // 구조 검수
// $block.value.childNodes.forEach((child: ChildNode) => {
// const $child = child as HTMLElement;
// if (child.constructor.name !== "Text") {
// // 텍스트가 아닐경우
// if (child.constructor.name !== "HTMLBRElement") {
// // br 태그 유지
// if (child.textContent === "") {
// // 빈 태그 삭제
// child.remove();
// } else if ($child.classList.length === 0) {
// // 클레스 없는 엘리먼트 처리
// $child.insertAdjacentHTML("afterend", $child.innerHTML);
// child.remove();
// }
// } else {
// $child.removeAttribute("class");
// }
// }
// });
// }
// }, 100);
// } else {
// emit("update:modelValue", data.value);
// }
if (cursorData.startNode) {
const editableNode = findEditableElement(cursorData.startNode);
let childIdx = -1;
$childList.forEach((row, count) => {
if (row === editableNode) {
childIdx = count;
}
});
if (childIdx > -1) {
// 기본 로직
itemIdx.value = childIdx;
setTimeout(() => {
const afterChildList = $ol.value.querySelectorAll("li");
setCursor(afterChildList[childIdx], cursorData.startOffset as number);
}, 100);
} else {
// 중간 엔터
$childList.forEach((row, count) => {
if (row === (cursorData.startNode as Node).parentNode) {
childIdx = count;
}
});
setTimeout(() => {
const afterChildList = $ol.value.querySelectorAll("li");
setCursor(afterChildList[childIdx], cursorData.startOffset as number);
}, 100);
}
}
}
// 포커스
Expand Down
8 changes: 7 additions & 1 deletion src/runtime/core/components/editor/TextBlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,13 @@ function updateBlockData() {
// 포커스
function focus(type: string = "first") {
if (type === "first") {
setCursor($block.value, 0);
if ($block.value.childNodes.length > 0) {
setTimeout(() => {
setCursor($block.value.childNodes[0], 0);
}, 100);
} else {
setCursor($block.value, 0);
}
} else {
const childCount = $block.value.childNodes.length;
const targetChild = $block.value.childNodes[childCount - 1];
Expand Down
162 changes: 162 additions & 0 deletions src/runtime/core/components/editor/UlBlock.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<template>
<ul class="d-ul-block" @keydown="textKeyboardEvent" ref="$ul" :key="updateCount">
<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>
</ul>
</template>

<script setup lang="ts">
// @ts-ignore
import { ref, unref } from "#imports";
import { cursorSelection, liItem, listBlock, styleFunctionArgument } from "../../../../types";
import { getArrangementCursorData, setCursor, pasteText, styleSettings, keyboardEvent, getCursor, findEditableElement } from "../../utils";
const updateCount = ref<number>(0);
const $ul = ref();
const $item = ref();
const itemIdx = ref<number>(0);
const data = ref<listBlock>({
type: "",
id: "",
childList: [
{
classList: [],
content: "",
},
],
});
const props = defineProps<{ modelValue: listBlock; cursorData: cursorSelection }>();
const emit = defineEmits<{
(e: "update:modelValue", modelValue: listBlock): void;
(e: "addBlock", {}: { name: string; value: object }): void;
(e: "deleteBlockLocal", index?: number): void;
}>();
data.value = unref(props.modelValue) as listBlock;
if (data.value.childList.length === 0) {
}
// 키보드 이벤트 할당
function textKeyboardEvent(e: KeyboardEvent) {
keyboardEvent("list", e, emit, updateBlockData);
}
/**
* 외부용 함수
*/
// 데이터 정규화 및 검수
function updateBlockData() {
const $block = $ul.value;
const $childList = $block.querySelectorAll("li");
const childData: liItem[] = [];
const cursorData = getCursor();
$childList.forEach((row) => {
row.childNodes.forEach((child: ChildNode) => {
const $child = child as HTMLElement;
if (child.constructor.name !== "Text") {
// 텍스트가 아닐경우
if (child.constructor.name !== "HTMLBRElement") {
// br 태그 유지
if (child.textContent === "") {
// 빈 태그 삭제
child.remove();
} else if ($child.classList.length === 0) {
// 클레스 없는 엘리먼트 처리
$child.insertAdjacentHTML("afterend", $child.innerHTML);
child.remove();
}
} else {
$child.removeAttribute("class");
}
}
});
childData.push({
classList: [...row.classList].splice(1),
content: row.innerHTML,
});
});
data.value.childList = childData;
emit("update:modelValue", data.value);
updateCount.value += 1;
if (cursorData.startNode) {
const editableNode = findEditableElement(cursorData.startNode);
let childIdx = -1;
$childList.forEach((row, count) => {
if (row === editableNode) {
childIdx = count;
}
});
if (childIdx > -1) {
// 기본 로직
itemIdx.value = childIdx;
setTimeout(() => {
const afterChildList = $ul.value.querySelectorAll("li");
setCursor(afterChildList[childIdx], cursorData.startOffset as number);
}, 100);
} else {
// 중간 엔터
$childList.forEach((row, count) => {
if (row === (cursorData.startNode as Node).parentNode) {
childIdx = count;
}
});
setTimeout(() => {
const afterChildList = $ul.value.querySelectorAll("li");
setCursor(afterChildList[childIdx], cursorData.startOffset as number);
}, 100);
}
}
}
// 포커스
function focus() {
const childList = $ul.value.querySelectorAll(".d-li-item");
setCursor(childList[itemIdx.value], 0);
}
// 블럭 위치 주기
function getBoundingClientRect() {
return $ul.value.parentNode.getBoundingClientRect();
}
// 타입 전달
function getType() {
return data.value.type;
}
// 붙여넣기 이벤트
function pasteEvent(text: string) {
pasteText("text", text);
}
// 텍스트 스타일 지정
function setStyles({ type, url }: styleFunctionArgument) {
data.value = styleSettings({
kind: type,
blockData: data.value,
$target: $item[itemIdx.value],
url: url,
cursorData: props.cursorData,
});
setTimeout(() => {
updateBlockData();
}, 250);
}
defineExpose({
updateBlockData,
focus,
getType,
pasteEvent,
setStyles,
getBoundingClientRect,
});
</script>
17 changes: 17 additions & 0 deletions src/runtime/core/style/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,23 @@
content: "Write text";
color: #ccc;
}
.dragon-editor .d-ul-block {
padding-left: 30px;
cursor: text;
list-style: disc;
}
.dragon-editor .d-ul-block .d-li-item {
list-style: inherit;
outline: 0;
}
.dragon-editor .d-ul-block .d-li-item:empty {
min-height: 1.6em;
}
.dragon-editor .d-ul-block .d-li-item:empty::after {
display: inline;
content: "Write text";
color: #ccc;
}
.dragon-editor .d-image-block {
display: flex;
flex-direction: column;
Expand Down
Loading

0 comments on commit 06761cc

Please sign in to comment.