diff --git a/app/src/assets/scss/base.scss b/app/src/assets/scss/base.scss index c9c1da565..1999c379d 100644 --- a/app/src/assets/scss/base.scss +++ b/app/src/assets/scss/base.scss @@ -101,7 +101,7 @@ html { } .protyle-breadcrumb > .protyle-breadcrumb__space, - & > .block__icons > .fn__flex-1{ + & > .block__icons > .fn__flex-1 { -webkit-app-region: drag; min-width: 32px; @@ -162,6 +162,8 @@ html { white-space: nowrap; align-self: center; font-size: 12px; + min-width: 148px; + box-sizing: border-box; } &__item { diff --git a/app/src/boot/onGetConfig.ts b/app/src/boot/onGetConfig.ts index 90f979c23..c5910a5d7 100644 --- a/app/src/boot/onGetConfig.ts +++ b/app/src/boot/onGetConfig.ts @@ -1,4 +1,4 @@ -import {exportLayout, JSONToLayout, resetLayout, resizeDrag, resizeTabs} from "../layout/util"; +import {exportLayout, JSONToLayout, resetLayout, resizeTopbar, resizeTabs} from "../layout/util"; import {hotKey2Electron, setStorageVal} from "../protyle/util/compatibility"; /// #if !BROWSER import {dialog, getCurrentWindow} from "@electron/remote"; @@ -154,7 +154,7 @@ export const onGetConfig = (isStart: boolean, app: App) => { window.clearTimeout(resizeTimeout); resizeTimeout = window.setTimeout(() => { resizeTabs(); - resizeDrag(); + resizeTopbar(); }, 200); }); addGA(); diff --git a/app/src/index.ts b/app/src/index.ts index 92b580d7c..aad028677 100644 --- a/app/src/index.ts +++ b/app/src/index.ts @@ -23,7 +23,6 @@ import { } from "./dialog/processSystem"; import {promiseTransactions} from "./protyle/wysiwyg/transaction"; import {initMessage} from "./dialog/message"; -import {resizeDrag} from "./layout/util"; import {getAllTabs} from "./layout/getAll"; import {getLocalStorage} from "./protyle/util/compatibility"; import {updateEditModeElement} from "./layout/topBar"; @@ -174,7 +173,6 @@ export class App { window.siyuan.user = userResponse.data; onGetConfig(response.data.start, this); account.onSetaccount(); - resizeDrag(); setTitle(window.siyuan.languages.siyuanNote); initMessage(); }); diff --git a/app/src/layout/topBar.ts b/app/src/layout/topBar.ts index c5ebf46ad..e5be2725d 100644 --- a/app/src/layout/topBar.ts +++ b/app/src/layout/topBar.ts @@ -59,32 +59,70 @@ export const initBar = (app: App) => {
+ `; processSync(); toolbarElement.addEventListener("click", (event: MouseEvent) => { let target = event.target as HTMLElement; + if (typeof event.detail === "string") { + target = toolbarElement.querySelector("#" + event.detail); + } while (!target.classList.contains("toolbar")) { - if (target.id === "barBack") { + const targetId = typeof event.detail === "string" ? event.detail : target.id; + if (targetId === "barBack") { goBack(app); event.stopPropagation(); break; - } else if (target.id === "barForward") { + } else if (targetId === "barMore") { + if (!window.siyuan.menus.menu.element.classList.contains("fn__none") && + window.siyuan.menus.menu.element.getAttribute("data-name") === "barmore") { + window.siyuan.menus.menu.remove(); + return; + } + window.siyuan.menus.menu.remove(); + window.siyuan.menus.menu.element.setAttribute("data-name", "barmore"); + (target.getAttribute("data-hideids") || "").split(",").forEach((itemId) => { + const hideElement = toolbarElement.querySelector("#" + itemId); + window.siyuan.menus.menu.append(new MenuItem({ + label: itemId === "toolbarVIP" ? window.siyuan.languages.account : hideElement.getAttribute("aria-label"), + icon: itemId === "toolbarVIP" ? "iconAccount" : hideElement.querySelector("use").getAttribute("xlink:href").substring(1), + click: () => { + if (itemId.startsWith("plugin")) { + hideElement.dispatchEvent(new CustomEvent("click")); + } else { + toolbarElement.dispatchEvent(new CustomEvent("click", {detail: itemId})); + } + if (!window.siyuan.menus.menu.element.classList.contains("fn__none") && + window.siyuan.menus.menu.element.getAttribute("data-name") === "barmore") { + return false; + } + return true; + } + }).element); + }); + const rect = target.getBoundingClientRect(); + window.siyuan.menus.menu.popup({x: rect.right, y: rect.bottom}, true); + event.stopPropagation(); + break; + } else if (targetId === "barForward") { goForward(app); event.stopPropagation(); break; - } else if (target.id === "barSync") { + } else if (targetId === "barSync") { syncGuide(app); event.stopPropagation(); break; - } else if (target.id === "barWorkspace") { + } else if (targetId === "barWorkspace") { workspaceMenu(app, target.getBoundingClientRect()); event.stopPropagation(); break; - } else if (target.id === "barReadonly") { + } else if (targetId === "barReadonly") { editor.setReadonly(); event.stopPropagation(); break; - } else if (target.id === "barMode") { + } else if (targetId === "barMode") { if (!window.siyuan.menus.menu.element.classList.contains("fn__none") && window.siyuan.menus.menu.element.getAttribute("data-name") === "barmode") { window.siyuan.menus.menu.remove(); @@ -116,25 +154,28 @@ export const initBar = (app: App) => { setMode(2); } }).element); - const rect = target.getBoundingClientRect(); + let rect = target.getBoundingClientRect(); + if (rect.width === 0) { + rect = toolbarElement.querySelector("#barMore").getBoundingClientRect(); + } window.siyuan.menus.menu.popup({x: rect.right, y: rect.bottom}, true); event.stopPropagation(); break; - } else if (target.id === "toolbarVIP") { + } else if (targetId === "toolbarVIP") { if (!window.siyuan.config.readonly) { const dialogSetting = openSetting(app); dialogSetting.element.querySelector('.b3-tab-bar [data-name="account"]').dispatchEvent(new CustomEvent("click")); } event.stopPropagation(); break; - } else if (target.id === "barSearch") { + } else if (targetId === "barSearch") { openSearch({ app, hotkey: window.siyuan.config.keymap.general.globalSearch.custom }); event.stopPropagation(); break; - } else if (target.id === "barZoom") { + } else if (targetId === "barZoom") { if (!window.siyuan.menus.menu.element.classList.contains("fn__none") && window.siyuan.menus.menu.element.getAttribute("data-name") === "barZoom") { window.siyuan.menus.menu.remove(); @@ -165,7 +206,10 @@ export const initBar = (app: App) => { setZoom("restore"); } }).element); - const rect = target.getBoundingClientRect(); + let rect = target.getBoundingClientRect(); + if (rect.width === 0) { + rect = toolbarElement.querySelector("#barMore").getBoundingClientRect(); + } window.siyuan.menus.menu.popup({x: rect.right, y: rect.bottom}, true); event.stopPropagation(); break; diff --git a/app/src/layout/util.ts b/app/src/layout/util.ts index bbf825312..0e758cae3 100644 --- a/app/src/layout/util.ts +++ b/app/src/layout/util.ts @@ -444,6 +444,7 @@ export const JSONToLayout = (app: App, isStart: boolean) => { tab.parent.switchTab(item, false, false); }); } + resizeTopbar(); }; export const layoutToJSON = (layout: Layout | Wnd | Tab | Model, json: any, dropEditScroll = false) => { @@ -578,18 +579,57 @@ export const layoutToJSON = (layout: Layout | Wnd | Tab | Model, json: any, drop } }; -export const resizeDrag = () => { - const dragElement = document.getElementById("drag"); +export const resizeTopbar = () => { + const toolbarElement = document.querySelector("#toolbar"); + const dragElement = toolbarElement.querySelector("#drag") as HTMLElement; + + dragElement.style.padding = ""; + const barMoreElement = toolbarElement.querySelector("#barMore") + barMoreElement.classList.remove("fn__none") + barMoreElement.removeAttribute("data-hideids") + + Array.from(toolbarElement.querySelectorAll('[data-hide="true"]')).forEach((item) => { + item.classList.remove("fn__none") + item.removeAttribute("data-hide"); + }) + + let afterDragElement = dragElement.nextElementSibling + const hideIds: string[] = [] + while (toolbarElement.scrollWidth > toolbarElement.clientWidth + 2) { + hideIds.push(afterDragElement.id) + afterDragElement.classList.add("fn__none") + afterDragElement.setAttribute("data-hide", "true") + afterDragElement = afterDragElement.nextElementSibling + if (afterDragElement.id === "barMore") { + break; + } + } + + let beforeDragElement = dragElement.previousElementSibling + while (toolbarElement.scrollWidth > toolbarElement.clientWidth + 2) { + hideIds.push(beforeDragElement.id) + beforeDragElement.classList.add("fn__none") + beforeDragElement.setAttribute("data-hide", "true") + beforeDragElement = beforeDragElement.previousElementSibling + if (beforeDragElement.id === "barWorkspace") { + break; + } + } + if (hideIds.length > 0) { + barMoreElement.classList.remove("fn__none") + } else { + barMoreElement.classList.add("fn__none") + } + barMoreElement.setAttribute("data-hideids", hideIds.join(",")) + const width = dragElement.clientWidth; const dragRect = dragElement.getBoundingClientRect(); const left = dragRect.left; const right = window.innerWidth - dragRect.right; - if (left > right && left - right < width) { + if (left > right && left - right < width / 3) { dragElement.style.paddingRight = (left - right) + "px"; - } else if (left < right && right - left < width) { + } else if (left < right && right - left < width / 3) { dragElement.style.paddingLeft = (right - left) + "px"; - } else { - dragElement.style.padding = ""; } }; diff --git a/app/src/menus/Menu.ts b/app/src/menus/Menu.ts index 6b40bb8c3..e744835c5 100644 --- a/app/src/menus/Menu.ts +++ b/app/src/menus/Menu.ts @@ -179,11 +179,13 @@ export class MenuItem { if (this.element.getAttribute("disabled")) { return; } - options.click(this.element); + const result = options.click(this.element); event.preventDefault(); event.stopImmediatePropagation(); event.stopPropagation(); - window.siyuan.menus.menu.remove(); + if (typeof result === "undefined" || !result) { + window.siyuan.menus.menu.remove(); + } }); } if (options.id) { diff --git a/app/src/plugin/index.ts b/app/src/plugin/index.ts index 0c8cc257e..a7122c9d6 100644 --- a/app/src/plugin/index.ts +++ b/app/src/plugin/index.ts @@ -9,6 +9,7 @@ import {Tab} from "../layout/Tab"; import {getDockByType, setPanelFocus} from "../layout/util"; import {hasClosestByAttribute} from "../protyle/util/hasClosest"; import {BlockPanel} from "../block/Panel"; +import {genUUID} from "../util/genID"; export class Plugin { private app: App; @@ -66,16 +67,16 @@ export class Plugin { callback: (evt: MouseEvent) => void }) { const iconElement = document.createElement("div"); + iconElement.setAttribute("data-menu", "true"); + iconElement.addEventListener("click", options.callback); + iconElement.id = "plugin" + genUUID(); if (isMobile()) { iconElement.className = "b3-menu__item"; - iconElement.setAttribute("data-menu", "true"); iconElement.innerHTML = (options.icon.startsWith("icon") ? `` : options.icon) + ``; - iconElement.addEventListener("click", options.callback); } else if (!isWindow()) { iconElement.className = "toolbar__item b3-tooltips b3-tooltips__sw"; iconElement.setAttribute("aria-label", options.title); - iconElement.setAttribute("data-menu", "true"); iconElement.innerHTML = options.icon.startsWith("icon") ? `` : options.icon; iconElement.addEventListener("click", options.callback); iconElement.setAttribute("data-position", options.position); diff --git a/app/src/types/index.d.ts b/app/src/types/index.d.ts index c70777121..14a358394 100644 --- a/app/src/types/index.d.ts +++ b/app/src/types/index.d.ts @@ -723,7 +723,7 @@ declare interface IModels { declare interface IMenu { label?: string, - click?: (element: HTMLElement) => void, + click?: (element: HTMLElement) => boolean | void, type?: "separator" | "submenu" | "readonly", accelerator?: string, action?: string,