mirror of
https://github.com/zadam/trilium.git
synced 2025-12-23 08:39:57 +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 { TreeCommandNames } from "../../menus/tree_context_menu";
|
||||
import { Suggestion } from "../../services/note_autocomplete";
|
||||
import Badge from "../react/Badge";
|
||||
import SimpleBadge from "../react/Badge";
|
||||
import { useTriliumEvent } from "../react/hooks";
|
||||
|
||||
export interface ChooseNoteTypeResponse {
|
||||
@@ -108,7 +108,7 @@ export default function NoteTypeChooserDialogComponent() {
|
||||
value={[ item.type, item.templateNoteId ].join(",") }
|
||||
icon={item.uiIcon}>
|
||||
{item.title}
|
||||
{item.badges && item.badges.map((badge) => <Badge {...badge} />)}
|
||||
{item.badges && item.badges.map((badge) => <SimpleBadge {...badge} />)}
|
||||
</FormListItem>;
|
||||
}
|
||||
})}
|
||||
|
||||
@@ -40,3 +40,19 @@ body.prefers-centered-content .inline-title {
|
||||
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 clsx from "clsx";
|
||||
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 FNote from "../../entities/fnote";
|
||||
@@ -14,6 +14,9 @@ import NoteTitleWidget from "../note_title";
|
||||
import { useNoteContext, useStaticTooltip } from "../react/hooks";
|
||||
import { joinElements } from "../react/react_utils";
|
||||
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>([
|
||||
"text", "code"
|
||||
@@ -60,6 +63,7 @@ export default function InlineTitle() {
|
||||
</div>
|
||||
|
||||
<NoteTitleDetails />
|
||||
<NoteTypeSwitcher />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -71,6 +75,7 @@ function shouldShow(note: FNote | null | undefined, viewScope: ViewScope | undef
|
||||
return supportedNoteTypes.has(note.type);
|
||||
}
|
||||
|
||||
//#region Title details
|
||||
export function NoteTitleDetails() {
|
||||
const { note } = useNoteContext();
|
||||
const { metadata } = useNoteMetadata(note);
|
||||
@@ -121,3 +126,23 @@ function TextWithValue({ i18nKey, value, valueTooltip }: {
|
||||
</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;
|
||||
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;
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: color-mix(in srgb, var(--color, --badge-background-color) 80%, black);
|
||||
}
|
||||
}
|
||||
|
||||
.ext-badge {
|
||||
&.temporarily-editable-badge { --color: #4fa52b; }
|
||||
&.read-only-badge { --color: #e33f3b; }
|
||||
&.share-badge { --color: #3b82f6; }
|
||||
&.clipped-note-badge { --color: #57a2a5; }
|
||||
&.execute-badge { --color: #f59e0b; }
|
||||
|
||||
a {
|
||||
color: inherit !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
> * {
|
||||
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-badge {
|
||||
&.dropdown-backlinks-badge .dropdown-menu {
|
||||
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 Icon from "../react/Icon";
|
||||
import { useShareInfo } from "../shared_info";
|
||||
import { Badge } from "../react/Badge";
|
||||
|
||||
export default function NoteBadges() {
|
||||
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;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export default function Badge({ title, className }: BadgeProps) {
|
||||
return <span class={`badge ${className ?? ""}`}>{title}</span>
|
||||
interface BadgeProps {
|
||||
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