Skip to content

Commit fc0953f

Browse files
committed
🎨 Improve tab title editing width, borders and gutter alignment #17642
1 parent 9e875a1 commit fc0953f

3 files changed

Lines changed: 31 additions & 3 deletions

File tree

app/src/assets/scss/protyle/_tabs.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
padding: 0;
55
margin: 4px 0;
66
position: relative;
7+
border: 1px solid var(--b3-border-color);
8+
border-radius: var(--b3-border-radius-b);
9+
10+
&.protyle-wysiwyg--select::after,
11+
&.protyle-wysiwyg--select-mode::after,
12+
&.protyle-wysiwyg--hl::after {
13+
border-radius: inherit;
14+
}
715
}
816

917
.tabs-header {
@@ -112,6 +120,12 @@
112120
visibility: hidden;
113121
}
114122

123+
.tabs[data-tabs-orientation="horizontal"] > .tabs-header > .tabs-list > .tabs-tab--editing {
124+
width: 186px;
125+
max-width: 100%;
126+
justify-content: flex-start;
127+
}
128+
115129
.tabs-title-editor {
116130
position: absolute;
117131
z-index: 1;

app/src/protyle/gutter/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3882,7 +3882,8 @@ data-type="fold"${viewOccurrenceID ? ` data-view-occurrence-id="${encodeURICompo
38823882
rect = nodeElement.getBoundingClientRect();
38833883
space = 0;
38843884
}
3885-
let horizontalAnchorLeft = rect.left;
3885+
// 页签栏只用于垂直定位;水平方向和其他块一样以整个块的外边界为基准。
3886+
let horizontalAnchorLeft = tabsHeader ? tabsHeader.parentElement.getBoundingClientRect().left : rect.left;
38863887
if (listItem && element.classList.contains("protyle-action") &&
38873888
element.parentElement.getAttribute("data-type") === "NodeListItem") {
38883889
const listItemElement = element.parentElement;
@@ -3891,6 +3892,8 @@ data-type="fold"${viewOccurrenceID ? ` data-view-occurrence-id="${encodeURICompo
38913892
horizontalAnchorLeft = getListGutterAnchorLeft(rect.left, contentLeft, isRTL);
38923893
}
38933894
const buttonCount = html.split("</button>").length - 1;
3895+
// 所有块标与内容统一留出间距,并在判断是否压缩时计入这段空间。
3896+
const gutterGap = 4;
38943897
const getNaturalLeft = (width: number) => {
38953898
let left = horizontalAnchorLeft - width - space;
38963899
if (nodeElement.getAttribute("data-type") === "NodeBlockQueryEmbed" && buttonCount === 1) {
@@ -3900,7 +3903,7 @@ data-type="fold"${viewOccurrenceID ? ` data-view-occurrence-id="${encodeURICompo
39003903
// 为数据库行
39013904
left = nodeElement.getBoundingClientRect().left - width - space + parseInt(getComputedStyle(nodeElement).paddingLeft);
39023905
}
3903-
return left;
3906+
return left - gutterGap;
39043907
};
39053908
let naturalLeft = getNaturalLeft(this.naturalWidth || this.element.clientWidth);
39063909
let compressed = naturalLeft < this.element.parentElement.getBoundingClientRect().left;
@@ -3951,7 +3954,7 @@ data-type="fold"${viewOccurrenceID ? ` data-view-occurrence-id="${encodeURICompo
39513954
const top = Math.max(rect.top + marginHeight, contentTop, foldElement ? foldElement.getBoundingClientRect().top : 0);
39523955
this.element.style.top = `${getFixedGutterPosition(top, fixedContainerRect?.top)}px`;
39533956
// 压缩模式需加 2,否则和折叠标题无法对齐
3954-
const left = compressed ? horizontalAnchorLeft - this.element.clientWidth - space / 2 + 3 :
3957+
const left = compressed ? horizontalAnchorLeft - this.element.clientWidth - space / 2 + 3 - gutterGap :
39553958
getNaturalLeft(this.element.clientWidth);
39563959
this.element.style.left = `${getFixedGutterPosition(left, fixedContainerRect?.left)}px`;
39573960
}

app/src/protyle/render/tabsRender.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ export const tabsRender = (element: Element, options: ITabsRenderOptions = {}) =
284284
}
285285
const list = header.querySelector<HTMLElement>(".tabs-list");
286286
list.setAttribute("aria-orientation", vertical ? "vertical" : "horizontal");
287+
let editingButton: HTMLElement;
287288
items.forEach((item, index) => {
288289
if (readonly) {
289290
item.dataset.tabsEditing = "false";
@@ -312,6 +313,9 @@ export const tabsRender = (element: Element, options: ITabsRenderOptions = {}) =
312313
title.setAttribute("contenteditable", "true");
313314
}
314315
const editing = !readonly && selected && item.dataset.tabsEditing === "true";
316+
if (editing && !button.classList.contains("tabs-tab--editing")) {
317+
editingButton = button;
318+
}
315319
button.classList.toggle("tabs-tab--editing", editing);
316320
button.draggable = !readonly && !editing && !!controller.options.move;
317321
const info = item.querySelector<HTMLElement>(":scope > .tab-item-info");
@@ -323,6 +327,13 @@ export const tabsRender = (element: Element, options: ITabsRenderOptions = {}) =
323327
}
324328
});
325329
tabs.setAttribute("data-tabs-ready", "true");
330+
if (editingButton && !vertical) {
331+
// 展开编辑标签后滚动到完整可见的位置,避免右侧页签的输入区域被裁切。
332+
const listRect = list.getBoundingClientRect();
333+
const rect = editingButton.getBoundingClientRect();
334+
list.scrollLeft += rect.left < listRect.left ? rect.left - listRect.left :
335+
Math.max(0, rect.right - listRect.right);
336+
}
326337
items.forEach((item, index) => {
327338
const info = item.querySelector<HTMLElement>(":scope > .tabs-title-editor");
328339
if (!info) {

0 commit comments

Comments
 (0)