diff --git a/apps/client/src/widgets/Breadcrumb.tsx b/apps/client/src/widgets/Breadcrumb.tsx index 83ff966b8..c218f7cea 100644 --- a/apps/client/src/widgets/Breadcrumb.tsx +++ b/apps/client/src/widgets/Breadcrumb.tsx @@ -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 ; + return <> + + + ; } return ; diff --git a/apps/client/src/widgets/BreadcrumbBadges.css b/apps/client/src/widgets/BreadcrumbBadges.css index 7d6405159..a8fcd6657 100644 --- a/apps/client/src/widgets/BreadcrumbBadges.css +++ b/apps/client/src/widgets/BreadcrumbBadges.css @@ -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; } } diff --git a/apps/client/src/widgets/BreadcrumbBadges.tsx b/apps/client/src/widgets/BreadcrumbBadges.tsx index 0e3cf986f..9f8b0de93 100644 --- a/apps/client/src/widgets/BreadcrumbBadges.tsx +++ b/apps/client/src/widgets/BreadcrumbBadges.tsx @@ -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 && + +
    + + + {note.type} {note.mime && ({note.mime})}} /> + {note.noteId}} /> + } /> +
+
+ ); +} + +function NoteInfoValue({ text, title, value }: { text: string; title?: string, value: ComponentChildren }) { + return ( +
  • + {text}{": "} + {value} +
  • + ); +} + 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 ( } 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} ); } diff --git a/apps/client/src/widgets/NoteTitleDetails.tsx b/apps/client/src/widgets/NoteTitleDetails.tsx index 58f4da0f7..c9992578f 100644 --- a/apps/client/src/widgets/NoteTitleDetails.tsx +++ b/apps/client/src/widgets/NoteTitleDetails.tsx @@ -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 && - ), - (isDefaultView && !isHiddenNote && metadata?.dateModified && - ) - ].filter(item => !!item); + const items: ComponentChild[] = [].filter(item => !!item); - return ( + return items.length && (
    {joinElements(items, " • ")}
    diff --git a/apps/client/src/widgets/ribbon/NoteInfoTab.tsx b/apps/client/src/widgets/ribbon/NoteInfoTab.tsx index 888412867..8180f74a6 100644 --- a/apps/client/src/widgets/ribbon/NoteInfoTab.tsx +++ b/apps/client/src/widgets/ribbon/NoteInfoTab.tsx @@ -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 (
    @@ -42,23 +41,7 @@ export default function NoteInfoTab({ note }: TabContext) {
    {t("note_info_widget.note_size")}: - {!isLoading && !noteSizeResponse && !subtreeSizeResponse && ( -
    @@ -67,6 +50,28 @@ export default function NoteInfoTab({ note }: TabContext) { ); } +export function NoteSizeWidget({ isLoading, noteSizeResponse, subtreeSizeResponse, requestSizeInfo }: Omit, "metadata">) { + return <> + {!isLoading && !noteSizeResponse && !subtreeSizeResponse && ( +