fix(client/rtl): tooltips not inverted

This commit is contained in:
Elian Doran 2025-10-08 19:06:19 +03:00
parent 9d1e89268f
commit b5f73874cb
No known key found for this signature in database
6 changed files with 29 additions and 14 deletions

View File

@ -869,6 +869,18 @@ export function getErrorMessage(e: unknown) {
} }
} }
/**
* Handles left or right placement of e.g. tooltips in case of right-to-left languages. If the current language is a RTL one, then left and right are swapped. Other directions are unaffected.
* @param placement a string optionally containing a "left" or "right" value.
* @returns a left/right value swapped if needed, or the same as input otherwise.
*/
export function handleRightToLeftPlacement<T extends string>(placement: T) {
if (!glob.isRtl) return placement;
if (placement === "left") return "right";
if (placement === "right") return "left";
return placement;
}
export default { export default {
reloadFrontendApp, reloadFrontendApp,
restartDesktopApp, restartDesktopApp,

View File

@ -1,5 +1,6 @@
import { Tooltip } from "bootstrap"; import { Tooltip } from "bootstrap";
import NoteContextAwareWidget from "../note_context_aware_widget.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js";
import { handleRightToLeftPlacement } from "../../services/utils.js";
const TPL = /*html*/`<button class="button-widget bx" const TPL = /*html*/`<button class="button-widget bx"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
@ -26,13 +27,14 @@ export default class AbstractButtonWidget<SettingsT extends AbstractButtonWidget
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.tooltip = new Tooltip(this.$widget[0], { this.tooltip = new Tooltip(this.$widget[0], {
html: true, html: true,
// in case getTitle() returns null -> use empty string as fallback // in case getTitle() returns null -> use empty string as fallback
title: () => this.getTitle() || "", title: () => this.getTitle() || "",
trigger: "hover", trigger: "hover",
placement: this.settings.titlePlacement, placement: handleRightToLeftPlacement(this.settings.titlePlacement),
fallbackPlacements: [this.settings.titlePlacement] fallbackPlacements: [ handleRightToLeftPlacement(this.settings.titlePlacement) ]
}); });
if (this.settings.onContextMenu) { if (this.settings.onContextMenu) {

View File

@ -1,3 +1,4 @@
import { handleRightToLeftPlacement } from "../../services/utils.js";
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import { Tooltip, Dropdown } from "bootstrap"; import { Tooltip, Dropdown } from "bootstrap";
type PopoverPlacement = Tooltip.PopoverPlacement; type PopoverPlacement = Tooltip.PopoverPlacement;
@ -48,8 +49,8 @@ export default class RightDropdownButtonWidget extends BasicWidget {
this.$tooltip = this.$widget.find(".tooltip-trigger").attr("title", this.title); this.$tooltip = this.$widget.find(".tooltip-trigger").attr("title", this.title);
this.tooltip = new Tooltip(this.$tooltip[0], { this.tooltip = new Tooltip(this.$tooltip[0], {
placement: this.settings.titlePlacement, placement: handleRightToLeftPlacement(this.settings.titlePlacement),
fallbackPlacements: [this.settings.titlePlacement] fallbackPlacements: [ handleRightToLeftPlacement(this.settings.titlePlacement) ]
}); });
this.$widget this.$widget

View File

@ -2,7 +2,7 @@ import BasicWidget from "./basic_widget.js";
import server from "../services/server.js"; import server from "../services/server.js";
import linkService from "../services/link.js"; import linkService from "../services/link.js";
import froca from "../services/froca.js"; import froca from "../services/froca.js";
import utils from "../services/utils.js"; import utils, { handleRightToLeftPlacement } from "../services/utils.js";
import appContext from "../components/app_context.js"; import appContext from "../components/app_context.js";
import shortcutService, { isIMEComposing } from "../services/shortcuts.js"; import shortcutService, { isIMEComposing } from "../services/shortcuts.js";
import { t } from "../services/i18n.js"; import { t } from "../services/i18n.js";
@ -248,7 +248,7 @@ export default class QuickSearchWidget extends BasicWidget {
let tooltip = new Tooltip(this.$searchString[0], { let tooltip = new Tooltip(this.$searchString[0], {
trigger: "manual", trigger: "manual",
title: `Search error: ${error}`, title: `Search error: ${error}`,
placement: "right" placement: handleRightToLeftPlacement("right")
}); });
tooltip.show(); tooltip.show();

View File

@ -5,7 +5,7 @@ import { useEffect, useMemo, useRef, useState, type CSSProperties } from "preact
import "./FormList.css"; import "./FormList.css";
import { CommandNames } from "../../components/app_context"; import { CommandNames } from "../../components/app_context";
import { useStaticTooltip } from "./hooks"; import { useStaticTooltip } from "./hooks";
import { isMobile } from "../../services/utils"; import { handleRightToLeftPlacement, isMobile } from "../../services/utils";
interface FormListOpts { interface FormListOpts {
children: ComponentChildren; children: ComponentChildren;
@ -22,7 +22,7 @@ export default function FormList({ children, onSelect, style, fullHeight }: Form
if (!triggerRef.current || !wrapperRef.current) { if (!triggerRef.current || !wrapperRef.current) {
return; return;
} }
const $wrapperRef = $(wrapperRef.current); const $wrapperRef = $(wrapperRef.current);
const dropdown = BootstrapDropdown.getOrCreateInstance(triggerRef.current); const dropdown = BootstrapDropdown.getOrCreateInstance(triggerRef.current);
$wrapperRef.on("hide.bs.dropdown", (e) => e.preventDefault()); $wrapperRef.on("hide.bs.dropdown", (e) => e.preventDefault());
@ -93,8 +93,8 @@ interface FormListItemOpts {
} }
const TOOLTIP_CONFIG: Partial<Tooltip.Options> = { const TOOLTIP_CONFIG: Partial<Tooltip.Options> = {
placement: "right", placement: handleRightToLeftPlacement("right"),
fallbackPlacements: [ "right" ] fallbackPlacements: [ handleRightToLeftPlacement("right") ]
} }
export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, ...contentProps }: FormListItemOpts) { export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, ...contentProps }: FormListItemOpts) {
@ -178,4 +178,4 @@ export function FormDropdownSubmenu({ icon, title, children }: { icon: string, t
</ul> </ul>
</li> </li>
) )
} }

View File

@ -3,7 +3,7 @@ import BasicWidget from "./basic_widget.js";
import ws from "../services/ws.js"; import ws from "../services/ws.js";
import options from "../services/options.js"; import options from "../services/options.js";
import syncService from "../services/sync.js"; import syncService from "../services/sync.js";
import { escapeQuotes } from "../services/utils.js"; import { escapeQuotes, handleRightToLeftPlacement } from "../services/utils.js";
import { Tooltip } from "bootstrap"; import { Tooltip } from "bootstrap";
import { WebSocketMessage } from "@triliumnext/commons"; import { WebSocketMessage } from "@triliumnext/commons";
@ -109,8 +109,8 @@ export default class SyncStatusWidget extends BasicWidget {
Tooltip.getOrCreateInstance(this.$widget.find(`.sync-status-${className}`)[0], { Tooltip.getOrCreateInstance(this.$widget.find(`.sync-status-${className}`)[0], {
html: true, html: true,
placement: this.settings.titlePlacement, placement: handleRightToLeftPlacement(this.settings.titlePlacement),
fallbackPlacements: [this.settings.titlePlacement] fallbackPlacements: [ handleRightToLeftPlacement(this.settings.titlePlacement) ]
}); });
this.$widget.show(); this.$widget.show();