Skip to content

Commit

Permalink
feat:table拖拽排序 (#826)
Browse files Browse the repository at this point in the history
* feat: 实现表格拖拽排序

* fix: error performance when drag empty col

* feat: remove useless code

---------

Co-authored-by: sunsonliu <[email protected]>
  • Loading branch information
coderz-w and sunsonliu authored Sep 25, 2024
1 parent d8ed2ec commit 4d51a56
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 40 deletions.
2 changes: 1 addition & 1 deletion dist/cherry-markdown.css
Git LFS file not shown
2 changes: 1 addition & 1 deletion dist/cherry-markdown.js
Git LFS file not shown
2 changes: 1 addition & 1 deletion dist/cherry-markdown.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/fonts/ch-icon.eot
Git LFS file not shown
2 changes: 1 addition & 1 deletion dist/fonts/ch-icon.ttf
Git LFS file not shown
2 changes: 1 addition & 1 deletion dist/fonts/ch-icon.woff2
Git LFS file not shown
19 changes: 17 additions & 2 deletions src/sass/previewer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -150,4 +165,4 @@
.cherry-highlight-line {
animation: changeBgColor 3s;
}
}

186 changes: 154 additions & 32 deletions src/utils/tableContentHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -309,6 +308,7 @@ export default class TableHandler {
return;
}
this.$drawSymbol();
this.$drawSortSymbol();
this.$drawDelete();
}

Expand Down Expand Up @@ -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');
}
});
}
/**
* 添加上一行
*/
Expand Down Expand Up @@ -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();
}
}

/**
* 拖拽列
Expand All @@ -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 });
}

/**
Expand All @@ -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;
Expand All @@ -667,21 +782,28 @@ 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`);
} else if (oldIndex > index) {
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`);
Expand Down
2 changes: 2 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit 4d51a56

Please sign in to comment.