Vanessa 2025-10-12 20:29:42 +08:00
parent 4aede9687e
commit a1f275e3c3
3 changed files with 24 additions and 29 deletions

View file

@ -7,10 +7,6 @@
border-radius: var(--b3-border-radius); border-radius: var(--b3-border-radius);
display: flex; display: flex;
&__transition {
transition: top .15s cubic-bezier(0, 0, .2, 1) 0ms;
}
&__item { &__item {
color: var(--b3-theme-on-surface); color: var(--b3-theme-on-surface);
border: 0; border: 0;

View file

@ -164,17 +164,15 @@ export class Toolbar {
this.element.classList.add("fn__none"); this.element.classList.add("fn__none");
return; return;
} }
this.element.classList.remove("fn__none", "protyle-toolbar__transition"); const rangePosition = getSelectionPosition(nodeElement, range, true);
this.element.classList.remove("fn__none");
this.toolbarHeight = this.element.clientHeight; this.toolbarHeight = this.element.clientHeight;
const rangePosition = getSelectionPosition(nodeElement, range, this.element.clientWidth); const y = rangePosition.isBottom ?
const y = rangePosition.isToolbarAtBottom ?
Math.min(rangePosition.top + 4, protyle.element.getBoundingClientRect().bottom - this.toolbarHeight) : Math.min(rangePosition.top + 4, protyle.element.getBoundingClientRect().bottom - this.toolbarHeight) :
Math.max(rangePosition.top - this.toolbarHeight - 4, protyle.element.getBoundingClientRect().top + 30); Math.max(rangePosition.top - this.toolbarHeight - 4, protyle.element.getBoundingClientRect().top + 30);
this.element.setAttribute("data-inity", y + Constants.ZWSP + protyle.contentElement.scrollTop.toString()); this.element.setAttribute("data-inity", y + Constants.ZWSP + protyle.contentElement.scrollTop.toString());
setPosition(this.element, rangePosition.left, y); setPosition(this.element, rangePosition.left - this.element.clientWidth / 4, y);
this.element.classList.add("protyle-toolbar__transition");
this.element.querySelectorAll(".protyle-toolbar__item--current").forEach(item => { this.element.querySelectorAll(".protyle-toolbar__item--current").forEach(item => {
item.classList.remove("protyle-toolbar__item--current"); item.classList.remove("protyle-toolbar__item--current");
}); });

View file

@ -194,7 +194,7 @@ export const getEditorRange = (element: Element): Range => {
return range; return range;
}; };
export const getSelectionPosition = (nodeElement: Element, range?: Range, toolbarWidth?: number) => { export const getSelectionPosition = (nodeElement: Element, range?: Range, useDirect = false) => {
if (!range) { if (!range) {
range = getEditorRange(nodeElement); range = getEditorRange(nodeElement);
} }
@ -261,25 +261,26 @@ export const getSelectionPosition = (nodeElement: Element, range?: Range, toolba
} else { } else {
const rects = range.getClientRects(); // 由于长度过长折行,光标在行首时有多个 rects https://github.com/siyuan-note/siyuan/issues/6156 const rects = range.getClientRects(); // 由于长度过长折行,光标在行首时有多个 rects https://github.com/siyuan-note/siyuan/issues/6156
if (range.toString()) { if (range.toString()) {
if (useDirect) {
const selection = window.getSelection(); const selection = window.getSelection();
// 判断选择方向 // 判断选择方向
const isBackward = selection && "direction" in selection ? const isBackward = (selection && "direction" in selection && selection.direction !== "none") ?
(selection as { direction: "forward" | "backward" | "none" }).direction === "backward" selection.direction === "backward"
: range.startContainer === selection?.focusNode && range.startOffset === selection?.focusOffset; : range.startContainer === selection?.focusNode && range.startOffset === selection?.focusOffset;
let isToolbarAtBottom = false; const isBottom = !isBackward && rects[0].top !== rects[rects.length - 1].top;
if (!isBackward) {
// 检查是否有多个垂直位置不同的矩形
isToolbarAtBottom = rects.length > 1 && Array.from(rects).some((rect: DOMRect) => rect.top !== rects[0].top);
}
return { return {
// 向左选择:使用第一个矩形的左边界;向右选择:使用最后一个矩形的右边界 // 向左选择:使用第一个矩形的左边界;向右选择:使用最后一个矩形的右边界
// 减去工具栏宽度的1/4将工具栏中不太常用的按钮往右偏一点 left: isBackward ? rects[0].left : rects[rects.length - 1].right,
left: (isBackward ? rects[0].left : rects[rects.length - 1].right) - (toolbarWidth || 0) / 4,
// 如果向右选择时有多个垂直位置不同的矩形:使用最后一个矩形的下边界;否则使用第一个矩形的上边界 // 如果向右选择时有多个垂直位置不同的矩形:使用最后一个矩形的下边界;否则使用第一个矩形的上边界
top: isToolbarAtBottom ? rects[rects.length - 1].bottom : rects[0].top, top: isBottom ? rects[rects.length - 1].bottom : rects[0].top,
isToolbarAtBottom: isToolbarAtBottom, isBottom
}; };
} else {
return { // 选中多行不应遮挡第一行 https://github.com/siyuan-note/siyuan/issues/7541
left: rects[rects.length - 1].left,
top: rects[0].top
};
}
} else { } else {
return { // 代码块首 https://github.com/siyuan-note/siyuan/issues/13113 return { // 代码块首 https://github.com/siyuan-note/siyuan/issues/13113
left: rects[rects.length - 1].left, left: rects[rects.length - 1].left,