diff --git a/app/src/protyle/util/insertHTML.ts b/app/src/protyle/util/insertHTML.ts index 0b28250e9..40c631c5b 100644 --- a/app/src/protyle/util/insertHTML.ts +++ b/app/src/protyle/util/insertHTML.ts @@ -7,7 +7,7 @@ import { focusBlock, focusByWbr, getEditorRange, - getSelectionOffset, + getSelectionOffset, setLastNodeRange, } from "./selection"; import {Constants} from "../../constants"; import {highlightRender} from "../render/highlightRender"; @@ -199,6 +199,33 @@ const processAV = (range: Range, html: string, protyle: IProtyle, blockElement: }); }; +const processTable = (range: Range, html: string, protyle: IProtyle, blockElement: HTMLElement) => { + const tempElement = document.createElement("template"); + tempElement.innerHTML = html; + const cellElements = tempElement.content.querySelectorAll("th, td"); + if (cellElements.length === 0) { + return false; + } + const scrollLeft = blockElement.firstElementChild.scrollLeft; + const tableSelectElement = blockElement.querySelector(".table__select") as HTMLElement; + let index = 0; + const oldHTML = blockElement.outerHTML; + blockElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss")); + blockElement.querySelectorAll("th, td").forEach((item: HTMLTableCellElement) => { + if (!item.classList.contains("fn__none") && + item.offsetLeft + 6 > tableSelectElement.offsetLeft + scrollLeft && item.offsetLeft + item.clientWidth - 6 < tableSelectElement.offsetLeft + scrollLeft + tableSelectElement.clientWidth && + item.offsetTop + 6 > tableSelectElement.offsetTop && item.offsetTop + item.clientHeight - 6 < tableSelectElement.offsetTop + tableSelectElement.clientHeight && + cellElements.length > index) { + item.innerHTML = cellElements[index].innerHTML; + index++; + setLastNodeRange(item, range, false); + } + }); + range.collapse(false); + updateTransaction(protyle, blockElement.getAttribute("data-node-id"), blockElement.outerHTML, oldHTML); + return true; +} + export const insertHTML = (html: string, protyle: IProtyle, isBlock = false, // 移动端插入嵌入块时,获取到的 range 为旧值 useProtyleRange = false, @@ -217,13 +244,13 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false, isBlock = true; } } - let blockElement = hasClosestBlock(range.startContainer) as Element; + let blockElement = hasClosestBlock(range.startContainer) as HTMLElement; if (!blockElement) { // 使用鼠标点击选则模版提示列表后 range 丢失 if (protyle.toolbar.range) { - blockElement = hasClosestBlock(protyle.toolbar.range.startContainer) as Element; + blockElement = hasClosestBlock(protyle.toolbar.range.startContainer) as HTMLElement; } else { - blockElement = protyle.wysiwyg.element.firstElementChild as Element; + blockElement = protyle.wysiwyg.element.firstElementChild as HTMLElement; } } if (!blockElement) { @@ -234,6 +261,10 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false, processAV(range, html, protyle, blockElement as HTMLElement); return; } + if (blockElement.classList.contains("table") && blockElement.querySelector(".table__select").clientWidth > 0 && + processTable(range, html, protyle, blockElement)) { + return; + } let id = blockElement.getAttribute("data-node-id"); range.insertNode(document.createElement("wbr")); let oldHTML = blockElement.outerHTML; diff --git a/app/src/protyle/wysiwyg/index.ts b/app/src/protyle/wysiwyg/index.ts index 134713e6f..6ea8cd9cd 100644 --- a/app/src/protyle/wysiwyg/index.ts +++ b/app/src/protyle/wysiwyg/index.ts @@ -1,4 +1,4 @@ -import {getTextStar, paste} from "../util/paste"; +import {getTextStar, paste, pasteText} from "../util/paste"; import { hasClosestBlock, hasClosestByAttribute, @@ -62,7 +62,7 @@ import {openGlobalSearch} from "../../search/util"; import {popSearch} from "../../mobile/menu/search"; /// #endif import {BlockPanel} from "../../block/Panel"; -import {isInIOS, isOnlyMeta} from "../util/compatibility"; +import {isInIOS, isOnlyMeta, readText} from "../util/compatibility"; import {MenuItem} from "../../menus/Menu"; import {fetchPost} from "../../util/fetch"; import {onGet} from "../util/onGet"; @@ -274,9 +274,10 @@ export class WYSIWYG { } const selectImgElement = nodeElement.querySelector(".img--select"); const selectAVElement = nodeElement.querySelector(".av__row--select, .av__cell--select"); + const selectTableElement = nodeElement.querySelector(".table__select")?.clientWidth > 0; let selectElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")); if (selectElements.length === 0 && range.toString() === "" && !range.cloneContents().querySelector("img") && - !selectImgElement && !selectAVElement) { + !selectImgElement && !selectAVElement && !selectTableElement) { nodeElement.classList.add("protyle-wysiwyg--select"); countBlockWord([nodeElement.getAttribute("data-node-id")]); selectElements = [nodeElement]; @@ -331,6 +332,31 @@ export class WYSIWYG { textPlain = textPlain.substring(0, textPlain.length - 2); html = html.substring(0, html.length - 1) + "]"; } + } else if (selectTableElement) { + const selectCellElements: HTMLTableCellElement[] = []; + const scrollLeft = nodeElement.firstElementChild.scrollLeft; + const tableSelectElement = nodeElement.querySelector(".table__select") as HTMLElement; + html = '' + nodeElement.querySelectorAll("th, td").forEach((item: HTMLTableCellElement) => { + if (!item.classList.contains("fn__none") && + item.offsetLeft + 6 > tableSelectElement.offsetLeft + scrollLeft && item.offsetLeft + item.clientWidth - 6 < tableSelectElement.offsetLeft + scrollLeft + tableSelectElement.clientWidth && + item.offsetTop + 6 > tableSelectElement.offsetTop && item.offsetTop + item.clientHeight - 6 < tableSelectElement.offsetTop + tableSelectElement.clientHeight) { + selectCellElements.push(item); + } + }); + selectCellElements.forEach((item, index) => { + if (index === 0 || !item.previousElementSibling || + !item.previousElementSibling.isSameNode(selectCellElements[index - 1])) { + html += "" + } + html += item.outerHTML; + if (!item.nextElementSibling || !selectCellElements[index + 1] || + !item.nextElementSibling.isSameNode(selectCellElements[index + 1])) { + html += ""; + } + }) + html += '
' + textPlain = protyle.lute.HTML2Md(html); } else { const tempElement = document.createElement("div"); // https://github.com/siyuan-note/siyuan/issues/5540 @@ -394,8 +420,8 @@ export class WYSIWYG { textPlain = textPlain || protyle.lute.BlockDOM2StdMd(html).trimEnd(); textPlain = textPlain.replace(/\u00A0/g, " "); // Replace non-breaking spaces with normal spaces when copying https://github.com/siyuan-note/siyuan/issues/9382 event.clipboardData.setData("text/plain", textPlain); - event.clipboardData.setData("text/html", protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html)); - event.clipboardData.setData("text/siyuan", html); + event.clipboardData.setData("text/html", selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html)); + event.clipboardData.setData("text/siyuan", selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html); }); this.element.addEventListener("mousedown", (event: MouseEvent) => { @@ -1222,6 +1248,28 @@ export class WYSIWYG { } }).element); window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element); + window.siyuan.menus.menu.append(new MenuItem({ + icon: "iconCopy", + accelerator: "⌘C", + label: window.siyuan.languages.copy, + click() { + if (tableBlockElement) { + focusByRange(getEditorRange(tableBlockElement)); + document.execCommand("copy"); + } + } + }).element); + window.siyuan.menus.menu.append(new MenuItem({ + icon: "iconCut", + accelerator: "⌘X", + label: window.siyuan.languages.cut, + click() { + if (tableBlockElement) { + focusByRange(getEditorRange(tableBlockElement)); + document.execCommand("cut"); + } + } + }).element); window.siyuan.menus.menu.append(new MenuItem({ label: window.siyuan.languages.clear, icon: "iconTrashcan", @@ -1238,6 +1286,7 @@ export class WYSIWYG { }); tableSelectElement.removeAttribute("style"); const oldHTML = tableBlockElement.outerHTML; + tableBlockElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss")); selectCellElements.forEach(item => { item.innerHTML = ""; }); @@ -1245,6 +1294,23 @@ export class WYSIWYG { } } }).element); + window.siyuan.menus.menu.append(new MenuItem({ + label: window.siyuan.languages.paste, + icon: "iconPaste", + accelerator: "⌘V", + async click() { + if (document.queryCommandSupported("paste")) { + document.execCommand("paste"); + } else if (tableBlockElement) { + try { + const clipText = await readText(); + pasteText(protyle, clipText, tableBlockElement); + } catch (e) { + console.log(e); + } + } + } + }).element); window.siyuan.menus.menu.popup({x: mouseUpEvent.clientX - 8, y: mouseUpEvent.clientY - 16}); } } @@ -1359,9 +1425,10 @@ export class WYSIWYG { event.preventDefault(); const selectImgElement = nodeElement.querySelector(".img--select"); const selectAVElement = nodeElement.querySelector(".av__row--select, .av__cell--select"); + const selectTableElement = nodeElement.querySelector(".table__select")?.clientWidth > 0; let selectElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")); if (selectElements.length === 0 && range.toString() === "" && !range.cloneContents().querySelector("img") && - !selectImgElement && !selectAVElement) { + !selectImgElement && !selectAVElement && !selectTableElement) { nodeElement.classList.add("protyle-wysiwyg--select"); selectElements = [nodeElement]; } @@ -1395,6 +1462,35 @@ export class WYSIWYG { const cellsValue = updateCellsValue(protyle, nodeElement); html = JSON.stringify(cellsValue.json); textPlain = cellsValue.text; + } else if (selectTableElement) { + const selectCellElements: HTMLTableCellElement[] = []; + const scrollLeft = nodeElement.firstElementChild.scrollLeft; + const tableSelectElement = nodeElement.querySelector(".table__select") as HTMLElement; + html = '' + const oldHTML = nodeElement.outerHTML; + nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss")); + nodeElement.querySelectorAll("th, td").forEach((item: HTMLTableCellElement) => { + if (!item.classList.contains("fn__none") && + item.offsetLeft + 6 > tableSelectElement.offsetLeft + scrollLeft && item.offsetLeft + item.clientWidth - 6 < tableSelectElement.offsetLeft + scrollLeft + tableSelectElement.clientWidth && + item.offsetTop + 6 > tableSelectElement.offsetTop && item.offsetTop + item.clientHeight - 6 < tableSelectElement.offsetTop + tableSelectElement.clientHeight) { + selectCellElements.push(item); + } + }); + selectCellElements.forEach((item, index) => { + if (index === 0 || !item.previousElementSibling || + !item.previousElementSibling.isSameNode(selectCellElements[index - 1])) { + html += "" + } + html += item.outerHTML; + if (!item.nextElementSibling || !selectCellElements[index + 1] || + !item.nextElementSibling.isSameNode(selectCellElements[index + 1])) { + html += ""; + } + item.innerHTML = ""; + }) + html += '
' + textPlain = protyle.lute.HTML2Md(html); + updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, oldHTML); } else { const id = nodeElement.getAttribute("data-node-id"); const oldHTML = nodeElement.outerHTML; @@ -1550,8 +1646,8 @@ export class WYSIWYG { } textPlain = textPlain.replace(/\u00A0/g, " "); // Replace non-breaking spaces with normal spaces when copying https://github.com/siyuan-note/siyuan/issues/9382 event.clipboardData.setData("text/plain", textPlain); - event.clipboardData.setData("text/html", protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html)); - event.clipboardData.setData("text/siyuan", html); + event.clipboardData.setData("text/html", selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html)); + event.clipboardData.setData("text/siyuan", selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html); }); let beforeContextmenuRange: Range; diff --git a/app/src/types/protyle.d.ts b/app/src/types/protyle.d.ts index a000df009..fdc667430 100644 --- a/app/src/types/protyle.d.ts +++ b/app/src/types/protyle.d.ts @@ -276,6 +276,10 @@ declare class Lute { public BlockDOM2InlineBlockDOM(html: string): string; public BlockDOM2HTML(html: string): string; + + public HTML2Md(html: string): string; + + public HTML2BlockDOM(html: string): string; } declare const webkitAudioContext: {