diff --git a/app/src/assets/scss/_layout.scss b/app/src/assets/scss/_layout.scss index ae21a0717..0233c37da 100644 --- a/app/src/assets/scss/_layout.scss +++ b/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); } diff --git a/app/src/assets/scss/base.scss b/app/src/assets/scss/base.scss index a5ea15892..148c03204 100644 --- a/app/src/assets/scss/base.scss +++ b/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 { diff --git a/app/src/block/popover.ts b/app/src/block/popover.ts index f8372d4bc..020867adf 100644 --- a/app/src/block/popover.ts +++ b/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); diff --git a/app/src/constants.ts b/app/src/constants.ts index c7e49859a..e44cbc7d4 100644 --- a/app/src/constants.ts +++ b/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 diff --git a/app/src/layout/dock/index.ts b/app/src/layout/dock/index.ts index bcb3f00aa..94f56398d 100644 --- a/app/src/layout/dock/index.ts +++ b/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 = `
`; 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 ? "" : ` + +`; data.forEach(item => { html += ` diff --git a/app/src/layout/getAll.ts b/app/src/layout/getAll.ts index f83788424..0dd35ebe1 100644 --- a/app/src/layout/getAll.ts +++ b/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); }); diff --git a/app/src/layout/status.ts b/app/src/layout/status.ts index b9aa62ad2..73f979156 100644 --- a/app/src/layout/status.ts +++ b/app/src/layout/status.ts @@ -38,15 +38,6 @@ export const initStatus = (isWindow = false) => { `; - 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; diff --git a/app/src/layout/util.ts b/app/src/layout/util.ts index f46ddf1ff..e5347ffa6 100644 --- a/app/src/layout/util.ts +++ b/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) => { diff --git a/app/src/util/globalShortcut.ts b/app/src/util/globalShortcut.ts index c327f4fa9..1afe2fda5 100644 --- a/app/src/util/globalShortcut.ts +++ b/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) { // 光标在编辑器右边也需要进行显示