chore(breadcrumb_badges/backlinks): display list of backlinks on click

This commit is contained in:
Elian Doran 2025-12-10 11:41:14 +02:00
parent 66008489c4
commit b03e6c3b19
No known key found for this signature in database
3 changed files with 64 additions and 16 deletions

View File

@ -37,4 +37,16 @@
text-decoration: none; text-decoration: none;
} }
} }
.dropdown {
&.dropdown-backlinks-badge .dropdown-menu {
min-width: 500px;
}
.btn {
border: 0;
margin: 0;
padding: 0;
}
}
} }

View File

@ -5,6 +5,8 @@ import { ComponentChildren, MouseEventHandler } from "preact";
import { useRef } from "preact/hooks"; import { useRef } from "preact/hooks";
import { t } from "../services/i18n"; import { t } from "../services/i18n";
import { BacklinksList } from "./FloatingButtonsDefinitions";
import Dropdown, { DropdownProps } from "./react/Dropdown";
import { useIsNoteReadOnly, useNoteContext, useStaticTooltip } from "./react/hooks"; import { useIsNoteReadOnly, useNoteContext, useStaticTooltip } from "./react/hooks";
import Icon from "./react/Icon"; import Icon from "./react/Icon";
import { useShareInfo } from "./shared_info"; import { useShareInfo } from "./shared_info";
@ -28,21 +30,19 @@ function ReadOnlyBadge() {
if (isTemporarilyEditable) { if (isTemporarilyEditable) {
return <Badge return <Badge
icon="bx bx-lock-open-alt" icon="bx bx-lock-open-alt"
text={t("breadcrumb_badges.read_only_temporarily_disabled")}
tooltip={t("breadcrumb_badges.read_only_temporarily_disabled_description")} tooltip={t("breadcrumb_badges.read_only_temporarily_disabled_description")}
className="temporarily-editable-badge" className="temporarily-editable-badge"
onClick={() => enableEditing(false)} onClick={() => enableEditing(false)}
> />;
{t("breadcrumb_badges.read_only_temporarily_disabled")}
</Badge>;
} else if (isReadOnly) { } else if (isReadOnly) {
return <Badge return <Badge
icon="bx bx-lock-alt" icon="bx bx-lock-alt"
text={isExplicitReadOnly ? t("breadcrumb_badges.read_only_explicit") : t("breadcrumb_badges.read_only_auto")}
tooltip={isExplicitReadOnly ? t("breadcrumb_badges.read_only_explicit_description") : t("breadcrumb_badges.read_only_auto_description")} tooltip={isExplicitReadOnly ? t("breadcrumb_badges.read_only_explicit_description") : t("breadcrumb_badges.read_only_auto_description")}
className="read-only-badge" className="read-only-badge"
onClick={() => enableEditing()} onClick={() => enableEditing()}
> />;
{isExplicitReadOnly ? t("breadcrumb_badges.read_only_explicit") : t("breadcrumb_badges.read_only_auto")}
</Badge>;
} }
} }
@ -53,28 +53,45 @@ function ShareBadge() {
return (link && return (link &&
<Badge <Badge
icon={isSharedExternally ? "bx bx-world" : "bx bx-link"} icon={isSharedExternally ? "bx bx-world" : "bx bx-link"}
text={isSharedExternally ? t("breadcrumb_badges.shared_publicly") : t("breadcrumb_badges.shared_locally")}
tooltip={isSharedExternally ? tooltip={isSharedExternally ?
t("breadcrumb_badges.shared_publicly_description", { link }) : t("breadcrumb_badges.shared_publicly_description", { link }) :
t("breadcrumb_badges.shared_locally_description", { link }) t("breadcrumb_badges.shared_locally_description", { link })
} }
className="share-badge" className="share-badge"
href={linkHref} href={linkHref}
> />
{isSharedExternally ? t("breadcrumb_badges.shared_publicly") : t("breadcrumb_badges.shared_locally")}
</Badge>
); );
} }
function BacklinksBadge() { function BacklinksBadge() {
const { note } = useNoteContext();
const count = 1; const count = 1;
return ( return (note && count > 0 &&
<Badge className="backlinks-badge" icon="bx bx-revision" tooltip={t("breadcrumb_badges.backlinks_description", { count })}> <BadgeWithDropdown
{t("breadcrumb_badges.backlinks", { count })} className="backlinks-badge backlinks-widget"
</Badge> icon="bx bx-revision"
text={t("breadcrumb_badges.backlinks", { count })}
tooltip={t("breadcrumb_badges.backlinks_description", { count })}
dropdownOptions={{
dropdownContainerClassName: "backlinks-items"
}}
>
<BacklinksList note={note} />
</BadgeWithDropdown>
); );
} }
function Badge({ icon, className, children, tooltip, onClick, href }: { icon?: string, className: string, tooltip: string, children: ComponentChildren, onClick?: MouseEventHandler<HTMLDivElement>, href?: string }) { interface BadgeProps {
text: string;
icon?: string;
className: string;
tooltip?: string;
onClick?: MouseEventHandler<HTMLDivElement>;
href?: string;
}
function Badge({ icon, className, text, tooltip, onClick, href }: BadgeProps) {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
useStaticTooltip(containerRef, { useStaticTooltip(containerRef, {
placement: "bottom", placement: "bottom",
@ -86,7 +103,7 @@ function Badge({ icon, className, children, tooltip, onClick, href }: { icon?: s
const content = <> const content = <>
{icon && <><Icon icon={icon} />&nbsp;</>} {icon && <><Icon icon={icon} />&nbsp;</>}
{children} {text}
</>; </>;
return ( return (
@ -99,3 +116,22 @@ function Badge({ icon, className, children, tooltip, onClick, href }: { icon?: s
</div> </div>
); );
} }
function BadgeWithDropdown({ children, tooltip, className, dropdownOptions, ...props }: BadgeProps & {
children: ComponentChildren,
dropdownOptions?: Partial<DropdownProps>
}) {
return (
<Dropdown
className={`dropdown-${className}`}
text={<Badge className={className} {...props} />}
noDropdownListStyle
noSelectButtonStyle
hideToggleArrow
title={tooltip}
titlePosition="bottom"
dropdownOptions={{ popperConfig: { placement: "bottom" } }}
{...dropdownOptions}
>{children}</Dropdown>
);
}

View File

@ -355,7 +355,7 @@ function Backlinks({ note, isDefaultViewMode }: FloatingButtonContext) {
); );
} }
function BacklinksList({ note }: { note: FNote }) { export function BacklinksList({ note }: { note: FNote }) {
const [ backlinks, setBacklinks ] = useState<BacklinksResponse>([]); const [ backlinks, setBacklinks ] = useState<BacklinksResponse>([]);
function refresh() { function refresh() {