Sfoglia il codice sorgente

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

Vanessa 2 anni fa
parent
commit
165457429b

+ 9 - 1
app/src/assets/scss/_layout.scss

@@ -11,6 +11,13 @@
     }
   }
 
+  &--float {
+    position: fixed;
+    z-index: 2;
+    min-height: auto;
+    transition: left .15s cubic-bezier(0, 0.88, 0.42, 0.74) 0ms, opacity .15s cubic-bezier(0, 0.88, 0.42, 0.74) 0ms;
+  }
+
   &__tab--active {
     .b3-list--background .b3-list-item--focus:not(.dragover):not(.dragover__top):not(.dragover__bottom) {
       background-color: var(--b3-theme-primary-lightest);
@@ -316,7 +323,8 @@
     margin: 5px;
 
     &:hover,
-    &--active {
+    &--active,
+    &--pin {
       background-color: var(--b3-theme-background-light);
     }
 

+ 8 - 2
app/src/assets/scss/base.scss

@@ -338,8 +338,14 @@ progressLoading: 400
     }
   }
 
-  #barDock .b3-menu__item:hover {
-    background-color: var(--b3-list-hover);
+  #barDock {
+    &:hover .b3-menu {
+      display: block !important;
+    }
+
+    .b3-menu__item:hover {
+      background-color: var(--b3-list-hover);
+    }
   }
 
   .fn__space:last-child {

+ 1 - 1
app/src/block/popover.ts

@@ -100,7 +100,7 @@ const hidePopover = (event: MouseEvent & { target: HTMLElement, path: HTMLElemen
         // 移动到弹窗的 loading 元素上,但经过 settimeout 后 loading 已经被移除了
         // https://ld246.com/article/1673596577519/comment/1673767749885#comments
         let targetElement = event.target;
-        if (!targetElement.parentElement && event.path[1]) {
+        if (!targetElement.parentElement && event.path && event.path[1]) {
             targetElement = event.path[1];
         }
         const blockElement = hasClosestByClassName(targetElement, "block__popover", true);

+ 68 - 56
app/src/constants.ts

@@ -315,62 +315,74 @@ export abstract class Constants {
                 }]
             }]
         },
-        top: [],
-        bottom: [],
-        left: [
-            [{
-                type: "file",
-                size: {width: 240, height: 0},
-                show: true,
-                icon: "iconFiles",
-                hotkeyLangId: "fileTree",
-            }, {
-                type: "outline",
-                size: {width: 240, height: 0},
-                show: false,
-                icon: "iconAlignCenter",
-                hotkeyLangId: "outline",
-            }, {
-                type: "inbox",
-                size: {width: 252, height: 0},
-                show: false,
-                icon: "iconInbox",
-                hotkeyLangId: "inbox",
-            }], [{
-                type: "bookmark",
-                size: {width: 240, height: 0},
-                show: false,
-                icon: "iconBookmark",
-                hotkeyLangId: "bookmark",
-            }, {
-                type: "tag",
-                size: {width: 240, height: 0},
-                show: false,
-                icon: "iconTags",
-                hotkeyLangId: "tag",
-            }]
-        ],
-        right: [
-            [{
-                type: "graph",
-                size: {width: 360, height: 0},
-                show: false,
-                icon: "iconGraph",
-                hotkeyLangId: "graphView",
-            }, {
-                type: "globalGraph",
-                size: {width: 360, height: 0},
-                show: false,
-                icon: "iconGlobalGraph",
-                hotkeyLangId: "globalGraph",
-            }], [{
-                type: "backlink",
-                size: {width: 360, height: 0},
-                show: false,
-                icon: "iconLink",
-                hotkeyLangId: "backlinks",
-            }]
-        ]
+        top: {
+            pin: true,
+            data: []
+        },
+        bottom: {
+            pin: true,
+            data: []
+        },
+        left: {
+            pin: true,
+            data: [
+                [{
+                    type: "file",
+                    size: {width: 240, height: 0},
+                    show: true,
+                    icon: "iconFiles",
+                    hotkeyLangId: "fileTree",
+                }, {
+                    type: "outline",
+                    size: {width: 240, height: 0},
+                    show: false,
+                    icon: "iconAlignCenter",
+                    hotkeyLangId: "outline",
+                }, {
+                    type: "inbox",
+                    size: {width: 252, height: 0},
+                    show: false,
+                    icon: "iconInbox",
+                    hotkeyLangId: "inbox",
+                }], [{
+                    type: "bookmark",
+                    size: {width: 240, height: 0},
+                    show: false,
+                    icon: "iconBookmark",
+                    hotkeyLangId: "bookmark",
+                }, {
+                    type: "tag",
+                    size: {width: 240, height: 0},
+                    show: false,
+                    icon: "iconTags",
+                    hotkeyLangId: "tag",
+                }]
+            ]
+        },
+        right: {
+            pin: true,
+            data: [
+                [{
+                    type: "graph",
+                    size: {width: 360, height: 0},
+                    show: false,
+                    icon: "iconGraph",
+                    hotkeyLangId: "graphView",
+                }, {
+                    type: "globalGraph",
+                    size: {width: 360, height: 0},
+                    show: false,
+                    icon: "iconGlobalGraph",
+                    hotkeyLangId: "globalGraph",
+                }], [{
+                    type: "backlink",
+                    size: {width: 360, height: 0},
+                    show: false,
+                    icon: "iconLink",
+                    hotkeyLangId: "backlinks",
+                }]
+            ]
+        }
     };
 
     // image

