feat(status_bar): basic integration of attribute editor

This commit is contained in:
Elian Doran
2025-12-12 21:29:40 +02:00
parent efff38b116
commit c6d97e3d4b
3 changed files with 112 additions and 72 deletions

View File

@@ -2,70 +2,74 @@
contain: none; contain: none;
border-top: 1px solid var(--main-border-color); border-top: 1px solid var(--main-border-color);
background-color: var(--left-pane-background-color); background-color: var(--left-pane-background-color);
display: flex;
align-items: center;
padding-inline: 0.25em;
min-height: 32px;
> .breadcrumb-row { .status-bar-main-row {
flex-grow: 1; min-height: 32px;
}
> .actions-row {
padding: 0.1em;
display: flex; display: flex;
gap: 0.1em; align-items: center;
font-size: 0.85em; padding-inline: 0.25em;
.btn { > .breadcrumb-row {
padding: 0 0.5em !important; flex-grow: 1;
background: transparent; }
> .actions-row {
padding: 0.1em;
display: flex; display: flex;
align-items: center; gap: 0.1em;
border: 0; font-size: 0.85em;
&:focus, .btn {
&:hover { padding: 0 0.5em !important;
background: var(--input-background-color); background: transparent;
} display: flex;
} align-items: center;
border: 0;
.status-bar-dropdown-button { &:focus,
&:after { &:hover {
content: unset; background: var(--input-background-color);
}
}
}
.dropdown {
.dropdown-toggle {
padding: 0.1em 0.25em;
}
}
.dropdown-note-info {
width: max-content;
ul {
list-style-type: none;
padding: 0.5em;
margin: 0;
display: table;
li {
display: table-row;
> strong {
display: table-cell;
padding: 0.2em 0;
} }
}
> span { .status-bar-dropdown-button {
display: table-cell; &:after {
user-select: text; content: unset;
padding-left: 2em; }
}
}
.dropdown {
.dropdown-toggle {
padding: 0.1em 0.25em;
}
}
.dropdown-note-info {
width: max-content;
ul {
list-style-type: none;
padding: 0.5em;
margin: 0;
display: table;
li {
display: table-row;
> strong {
display: table-cell;
padding: 0.2em 0;
}
> span {
display: table-cell;
user-select: text;
padding-left: 2em;
}
} }
} }
} }
} }
} }

View File

