Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions app/src/assets/scss/component/_menu.scss
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@
}

&__items {
max-height: 80vh;
overflow: auto;
padding: 0 8px;
}
Expand Down Expand Up @@ -367,7 +366,6 @@
&__submenu {
overflow: auto;
display: none;
max-height: 80vh;
border: 1px solid var(--b3-theme-surface-lighter);
border-radius: var(--b3-border-radius-b);
background-color: var(--b3-menu-background);
Expand Down
1 change: 1 addition & 0 deletions app/src/boot/onGetConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const onGetConfig = (isStart: boolean, app: App) => {
adjustLayout();
resizeTabs();
resizeTopBar();
window.siyuan.menus.menu.resetPosition();
firstResize = true;
}, 200);
});
Expand Down
62 changes: 50 additions & 12 deletions app/src/menus/Menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,47 @@ export class Menu {
});
}

public showSubMenu(subMenuElement: HTMLElement) {
public showSubMenu(subMenuElement: HTMLElement | null) {
if (!subMenuElement) {
return;
}
const itemsMenuElement = subMenuElement.lastElementChild as HTMLElement;
if (itemsMenuElement) {
itemsMenuElement.style.maxHeight = "";
}
const itemRect = subMenuElement.parentElement.getBoundingClientRect();
subMenuElement.style.top = (itemRect.top - 8) + "px";
subMenuElement.style.left = (itemRect.right + 8) + "px";
subMenuElement.style.bottom = "auto";
const rect = subMenuElement.getBoundingClientRect();
if (rect.right > window.innerWidth) {
if (itemRect.left - 8 > rect.width) {
subMenuElement.style.left = (itemRect.left - 8 - rect.width) + "px";
const subMenuRect = subMenuElement.getBoundingClientRect();

// 垂直方向位置调整
// 减 9px 是为了尽量对齐菜单选项(b3-menu__submenu 的默认 padding-top 加上子菜单首个 b3-menu__item 的默认 margin-top)
// 减 1px 是为了避免在特定情况下渲染出不应存在的滚动条而做的兼容处理
const top = Math.min(itemRect.top - 9, window.innerHeight - subMenuRect.height - 1);
subMenuElement.style.top = Math.max(Constants.SIZE_TOOLBAR_HEIGHT, top) + "px";

// 水平方向位置调整
if (subMenuRect.right <= window.innerWidth) {
// 8px 是 b3-menu__items 的默认 padding-right
subMenuElement.style.left = (itemRect.right + 8) + "px";
} else {
if (itemRect.left - 8 > subMenuRect.width) {
subMenuElement.style.left = (itemRect.left - 8 - subMenuRect.width) + "px";
} else {
subMenuElement.style.left = (window.innerWidth - rect.width) + "px";
subMenuElement.style.left = (window.innerWidth - subMenuRect.width) + "px";
}
}
if (rect.bottom > window.innerHeight) {
subMenuElement.style.top = "auto";
subMenuElement.style.bottom = "8px";

this.updateMaxHeight(subMenuElement, itemsMenuElement);
}

private updateMaxHeight(menuElement: HTMLElement, itemsMenuElement: HTMLElement) {
if (!menuElement || !itemsMenuElement) {
return;
}
const menuRect = menuElement.getBoundingClientRect();
const itemsMenuRect = itemsMenuElement.getBoundingClientRect();
// 加 1px 是为了避免在特定情况下渲染出不应存在的滚动条而做的兼容处理
const availableHeight = (window.innerHeight - menuRect.top) - (menuRect.height - itemsMenuRect.height) + 1;
itemsMenuElement.style.maxHeight = Math.max(availableHeight, 0) + "px";
}

private preventDefault(event: KeyboardEvent) {
Expand Down Expand Up @@ -146,6 +170,20 @@ export class Menu {
this.element.style.zIndex = (++window.siyuan.zIndex).toString();
this.element.classList.remove("fn__none");
setPosition(this.element, options.x - (options.isLeft ? this.element.clientWidth : 0), options.y, options.h, options.w);
this.updateMaxHeight(this.element, this.element.lastElementChild as HTMLElement);
}

public resetPosition() {
if (this.element.classList.contains("fn__none")) {
return;
}
setPosition(this.element, parseFloat(this.element.style.left), parseFloat(this.element.style.top), 0, 0); // 如果不存在 left 或 top,则得到 NaN
this.updateMaxHeight(this.element, this.element.lastElementChild as HTMLElement);
const subMenuElements = this.element.querySelectorAll(".b3-menu__item--show .b3-menu__submenu") as NodeListOf<HTMLElement>;
subMenuElements.forEach((subMenuElement) => {
// 可能有多层子菜单,都要重新定位
this.showSubMenu(subMenuElement);
});
}

public fullscreen(position: "bottom" | "all" = "all") {
Expand Down
51 changes: 31 additions & 20 deletions app/src/util/setPosition.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
import {Constants} from "../constants";

export const setPosition = (element: HTMLElement, x: number, y: number, targetHeight = 0, targetLeft = 0) => {
element.style.top = y + "px";
element.style.left = x + "px";
export const setPosition = (element: HTMLElement, left: number, top: number, targetHeight = 0, targetLeft = 0) => {
const isTopValid = !isNaN(top); // 存在 top 时调整垂直方向位置
const isLeftValid = !isNaN(left); // 存在 left 时调整水平方向位置
if (isTopValid) {
element.style.top = top + "px";
}
if (isLeftValid) {
element.style.left = left + "px";
}
const rect = element.getBoundingClientRect();
// 上下超出屏幕
if (rect.bottom > window.innerHeight || rect.top < Constants.SIZE_TOOLBAR_HEIGHT) {
const top = y - rect.height - targetHeight;
if (top > Constants.SIZE_TOOLBAR_HEIGHT && (top + rect.height) < window.innerHeight) {
// 上部
element.style.top = top + "px";
} else if (top <= Constants.SIZE_TOOLBAR_HEIGHT) {
// 位置超越到屏幕上方外时,需移动到屏幕顶部。eg:光标在第一个块,然后滚动到上方看不见的位置,按 ctrl+a

if (isTopValid) {
if (rect.top < Constants.SIZE_TOOLBAR_HEIGHT) {
// 如果元素接触顶栏,向下移
element.style.top = Constants.SIZE_TOOLBAR_HEIGHT + "px";
} else {
// 依旧展现在下部,只是位置上移
element.style.top = Math.max(Constants.SIZE_TOOLBAR_HEIGHT, window.innerHeight - rect.height) + "px";
} else if (rect.bottom > window.innerHeight) {
// 如果元素底部超出窗口(下方空间不够),向上移
if (top - Constants.SIZE_TOOLBAR_HEIGHT >= rect.height) {
// 如果上方空间足够,向上移
element.style.top = (top - rect.height - targetHeight) + "px";
} else {
// 如果上下空间都不够,向上移,但尽量靠底部
element.style.top = Math.max(Constants.SIZE_TOOLBAR_HEIGHT, window.innerHeight - rect.height) + "px";
}
}
}
if (rect.right > window.innerWidth) {
// 展现在左侧
element.style.left = `${window.innerWidth - rect.width - targetLeft}px`;
} else if (rect.left < 0) {
// 依旧展现在左侧,只是位置右移
element.style.left = "0";

if (isLeftValid) {
if (rect.right > window.innerWidth) {
// 展现在左侧
element.style.left = window.innerWidth - rect.width - targetLeft + "px";
} else if (rect.left < 0) {
// 依旧展现在左侧,只是位置右移
element.style.left = "0";
}
}
};
1 change: 1 addition & 0 deletions app/src/window/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const init = (app: App) => {
resizeTimeout = window.setTimeout(() => {
adjustLayout(window.siyuan.layout.centerLayout);
resizeTabs();
window.siyuan.menus.menu.resetPosition();
}, 200);
});
};
Expand Down