diff --git a/dist/cherry-markdown.css b/dist/cherry-markdown.css index 08d73628..bca67311 100644 --- a/dist/cherry-markdown.css +++ b/dist/cherry-markdown.css @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 oid sha256:ee9b384e9e8d553258192eb81005cdc0b25913b8120201e902bb11537a6834dc -size 223792 +size 223792 \ No newline at end of file diff --git a/dist/cherry-markdown.js b/dist/cherry-markdown.js index 5ad97a8e..f09bf107 100644 --- a/dist/cherry-markdown.js +++ b/dist/cherry-markdown.js @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 oid sha256:023ce4923c67a36c82b586844d662a2e8d2c969320ecc30e8ba76cc7737a9e3b -size 8056353 +size 8056353 \ No newline at end of file diff --git a/dist/cherry-markdown.js.map b/dist/cherry-markdown.js.map index 98131e33..c739e616 100644 --- a/dist/cherry-markdown.js.map +++ b/dist/cherry-markdown.js.map @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 oid sha256:93cca4aa35cfc6ecf0d4c21d120024b6ae94ad8f0797e3e0f31684124ca12ac4 -size 19133533 +size 19133533 \ No newline at end of file diff --git a/dist/fonts/ch-icon.eot b/dist/fonts/ch-icon.eot index 6344fe24..1d43c773 100644 --- a/dist/fonts/ch-icon.eot +++ b/dist/fonts/ch-icon.eot @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 oid sha256:8989ea021363279d19798eca69b71cac4b59427a256c1c4ac456dc8b80df291e -size 20940 +size 20940 \ No newline at end of file diff --git a/dist/fonts/ch-icon.ttf b/dist/fonts/ch-icon.ttf index e30f367f..f71e8c01 100644 --- a/dist/fonts/ch-icon.ttf +++ b/dist/fonts/ch-icon.ttf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 oid sha256:ae1418a5b5f12acb324c25c601be6106581114ad4f6135f9ae37d08ef6a4ca8f -size 20776 +size 20776 \ No newline at end of file diff --git a/dist/fonts/ch-icon.woff2 b/dist/fonts/ch-icon.woff2 index d4c17018..ccc305f3 100644 --- a/dist/fonts/ch-icon.woff2 +++ b/dist/fonts/ch-icon.woff2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 oid sha256:2e48de00fd3ffc179107d269879f2362135dcbf686388f5e821efc36b327cec3 -size 10256 +size 10256 \ No newline at end of file diff --git a/src/sass/previewer.scss b/src/sass/previewer.scss index 5ba9b5dc..2496dee5 100644 --- a/src/sass/previewer.scss +++ b/src/sass/previewer.scss @@ -116,12 +116,27 @@ color: #FFF; } } - &-delete-container { + &-sort-container, &-delete-container { position: absolute; height: 100%; width: 100%; padding: 0; margin: 0; + list-style-type: none; + } + &__sort { + pointer-events: auto; + display: flex; + justify-content: center; + position: absolute; + color: #3582fb; + width: 12px; + height: 12px; + line-height: 12px; + border: 1px solid #3582fb00; + background-color: #ffffff00; + cursor: pointer; + transition: all 0.3s; } &__delete { pointer-events: auto; @@ -150,4 +165,4 @@ .cherry-highlight-line { animation: changeBgColor 3s; } -} + diff --git a/src/utils/tableContentHandler.js b/src/utils/tableContentHandler.js index 57e75044..9137c7df 100644 --- a/src/utils/tableContentHandler.js +++ b/src/utils/tableContentHandler.js @@ -51,7 +51,6 @@ export default class TableHandler { case 'previewUpdate': return this.$refreshPosition(); case 'mousedown': - // return this.trigger !== 'click' && this.$drawDrag(); return; case 'mouseup': return this.trigger === 'click' && this.$tryRemoveMe(event, callback); @@ -309,6 +308,7 @@ export default class TableHandler { return; } this.$drawSymbol(); + this.$drawSortSymbol(); this.$drawDelete(); } @@ -413,7 +413,118 @@ export default class TableHandler { this.container.appendChild(this.tableEditor.editorDom.symbolContainer); this.$setSymbolOffset(); } + $drawSortSymbol() { + const types = ['RowLeft', 'RowRight', 'ColUp', 'ColDown']; + const container = document.createElement('ul'); + container.className = 'cherry-previewer-table-hover-handler-sort-container'; + types.forEach((type) => { + const sortSymbol = document.createElement('li'); + sortSymbol.setAttribute('data-type', type); + sortSymbol.className = 'cherry-previewer-table-hover-handler__sort'; + sortSymbol.draggable = true; + sortSymbol.addEventListener('mousedown', () => (sortSymbol.innerHTML = '▮')); + if (type.startsWith('Row')) { + sortSymbol.title = '移动行'; + sortSymbol.innerHTML = '▯'; + sortSymbol.addEventListener('mouseover', () => { + const { tdNode } = this.tableEditor.info; + tdNode.draggable = true; + + tdNode.parentNode.style.backgroundColor = 'rgb(206,226,248)'; + }); + sortSymbol.addEventListener('mouseleave', () => { + const { tdNode } = this.tableEditor.info; + tdNode.draggable = false; + tdNode.parentNode.style.backgroundColor = ''; + }); + sortSymbol.addEventListener('mousedown', (e) => { + this.$setSelection(this.tableEditor.info.tableIndex, 'table'); + this.$dragLine(); + }); + } else { + sortSymbol.title = '移动列'; + sortSymbol.innerHTML = '▯'; + sortSymbol.style.transform = 'rotate(90deg)'; + const highLightTrDom = []; + sortSymbol.addEventListener('mouseover', () => { + const { tdNode } = this.tableEditor.info; + tdNode.draggable = true; + + const index = Array.from(tdNode.parentNode.children).indexOf(tdNode); + + Array.from(tdNode.parentNode.parentNode.parentNode.children) + .map((item) => item.children) + .forEach((item) => { + Array.from(item).forEach((tr) => { + highLightTrDom.push(tr); + }); + }); + highLightTrDom.forEach((tr) => (tr.children[index].style.backgroundColor = 'rgb(206,226,248)')); + }); + sortSymbol.addEventListener('mouseleave', () => { + const { tdNode } = this.tableEditor.info; + tdNode.draggable = false; + const index = Array.from(tdNode.parentNode.children).indexOf(tdNode); + highLightTrDom.forEach((tr) => (tr.children[index].style.backgroundColor = '')); + }); + sortSymbol.addEventListener('mousedown', (e) => { + this.$setSelection(this.tableEditor.info.tableIndex, 'table'); + this.$dragCol(); + }); + } + container.appendChild(sortSymbol); + }); + this.tableEditor.editorDom.sortContainer = container; + this.container.appendChild(this.tableEditor.editorDom.sortContainer); + this.$setSortSymbolsPosition(); + } + $setSortSymbolsPosition() { + const container = this.tableEditor.editorDom.sortContainer; + const { tableNode, tdNode, isTHead } = this.tableEditor.info; + const tableInfo = this.$getPosition(tableNode); + const tdInfo = this.$getPosition(tdNode); + + this.setStyle(this.container, 'width', `${tableInfo.width}px`); + this.setStyle(this.container, 'height', `${tableInfo.height}px`); + this.setStyle(this.container, 'top', `${tableInfo.top}px`); + this.setStyle(this.container, 'left', `${tableInfo.left}px`); + + container.childNodes.forEach((node) => { + const { type } = node.dataset; + + switch (type) { + case 'RowLeft': + this.setStyle( + node, + 'top', + `${tdInfo.top - tableInfo.top - tableInfo.height + tdInfo.height / 2 - node.offsetHeight / 2}px`, + ); + this.setStyle(node, 'left', `${-node.offsetWidth / 2}px`); + break; + case 'RowRight': + this.setStyle( + node, + 'top', + `${tdInfo.top - tableInfo.top - tableInfo.height + tdInfo.height / 2 - node.offsetHeight / 2}px`, + ); + this.setStyle(node, 'left', `${tableInfo.width - node.offsetWidth / 2}px`); + break; + case 'ColUp': + this.setStyle(node, 'left', `${tdInfo.left - tableInfo.left + tdInfo.width / 2 - node.offsetWidth / 2}px`); + this.setStyle(node, 'top', `${-tableInfo.height - node.offsetHeight / 2}px`); + break; + case 'ColDown': + this.setStyle(node, 'left', `${tdInfo.left - tableInfo.left + tdInfo.width / 2 - node.offsetWidth / 2}px`); + this.setStyle(node, 'top', `${-node.offsetHeight / 2}px`); + + break; + } + if (isTHead && type.startsWith('Row')) { + this.setStyle(node, 'display', 'none'); + } + }); + } /** * 添加上一行 */ @@ -589,15 +700,6 @@ export default class TableHandler { /** * 拖拽 */ - $drawDrag() { - const { isTHead } = this.tableEditor.info; - this.$setSelection(this.tableEditor.info.tableIndex, 'table'); - if (isTHead) { - this.$dragCol(); - } else { - this.$dragLine(); - } - } /** * 拖拽列 @@ -610,29 +712,40 @@ export default class TableHandler { const that = this; tdNode.setAttribute('draggable', true); - // 离开需要还原边框 - thNode.addEventListener('dragleave', function (event) { + function handleDragLeave(event) { that.setStyle(event.target, 'border', '1px solid #dfe6ee'); - }); - // 改变将要放到的位置边框的颜色 - thNode.addEventListener('dragover', function (event) { + } + + function handleDragOver(event) { event.preventDefault(); const tdIndex = Array.from(event.target.parentElement.childNodes).indexOf(event.target); - that.$dragSymbol(event.target, oldTdIndex, tdIndex); - }); - thNode.addEventListener('drop', function (event) { + that.$dragSymbol(event.target, oldTdIndex, tdIndex, 'Col'); + thNode.setAttribute('draggable', false); + } + + function handleDrop(event) { event.preventDefault(); const tdIndex = Array.from(event.target.parentElement.childNodes).indexOf(event.target); const newLines = lines.map((line, index) => { - const cells = line.split('|').filter((item, i) => item !== ''); + const cells = line + .split('|') + .map((item) => (item === '' ? 'CHERRY_MARKDOWN_PENDING_TEXT_FOR_EMPTY_CELL' : item)) + .slice(1, -1); return `|${that.$operateLines(oldTdIndex, tdIndex, cells).join('|')}|`; }); - const newText = newLines.join('\n'); + const newText = newLines.join('\n').replace(/CHERRY_MARKDOWN_PENDING_TEXT_FOR_EMPTY_CELL/g, ''); that.codeMirror.replaceSelection(newText); that.setStyle(event.target, 'border', '1px solid #dfe6ee'); that.$findTableInEditor(); that.$setSelection(that.tableEditor.info.tableIndex, 'table'); - }); + + thNode.removeEventListener('dragleave', handleDragLeave); + thNode.removeEventListener('dragover', handleDragOver); + } + + thNode.addEventListener('dragleave', handleDragLeave); + thNode.addEventListener('dragover', handleDragOver); + thNode.addEventListener('drop', handleDrop, { once: true }); } /** @@ -647,17 +760,19 @@ export default class TableHandler { const lines = this.codeMirror.getSelection().split(/\n/); const that = this; - tBody.addEventListener('dragleave', function (event) { + function handleDragLeave(event) { that.setStyle(event.target.parentElement, 'border', '1px solid #dfe6ee'); - }); - tBody.addEventListener('dragover', function (event) { - // 默认元素不可放置,这里取消默认,将放置目标修改为可放置的 + } + + function handleDragOver(event) { event.preventDefault(); const trIndex = Array.from(event.target.parentElement.parentElement.childNodes).indexOf(event.target.parentElement) + 2; - that.$dragSymbol(event.target, oldTrIndex, trIndex); - }); - tBody.addEventListener('drop', function (event) { + that.$dragSymbol(event.target, oldTrIndex, trIndex, 'Line'); + trNode.setAttribute('draggable', false); + } + + function handleDrop(event) { event.preventDefault(); const trIndex = Array.from(event.target.parentElement.parentElement.childNodes).indexOf(event.target.parentElement) + 2; @@ -667,13 +782,20 @@ export default class TableHandler { that.$findTableInEditor(); that.$setSelection(that.tableEditor.info.tableIndex, 'table'); that.setStyle(event.target.parentElement, 'border', '1px solid #dfe6ee'); - }); + + tBody.removeEventListener('dragleave', handleDragLeave); + tBody.removeEventListener('dragover', handleDragOver); + } + + tBody.addEventListener('dragleave', handleDragLeave); + tBody.addEventListener('dragover', handleDragOver); + tBody.addEventListener('drop', handleDrop, { once: true }); } - $dragSymbol(objTarget, oldIndex, index) { + $dragSymbol(objTarget, oldIndex, index, type) { const { target } = this; if (target !== objTarget && oldIndex !== index) { - if (target.tagName === 'TH') { + if ((target.tagName === 'TH' || target.tagName === 'TD') && type === 'Col') { if (oldIndex < index) { this.setStyle(objTarget, 'border', `1px solid #dfe6ee`); this.setStyle(objTarget, 'border-right', `2px solid #6897bb`); @@ -681,7 +803,7 @@ export default class TableHandler { this.setStyle(objTarget, 'border', `1px solid #dfe6ee`); this.setStyle(objTarget, 'border-left', `2px solid #6897bb`); } - } else if (target.tagName === 'TD') { + } else if (target.tagName === 'TD' && type === 'Line') { if (oldIndex < index) { this.setStyle(objTarget.parentElement, 'border', `1px solid #dfe6ee`); this.setStyle(objTarget.parentElement, 'border-bottom', `2px solid #6897bb`); diff --git a/yarn.lock b/yarn.lock index cb0f6f62..c236ad42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25,6 +25,7 @@ dependencies: "@babel/highlight" "^7.10.4" + "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz" @@ -238,6 +239,7 @@ resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz" integrity sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg== + "@babel/helper-validator-identifier@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz"