mirror of
https://github.com/zadam/trilium.git
synced 2025-12-12 10:24:23 +01:00
New layout: Note info (#8015)
This commit is contained in:
commit
c3829f82ab
@ -14,6 +14,7 @@ import NoteLink from "./react/NoteLink";
|
||||
import link_context_menu from "../menus/link_context_menu";
|
||||
import { TitleEditor } from "./collections/board";
|
||||
import server from "../services/server";
|
||||
import { NoteInfoBadge } from "./BreadcrumbBadges";
|
||||
|
||||
const COLLAPSE_THRESHOLD = 5;
|
||||
const INITIAL_ITEMS = 2;
|
||||
@ -119,7 +120,10 @@ function BreadcrumbItem({ index, notePath, noteContext, notePathLength }: { inde
|
||||
}
|
||||
|
||||
if (index === notePathLength - 1) {
|
||||
return <BreadcrumbLastItem notePath={notePath} />;
|
||||
return <>
|
||||
<BreadcrumbLastItem notePath={notePath} />
|
||||
<NoteInfoBadge note={noteContext?.note} />
|
||||
</>;
|
||||
}
|
||||
|
||||
return <BreadcrumbLink notePath={notePath} />;
|
||||
|
||||
@ -9,63 +9,87 @@
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
--badge-radius: 12px;
|
||||
}
|
||||
|
||||
.breadcrumb-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 6px;
|
||||
border-radius: var(--badge-radius);
|
||||
font-size: 0.75em;
|
||||
background-color: var(--color, transparent);
|
||||
color: white;
|
||||
min-width: 0;
|
||||
flex-shrink: 1;
|
||||
.breadcrumb-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 6px;
|
||||
border-radius: var(--badge-radius);
|
||||
font-size: 0.75em;
|
||||
background-color: var(--color, transparent);
|
||||
color: white;
|
||||
min-width: 0;
|
||||
flex-shrink: 1;
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: color-mix(in srgb, var(--color, --badge-background-color) 80%, black);
|
||||
}
|
||||
}
|
||||
|
||||
&.temporarily-editable-badge { --color: #4fa52b; }
|
||||
&.read-only-badge { --color: #e33f3b; }
|
||||
&.share-badge { --color: #3b82f6; }
|
||||
&.backlinks-badge { color: var(--badge-text-color); }
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
> * {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
&:hover {
|
||||
background-color: color-mix(in srgb, var(--color, --badge-background-color) 80%, black);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
&.temporarily-editable-badge { --color: #4fa52b; }
|
||||
&.read-only-badge { --color: #e33f3b; }
|
||||
&.share-badge { --color: #3b82f6; }
|
||||
&.backlinks-badge { color: var(--badge-text-color); }
|
||||
|
||||
a {
|
||||
color: inherit !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
> * {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
border-radius: var(--badge-radius);
|
||||
|
||||
&.dropdown-backlinks-badge .dropdown-menu {
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
.breadcrumb-badge {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb-dropdown-badge {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
border-radius: var(--badge-radius);
|
||||
|
||||
&.dropdown-backlinks-badge .dropdown-menu {
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
&.dropdown-note-info-badge {
|
||||
.dropdown-menu.show 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb-badge {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,11 +5,14 @@ import { ComponentChildren, MouseEventHandler } from "preact";
|
||||
import { useRef } from "preact/hooks";
|
||||
|
||||
import { t } from "../services/i18n";
|
||||
import { formatDateTime } from "../utils/formatters";
|
||||
import { BacklinksList, useBacklinkCount } from "./FloatingButtonsDefinitions";
|
||||
import Dropdown, { DropdownProps } from "./react/Dropdown";
|
||||
import { useIsNoteReadOnly, useNoteContext, useStaticTooltip } from "./react/hooks";
|
||||
import Icon from "./react/Icon";
|
||||
import { NoteSizeWidget, useNoteMetadata } from "./ribbon/NoteInfoTab";
|
||||
import { useShareInfo } from "./shared_info";
|
||||
import FNote from "../entities/fnote";
|
||||
|
||||
export default function BreadcrumbBadges() {
|
||||
return (
|
||||
@ -21,6 +24,35 @@ export default function BreadcrumbBadges() {
|
||||
);
|
||||
}
|
||||
|
||||
export function NoteInfoBadge({ note }: { note: FNote | null | undefined }) {
|
||||
const { metadata, ...sizeProps } = useNoteMetadata(note);
|
||||
|
||||
return (note &&
|
||||
<BadgeWithDropdown
|
||||
icon="bx bx-info-circle"
|
||||
className="note-info-badge"
|
||||
dropdownOptions={{ dropdownOptions: { autoClose: "outside" } }}
|
||||
>
|
||||
<ul>
|
||||
<NoteInfoValue text={t("note_info_widget.created")} value={formatDateTime(metadata?.dateCreated)} />
|
||||
<NoteInfoValue text={t("note_info_widget.modified")} value={formatDateTime(metadata?.dateModified)} />
|
||||
<NoteInfoValue text={t("note_info_widget.type")} value={<span>{note.type} {note.mime && <span>({note.mime})</span>}</span>} />
|
||||
<NoteInfoValue text={t("note_info_widget.note_id")} value={<code>{note.noteId}</code>} />
|
||||
<NoteInfoValue text={t("note_info_widget.note_size")} title={t("note_info_widget.note_size_info")} value={<NoteSizeWidget {...sizeProps} />} />
|
||||
</ul>
|
||||
</BadgeWithDropdown>
|
||||
);
|
||||
}
|
||||
|
||||
function NoteInfoValue({ text, title, value }: { text: string; title?: string, value: ComponentChildren }) {
|
||||
return (
|
||||
<li>
|
||||
<strong title={title}>{text}{": "}</strong>
|
||||
<span>{value}</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
function ReadOnlyBadge() {
|
||||
const { note, noteContext } = useNoteContext();
|
||||
const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext);
|
||||
@ -83,7 +115,7 @@ function BacklinksBadge() {
|
||||
}
|
||||
|
||||
interface BadgeProps {
|
||||
text: string;
|
||||
text?: string;
|
||||
icon?: string;
|
||||
className: string;
|
||||
tooltip?: string;
|
||||
@ -123,15 +155,21 @@ function BadgeWithDropdown({ children, tooltip, className, dropdownOptions, ...p
|
||||
}) {
|
||||
return (
|
||||
<Dropdown
|
||||
className={`dropdown-${className}`}
|
||||
className={`breadcrumb-dropdown-badge dropdown-${className}`}
|
||||
text={<Badge className={className} {...props} />}
|
||||
noDropdownListStyle
|
||||
noSelectButtonStyle
|
||||
hideToggleArrow
|
||||
title={tooltip}
|
||||
titlePosition="bottom"
|
||||
dropdownOptions={{ popperConfig: { placement: "bottom", strategy: "fixed" } }}
|
||||
{...dropdownOptions}
|
||||
dropdownOptions={{
|
||||
...dropdownOptions?.dropdownOptions,
|
||||
popperConfig: {
|
||||
...dropdownOptions?.dropdownOptions?.popperConfig,
|
||||
placement: "bottom", strategy: "fixed"
|
||||
}
|
||||
}}
|
||||
>{children}</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
@ -9,26 +9,12 @@ import { useRef } from "preact/hooks";
|
||||
|
||||
export default function NoteTitleDetails() {
|
||||
const { note, noteContext } = useNoteContext();
|
||||
const { metadata } = useNoteMetadata(note);
|
||||
const isHiddenNote = note?.noteId.startsWith("_");
|
||||
const isDefaultView = noteContext?.viewScope?.viewMode === "default";
|
||||
|
||||
const items: ComponentChild[] = [
|
||||
(isDefaultView && !isHiddenNote && metadata?.dateCreated &&
|
||||
<TextWithValue
|
||||
i18nKey="note_title.created_on"
|
||||
value={formatDateTime(metadata.dateCreated, "medium", "none")}
|
||||
valueTooltip={formatDateTime(metadata.dateCreated, "full", "long")}
|
||||
/>),
|
||||
(isDefaultView && !isHiddenNote && metadata?.dateModified &&
|
||||
<TextWithValue
|
||||
i18nKey="note_title.last_modified"
|
||||
value={formatDateTime(metadata.dateModified, "medium", "none")}
|
||||
valueTooltip={formatDateTime(metadata.dateModified, "full", "long")}
|
||||
/>)
|
||||
].filter(item => !!item);
|
||||
const items: ComponentChild[] = [].filter(item => !!item);
|
||||
|
||||
return (
|
||||
return items.length && (
|
||||
<div className="title-details">
|
||||
{joinElements(items, " • ")}
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { t } from "../../services/i18n";
|
||||
import { TabContext } from "./ribbon-interface";
|
||||
import { MetadataResponse, NoteSizeResponse, SubtreeSizeResponse } from "@triliumnext/commons";
|
||||
import server from "../../services/server";
|
||||
import Button from "../react/Button";
|
||||
@ -13,8 +12,8 @@ import FNote from "../../entities/fnote";
|
||||
|
||||
const isNewLayout = isExperimentalFeatureEnabled("new-layout");
|
||||
|
||||
export default function NoteInfoTab({ note }: TabContext) {
|
||||
const { isLoading, metadata, noteSizeResponse, subtreeSizeResponse, requestSizeInfo } = useNoteMetadata(note);
|
||||
export default function NoteInfoTab({ note }: { note: FNote | null | undefined }) {
|
||||
const { metadata, ...sizeProps } = useNoteMetadata(note);
|
||||
|
||||
return (
|
||||
<div className="note-info-widget">
|
||||
@ -42,23 +41,7 @@ export default function NoteInfoTab({ note }: TabContext) {
|
||||
<div className="note-info-item">
|
||||
<span title={t("note_info_widget.note_size_info")}>{t("note_info_widget.note_size")}:</span>
|
||||
<span className="note-info-size-col-span">
|
||||
{!isLoading && !noteSizeResponse && !subtreeSizeResponse && (
|
||||
<Button
|
||||
className="calculate-button"
|
||||
icon="bx bx-calculator"
|
||||
text={t("note_info_widget.calculate")}
|
||||
onClick={requestSizeInfo}
|
||||
/>
|
||||
)}
|
||||
|
||||
<span className="note-sizes-wrapper selectable-text">
|
||||
<span className="note-size">{formatSize(noteSizeResponse?.noteSize)}</span>
|
||||
{" "}
|
||||
{subtreeSizeResponse && subtreeSizeResponse.subTreeNoteCount > 1 &&
|
||||
<span className="subtree-size">{t("note_info_widget.subtree_size", { size: formatSize(subtreeSizeResponse.subTreeSize), count: subtreeSizeResponse.subTreeNoteCount })}</span>
|
||||
}
|
||||
{isLoading && <LoadingSpinner />}
|
||||
</span>
|
||||
<NoteSizeWidget {...sizeProps} />
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
@ -67,6 +50,28 @@ export default function NoteInfoTab({ note }: TabContext) {
|
||||
);
|
||||
}
|
||||
|
||||
export function NoteSizeWidget({ isLoading, noteSizeResponse, subtreeSizeResponse, requestSizeInfo }: Omit<ReturnType<typeof useNoteMetadata>, "metadata">) {
|
||||
return <>
|
||||
{!isLoading && !noteSizeResponse && !subtreeSizeResponse && (
|
||||
<Button
|
||||
className="calculate-button"
|
||||
icon="bx bx-calculator"
|
||||
text={t("note_info_widget.calculate")}
|
||||
onClick={requestSizeInfo}
|
||||
/>
|
||||
)}
|
||||
|
||||
<span className="note-sizes-wrapper selectable-text">
|
||||
<span className="note-size">{formatSize(noteSizeResponse?.noteSize)}</span>
|
||||
{" "}
|
||||
{subtreeSizeResponse && subtreeSizeResponse.subTreeNoteCount > 1 &&
|
||||
<span className="subtree-size">{t("note_info_widget.subtree_size", { size: formatSize(subtreeSizeResponse.subTreeSize), count: subtreeSizeResponse.subTreeNoteCount })}</span>
|
||||
}
|
||||
{isLoading && <LoadingSpinner />}
|
||||
</span>
|
||||
</>;
|
||||
}
|
||||
|
||||
export function useNoteMetadata(note: FNote | null | undefined) {
|
||||
const [ isLoading, setIsLoading ] = useState(false);
|
||||
const [ noteSizeResponse, setNoteSizeResponse ] = useState<NoteSizeResponse>();
|
||||
|
||||
@ -132,7 +132,7 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [
|
||||
{
|
||||
title: t("note_info_widget.title"),
|
||||
icon: "bx bx-info-circle",
|
||||
show: ({ note }) => !!note,
|
||||
show: ({ note }) => !isNewLayout && !!note,
|
||||
content: NoteInfoTab,
|
||||
toggleCommand: "toggleRibbonTabNoteInfo"
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user