From 48cbb80e790b4032a53656ab12d2ed6ca621f389 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 4 Dec 2025 14:18:04 +0200 Subject: [PATCH] chore(react/launch_bar): port open_note_button_widget --- apps/client/src/services/utils.ts | 2 +- apps/client/src/widgets/bookmark_buttons.ts | 8 +-- .../buttons/open_note_button_widget.ts | 49 ------------- .../widgets/launch_bar/BookmarkButtons.tsx | 70 ++++++++++++++++++- .../client/src/widgets/react/ActionButton.tsx | 8 +-- apps/client/src/widgets/react/hooks.tsx | 13 ++++ 6 files changed, 87 insertions(+), 63 deletions(-) delete mode 100644 apps/client/src/widgets/buttons/open_note_button_widget.ts diff --git a/apps/client/src/services/utils.ts b/apps/client/src/services/utils.ts index 2045cd4d7..e6da60ae5 100644 --- a/apps/client/src/services/utils.ts +++ b/apps/client/src/services/utils.ts @@ -150,7 +150,7 @@ export function isMac() { export const hasTouchBar = (isMac() && isElectron()); -function isCtrlKey(evt: KeyboardEvent | MouseEvent | JQuery.ClickEvent | JQuery.ContextMenuEvent | JQuery.TriggeredEvent | React.PointerEvent | JQueryEventObject) { +export function isCtrlKey(evt: KeyboardEvent | MouseEvent | JQuery.ClickEvent | JQuery.ContextMenuEvent | JQuery.TriggeredEvent | React.PointerEvent | JQueryEventObject) { return (!isMac() && evt.ctrlKey) || (isMac() && evt.metaKey); } diff --git a/apps/client/src/widgets/bookmark_buttons.ts b/apps/client/src/widgets/bookmark_buttons.ts index b32393c9a..88ff3c7e9 100644 --- a/apps/client/src/widgets/bookmark_buttons.ts +++ b/apps/client/src/widgets/bookmark_buttons.ts @@ -1,5 +1,4 @@ import FlexContainer from "./containers/flex_container.js"; -import OpenNoteButtonWidget from "./buttons/open_note_button_widget.js"; import BookmarkFolderWidget from "./buttons/bookmark_folder.js"; import froca from "../services/froca.js"; import utils from "../services/utils.js"; @@ -23,10 +22,6 @@ export default class BookmarkButtons extends FlexContainer { } async refresh(): Promise { - this.$widget.empty(); - this.children = []; - this.noteIds = []; - const bookmarkParentNote = await froca.getNote("_lbBookmarks"); if (!bookmarkParentNote) { @@ -37,8 +32,7 @@ export default class BookmarkButtons extends FlexContainer { this.noteIds.push(note.noteId); let buttonWidget: OpenNoteButtonWidget | BookmarkFolderWidget = note.isLabelTruthy("bookmarkFolder") - ? new BookmarkFolderWidget(note) - : new OpenNoteButtonWidget(note).class("launcher-button"); + ? new BookmarkFolderWidget(note); if (this.settings.titlePlacement) { if (!("settings" in buttonWidget)) { diff --git a/apps/client/src/widgets/buttons/open_note_button_widget.ts b/apps/client/src/widgets/buttons/open_note_button_widget.ts deleted file mode 100644 index c0a4c6334..000000000 --- a/apps/client/src/widgets/buttons/open_note_button_widget.ts +++ /dev/null @@ -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 - } -} diff --git a/apps/client/src/widgets/launch_bar/BookmarkButtons.tsx b/apps/client/src/widgets/launch_bar/BookmarkButtons.tsx index 4d6abec7d..45941c699 100644 --- a/apps/client/src/widgets/launch_bar/BookmarkButtons.tsx +++ b/apps/client/src/widgets/launch_bar/BookmarkButtons.tsx @@ -1,5 +1,71 @@ +import { useMemo } from "preact/hooks"; import type { LaunchBarWidgetProps } from "./launch_bar_widget"; +import { CSSProperties } from "preact"; +import type FNote from "../../entities/fnote"; +import { useChildNotes, useNoteLabel, useNoteProperty } from "../react/hooks"; +import Dropdown from "../react/Dropdown"; +import ActionButton from "../react/ActionButton"; +import appContext from "../../components/app_context"; +import { escapeHtml, isCtrlKey } from "../../services/utils"; +import link_context_menu from "../../menus/link_context_menu"; -export default function BookmarkButtons({ }: LaunchBarWidgetProps) { - return

Bookmarks goes here.

; +const PARENT_NOTE_ID = "_lbBookmarks"; + +export default function BookmarkButtons({ isHorizontalLayout }: LaunchBarWidgetProps) { + const style = useMemo(() => ({ + display: "flex", + flexDirection: isHorizontalLayout ? "row" : "column", + contain: "none" + }), [ isHorizontalLayout ]); + const childNotes = useChildNotes(PARENT_NOTE_ID); + + return ( +
+ {childNotes?.map(childNote => )} +
+ ) +} + +function SingleBookmark({ note }: { note: FNote }) { + return +} + +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 && ( + { + evt.preventDefault(); + link_context_menu.openContextMenu(note.noteId, evt); + }} + /> + ) +} + +function getHoistedNoteId(noteToOpen: FNote) { + return noteToOpen.getRelationValue("hoistedNote") || appContext.tabManager.getActiveContext()?.hoistedNoteId; } diff --git a/apps/client/src/widgets/react/ActionButton.tsx b/apps/client/src/widgets/react/ActionButton.tsx index 28489005d..a37f34514 100644 --- a/apps/client/src/widgets/react/ActionButton.tsx +++ b/apps/client/src/widgets/react/ActionButton.tsx @@ -2,13 +2,13 @@ import { useEffect, useRef, useState } from "preact/hooks"; import { CommandNames } from "../../components/app_context"; import { useStaticTooltip } from "./hooks"; import keyboard_actions from "../../services/keyboard_actions"; +import { HTMLAttributes } from "preact"; -export interface ActionButtonProps { +export interface ActionButtonProps extends Pick, "onClick" | "onAuxClick" | "onContextMenu"> { text: string; titlePosition?: "top" | "right" | "bottom" | "left"; icon: string; className?: string; - onClick?: (e: MouseEvent) => void; triggerCommand?: CommandNames; noIconActionClass?: boolean; frame?: boolean; @@ -16,7 +16,7 @@ export interface ActionButtonProps { 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(null); const [ keyboardShortcut, setKeyboardShortcut ] = useState(); @@ -35,8 +35,8 @@ export default function ActionButton({ text, icon, className, onClick, triggerCo return