mirror of
https://github.com/zadam/trilium.git
synced 2025-12-23 16:49:58 +01:00
feat(layout/inline-title): basic note type switcher
This commit is contained in:
@@ -8,7 +8,7 @@ import note_types from "../../services/note_types";
|
|||||||
import { MenuCommandItem, MenuItem } from "../../menus/context_menu";
|
import { MenuCommandItem, MenuItem } from "../../menus/context_menu";
|
||||||
import { TreeCommandNames } from "../../menus/tree_context_menu";
|
import { TreeCommandNames } from "../../menus/tree_context_menu";
|
||||||
import { Suggestion } from "../../services/note_autocomplete";
|
import { Suggestion } from "../../services/note_autocomplete";
|
||||||
import Badge from "../react/Badge";
|
import SimpleBadge from "../react/Badge";
|
||||||
import { useTriliumEvent } from "../react/hooks";
|
import { useTriliumEvent } from "../react/hooks";
|
||||||
|
|
||||||
export interface ChooseNoteTypeResponse {
|
export interface ChooseNoteTypeResponse {
|
||||||
@@ -108,7 +108,7 @@ export default function NoteTypeChooserDialogComponent() {
|
|||||||
value={[ item.type, item.templateNoteId ].join(",") }
|
value={[ item.type, item.templateNoteId ].join(",") }
|
||||||
icon={item.uiIcon}>
|
icon={item.uiIcon}>
|
||||||
{item.title}
|
{item.title}
|
||||||
{item.badges && item.badges.map((badge) => <Badge {...badge} />)}
|
{item.badges && item.badges.map((badge) => <SimpleBadge {...badge} />)}
|
||||||
</FormListItem>;
|
</FormListItem>;
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -40,3 +40,19 @@ body.prefers-centered-content .inline-title {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note-type-switcher {
|
||||||
|
padding: 1em 0;
|
||||||
|
display: flex;
|
||||||
|
overflow-x: auto;
|
||||||
|
min-width: 0;
|
||||||
|
gap: 5px;
|
||||||
|
--badge-radius: 12px;
|
||||||
|
|
||||||
|
.ext-badge {
|
||||||
|
--color: var(--input-background-color);
|
||||||
|
color: var(--main-text-color);
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import "./InlineTitle.css";
|
|||||||
import { NoteType } from "@triliumnext/commons";
|
import { NoteType } from "@triliumnext/commons";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { ComponentChild } from "preact";
|
import { ComponentChild } from "preact";
|
||||||
import { useEffect, useRef, useState } from "preact/hooks";
|
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||||
import { Trans } from "react-i18next";
|
import { Trans } from "react-i18next";
|
||||||
|
|
||||||
import FNote from "../../entities/fnote";
|
import FNote from "../../entities/fnote";
|
||||||
@@ -14,6 +14,9 @@ import NoteTitleWidget from "../note_title";
|
|||||||
import { useNoteContext, useStaticTooltip } from "../react/hooks";
|
import { useNoteContext, useStaticTooltip } from "../react/hooks";
|
||||||
import { joinElements } from "../react/react_utils";
|
import { joinElements } from "../react/react_utils";
|
||||||
import { useNoteMetadata } from "../ribbon/NoteInfoTab";
|
import { useNoteMetadata } from "../ribbon/NoteInfoTab";
|
||||||
|
import { NOTE_TYPES } from "../../services/note_types";
|
||||||
|
import { Badge } from "../react/Badge";
|
||||||
|
import server from "../../services/server";
|
||||||
|
|
||||||
const supportedNoteTypes = new Set<NoteType>([
|
const supportedNoteTypes = new Set<NoteType>([
|
||||||
"text", "code"
|
"text", "code"
|
||||||
@@ -60,6 +63,7 @@ export default function InlineTitle() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NoteTitleDetails />
|
<NoteTitleDetails />
|
||||||
|
<NoteTypeSwitcher />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -71,6 +75,7 @@ function shouldShow(note: FNote | null | undefined, viewScope: ViewScope | undef
|
|||||||
return supportedNoteTypes.has(note.type);
|
return supportedNoteTypes.has(note.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#region Title details
|
||||||
export function NoteTitleDetails() {
|
export function NoteTitleDetails() {
|
||||||
const { note } = useNoteContext();
|
const { note } = useNoteContext();
|
||||||
const { metadata } = useNoteMetadata(note);
|
const { metadata } = useNoteMetadata(note);
|
||||||
@@ -121,3 +126,23 @@ function TextWithValue({ i18nKey, value, valueTooltip }: {
|
|||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Note type switcher
|
||||||
|
function NoteTypeSwitcher() {
|
||||||
|
const { note } = useNoteContext();
|
||||||
|
const noteTypes = useMemo(() => NOTE_TYPES.filter((nt) => !nt.reserved && !nt.static), []);
|
||||||
|
|
||||||
|
return (note &&
|
||||||
|
<div className="note-type-switcher">
|
||||||
|
{noteTypes.map(noteType => (
|
||||||
|
<Badge
|
||||||
|
key={noteType.type}
|
||||||
|
text={noteType.title}
|
||||||
|
onClick={() => server.put(`notes/${note.noteId}/type`, { type: noteType.type, mime: noteType.mime })}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|||||||
@@ -9,64 +9,19 @@
|
|||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
--badge-radius: 12px;
|
--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;
|
|
||||||
|
|
||||||
&.clickable {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: color-mix(in srgb, var(--color, --badge-background-color) 80%, black);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
.ext-badge {
|
||||||
&.temporarily-editable-badge { --color: #4fa52b; }
|
&.temporarily-editable-badge { --color: #4fa52b; }
|
||||||
&.read-only-badge { --color: #e33f3b; }
|
&.read-only-badge { --color: #e33f3b; }
|
||||||
&.share-badge { --color: #3b82f6; }
|
&.share-badge { --color: #3b82f6; }
|
||||||
&.clipped-note-badge { --color: #57a2a5; }
|
&.clipped-note-badge { --color: #57a2a5; }
|
||||||
&.execute-badge { --color: #f59e0b; }
|
&.execute-badge { --color: #f59e0b; }
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit !important;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
.dropdown-badge {
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.breadcrumb-dropdown-badge {
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
border-radius: var(--badge-radius);
|
|
||||||
|
|
||||||
&.dropdown-backlinks-badge .dropdown-menu {
|
&.dropdown-backlinks-badge .dropdown-menu {
|
||||||
min-width: 500px;
|
min-width: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb-badge {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
border: 0;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import Dropdown, { DropdownProps } from "../react/Dropdown";
|
|||||||
import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean, useStaticTooltip } from "../react/hooks";
|
import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean, useStaticTooltip } from "../react/hooks";
|
||||||
import Icon from "../react/Icon";
|
import Icon from "../react/Icon";
|
||||||
import { useShareInfo } from "../shared_info";
|
import { useShareInfo } from "../shared_info";
|
||||||
|
import { Badge } from "../react/Badge";
|
||||||
|
|
||||||
export default function NoteBadges() {
|
export default function NoteBadges() {
|
||||||
return (
|
return (
|
||||||
@@ -97,63 +98,3 @@ function ExecuteBadge() {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
useStaticTooltip(containerRef, {
|
|
||||||
placement: "bottom",
|
|
||||||
fallbackPlacements: [ "bottom" ],
|
|
||||||
animation: false,
|
|
||||||
html: true,
|
|
||||||
title: tooltip
|
|
||||||
});
|
|
||||||
|
|
||||||
const content = <>
|
|
||||||
{icon && <><Icon icon={icon} /> </>}
|
|
||||||
<span class="text">{text}</span>
|
|
||||||
</>;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={containerRef}
|
|
||||||
className={clsx("breadcrumb-badge", className, { "clickable": !!onClick })}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
{href ? <a href={href}>{content}</a> : <span>{content}</span>}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function BadgeWithDropdown({ children, tooltip, className, dropdownOptions, ...props }: BadgeProps & {
|
|
||||||
children: ComponentChildren,
|
|
||||||
dropdownOptions?: Partial<DropdownProps>
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<Dropdown
|
|
||||||
className={`breadcrumb-dropdown-badge dropdown-${className}`}
|
|
||||||
text={<Badge className={className} {...props} />}
|
|
||||||
noDropdownListStyle
|
|
||||||
noSelectButtonStyle
|
|
||||||
hideToggleArrow
|
|
||||||
title={tooltip}
|
|
||||||
titlePosition="bottom"
|
|
||||||
{...dropdownOptions}
|
|
||||||
dropdownOptions={{
|
|
||||||
...dropdownOptions?.dropdownOptions,
|
|
||||||
popperConfig: {
|
|
||||||
...dropdownOptions?.dropdownOptions?.popperConfig,
|
|
||||||
placement: "bottom", strategy: "fixed"
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>{children}</Dropdown>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
49
apps/client/src/widgets/react/Badge.css
Normal file
49
apps/client/src/widgets/react/Badge.css
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
.ext-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;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: color-mix(in srgb, var(--color, --badge-background-color) 80%, black);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit !important;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-badge {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
border-radius: var(--badge-radius);
|
||||||
|
|
||||||
|
.ext-badge {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,78 @@
|
|||||||
interface BadgeProps {
|
import "./Badge.css";
|
||||||
|
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { ComponentChildren, MouseEventHandler } from "preact";
|
||||||
|
import { useRef } from "preact/hooks";
|
||||||
|
|
||||||
|
import Dropdown, { DropdownProps } from "./Dropdown";
|
||||||
|
import { useStaticTooltip } from "./hooks";
|
||||||
|
import Icon from "./Icon";
|
||||||
|
|
||||||
|
interface SimpleBadgeProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Badge({ title, className }: BadgeProps) {
|
interface BadgeProps {
|
||||||
return <span class={`badge ${className ?? ""}`}>{title}</span>
|
text?: string;
|
||||||
|
icon?: string;
|
||||||
|
className?: string;
|
||||||
|
tooltip?: string;
|
||||||
|
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||||
|
href?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SimpleBadge({ title, className }: SimpleBadgeProps) {
|
||||||
|
return <span class={`badge ${className ?? ""}`}>{title}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Badge({ icon, className, text, tooltip, onClick, href }: BadgeProps) {
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
useStaticTooltip(containerRef, {
|
||||||
|
placement: "bottom",
|
||||||
|
fallbackPlacements: [ "bottom" ],
|
||||||
|
animation: false,
|
||||||
|
html: true,
|
||||||
|
title: tooltip
|
||||||
|
});
|
||||||
|
|
||||||
|
const content = <>
|
||||||
|
{icon && <><Icon icon={icon} /> </>}
|
||||||
|
<span class="text">{text}</span>
|
||||||
|
</>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className={clsx("ext-badge", className, { "clickable": !!onClick })}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{href ? <a href={href}>{content}</a> : <span>{content}</span>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BadgeWithDropdown({ children, tooltip, className, dropdownOptions, ...props }: BadgeProps & {
|
||||||
|
children: ComponentChildren,
|
||||||
|
dropdownOptions?: Partial<DropdownProps>
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
className={`dropdown-badge dropdown-${className}`}
|
||||||
|
text={<Badge className={className} {...props} />}
|
||||||
|
noDropdownListStyle
|
||||||
|
noSelectButtonStyle
|
||||||
|
hideToggleArrow
|
||||||
|
title={tooltip}
|
||||||
|
titlePosition="bottom"
|
||||||
|
{...dropdownOptions}
|
||||||
|
dropdownOptions={{
|
||||||
|
...dropdownOptions?.dropdownOptions,
|
||||||
|
popperConfig: {
|
||||||
|
...dropdownOptions?.dropdownOptions?.popperConfig,
|
||||||
|
placement: "bottom", strategy: "fixed"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>{children}</Dropdown>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user