Pārlūkot izejas kodu

:art: https://github.com/siyuan-note/siyuan/issues/10488

Vanessa 9 mēneši atpakaļ
vecāks
revīzija
6fe03226c5

+ 35 - 4
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;

+ 104 - 8
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 = '<table>'
+                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 += "<tr>"
+                    }
+                    html += item.outerHTML;
+                    if (!item.nextElementSibling || !selectCellElements[index + 1] ||
+                        !item.nextElementSibling.isSameNode(selectCellElements[index + 1])) {
+                        html += "</tr>";
+                    }
+                })
+                html += '</table>'
+                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 = '<table>'
+                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 += "<tr>"
+                    }
+                    html += item.outerHTML;
+                    if (!item.nextElementSibling || !selectCellElements[index + 1] ||
+                        !item.nextElementSibling.isSameNode(selectCellElements[index + 1])) {
+                        html += "</tr>";
+                    }
+                    item.innerHTML = "";
+                })
+                html += '</table>'
+                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;

+ 4 - 0
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: {