+ 48 - 8
app/src/layout/dock/index.ts

@@ -19,9 +19,10 @@ export class Dock {
     public layout: Layout;
     private position: TDockPosition;
     public resizeElement: HTMLElement;
+    public pin = true;
     public data: { [key: string]: Model | boolean };
 
-    constructor(options: { data: IDockTab[][], position: TDockPosition }) {
+    constructor(options: { data: { pin: boolean, data: IDockTab[][] }, position: TDockPosition }) {
         switch (options.position) {
             case "Left":
                 this.layout = window.siyuan.layout.layout.children[1].children[0] as Layout;
@@ -44,16 +45,16 @@ export class Dock {
         const dockClass = (options.position === "Bottom" || options.position === "Top") ? ' class="fn__flex"' : "";
         this.element.innerHTML = `<div${dockClass}></div><div class="fn__flex-1"></div><div${dockClass}></div>`;
         this.position = options.position;
+        this.pin = options.data.pin
         this.data = {};
-        if (options.data.length === 0) {
+        if (options.data.data.length === 0) {
             this.element.classList.add("fn__none");
         } else {
-            this.genButton(options.data[0], 0);
-            if (options.data[1]) {
-                this.genButton(options.data[1], 1);
+            this.genButton(options.data.data[0], 0);
+            if (options.data.data[1]) {
+                this.genButton(options.data.data[1], 1);
             }
         }
-
         const activeElements = this.element.querySelectorAll(".dock__item--active");
 
         // 初始化文件树
@@ -71,7 +72,6 @@ export class Dock {
                 this.toggleModel(item.getAttribute("data-type") as TDockType, true);
             });
         }
-
         this.element.addEventListener("click", (event) => {
             let target = event.target as HTMLElement;
             while (target && !target.isEqualNode(this.element)) {
@@ -80,16 +80,54 @@ export class Dock {
                     this.toggleModel(type, false, true);
                     event.preventDefault();
                     break;
+                } else if (target.classList.contains("dock__item")) {
+                    this.pin = !target.classList.contains("dock__item--pin");
+                    if (!this.pin) {
+                        const layoutRect = this.layout.element.getBoundingClientRect();
+                        this.layout.element.setAttribute("style", `left:${layoutRect.left}px; top: ${layoutRect.top}px; bottom: ${window.innerHeight - layoutRect.bottom}px; width: ${layoutRect.width}px;`);
+                    }
+                    target.classList.toggle("dock__item--pin");
+                    this.layout.element.classList.toggle("layout--float");
+                    this.resizeElement.classList.toggle("fn__none");
+                    event.preventDefault();
+                    break;
                 }
                 target = target.parentElement;
             }
         });
+        this.layout.element.addEventListener("mouseleave", (event) => {
+            if (this.pin) {
+                return
+            }
+            if (this.position === "Left" && event.clientX < 43) {
+                return;
+            }
+            this.hideDock()
+        })
         if (window.siyuan.config.uiLayout.hideDock) {
             this.element.classList.add("fn__none");
         }
+        if (!this.pin) {
+            setTimeout(() => {
+                const layoutRect = this.layout.element.getBoundingClientRect();
+                this.layout.element.setAttribute("style", `width:${layoutRect.width}px;opacity:0px;left:-${layoutRect.width}px; top: ${layoutRect.top}px; bottom: ${window.innerHeight - layoutRect.bottom}px;`);
+                this.layout.element.classList.add("layout--float");
+                this.resizeElement.classList.add("fn__none");
+            }, 1000);
+        }
+    }
+
+    public hideDock() {
+        if (this.layout.element.style.opacity === "1") {
+            this.layout.element.style.left = -this.layout.element.clientWidth + "px";
+            this.layout.element.style.opacity = "0";
+        }
     }
 
     public toggleModel(type: TDockType, show = false, close = false) {
+        if (!type) {
+            return;
+        }
         const target = this.element.querySelector(`[data-type="${type}"]`) as HTMLElement;
         if (show && target.classList.contains("dock__item--active")) {
             target.classList.remove("dock__item--active", "dock__item--activefocus");
@@ -395,7 +433,9 @@ export class Dock {
     }
 
     private genButton(data: IDockTab[], index: number) {
-        let html = "";
+        let html = index ? "" : `<span class="dock__item ${this.pin ? "dock__item--pin " : ""}b3-tooltips b3-tooltips__${this.getClassDirect(index)}" aria-label="${window.siyuan.languages.pin}">
+    <svg><use xlink:href="#iconPin"></use></svg>
+</span>`;
         data.forEach(item => {
             html += `<span data-height="${item.size.height}" data-width="${item.size.width}" data-type="${item.type}" data-index="${index}" data-hotkeylangid="${item.hotkeyLangId}" class="dock__item${item.show ? " dock__item--active" : ""} b3-tooltips b3-tooltips__${this.getClassDirect(index)}" aria-label="${window.siyuan.languages[item.hotkeyLangId] + " " + updateHotkeyTip(window.siyuan.config.keymap.general[item.hotkeyLangId].custom)}${window.siyuan.languages.dockTip}">
     <svg><use xlink:href="#${item.icon}"></use></svg>

+ 4 - 4
app/src/layout/getAll.ts

@@ -67,22 +67,22 @@ export const getAllTabs = () => {
 
 export const getAllDocks = () => {
     const docks: IDockTab[] = [];
-    window.siyuan.config.uiLayout.left.forEach((item: IDockTab[]) => {
+    window.siyuan.config.uiLayout.left.data.forEach((item: IDockTab[]) => {
         item.forEach((dock: IDockTab) => {
             docks.push(dock);
         });
     });
-    window.siyuan.config.uiLayout.right.forEach((item: IDockTab[]) => {
+    window.siyuan.config.uiLayout.right.data.forEach((item: IDockTab[]) => {
         item.forEach((dock: IDockTab) => {
             docks.push(dock);
         });
     });
-    window.siyuan.config.uiLayout.top.forEach((item: IDockTab[]) => {
+    window.siyuan.config.uiLayout.top.data.forEach((item: IDockTab[]) => {
         item.forEach((dock: IDockTab) => {
             docks.push(dock);
         });
     });
-    window.siyuan.config.uiLayout.bottom.forEach((item: IDockTab[]) => {
+    window.siyuan.config.uiLayout.bottom.data.forEach((item: IDockTab[]) => {
         item.forEach((dock: IDockTab) => {
             docks.push(dock);
         });

+ 0 - 9
app/src/layout/status.ts

@@ -38,15 +38,6 @@ export const initStatus = (isWindow = false) => {
 <div id="statusHelp" class="toolbar__item b3-tooltips b3-tooltips__w" aria-label="${window.siyuan.languages.help}">
     <svg><use xlink:href="#iconHelp"></use></svg>
 </div>`;
-    if (!isWindow) {
-        const dockElement = document.getElementById("barDock");
-        dockElement.addEventListener("mousemove", () => {
-            dockElement.querySelector(".b3-menu").classList.remove("fn__none");
-        });
-        dockElement.addEventListener("mouseleave", () => {
-            dockElement.querySelector(".b3-menu").classList.add("fn__none");
-        });
-    }
 
     document.querySelector("#status").addEventListener("click", (event) => {
         let target = event.target as HTMLElement;

+ 45 - 5
app/src/layout/util.ts

@@ -135,7 +135,10 @@ const dockToJSON = (dock: Dock) => {
     if (data2.length > 0) {
         json.push(data2);
     }
-    return json;
+    return {
+        pin: dock.pin,
+        data: json
+    }
 };
 
 export const resetLayout = () => {
@@ -169,10 +172,47 @@ export const exportLayout = (reload: boolean, cb?: () => void) => {
 
 const JSONToDock = (json: any) => {
     window.siyuan.layout.centerLayout = window.siyuan.layout.layout.children[1].children[1] as Layout;
-    window.siyuan.layout.topDock = new Dock({position: "Top", data: json.top});
-    window.siyuan.layout.leftDock = new Dock({position: "Left", data: json.left});
-    window.siyuan.layout.rightDock = new Dock({position: "Right", data: json.right});
-    window.siyuan.layout.bottomDock = new Dock({position: "Bottom", data: json.bottom});
+    // 历史数据兼容,202306后可删除
+    let topData: { pin: boolean, data: IDockTab[][] }
+    if (!json.top.data) {
+        topData = {
+            pin: true,
+            data: json.top
+        }
+    } else {
+        topData = json.top;
+    }
+    let bottomData: { pin: boolean, data: IDockTab[][] }
+    if (!json.bottom.data) {
+        bottomData = {
+            pin: true,
+            data: json.bottom
+        }
+    } else {
+        bottomData = json.bottom;
+    }
+    let rightData: { pin: boolean, data: IDockTab[][] }
+    if (!json.right.data) {
+        rightData = {
+            pin: true,
+            data: json.right
+        }
+    } else {
+        rightData = json.right;
+    }
+    let leftData: { pin: boolean, data: IDockTab[][] }
+    if (!json.left.data) {
+        leftData = {
+            pin: true,
+            data: json.left
+        }
+    } else {
+        leftData = json.left;
+    }
+    window.siyuan.layout.topDock = new Dock({position: "Top", data: topData});
+    window.siyuan.layout.leftDock = new Dock({position: "Left", data: leftData});
+    window.siyuan.layout.rightDock = new Dock({position: "Right", data: rightData});
+    window.siyuan.layout.bottomDock = new Dock({position: "Bottom", data: bottomData});
 };
 
 export const JSONToCenter = (json: any, layout?: Layout | Wnd | Tab | Model, isStart = false) => {

+ 17 - 0
app/src/util/globalShortcut.ts

@@ -96,6 +96,23 @@ export const globalShortcut = () => {
             window.siyuan.hideBreadcrumb = false;
         }
 
+        if (event.clientX < 43) {
+            if (!window.siyuan.layout.leftDock.pin) {
+                if (event.clientY > 32 &&
+                    (window.siyuan.config.appearance.hideStatusBar ||
+                        (!window.siyuan.config.appearance.hideStatusBar && event.clientY < window.innerHeight - 32))
+                ) {
+                    if (!hasClosestByClassName(event.target as HTMLElement, "b3-menu") &&
+                        window.siyuan.layout.leftDock.layout.element.style.opacity !== "1") {
+                        window.siyuan.layout.leftDock.layout.element.style.left = (window.siyuan.layout.leftDock.element.clientWidth + .5) + "px"
+                        window.siyuan.layout.leftDock.layout.element.style.opacity = "1"
+                    }
+                } else {
+                    window.siyuan.layout.leftDock.hideDock();
+                }
+            }
+        }
+
         const eventPath0 = event.composedPath()[0] as HTMLElement;
         if (eventPath0 && eventPath0.nodeType !== 3 && eventPath0.classList.contains("protyle-wysiwyg") && eventPath0.style.paddingLeft) {
             // 光标在编辑器右边也需要进行显示