mirror of
https://github.com/zadam/trilium.git
synced 2025-12-04 14:34:24 +01:00
Merge 5b310f3e46e18071bef89ae7cf5cba17c7cd6ae8 into b8585594cd138783588a7ac4d6a3260de779427d
This commit is contained in:
commit
341b85a8cb
@ -30,7 +30,6 @@ import ScrollingContainer from "../widgets/containers/scrolling_container.js";
|
|||||||
import ScrollPadding from "../widgets/scroll_padding.js";
|
import ScrollPadding from "../widgets/scroll_padding.js";
|
||||||
import SearchResult from "../widgets/search_result.jsx";
|
import SearchResult from "../widgets/search_result.jsx";
|
||||||
import SharedInfo from "../widgets/shared_info.jsx";
|
import SharedInfo from "../widgets/shared_info.jsx";
|
||||||
import SpacerWidget from "../widgets/spacer.js";
|
|
||||||
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
|
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
|
||||||
import SqlResults from "../widgets/sql_result.js";
|
import SqlResults from "../widgets/sql_result.js";
|
||||||
import SqlTableSchemas from "../widgets/sql_table_schemas.js";
|
import SqlTableSchemas from "../widgets/sql_table_schemas.js";
|
||||||
@ -43,8 +42,8 @@ import UploadAttachmentsDialog from "../widgets/dialogs/upload_attachments.js";
|
|||||||
import utils from "../services/utils.js";
|
import utils from "../services/utils.js";
|
||||||
import WatchedFileUpdateStatusWidget from "../widgets/watched_file_update_status.js";
|
import WatchedFileUpdateStatusWidget from "../widgets/watched_file_update_status.js";
|
||||||
import NoteDetail from "../widgets/NoteDetail.jsx";
|
import NoteDetail from "../widgets/NoteDetail.jsx";
|
||||||
import RightPanelWidget from "../widgets/sidebar/RightPanelWidget.jsx";
|
|
||||||
import PromotedAttributes from "../widgets/PromotedAttributes.jsx";
|
import PromotedAttributes from "../widgets/PromotedAttributes.jsx";
|
||||||
|
import SpacerWidget from "../widgets/launch_bar/SpacerWidget.jsx";
|
||||||
|
|
||||||
export default class DesktopLayout {
|
export default class DesktopLayout {
|
||||||
|
|
||||||
@ -125,7 +124,7 @@ export default class DesktopLayout {
|
|||||||
.cssBlock(".title-row > * { margin: 5px; }")
|
.cssBlock(".title-row > * { margin: 5px; }")
|
||||||
.child(<NoteIconWidget />)
|
.child(<NoteIconWidget />)
|
||||||
.child(<NoteTitleWidget />)
|
.child(<NoteTitleWidget />)
|
||||||
.child(new SpacerWidget(0, 1))
|
.child(<SpacerWidget baseSize={0} growthFactor={1} />)
|
||||||
.child(<MovePaneButton direction="left" />)
|
.child(<MovePaneButton direction="left" />)
|
||||||
.child(<MovePaneButton direction="right" />)
|
.child(<MovePaneButton direction="right" />)
|
||||||
.child(<ClosePaneButton />)
|
.child(<ClosePaneButton />)
|
||||||
|
|||||||
@ -150,7 +150,7 @@ export function isMac() {
|
|||||||
|
|
||||||
export const hasTouchBar = (isMac() && isElectron());
|
export const hasTouchBar = (isMac() && isElectron());
|
||||||
|
|
||||||
function isCtrlKey(evt: KeyboardEvent | MouseEvent | JQuery.ClickEvent | JQuery.ContextMenuEvent | JQuery.TriggeredEvent | React.PointerEvent<HTMLCanvasElement> | JQueryEventObject) {
|
export function isCtrlKey(evt: KeyboardEvent | MouseEvent | JQuery.ClickEvent | JQuery.ContextMenuEvent | JQuery.TriggeredEvent | React.PointerEvent<HTMLCanvasElement> | JQueryEventObject) {
|
||||||
return (!isMac() && evt.ctrlKey) || (isMac() && evt.metaKey);
|
return (!isMac() && evt.ctrlKey) || (isMac() && evt.metaKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import FlexContainer from "./containers/flex_container.js";
|
import FlexContainer from "./containers/flex_container.js";
|
||||||
import OpenNoteButtonWidget from "./buttons/open_note_button_widget.js";
|
|
||||||
import BookmarkFolderWidget from "./buttons/bookmark_folder.js";
|
import BookmarkFolderWidget from "./buttons/bookmark_folder.js";
|
||||||
import froca from "../services/froca.js";
|
import froca from "../services/froca.js";
|
||||||
import utils from "../services/utils.js";
|
import utils from "../services/utils.js";
|
||||||
@ -23,10 +22,6 @@ export default class BookmarkButtons extends FlexContainer<Component> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async refresh(): Promise<void> {
|
async refresh(): Promise<void> {
|
||||||
this.$widget.empty();
|
|
||||||
this.children = [];
|
|
||||||
this.noteIds = [];
|
|
||||||
|
|
||||||
const bookmarkParentNote = await froca.getNote("_lbBookmarks");
|
const bookmarkParentNote = await froca.getNote("_lbBookmarks");
|
||||||
|
|
||||||
if (!bookmarkParentNote) {
|
if (!bookmarkParentNote) {
|
||||||
@ -37,16 +32,7 @@ export default class BookmarkButtons extends FlexContainer<Component> {
|
|||||||
this.noteIds.push(note.noteId);
|
this.noteIds.push(note.noteId);
|
||||||
|
|
||||||
let buttonWidget: OpenNoteButtonWidget | BookmarkFolderWidget = note.isLabelTruthy("bookmarkFolder")
|
let buttonWidget: OpenNoteButtonWidget | BookmarkFolderWidget = note.isLabelTruthy("bookmarkFolder")
|
||||||
? new BookmarkFolderWidget(note)
|
? new BookmarkFolderWidget(note);
|
||||||
: new OpenNoteButtonWidget(note).class("launcher-button");
|
|
||||||
|
|
||||||
if (this.settings.titlePlacement) {
|
|
||||||
if (!("settings" in buttonWidget)) {
|
|
||||||
(buttonWidget as any).settings = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
(buttonWidget as any).settings.titlePlacement = this.settings.titlePlacement;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.child(buttonWidget);
|
this.child(buttonWidget);
|
||||||
|
|
||||||
|
|||||||
@ -1,88 +0,0 @@
|
|||||||
import RightDropdownButtonWidget from "./right_dropdown_button.js";
|
|
||||||
import linkService from "../../services/link.js";
|
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import type FNote from "../../entities/fnote.js";
|
|
||||||
|
|
||||||
const DROPDOWN_TPL = `
|
|
||||||
<div class="bookmark-folder-widget">
|
|
||||||
<style>
|
|
||||||
.bookmark-folder-widget {
|
|
||||||
min-width: 400px;
|
|
||||||
max-height: 500px;
|
|
||||||
padding: 7px 15px 0 15px;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bookmark-folder-widget ul {
|
|
||||||
padding: 0;
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bookmark-folder-widget .note-link {
|
|
||||||
display: block;
|
|
||||||
padding: 5px 10px 5px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bookmark-folder-widget .note-link:hover {
|
|
||||||
background-color: var(--accented-background-color);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu .bookmark-folder-widget a:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bookmark-folder-widget li .note-link {
|
|
||||||
padding-inline-start: 35px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="parent-note"></div>
|
|
||||||
|
|
||||||
<ul class="children-notes"></ul>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
interface LinkOptions {
|
|
||||||
showTooltip: boolean;
|
|
||||||
showNoteIcon: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class BookmarkFolderWidget extends RightDropdownButtonWidget {
|
|
||||||
private note: FNote;
|
|
||||||
private $parentNote!: JQuery<HTMLElement>;
|
|
||||||
private $childrenNotes!: JQuery<HTMLElement>;
|
|
||||||
declare $dropdownContent: JQuery<HTMLElement>;
|
|
||||||
|
|
||||||
constructor(note: FNote) {
|
|
||||||
super(utils.escapeHtml(note.title), note.getIcon(), DROPDOWN_TPL);
|
|
||||||
|
|
||||||
this.note = note;
|
|
||||||
}
|
|
||||||
|
|
||||||
doRender(): void {
|
|
||||||
super.doRender();
|
|
||||||
|
|
||||||
this.$parentNote = this.$dropdownContent.find(".parent-note");
|
|
||||||
this.$childrenNotes = this.$dropdownContent.find(".children-notes");
|
|
||||||
}
|
|
||||||
|
|
||||||
async dropdownShown(): Promise<void> {
|
|
||||||
this.$parentNote.empty();
|
|
||||||
this.$childrenNotes.empty();
|
|
||||||
|
|
||||||
const linkOptions: LinkOptions = {
|
|
||||||
showTooltip: false,
|
|
||||||
showNoteIcon: true
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$parentNote.append((await linkService.createLink(this.note.noteId, linkOptions)).addClass("note-link"));
|
|
||||||
|
|
||||||
for (const childNote of await this.note.getChildNotes()) {
|
|
||||||
this.$childrenNotes.append($("<li>").append((await linkService.createLink(childNote.noteId, linkOptions)).addClass("note-link")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshIcon(): void {}
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
import OnClickButtonWidget from "./onclick_button.js";
|
|
||||||
import linkContextMenuService from "../../menus/link_context_menu.js";
|
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import appContext from "../../components/app_context.js";
|
|
||||||
import type FNote from "../../entities/fnote.js";
|
|
||||||
|
|
||||||
export default class OpenNoteButtonWidget extends OnClickButtonWidget {
|
|
||||||
|
|
||||||
private noteToOpen: FNote;
|
|
||||||
|
|
||||||
constructor(noteToOpen: FNote) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.noteToOpen = noteToOpen;
|
|
||||||
|
|
||||||
this.title(() => utils.escapeHtml(this.noteToOpen.title))
|
|
||||||
.icon(() => this.noteToOpen.getIcon())
|
|
||||||
.onClick((widget, evt) => this.launch(evt))
|
|
||||||
.onAuxClick((widget, evt) => this.launch(evt))
|
|
||||||
.onContextMenu((evt) => {
|
|
||||||
if (evt) {
|
|
||||||
linkContextMenuService.openContextMenu(this.noteToOpen.noteId, evt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async launch(evt: JQuery.ClickEvent | JQuery.TriggeredEvent | JQuery.ContextMenuEvent) {
|
|
||||||
if (evt.which === 3) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const hoistedNoteId = this.getHoistedNoteId();
|
|
||||||
const ctrlKey = utils.isCtrlKey(evt);
|
|
||||||
|
|
||||||
if ((evt.which === 1 && ctrlKey) || evt.which === 2) {
|
|
||||||
const activate = evt.shiftKey ? true : false;
|
|
||||||
await appContext.tabManager.openInNewTab(this.noteToOpen.noteId, hoistedNoteId, activate);
|
|
||||||
} else {
|
|
||||||
await appContext.tabManager.openInSameTab(this.noteToOpen.noteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getHoistedNoteId() {
|
|
||||||
return this.noteToOpen.getRelationValue("hoistedNote") || appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialRenderCompleteEvent() {
|
|
||||||
// we trigger refresh above
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +1,7 @@
|
|||||||
import CalendarWidget from "../buttons/calendar.js";
|
import CalendarWidget from "../buttons/calendar.js";
|
||||||
import SpacerWidget from "../spacer.js";
|
|
||||||
import BookmarkButtons from "../bookmark_buttons.js";
|
|
||||||
import ProtectedSessionStatusWidget from "../buttons/protected_session_status.js";
|
import ProtectedSessionStatusWidget from "../buttons/protected_session_status.js";
|
||||||
import SyncStatusWidget from "../sync_status.js";
|
import SyncStatusWidget from "../sync_status.js";
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget, { wrapReactWidgets } from "../basic_widget.js";
|
||||||
import NoteLauncher from "../buttons/launcher/note_launcher.js";
|
import NoteLauncher from "../buttons/launcher/note_launcher.js";
|
||||||
import ScriptLauncher from "../buttons/launcher/script_launcher.js";
|
import ScriptLauncher from "../buttons/launcher/script_launcher.js";
|
||||||
import CommandButtonWidget from "../buttons/command_button.js";
|
import CommandButtonWidget from "../buttons/command_button.js";
|
||||||
@ -14,6 +12,8 @@ import QuickSearchLauncherWidget from "../quick_search_launcher.js";
|
|||||||
import type FNote from "../../entities/fnote.js";
|
import type FNote from "../../entities/fnote.js";
|
||||||
import type { CommandNames } from "../../components/app_context.js";
|
import type { CommandNames } from "../../components/app_context.js";
|
||||||
import AiChatButton from "../buttons/ai_chat_button.js";
|
import AiChatButton from "../buttons/ai_chat_button.js";
|
||||||
|
import BookmarkButtons from "../launch_bar/BookmarkButtons.jsx";
|
||||||
|
import SpacerWidget from "../launch_bar/SpacerWidget.jsx";
|
||||||
|
|
||||||
interface InnerWidget extends BasicWidget {
|
interface InnerWidget extends BasicWidget {
|
||||||
settings?: {
|
settings?: {
|
||||||
@ -64,7 +64,7 @@ export default class LauncherWidget extends BasicWidget {
|
|||||||
} else if (launcherType === "customWidget") {
|
} else if (launcherType === "customWidget") {
|
||||||
widget = await this.initCustomWidget(note);
|
widget = await this.initCustomWidget(note);
|
||||||
} else if (launcherType === "builtinWidget") {
|
} else if (launcherType === "builtinWidget") {
|
||||||
widget = this.initBuiltinWidget(note);
|
widget = wrapReactWidgets<BasicWidget>([ this.initBuiltinWidget(note) ])[0];
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Unrecognized launcher type '${launcherType}' for launcher '${note.noteId}' title '${note.title}'`);
|
throw new Error(`Unrecognized launcher type '${launcherType}' for launcher '${note.noteId}' title '${note.title}'`);
|
||||||
}
|
}
|
||||||
@ -109,9 +109,9 @@ export default class LauncherWidget extends BasicWidget {
|
|||||||
const baseSize = parseInt(note.getLabelValue("baseSize") || "40");
|
const baseSize = parseInt(note.getLabelValue("baseSize") || "40");
|
||||||
const growthFactor = parseInt(note.getLabelValue("growthFactor") || "100");
|
const growthFactor = parseInt(note.getLabelValue("growthFactor") || "100");
|
||||||
|
|
||||||
return new SpacerWidget(baseSize, growthFactor);
|
return <SpacerWidget baseSize={baseSize} growthFactor={growthFactor} />;
|
||||||
case "bookmarks":
|
case "bookmarks":
|
||||||
return new BookmarkButtons(this.isHorizontalLayout);
|
return <BookmarkButtons isHorizontalLayout={this.isHorizontalLayout} />
|
||||||
case "protectedSession":
|
case "protectedSession":
|
||||||
return new ProtectedSessionStatusWidget();
|
return new ProtectedSessionStatusWidget();
|
||||||
case "syncStatus":
|
case "syncStatus":
|
||||||
31
apps/client/src/widgets/launch_bar/BookmarkButtons.css
Normal file
31
apps/client/src/widgets/launch_bar/BookmarkButtons.css
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
.bookmark-folder-widget {
|
||||||
|
min-width: 400px;
|
||||||
|
max-height: 500px;
|
||||||
|
padding: 7px 15px 0 15px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark-folder-widget ul {
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark-folder-widget .note-link {
|
||||||
|
display: block;
|
||||||
|
padding: 5px 10px 5px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark-folder-widget .note-link:hover {
|
||||||
|
background-color: var(--accented-background-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu .bookmark-folder-widget a:hover:not(.disabled) {
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark-folder-widget li .note-link {
|
||||||
|
padding-inline-start: 35px;
|
||||||
|
}
|
||||||
98
apps/client/src/widgets/launch_bar/BookmarkButtons.tsx
Normal file
98
apps/client/src/widgets/launch_bar/BookmarkButtons.tsx
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { useMemo } from "preact/hooks";
|
||||||
|
import { LaunchBarActionButton, LaunchBarDropdownButton, type LaunchBarWidgetProps } from "./launch_bar_widgets";
|
||||||
|
import { CSSProperties } from "preact";
|
||||||
|
import type FNote from "../../entities/fnote";
|
||||||
|
import { useChildNotes, useNoteLabel, useNoteLabelBoolean, useNoteProperty } from "../react/hooks";
|
||||||
|
import appContext from "../../components/app_context";
|
||||||
|
import { escapeHtml, isCtrlKey } from "../../services/utils";
|
||||||
|
import link_context_menu from "../../menus/link_context_menu";
|
||||||
|
import "./BookmarkButtons.css";
|
||||||
|
import NoteLink from "../react/NoteLink";
|
||||||
|
|
||||||
|
const PARENT_NOTE_ID = "_lbBookmarks";
|
||||||
|
|
||||||
|
export default function BookmarkButtons({ isHorizontalLayout }: LaunchBarWidgetProps) {
|
||||||
|
const style = useMemo<CSSProperties>(() => ({
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: isHorizontalLayout ? "row" : "column",
|
||||||
|
contain: "none"
|
||||||
|
}), [ isHorizontalLayout ]);
|
||||||
|
const childNotes = useChildNotes(PARENT_NOTE_ID);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={style}>
|
||||||
|
{childNotes?.map(childNote => <SingleBookmark note={childNote} />)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SingleBookmark({ note }: { note: FNote }) {
|
||||||
|
const [ bookmarkFolder ] = useNoteLabelBoolean(note, "bookmarkFolder");
|
||||||
|
return bookmarkFolder
|
||||||
|
? <BookmarkFolder note={note} />
|
||||||
|
: <OpenNoteButtonWidget note={note} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function OpenNoteButtonWidget({ note }: { note: FNote }) {
|
||||||
|
const [ iconClass ] = useNoteLabel(note, "iconClass");
|
||||||
|
const title = useNoteProperty(note, "title");
|
||||||
|
|
||||||
|
async function launch(evt: MouseEvent) {
|
||||||
|
if (evt.which === 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const hoistedNoteId = getHoistedNoteId(note);
|
||||||
|
const ctrlKey = isCtrlKey(evt);
|
||||||
|
|
||||||
|
if ((evt.which === 1 && ctrlKey) || evt.which === 2) {
|
||||||
|
const activate = evt.shiftKey ? true : false;
|
||||||
|
await appContext.tabManager.openInNewTab(note.noteId, hoistedNoteId, activate);
|
||||||
|
} else {
|
||||||
|
await appContext.tabManager.openInSameTab(note.noteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return title && iconClass && (
|
||||||
|
<LaunchBarActionButton
|
||||||
|
icon={iconClass}
|
||||||
|
text={escapeHtml(title)}
|
||||||
|
onClick={launch}
|
||||||
|
onAuxClick={launch}
|
||||||
|
onContextMenu={evt => {
|
||||||
|
evt.preventDefault();
|
||||||
|
link_context_menu.openContextMenu(note.noteId, evt);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function BookmarkFolder({ note }: { note: FNote }) {
|
||||||
|
const [ iconClass ] = useNoteLabel(note, "iconClass");
|
||||||
|
const title = useNoteProperty(note, "title");
|
||||||
|
const childNotes = useChildNotes(note.noteId);
|
||||||
|
|
||||||
|
return title && iconClass && (
|
||||||
|
<LaunchBarDropdownButton
|
||||||
|
icon={iconClass}
|
||||||
|
title={escapeHtml(title)}
|
||||||
|
>
|
||||||
|
<div className="bookmark-folder-widget">
|
||||||
|
<div className="parent-note">
|
||||||
|
<NoteLink notePath={note.noteId} noPreview showNoteIcon containerClassName="note-link" noTnLink />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul className="children-notes">
|
||||||
|
{childNotes.map(childNote => (
|
||||||
|
<li>
|
||||||
|
<NoteLink notePath={childNote.noteId} noPreview showNoteIcon containerClassName="note-link" noTnLink />
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</LaunchBarDropdownButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHoistedNoteId(noteToOpen: FNote) {
|
||||||
|
return noteToOpen.getRelationValue("hoistedNote") || appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
export default function RightDropdownButton() {
|
||||||
|
return <p>Button goes here.</p>;
|
||||||
|
}
|
||||||
35
apps/client/src/widgets/launch_bar/SpacerWidget.tsx
Normal file
35
apps/client/src/widgets/launch_bar/SpacerWidget.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import appContext, { CommandNames } from "../../components/app_context";
|
||||||
|
import contextMenu from "../../menus/context_menu";
|
||||||
|
import { t } from "../../services/i18n";
|
||||||
|
import { isMobile } from "../../services/utils";
|
||||||
|
|
||||||
|
interface SpacerWidgetProps {
|
||||||
|
baseSize?: number;
|
||||||
|
growthFactor?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SpacerWidget({ baseSize, growthFactor }: SpacerWidgetProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="spacer"
|
||||||
|
style={{
|
||||||
|
flexBasis: baseSize ?? 0,
|
||||||
|
flexGrow: growthFactor ?? 1000,
|
||||||
|
flexShrink: 1000
|
||||||
|
}}
|
||||||
|
onContextMenu={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
contextMenu.show<CommandNames>({
|
||||||
|
x: e.pageX,
|
||||||
|
y: e.pageY,
|
||||||
|
items: [{ title: t("spacer.configure_launchbar"), command: "showLaunchBarSubtree", uiIcon: "bx " + (isMobile() ? "bx-mobile" : "bx-sidebar") }],
|
||||||
|
selectMenuItemHandler: ({ command }) => {
|
||||||
|
if (command) {
|
||||||
|
appContext.triggerCommand(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
30
apps/client/src/widgets/launch_bar/launch_bar_widgets.tsx
Normal file
30
apps/client/src/widgets/launch_bar/launch_bar_widgets.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import ActionButton, { ActionButtonProps } from "../react/ActionButton";
|
||||||
|
import Dropdown, { DropdownProps } from "../react/Dropdown";
|
||||||
|
import Icon from "../react/Icon";
|
||||||
|
|
||||||
|
export interface LaunchBarWidgetProps {
|
||||||
|
isHorizontalLayout: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LaunchBarActionButton(props: Omit<ActionButtonProps, "className" | "noIconActionClass" | "titlePosition">) {
|
||||||
|
return (
|
||||||
|
<ActionButton
|
||||||
|
className="button-widget launcher-button"
|
||||||
|
noIconActionClass
|
||||||
|
titlePosition="right"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LaunchBarDropdownButton({ children, icon, ...props }: Pick<DropdownProps, "title" | "children"> & { icon: string }) {
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
className="right-dropdown-widget"
|
||||||
|
buttonClassName="right-dropdown-button launcher-button"
|
||||||
|
hideToggleArrow
|
||||||
|
text={<Icon icon={icon} />}
|
||||||
|
{...props}
|
||||||
|
>{children}</Dropdown>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -2,13 +2,13 @@ import { useEffect, useRef, useState } from "preact/hooks";
|
|||||||
import { CommandNames } from "../../components/app_context";
|
import { CommandNames } from "../../components/app_context";
|
||||||
import { useStaticTooltip } from "./hooks";
|
import { useStaticTooltip } from "./hooks";
|
||||||
import keyboard_actions from "../../services/keyboard_actions";
|
import keyboard_actions from "../../services/keyboard_actions";
|
||||||
|
import { HTMLAttributes } from "preact";
|
||||||
|
|
||||||
export interface ActionButtonProps {
|
export interface ActionButtonProps extends Pick<HTMLAttributes<HTMLButtonElement>, "onClick" | "onAuxClick" | "onContextMenu"> {
|
||||||
text: string;
|
text: string;
|
||||||
titlePosition?: "top" | "right" | "bottom" | "left";
|
titlePosition?: "top" | "right" | "bottom" | "left";
|
||||||
icon: string;
|
icon: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: (e: MouseEvent) => void;
|
|
||||||
triggerCommand?: CommandNames;
|
triggerCommand?: CommandNames;
|
||||||
noIconActionClass?: boolean;
|
noIconActionClass?: boolean;
|
||||||
frame?: boolean;
|
frame?: boolean;
|
||||||
@ -16,7 +16,7 @@ export interface ActionButtonProps {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ActionButton({ text, icon, className, onClick, triggerCommand, titlePosition, noIconActionClass, frame, active, disabled }: ActionButtonProps) {
|
export default function ActionButton({ text, icon, className, triggerCommand, titlePosition, noIconActionClass, frame, active, disabled, ...restProps }: ActionButtonProps) {
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
const [ keyboardShortcut, setKeyboardShortcut ] = useState<string[]>();
|
const [ keyboardShortcut, setKeyboardShortcut ] = useState<string[]>();
|
||||||
|
|
||||||
@ -35,8 +35,8 @@ export default function ActionButton({ text, icon, className, onClick, triggerCo
|
|||||||
return <button
|
return <button
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
class={`${className ?? ""} ${!noIconActionClass ? "icon-action" : "btn"} ${icon} ${frame ? "btn btn-primary" : ""} ${disabled ? "disabled" : ""} ${active ? "active" : ""}`}
|
class={`${className ?? ""} ${!noIconActionClass ? "icon-action" : "btn"} ${icon} ${frame ? "btn btn-primary" : ""} ${disabled ? "disabled" : ""} ${active ? "active" : ""}`}
|
||||||
onClick={onClick}
|
|
||||||
data-trigger-command={triggerCommand}
|
data-trigger-command={triggerCommand}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
{...restProps}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { useImperativeSearchHighlighlighting, useTriliumEvent } from "./hooks";
|
|||||||
|
|
||||||
interface NoteLinkOpts {
|
interface NoteLinkOpts {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
containerClassName?: string;
|
||||||
notePath: string | string[];
|
notePath: string | string[];
|
||||||
showNotePath?: boolean;
|
showNotePath?: boolean;
|
||||||
showNoteIcon?: boolean;
|
showNoteIcon?: boolean;
|
||||||
@ -17,7 +18,7 @@ interface NoteLinkOpts {
|
|||||||
noContextMenu?: boolean;
|
noContextMenu?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NoteLink({ className, notePath, showNotePath, showNoteIcon, style, noPreview, noTnLink, highlightedTokens, title, viewScope, noContextMenu }: NoteLinkOpts) {
|
export default function NoteLink({ className, containerClassName, notePath, showNotePath, showNoteIcon, style, noPreview, noTnLink, highlightedTokens, title, viewScope, noContextMenu }: NoteLinkOpts) {
|
||||||
const stringifiedNotePath = Array.isArray(notePath) ? notePath.join("/") : notePath;
|
const stringifiedNotePath = Array.isArray(notePath) ? notePath.join("/") : notePath;
|
||||||
const noteId = stringifiedNotePath.split("/").at(-1);
|
const noteId = stringifiedNotePath.split("/").at(-1);
|
||||||
const ref = useRef<HTMLSpanElement>(null);
|
const ref = useRef<HTMLSpanElement>(null);
|
||||||
@ -71,6 +72,6 @@ export default function NoteLink({ className, notePath, showNotePath, showNoteIc
|
|||||||
$linkEl?.addClass(className);
|
$linkEl?.addClass(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <span ref={ref} />
|
return <span className={containerClassName} ref={ref} />
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import toast, { ToastOptions } from "../../services/toast";
|
|||||||
import utils, { escapeRegExp, reloadFrontendApp } from "../../services/utils";
|
import utils, { escapeRegExp, reloadFrontendApp } from "../../services/utils";
|
||||||
import server from "../../services/server";
|
import server from "../../services/server";
|
||||||
import { removeIndividualBinding } from "../../services/shortcuts";
|
import { removeIndividualBinding } from "../../services/shortcuts";
|
||||||
|
import froca from "../../services/froca";
|
||||||
|
|
||||||
export function useTriliumEvent<T extends EventNames>(eventName: T, handler: (data: EventData<T>) => void) {
|
export function useTriliumEvent<T extends EventNames>(eventName: T, handler: (data: EventData<T>) => void) {
|
||||||
const parentComponent = useContext(ParentComponent);
|
const parentComponent = useContext(ParentComponent);
|
||||||
@ -836,3 +837,15 @@ async function isNoteReadOnly(note: FNote, noteContext: NoteContext) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useChildNotes(parentNoteId: string) {
|
||||||
|
const [ childNotes, setChildNotes ] = useState<FNote[]>([]);
|
||||||
|
async function refreshChildNotes() {
|
||||||
|
const parentNote = await froca.getNote(parentNoteId);
|
||||||
|
const childNotes = await parentNote?.getChildNotes();
|
||||||
|
setChildNotes(childNotes ?? []);
|
||||||
|
}
|
||||||
|
useEffect(() => { refreshChildNotes() }, [ parentNoteId ]);
|
||||||
|
|
||||||
|
return childNotes;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,43 +0,0 @@
|
|||||||
import { t } from "../services/i18n.js";
|
|
||||||
import BasicWidget from "./basic_widget.js";
|
|
||||||
import contextMenu from "../menus/context_menu.js";
|
|
||||||
import appContext, { type CommandNames } from "../components/app_context.js";
|
|
||||||
import utils from "../services/utils.js";
|
|
||||||
|
|
||||||
const TPL = /*html*/`<div class="spacer"></div>`;
|
|
||||||
|
|
||||||
export default class SpacerWidget extends BasicWidget {
|
|
||||||
private baseSize: number;
|
|
||||||
private growthFactor: number;
|
|
||||||
|
|
||||||
constructor(baseSize = 0, growthFactor = 1000) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.baseSize = baseSize;
|
|
||||||
this.growthFactor = growthFactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
doRender() {
|
|
||||||
this.$widget = $(TPL);
|
|
||||||
this.$widget.css("flex-basis", this.baseSize);
|
|
||||||
this.$widget.css("flex-grow", this.growthFactor);
|
|
||||||
this.$widget.css("flex-shrink", 1000);
|
|
||||||
|
|
||||||
this.$widget.on("contextmenu", (e) => {
|
|
||||||
this.$widget.tooltip("hide");
|
|
||||||
|
|
||||||
contextMenu.show<CommandNames>({
|
|
||||||
x: e.pageX,
|
|
||||||
y: e.pageY,
|
|
||||||
items: [{ title: t("spacer.configure_launchbar"), command: "showLaunchBarSubtree", uiIcon: "bx " + (utils.isMobile() ? "bx-mobile" : "bx-sidebar") }],
|
|
||||||
selectMenuItemHandler: ({ command }) => {
|
|
||||||
if (command) {
|
|
||||||
appContext.triggerCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return false; // blocks default browser right click menu
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -24,6 +24,9 @@ type Labels = {
|
|||||||
orderBy: string;
|
orderBy: string;
|
||||||
orderDirection: string;
|
orderDirection: string;
|
||||||
|
|
||||||
|
// Launch bar
|
||||||
|
bookmarkFolder: boolean;
|
||||||
|
|
||||||
// Collection-specific
|
// Collection-specific
|
||||||
viewType: string;
|
viewType: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user