Forráskód Böngészése

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

Vanessa 2 éve
szülő
commit
ba705cd643

+ 20 - 23
app/src/assets/scss/export.scss

@@ -36,39 +36,36 @@ svg {
 }
 
 @media print {
-  .protyle-wysiwyg {
-    [data-node-id] {
-
-      // 尽量避免音频块内分页
-      &[data-type=NodeAudio],
+  .protyle-wysiwyg [data-node-id] {
+    // 尽量避免音频块内分页
+    &[data-type=NodeAudio],
       // 尽量避免图表块内分页
-      &[data-type=NodeCodeBlock][data-subtype=abc],
-      &[data-type=NodeCodeBlock][data-subtype=echarts],
-      &[data-type=NodeCodeBlock][data-subtype=flowchart],
-      &[data-type=NodeCodeBlock][data-subtype=graphviz],
-      &[data-type=NodeCodeBlock][data-subtype=mermaid],
-      &[data-type=NodeCodeBlock][data-subtype=mindmap],
-      &[data-type=NodeCodeBlock][data-subtype=plantuml],
+    &[data-type=NodeCodeBlock][data-subtype=abc],
+    &[data-type=NodeCodeBlock][data-subtype=echarts],
+    &[data-type=NodeCodeBlock][data-subtype=flowchart],
+    &[data-type=NodeCodeBlock][data-subtype=graphviz],
+    &[data-type=NodeCodeBlock][data-subtype=mermaid],
+    &[data-type=NodeCodeBlock][data-subtype=mindmap],
+    &[data-type=NodeCodeBlock][data-subtype=plantuml],
       // 尽量避免标题块内分页
-      &[data-type=NodeHeading],
+    &[data-type=NodeHeading],
       // 尽量避免 Iframe 块内分页
-      &[data-type=NodeIFrame],
+    &[data-type=NodeIFrame],
       // 尽量避免公式块内分页
-      &[data-type=NodeMathBlock],
+    &[data-type=NodeMathBlock],
       // 尽量避免段落块内分页
-      &[data-type=NodeParagraph],
+    &[data-type=NodeParagraph],
       // 尽量避免分割线内分页
-      &[data-type=NodeThematicBreak],
+    &[data-type=NodeThematicBreak],
       // 尽量避免视频块内分页
-      &[data-type=NodeVideo],
+    &[data-type=NodeVideo],
       // 尽量避免挂件块内分页
-      &[data-type=NodeWidget],
+    &[data-type=NodeWidget],
       // 尽量避免表格块行内分页
-      tr,
+    tr,
       // 尽量避免图片内分页
-      .img {
-        break-inside: avoid;
-      }
+    .img {
+      break-inside: avoid;
     }
   }
 }

+ 54 - 0
app/src/card/newCardTab.ts

@@ -0,0 +1,54 @@
+import {Wnd} from "../layout/Wnd";
+import {getInstanceById, getWndByLayout} from "../layout/util";
+import {Tab} from "../layout/Tab";
+import {Custom} from "../layout/dock/Custom";
+import {Dialog} from "../dialog";
+import {genCardHTML} from "./openCard";
+import {fetchPost} from "../util/fetch";
+
+export const newCardTab = (options: {
+    type: TCardType,
+    id: string,
+    dialog: Dialog
+}) => {
+    let wnd: Wnd;
+    const element = document.querySelector(".layout__wnd--active");
+    if (element) {
+        wnd = getInstanceById(element.getAttribute("data-id")) as Wnd;
+    }
+    if (!wnd) {
+        wnd = getWndByLayout(window.siyuan.layout.centerLayout);
+    }
+    const tab = new Tab({
+        icon: "iconRiffCard",
+        title: window.siyuan.languages.spaceRepetition,
+        callback(tab) {
+            const custom = new Custom({
+                type: "card",
+                tab,
+                data: {
+                    type: options.type,
+                    id: options.id
+                },
+                init(element) {
+                    fetchPost(options.type === "all" ? "/api/riff/getRiffDueCards" :
+                        (options.type === "doc" ? "/api/riff/getTreeRiffDueCards" : "/api/riff/getNotebookRiffDueCards"), {
+                        rootID: options.id,
+                        deckID: options.id,
+                        notebook: options.id,
+                    }, (response) => {
+                        element.innerHTML = genCardHTML({
+                            id: options.id,
+                            cardType: options.type,
+                            blocks: response.data,
+                            isTab: true,
+                        });
+                    });
+                }
+            });
+            tab.addModel(custom);
+        }
+    });
+    wnd.split("lr").addTab(tab);
+    options.dialog.destroy();
+}

+ 56 - 32
app/src/card/openCard.ts

@@ -11,52 +11,39 @@ import {fullscreen} from "../protyle/breadcrumb/action";
 import {MenuItem} from "../menus/Menu";
 import {escapeHtml} from "../util/escape";
 import {getDisplayName, movePathTo} from "../util/pathName";
+import {newCardTab} from "./newCardTab";
 
-export const openCard = () => {
-    fetchPost("/api/riff/getRiffDueCards", {deckID: ""}, (cardsResponse) => {
-        openCardByData(cardsResponse.data, "all");
-    });
-};
-
-export const openCardByData = (cardsData: {
-    cards: ICard[],
-    unreviewedCount: number
-}, cardType: "doc" | "notebook" | "all", id?: string, title?: string) => {
-    const exit = window.siyuan.dialogs.find(item => {
-        if (item.element.getAttribute("data-key") === window.siyuan.config.keymap.general.riffCard.custom) {
-            item.destroy();
-            return true;
-        }
-    });
-    if (exit) {
-        return;
-    }
-
-    let blocks = cardsData.cards;
-    let index = 0;
-    const dialog = new Dialog({
-        content: `<div class="card__main">
+export const genCardHTML = (options: {
+    id: string,
+    cardType: TCardType,
+    blocks: ICard[],
+    isTab: boolean
+}) => {
+    return `<div class="card__main">
     <div class="card__header">
         <span class="fn__flex-1 fn__flex-center">${window.siyuan.languages.riffCard}</span>
         <span class="fn__space"></span>
-        <div data-type="count" class="ft__on-surface ft__smaller fn__flex-center${blocks.length === 0 ? " fn__none" : ""}">1/${blocks.length}</span></div>
+        <div data-type="count" class="ft__on-surface ft__smaller fn__flex-center${options.blocks.length === 0 ? " fn__none" : ""}">1/${options.blocks.length}</span></div>
         <div class="fn__space"></div>
-        <div data-id="${id || ""}" data-cardtype="${cardType}" data-type="filter" class="block__icon block__icon--show">
+        <div data-id="${options.id || ""}" data-cardtype="${options.cardType}" data-type="filter" class="block__icon block__icon--show">
             <svg><use xlink:href="#iconFilter"></use></svg>
         </div>
         <div class="fn__space"></div>
         ${isMobile() ? `<div data-type="close" class="block__icon block__icon--show">
             <svg><use xlink:href="#iconCloseRound"></use></svg>
         </div>` : `<div data-type="fullscreen" class="b3-tooltips b3-tooltips__sw block__icon block__icon--show" aria-label="${window.siyuan.languages.fullscreen}">
-            <svg><use xlink:href="#iconFullscreen"></use></svg>
-        </div>`}
+    <svg><use xlink:href="#iconFullscreen"></use></svg>
+</div><div class="fn__space"></div>
+<div data-type="sticktab" class="b3-tooltips b3-tooltips__sw block__icon block__icon--show" aria-label="${window.siyuan.languages.openInNewTab}">
+    <svg><use xlink:href="#iconLayoutRight"></use></svg>
+</div>`}
     </div>
-    <div class="card__block fn__flex-1${blocks.length === 0 ? " fn__none" : ""}${window.siyuan.config.flashcard.mark ? " card__block--hidemark" : ""}${window.siyuan.config.flashcard.superBlock ? " card__block--hidesb" : ""}${window.siyuan.config.flashcard.list ? " card__block--hideli" : ""}" data-type="render"></div>
-    <div class="card__empty card__empty--space${blocks.length === 0 ? "" : " fn__none"}" data-type="empty">
+    <div class="card__block fn__flex-1${options.blocks.length === 0 ? " fn__none" : ""}${window.siyuan.config.flashcard.mark ? " card__block--hidemark" : ""}${window.siyuan.config.flashcard.superBlock ? " card__block--hidesb" : ""}${window.siyuan.config.flashcard.list ? " card__block--hideli" : ""}" data-type="render"></div>
+    <div class="card__empty card__empty--space${options.blocks.length === 0 ? "" : " fn__none"}" data-type="empty">
         <div>🔮</div>
         ${window.siyuan.languages.noDueCard}
     </div>
-    <div class="fn__flex card__action${blocks.length === 0 ? " fn__none" : ""}">
+    <div class="fn__flex card__action${options.blocks.length === 0 ? " fn__none" : ""}">
         <button class="b3-button b3-button--cancel" disabled="disabled" data-type="-2" style="width: 25%;min-width: 86px;display: flex">
             <svg><use xlink:href="#iconLeft"></use></svg>
             (p)
@@ -101,7 +88,33 @@ export const openCardByData = (cardsData: {
             </button>
         </div>
     </div>
-</div>`,
+</div>`;
+};
+
+export const openCard = () => {
+    fetchPost("/api/riff/getRiffDueCards", {deckID: ""}, (cardsResponse) => {
+        openCardByData(cardsResponse.data, "all");
+    });
+};
+
+export const openCardByData = (cardsData: {
+    cards: ICard[],
+    unreviewedCount: number
+}, cardType: TCardType, id?: string, title?: string) => {
+    const exit = window.siyuan.dialogs.find(item => {
+        if (item.element.getAttribute("data-key") === window.siyuan.config.keymap.general.riffCard.custom) {
+            item.destroy();
+            return true;
+        }
+    });
+    if (exit) {
+        return;
+    }
+
+    let blocks = cardsData.cards;
+    let index = 0;
+    const dialog = new Dialog({
+        content: genCardHTML({id, cardType, blocks, isTab: false}),
         width: isMobile() ? "100vw" : "80vw",
         height: isMobile() ? "100vh" : "70vh",
         destroyCallback() {
@@ -198,6 +211,17 @@ export const openCardByData = (cardsData: {
                 event.preventDefault();
                 return;
             }
+            const sticktabElement = hasClosestByAttribute(target, "data-type", "sticktab");
+            if (sticktabElement) {
+                newCardTab({
+                    type: filterElement.getAttribute("data-cardtype") as TCardType,
+                    id: filterElement.getAttribute("data-id"),
+                    dialog,
+                });
+                event.stopPropagation();
+                event.preventDefault();
+                return;
+            }
             const closeElement = hasClosestByAttribute(target, "data-type", "close");
             if (closeElement) {
                 dialog.destroy();

+ 29 - 0
app/src/layout/dock/Custom.ts

@@ -0,0 +1,29 @@
+import {Tab} from "../Tab";
+import {setPanelFocus} from "../util";
+import {Model} from "../Model";
+
+export class Custom extends Model {
+    private element: Element;
+
+    constructor(options: {
+        tab: Tab,
+        data: any,
+        type: string,   // 同一类型的唯一标识
+        init:(element:Element)=>void
+    }) {
+        super({id: options.tab.id});
+        if (window.siyuan.config.fileTree.openFilesUseCurrentTab) {
+            options.tab.headElement.classList.add("item--unupdate");
+        }
+        this.element = options.tab.panelElement;
+        options.init(this.element);
+        this.element.addEventListener("click", () => {
+            setPanelFocus(this.element.parentElement.parentElement);
+        });
+        this.update();
+    }
+
+    private update() {
+        this.element.innerHTML = `eee`
+    }
+}

+ 1 - 1
app/src/search/util.ts

@@ -129,7 +129,7 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: ()
             <span id="searchRefresh" aria-label="${window.siyuan.languages.refresh}" class="${closeCB ? "fn__none " : ""}block__icon b3-tooltips b3-tooltips__w">
                 <svg><use xlink:href="#iconRefresh"></use></svg>
             </span>
-            <span id="searchOpen" aria-label="${window.siyuan.languages.stickSearch}" class="${closeCB ? "" : "fn__none "}block__icon b3-tooltips b3-tooltips__w">
+            <span id="searchOpen" aria-label="${window.siyuan.languages.openInNewTab}" class="${closeCB ? "" : "fn__none "}block__icon b3-tooltips b3-tooltips__w">
                 <svg><use xlink:href="#iconLayoutRight"></use></svg>
             </span>
         </div>

+ 1 - 0
app/src/types/index.d.ts

@@ -30,6 +30,7 @@ type TOperation =
     | "addFlashcards"
     | "removeFlashcards"
 type TBazaarType = "templates" | "icons" | "widgets" | "themes"
+type TCardType = "doc" | "notebook" | "all"
 declare module "blueimp-md5"
 
 interface Window {