@@ -9,6 +9,7 @@ import { useContext, useRef, useState } from "preact/hooks";
import { CommandNames } from "../../components/app_context"; import { CommandNames } from "../../components/app_context";
import NoteContext from "../../components/note_context"; import NoteContext from "../../components/note_context";
import FNote from "../../entities/fnote"; import FNote from "../../entities/fnote";
import attributes from "../../services/attributes";
import { t } from "../../services/i18n"; import { t } from "../../services/i18n";
import { ViewScope } from "../../services/link"; import { ViewScope } from "../../services/link";
import { openInAppHelpFromUrl } from "../../services/utils"; import { openInAppHelpFromUrl } from "../../services/utils";
@@ -20,11 +21,11 @@ import { useActiveNoteContext, useStaticTooltip, useTriliumEvent } from "../reac
import Icon from "../react/Icon"; import Icon from "../react/Icon";
import { ParentComponent } from "../react/react_utils"; import { ParentComponent } from "../react/react_utils";
import { ContentLanguagesModal, useLanguageSwitcher } from "../ribbon/BasicPropertiesTab"; import { ContentLanguagesModal, useLanguageSwitcher } from "../ribbon/BasicPropertiesTab";
import AttributeEditor, { AttributeEditorImperativeHandlers } from "../ribbon/components/AttributeEditor";
import { NoteSizeWidget, useNoteMetadata } from "../ribbon/NoteInfoTab"; import { NoteSizeWidget, useNoteMetadata } from "../ribbon/NoteInfoTab";
import { useAttachments } from "../type_widgets/Attachment"; import { useAttachments } from "../type_widgets/Attachment";
import { useProcessedLocales } from "../type_widgets/options/components/LocaleSelector"; import { useProcessedLocales } from "../type_widgets/options/components/LocaleSelector";
import Breadcrumb from "./Breadcrumb"; import Breadcrumb from "./Breadcrumb";
import attributes from "../../services/attributes";
interface StatusBarContext { interface StatusBarContext {
note: FNote; note: FNote;
@@ -38,19 +39,23 @@ export default function StatusBar() {
return ( return (
<div className="status-bar"> <div className="status-bar">
{context && <> {context && <AttributesPane {...context} />}
<div className="breadcrumb-row">
<Breadcrumb {...context} />
</div>
<div className="actions-row"> <div className="status-bar-main-row">
<AttributesButton {...context} /> {context && <>
<AttachmentCount {...context} /> <div className="breadcrumb-row">
<BacklinksBadge {...context} /> <Breadcrumb {...context} />
<LanguageSwitcher {...context} /> </div>
<NoteInfoBadge {...context} />
</div> <div className="actions-row">
</>} <AttributesButton {...context} />
<AttachmentCount {...context} />
<BacklinksBadge {...context} />
<LanguageSwitcher {...context} />
<NoteInfoBadge {...context} />
</div>
</>}
</div>
</div> </div>
); );
} }
@@ -81,14 +86,18 @@ function StatusBarDropdown({ children, icon, text, buttonClassName, titleOptions
); );
} }
function StatusBarButton({ className, icon, text, title, triggerCommand }: { interface StatusBarButtonBaseProps {
className?: string; className?: string;
icon: string; icon: string;
title: string; title: string;
text?: string | number; text?: string | number;
disabled?: boolean; disabled?: boolean;
triggerCommand: CommandNames; }
}) {
type StatusBarButtonWithCommand = StatusBarButtonBaseProps & { triggerCommand: CommandNames; };
type StatusBarButtonWithClick = StatusBarButtonBaseProps & { onClick: () => void; };
function StatusBarButton({ className, icon, text, title, ...restProps }: StatusBarButtonWithCommand | StatusBarButtonWithClick) {
const parentComponent = useContext(ParentComponent); const parentComponent = useContext(ParentComponent);
const buttonRef = useRef<HTMLButtonElement>(null); const buttonRef = useRef<HTMLButtonElement>(null);
useStaticTooltip(buttonRef, { useStaticTooltip(buttonRef, {
@@ -103,7 +112,13 @@ function StatusBarButton({ className, icon, text, title, triggerCommand }: {
ref={buttonRef} ref={buttonRef}
className={clsx("btn select-button", className)} className={clsx("btn select-button", className)}
type="button" type="button"
onClick={() => parentComponent?.triggerCommand(triggerCommand)} onClick={() => {
if ("triggerCommand" in restProps) {
parentComponent?.triggerCommand(restProps.triggerCommand);
} else {
restProps.onClick();
}
}}
> >
<Icon icon={icon} />&nbsp;{text} <Icon icon={icon} />&nbsp;{text}
</button> </button>
@@ -246,7 +261,27 @@ function AttributesButton({ note }: StatusBarContext) {
icon="bx bx-list-check" icon="bx bx-list-check"
title={t("status_bar.attributes_title")} title={t("status_bar.attributes_title")}
text={t("status_bar.attributes", { count })} text={t("status_bar.attributes", { count })}
onClick={() => {
alert("Hi");
}}
/> />
); );
} }
function AttributesPane({ note, noteContext }: StatusBarContext) {
const parentComponent = useContext(ParentComponent);
const api = useRef<AttributeEditorImperativeHandlers>(null);
return (
<div className="attribute-list">
{parentComponent && <AttributeEditor
componentId={parentComponent.componentId}
api={api}
ntxId={noteContext.ntxId}
note={note}
hidden={!note}
/>}
</div>
);
}
//#endregion //#endregion

View File

@@ -1,4 +1,5 @@
import { useMemo, useRef } from "preact/hooks"; import { useMemo, useRef } from "preact/hooks";
import { useLegacyImperativeHandlers, useTriliumEvents } from "../react/hooks"; import { useLegacyImperativeHandlers, useTriliumEvents } from "../react/hooks";
import AttributeEditor, { AttributeEditorImperativeHandlers } from "./components/AttributeEditor"; import AttributeEditor, { AttributeEditorImperativeHandlers } from "./components/AttributeEditor";
import { TabContext } from "./ribbon-interface"; import { TabContext } from "./ribbon-interface";
@@ -25,5 +26,5 @@ export default function OwnedAttributesTab({ note, hidden, activate, ntxId, ...r
<AttributeEditor api={api} ntxId={ntxId} note={note} {...restProps} hidden={hidden} /> <AttributeEditor api={api} ntxId={ntxId} note={note} {...restProps} hidden={hidden} />
)} )}
</div> </div>
) );
} }