Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve tooltip #13966

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from
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
5 changes: 5 additions & 0 deletions app/src/assets/scss/component/_list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@
color: var(--b3-theme-on-background);
background-color: var(--b3-list-icon-hover);
}

&--warning:hover {
color: var(--b3-theme-error);
background-color: var(--b3-list-icon-hover);
}
}

.counter {
Expand Down
6 changes: 6 additions & 0 deletions app/src/assets/scss/component/_tooltips.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
}
}

.clonedTooltip {
animation: unset;
opacity: 0;
pointer-events: none;
}

.b3-tooltips {
position: relative;
cursor: pointer;
Expand Down
1 change: 1 addition & 0 deletions app/src/assets/template/app/index.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<div class="b3-menu__items"></div>
</div>
<div id="message" class="b3-snackbars"></div>
<div id="tooltip" class="tooltip fn__none"></div>
<script>
setTimeout(() => {
const refreshElement = document.getElementById("loadingRefresh")
Expand Down
21 changes: 13 additions & 8 deletions app/src/block/popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ export const initBlockPopover = (app: App) => {
hasClosestByAttribute(event.target, "data-type", "tab-header") ||
hasClosestByAttribute(event.target, "data-type", "inline-memo") ||
hasClosestByClassName(event.target, "av__calc--ashow") ||
hasClosestByClassName(event.target, "av__cell");
hasClosestByClassName(event.target, "av__cell") ||
hasClosestByAttribute(event.target, "data-type", "setRelationCell");
if (aElement) {
let tooltipClass = "";
let tip = aElement.getAttribute("aria-label");
let tip = aElement.getAttribute("aria-label") || "";
if (aElement.classList.contains("av__cell")) {
if (aElement.classList.contains("av__cell--header")) {
const textElement = aElement.querySelector(".av__celltext");
Expand Down Expand Up @@ -65,10 +66,16 @@ export const initBlockPopover = (app: App) => {
}
}
} else if (aElement.classList.contains("av__celltext--url")) {
tip = tip ? `<span style="word-break: break-all">${tip.substring(0, Constants.SIZE_TITLE)}</span><div class="fn__hr"></div>${aElement.getAttribute("data-name")}` : aElement.getAttribute("data-name");
const title = aElement.getAttribute("data-name") || "";
tip = tip ? `<span style="word-break: break-all">${tip.substring(0, Constants.SIZE_TITLE)}</span>${title ? '<div class="fn__hr"></div><span>' + title + "</span>" : ""}` : title;
tooltipClass = "href";
} else if (aElement.classList.contains("av__calc--ashow") && aElement.clientWidth + 2 < aElement.scrollWidth) {
tip = aElement.lastChild.textContent + " " + aElement.firstElementChild.textContent;
} else if (aElement.getAttribute("data-type") === "setRelationCell") {
const childElement = aElement.querySelector(".b3-menu__label");
if (childElement && childElement.clientWidth < childElement.scrollWidth) {
tip = childElement.textContent;
}
}
if (!tip) {
tip = aElement.getAttribute("data-inline-memo-content");
Expand All @@ -82,25 +89,23 @@ export const initBlockPopover = (app: App) => {
if (href) {
tip = `<span style="word-break: break-all">${href.substring(0, Constants.SIZE_TITLE)}</span>`;
tooltipClass = "href"; // 为超链接添加 class https://github.com/siyuan-note/siyuan/issues/11440#issuecomment-2119080691
} else {
tip = "";
}
const title = aElement.getAttribute("data-title");
if (tip && isLocalPath(href) && !aElement.classList.contains("b3-tooltips")) {
let assetTip = tip;
fetchPost("/api/asset/statAsset", {path: href}, (response) => {
if (response.code === 1) {
if (title) {
assetTip += '<div class="fn__hr"></div>' + title;
assetTip += '<div class="fn__hr"></div><span>' + title + "</span>";
}
} else {
assetTip += ` ${response.data.hSize}${title ? '<div class="fn__hr"></div>' + title : ""}<br>${window.siyuan.languages.modifiedAt} ${response.data.hUpdated}<br>${window.siyuan.languages.createdAt} ${response.data.hCreated}`;
assetTip += ` ${response.data.hSize}${title ? '<div class="fn__hr"></div><span>' + title + "</span>" : ""}<br>${window.siyuan.languages.modifiedAt} ${response.data.hUpdated}<br>${window.siyuan.languages.createdAt} ${response.data.hCreated}`;
}
showTooltip(assetTip, aElement, tooltipClass);
});
tip = "";
} else if (title) {
tip += '<div class="fn__hr"></div>' + title;
tip += '<div class="fn__hr"></div><span>' + title + "</span>";
}
}
if (tip && !aElement.classList.contains("b3-tooltips")) {
Expand Down
2 changes: 1 addition & 1 deletion app/src/card/makeCard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const genCardItem = (item: ICardPackage) => {
<span data-type="view" class="b3-list-item__action b3-tooltips b3-tooltips__w" aria-label="${window.siyuan.languages.cardPreview}">
<svg><use xlink:href="#iconEye"></use></svg>
</span>
<span data-type="remove" class="b3-list-item__action b3-tooltips b3-tooltips__w" aria-label="${window.siyuan.languages.removeDeck}">
<span data-type="remove" class="b3-list-item__action b3-list-item__action--warning b3-tooltips b3-tooltips__w" aria-label="${window.siyuan.languages.removeDeck}">
<svg><use xlink:href="#iconMin"></use></svg>
</span>
<span data-type="add" style="display: flex" class="b3-list-item__action b3-tooltips b3-tooltips__w" aria-label="${window.siyuan.languages.addDeck}">
Expand Down
4 changes: 2 additions & 2 deletions app/src/card/viewCards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ ${unicode2Emoji(item.ial.icon, "b3-list-item__graphic", true)}
<span data-position="parentE" data-type="reset" data-id="${item.id}" class="b3-list-item__action ariaLabel" aria-label="${window.siyuan.languages.reset}">
<svg><use xlink:href="#iconUndo"></use></svg>
</span>
<span data-position="parentE" data-type="remove" data-id="${item.id}" class="b3-list-item__action ariaLabel" aria-label="${window.siyuan.languages.removeDeck}">
<span data-position="parentE" data-type="remove" data-id="${item.id}" class="b3-list-item__action b3-list-item__action--warning ariaLabel" aria-label="${window.siyuan.languages.removeDeck}">
<svg><use xlink:href="#iconTrashcan"></use></svg>
</span>
</div>`;
Expand All @@ -273,7 +273,7 @@ ${unicode2Emoji(item.ial.icon, "b3-list-item__graphic", true)}
// 块被删除的情况
listHTML += `<div data-type="card-item" class="b3-list-item${isMobile() ? "" : " b3-list-item--hide-action"}">
<span class="b3-list-item__text">${item.content}</span>
<span data-position="parentE" data-type="remove" data-id="${item.id}" class="b3-list-item__action ariaLabel" aria-label="${window.siyuan.languages.removeDeck}">
<span data-position="parentE" data-type="remove" data-id="${item.id}" class="b3-list-item__action b3-list-item__action--warning ariaLabel" aria-label="${window.siyuan.languages.removeDeck}">
<svg><use xlink:href="#iconTrashcan"></use></svg>
</span>
</div>`;
Expand Down
86 changes: 47 additions & 39 deletions app/src/dialog/tooltip.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {isMobile} from "../util/functions";
import {Constants} from "../constants";

export const showTooltip = (message: string, target: Element, tooltipClass?: string) => {
if (isMobile()) {
Expand All @@ -11,33 +10,24 @@ export const showTooltip = (message: string, target: Element, tooltipClass?: str
return;
}

const className = tooltipClass ? `tooltip tooltip--${tooltipClass}` : "tooltip";
let messageElement = document.getElementById("tooltip");
if (!messageElement) {
document.body.insertAdjacentHTML("beforeend", `<div class="${className}" id="tooltip">${message}</div>`);
messageElement = document.getElementById("tooltip");
} else {
if (messageElement.className !== className) {
messageElement.className = className;
}
if (messageElement.innerHTML !== message) {
messageElement.innerHTML = message;
}
// 避免原本的 top 和 left 影响计算
messageElement.removeAttribute("style");
}

let left = targetRect.left;
let top = targetRect.bottom;
const tooltipElement = document.getElementById("tooltip");
const clonedTooltip = tooltipElement.cloneNode(true) as HTMLElement;
clonedTooltip.id = "clonedTooltip";
clonedTooltip.removeAttribute("style");
clonedTooltip.className = "tooltip";
clonedTooltip.innerHTML = message;
document.body.append(clonedTooltip);

// position: parentE; parentW; ${number}parentW; ${number}bottom;
// right; right${number}bottom; right${number}top; top;
const position = target.getAttribute("data-position");
const parentRect = target.parentElement.getBoundingClientRect();

let top, left;

if (position?.startsWith("right")) {
// block icon and background icon
left = targetRect.right - messageElement.clientWidth;
left = targetRect.right - clonedTooltip.clientWidth;
}

if (position === "parentE") {
Expand All @@ -47,43 +37,61 @@ export const showTooltip = (message: string, target: Element, tooltipClass?: str
} else if (position?.endsWith("parentW")) {
// 数据库属性视图
top = parentRect.top + (parseInt(position) || 8);
left = parentRect.left - messageElement.clientWidth;
left = parentRect.left - clonedTooltip.clientWidth;
} else if (position?.endsWith("bottom")) {
top += parseInt(position.replace("right", "").replace("left", ""));
top = targetRect.bottom + parseInt(position.replace("right", ""));
} else if (position?.endsWith("top")) {
// 编辑器动态滚动条
top = targetRect.top - messageElement.clientHeight;
// 数据库视图、编辑器动态滚动条
top = targetRect.top - clonedTooltip.clientHeight;
} else if (position === "directLeft") {
// 关联字段选项
top = targetRect.top + (parseInt(position) || 0);
left = targetRect.left - clonedTooltip.clientWidth - 8;
}

// 确保不会超出屏幕
top = top >= 0 ? top : targetRect.bottom;
left = left >= 0 ? left : targetRect.left;

const topHeight = position === "parentE" ? top : targetRect.top;
const bottomHeight = window.innerHeight - top;

messageElement.style.maxHeight = Math.max(topHeight, bottomHeight) + "px";
clonedTooltip.style.maxHeight = Math.max(topHeight, bottomHeight) + "px";

// 避免原本的 top 和 left 影响计算
messageElement.style.top = "0px";
messageElement.style.left = "0px";

if (top + messageElement.clientHeight > window.innerHeight && topHeight > bottomHeight) {
messageElement.style.top = ((position === "parentE" ? parentRect.bottom : targetRect.top) - messageElement.clientHeight) + "px";
if (top + clonedTooltip.clientHeight > window.innerHeight && topHeight > bottomHeight) {
clonedTooltip.style.top = ((position === "parentE" || position === "directLeft" ? parentRect.bottom : targetRect.top) - clonedTooltip.clientHeight) + "px";
} else {
messageElement.style.top = top + "px";
clonedTooltip.style.top = top + "px";
}

if (left + messageElement.clientWidth > window.innerWidth) {
if (left + clonedTooltip.clientWidth > window.innerWidth) {
if (position === "parentE") {
messageElement.style.left = (parentRect.left - 8 - messageElement.clientWidth) + "px";
clonedTooltip.style.left = (parentRect.left - 8 - clonedTooltip.clientWidth) + "px";
} else {
messageElement.style.left = (window.innerWidth - 1 - messageElement.clientWidth) + "px";
clonedTooltip.style.left = (window.innerWidth - 1 - clonedTooltip.clientWidth) + "px";
}
} else {
messageElement.style.left = Math.max(0, left) + "px";
clonedTooltip.style.left = Math.max(0, left) + "px";
}

const cloneStyle = clonedTooltip.getAttribute("style");
if (tooltipElement.getAttribute("style") !== cloneStyle) {
tooltipElement.setAttribute("style", cloneStyle);
}
const className = tooltipClass ? `tooltip tooltip--${tooltipClass}` : "tooltip";
if (tooltipElement.className !== className) {
tooltipElement.className = className;
}
if (tooltipElement.innerHTML !== clonedTooltip.innerHTML) {
tooltipElement.innerHTML = clonedTooltip.innerHTML;
}

clonedTooltip.remove();
};

export const hideTooltip = () => {
const messageElement = document.getElementById("tooltip");
if (messageElement) {
messageElement.remove();
const tooltipElement = document.getElementById("tooltip");
if (tooltipElement && !tooltipElement.classList.contains("fn__none")) {
tooltipElement.classList.add("fn__none");
}
};
2 changes: 1 addition & 1 deletion app/src/layout/dock/Files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ export class Files extends Model {
item.style.opacity = "";
// https://github.com/siyuan-note/siyuan/issues/11587
if (index === 0) {
const airaLabelElement = item.querySelector(".ariaLabel");
const airaLabelElement = item.querySelector(".ariaLabel") || item.querySelector(".b3-list-item__text");
if (airaLabelElement) {
showTooltip(airaLabelElement.getAttribute("aria-label"), airaLabelElement);
}
Expand Down
15 changes: 10 additions & 5 deletions app/src/protyle/render/av/relation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ const genSelectItemHTML = (type: "selected" | "empty" | "unselect", id?: string,
</button>`;
}
if (type == "unselect") {
return `<button data-id="${id}" class="b3-menu__item" data-type="setRelationCell">
return `<button data-id="${id}" class="b3-menu__item" data-position="directLeft" data-type="setRelationCell">
<span class="b3-menu__label fn__ellipsis${isDetached ? "" : " popover__block"}" ${isDetached ? "" : 'style="color:var(--b3-protyle-inline-blockref-color)"'} data-id="${id}">${text}</span>
<svg class="b3-menu__action"><use xlink:href="#iconAdd"></use></svg>
</button>`;
Expand All @@ -243,7 +243,9 @@ const filterItem = (menuElement: Element, cellElement: HTMLElement, keyword: str
cellElement.querySelectorAll(".av__cell--relation").forEach((relationItem: HTMLElement) => {
const item = relationItem.querySelector(".av__celltext") as HTMLElement;
hasIds.push(item.dataset.id);
selectHTML += `<button data-id="${item.dataset.id}" data-type="setRelationCell" class="b3-menu__item${item.textContent.indexOf(keyword) > -1 ? "" : " fn__none"}" draggable="true">${genSelectItemHTML("selected", item.dataset.id, !item.classList.contains("av__celltext--ref"), Lute.EscapeHTMLStr(item.textContent || window.siyuan.languages.untitled))}</button>`;
selectHTML += `<button data-id="${item.dataset.id}" data-position="directLeft" data-type="setRelationCell"
class="b3-menu__item${item.textContent.indexOf(keyword) > -1 ? "" : " fn__none"}"
draggable="true">${genSelectItemHTML("selected", item.dataset.id, !item.classList.contains("av__celltext--ref"), Lute.EscapeHTMLStr(item.textContent || window.siyuan.languages.untitled))}</button>`;
});
cells.forEach((item) => {
if (!hasIds.includes(item.block.id)) {
Expand Down Expand Up @@ -275,7 +277,8 @@ export const bindRelationEvent = (options: {
options.cellElements[0].querySelectorAll(".av__cell--relation").forEach((relationItem: HTMLElement) => {
const item = relationItem.querySelector(".av__celltext") as HTMLElement;
hasIds.push(item.dataset.id);
selectHTML += `<button data-id="${item.dataset.id}" data-type="setRelationCell" class="b3-menu__item" draggable="true">${genSelectItemHTML("selected", item.dataset.id, !item.classList.contains("av__celltext--ref"), Lute.EscapeHTMLStr(item.textContent || window.siyuan.languages.untitled))}</button>`;
selectHTML += `<button data-id="${item.dataset.id}" data-position="directLeft" data-type="setRelationCell" class="b3-menu__item"
draggable="true">${genSelectItemHTML("selected", item.dataset.id, !item.classList.contains("av__celltext--ref"), Lute.EscapeHTMLStr(item.textContent || window.siyuan.languages.untitled))}</button>`;
});
cells.forEach((item) => {
if (!hasIds.includes(item.block.id)) {
Expand Down Expand Up @@ -401,7 +404,8 @@ export const setRelationCell = (protyle: IProtyle, nodeElement: HTMLElement, tar
isDetached: !target.firstElementChild.getAttribute("style")
});
separatorElement.before(target);
target.outerHTML = `<button data-id="${targetId}" data-type="setRelationCell" class="b3-menu__item" draggable="true">${genSelectItemHTML("selected", targetId, !target.querySelector(".popover__block"), Lute.EscapeHTMLStr(target.querySelector(".b3-menu__label").textContent))}</button>`;
target.outerHTML = `<button data-id="${targetId}" data-type="setRelationCell" class="b3-menu__item"
draggable="true">${genSelectItemHTML("selected", targetId, !target.querySelector(".popover__block"), Lute.EscapeHTMLStr(target.querySelector(".b3-menu__label").textContent))}</button>`;
if (!separatorElement.nextElementSibling) {
separatorElement.insertAdjacentHTML("afterend", genSelectItemHTML("empty"));
}
Expand Down Expand Up @@ -433,7 +437,8 @@ export const setRelationCell = (protyle: IProtyle, nodeElement: HTMLElement, tar
},
isDetached: true
});
separatorElement.insertAdjacentHTML("beforebegin", `<button data-id="${rowId}" data-type="setRelationCell" class="b3-menu__item" draggable="true">${genSelectItemHTML("selected", rowId, true, Lute.EscapeHTMLStr(content))}</button>`);
separatorElement.insertAdjacentHTML("beforebegin", `<button data-id="${rowId}" data-position="directLeft" data-type="setRelationCell"
class="b3-menu__item" draggable="true">${genSelectItemHTML("selected", rowId, true, Lute.EscapeHTMLStr(content))}</button>`);
}
menuElement.querySelector(".b3-menu__item--current")?.classList.remove("b3-menu__item--current");
menuElement.querySelector(".b3-menu__items .b3-menu__item:not(.fn__none)").classList.add("b3-menu__item--current");
Expand Down
2 changes: 1 addition & 1 deletion app/src/protyle/render/av/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export const avRender = (element: Element, protyle: IProtyle, cb?: () => void, v
}
tableHTML += `<div class="av__cell av__cell--header" data-col-id="${column.id}" draggable="true"
data-icon="${column.icon}" data-dtype="${column.type}" data-wrap="${column.wrap}" data-pin="${column.pin}"
data-desc="${escapeAttr(column.desc)}"
data-desc="${escapeAttr(column.desc)}" data-position="top"
style="width: ${column.width || "200px"};">
${column.icon ? unicode2Emoji(column.icon, "av__cellheadericon", true) : `<svg class="av__cellheadericon"><use xlink:href="#${getColIconByType(column.type)}"></use></svg>`}
<span class="av__celltext fn__flex-1">${escapeHtml(column.name)}</span>
Expand Down