diff --git a/apps/client/src/widgets/bulk_actions/BulkAction.tsx b/apps/client/src/widgets/bulk_actions/BulkAction.tsx index 5f9c63750..5625413a1 100644 --- a/apps/client/src/widgets/bulk_actions/BulkAction.tsx +++ b/apps/client/src/widgets/bulk_actions/BulkAction.tsx @@ -1,7 +1,41 @@ +import { ComponentChildren } from "preact"; + interface BulkActionProps { - + label: string; + children: ComponentChildren; + helpText?: ComponentChildren; } -export default function BulkAction() { +export default function BulkAction({ label, children, helpText }: BulkActionProps) { + return ( + + +
+
{label}
+ {children} +
+ + + {helpText &&
+ +
+ {helpText} +
+
} + + + + + ); +} + +export function BulkActionText({ text }: { text: string }) { + return ( +
+ {text} +
+ ); } \ No newline at end of file diff --git a/apps/client/src/widgets/bulk_actions/abstract_bulk_action.ts b/apps/client/src/widgets/bulk_actions/abstract_bulk_action.ts index eb24bbd4d..c296b949f 100644 --- a/apps/client/src/widgets/bulk_actions/abstract_bulk_action.ts +++ b/apps/client/src/widgets/bulk_actions/abstract_bulk_action.ts @@ -3,8 +3,9 @@ import server from "../../services/server.js"; import ws from "../../services/ws.js"; import utils from "../../services/utils.js"; import type FAttribute from "../../entities/fattribute.js"; +import { VNode } from "preact"; -interface ActionDefinition { +export interface ActionDefinition { script: string; relationName: string; targetNoteId: string; @@ -30,15 +31,18 @@ export default abstract class AbstractBulkAction { render() { try { const $rendered = this.doRender(); + if (Array.isArray($rendered)) { + $rendered + .find(".action-conf-del") + .on("click", () => this.deleteAction()) + .attr("title", t("abstract_bulk_action.remove_this_search_action")); - $rendered - .find(".action-conf-del") - .on("click", () => this.deleteAction()) - .attr("title", t("abstract_bulk_action.remove_this_search_action")); + utils.initHelpDropdown($rendered); - utils.initHelpDropdown($rendered); - - return $rendered; + return $rendered; + } else { + return $rendered; + } } catch (e: any) { logError(`Failed rendering search action: ${JSON.stringify(this.attribute.dto)} with error: ${e.message} ${e.stack}`); return null; @@ -46,7 +50,7 @@ export default abstract class AbstractBulkAction { } // to be overridden - abstract doRender(): JQuery; + abstract doRender(): JQuery | VNode; static get actionName() { return ""; } diff --git a/apps/client/src/widgets/bulk_actions/label/add_label.ts b/apps/client/src/widgets/bulk_actions/label/add_label.ts deleted file mode 100644 index b17eec38f..000000000 --- a/apps/client/src/widgets/bulk_actions/label/add_label.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { t } from "../../../services/i18n.js"; -import SpacedUpdate from "../../../services/spaced_update.js"; -import AbstractBulkAction from "../abstract_bulk_action.js"; - -const TPL = /*html*/` - - -
-
${t("add_label.add_label")}
- - - -
${t("add_label.to_value")}
- - -
- - - - - - -`; - -export default class AddLabelBulkAction extends AbstractBulkAction { - static get actionName() { - return "addLabel"; - } - static get actionTitle() { - return t("add_label.add_label"); - } - - doRender() { - const $action = $(TPL); - - const $labelName = $action.find(".label-name"); - $labelName.val(this.actionDef.labelName || ""); - - const $labelValue = $action.find(".label-value"); - $labelValue.val(this.actionDef.labelValue || ""); - - const spacedUpdate = new SpacedUpdate(async () => { - await this.saveAction({ - labelName: $labelName.val(), - labelValue: $labelValue.val() - }); - }, 1000); - - $labelName.on("input", () => spacedUpdate.scheduleUpdate()); - $labelValue.on("input", () => spacedUpdate.scheduleUpdate()); - - return $action; - } -} diff --git a/apps/client/src/widgets/bulk_actions/label/add_label.tsx b/apps/client/src/widgets/bulk_actions/label/add_label.tsx new file mode 100644 index 000000000..a8c846dcb --- /dev/null +++ b/apps/client/src/widgets/bulk_actions/label/add_label.tsx @@ -0,0 +1,56 @@ +import { useEffect, useState } from "preact/hooks"; +import { t } from "../../../services/i18n"; +import FormTextBox from "../../react/FormTextBox"; +import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action"; +import BulkAction, { BulkActionText } from "../BulkAction"; +import { useSpacedUpdate } from "../../react/hooks"; + +function AddLabelBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) { + const [ labelName, setLabelName ] = useState(actionDef.labelName ?? ""); + const [ labelValue, setLabelValue ] = useState(actionDef.labelValue ?? ""); + const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ labelName, labelValue })); + useEffect(() => spacedUpdate.scheduleUpdate(), [labelName, labelValue]); + + return ( + +

{t("add_label.help_text")}

+ +
    +
  • {t("add_label.help_text_item1")}
  • +
  • {t("add_label.help_text_item2")}
  • +
+ + {t("add_label.help_text_note")} + } + > + + + +
+ ) +} + +export default class AddLabelBulkAction extends AbstractBulkAction { + + doRender() { + return ; + } + + static get actionName() { + return "addLabel"; + } + + static get actionTitle() { + return t("add_label.add_label"); + } +} diff --git a/apps/client/src/widgets/dialogs/bulk_actions.tsx b/apps/client/src/widgets/dialogs/bulk_actions.tsx index 9fabfd194..7cff1ece8 100644 --- a/apps/client/src/widgets/dialogs/bulk_actions.tsx +++ b/apps/client/src/widgets/dialogs/bulk_actions.tsx @@ -104,16 +104,7 @@ function ExistingActionsList({ existingActions }: { existingActions?: RenameNote { existingActions ? existingActions - .map(action => { - const renderedAction = action.render(); - if (renderedAction) { - return - } else { - return null; - } - }) + .map(action => action.render()) .filter(renderedAction => renderedAction !== null) :

{t("bulk_actions.none_yet")}

} diff --git a/apps/client/src/widgets/react/FormTextBox.tsx b/apps/client/src/widgets/react/FormTextBox.tsx index 8dc2b3f7f..e06004fb5 100644 --- a/apps/client/src/widgets/react/FormTextBox.tsx +++ b/apps/client/src/widgets/react/FormTextBox.tsx @@ -1,17 +1,13 @@ -import { HTMLInputTypeAttribute, RefObject } from "preact/compat"; +import { InputHTMLAttributes, RefObject } from "preact/compat"; -interface FormTextBoxProps { +interface FormTextBoxProps extends Pick, "placeholder" | "autoComplete" | "className" | "type" | "name" | "pattern" | "title"> { id?: string; - name: string; - type?: HTMLInputTypeAttribute; currentValue?: string; - className?: string; - autoComplete?: string; onChange?(newValue: string): void; inputRef?: RefObject; } -export default function FormTextBox({ id, type, name, className, currentValue, onChange, autoComplete, inputRef }: FormTextBoxProps) { +export default function FormTextBox({ id, type, name, className, currentValue, onChange, autoComplete, inputRef, placeholder, title, pattern }: FormTextBoxProps) { return ( onChange?.(e.currentTarget.value)} /> ); } \ No newline at end of file diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index 96ee36dc5..15849972d 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -1,6 +1,7 @@ -import { useContext, useEffect, useState } from "preact/hooks"; +import { useContext, useEffect, useMemo, useState } from "preact/hooks"; import { EventData, EventNames } from "../../components/app_context"; import { ParentComponent } from "./ReactBasicWidget"; +import SpacedUpdate from "../../services/spaced_update"; export default function useTriliumEvent(eventName: T, handler: (data: EventData) => void) { const parentWidget = useContext(ParentComponent); @@ -29,4 +30,12 @@ export default function useTriliumEvent(eventName: T, hand parentWidget[handlerName] = originalHandler; }; }, [parentWidget]); +} + +export function useSpacedUpdate(callback: () => Promise, interval = 1000) { + const spacedUpdate = useMemo(() => { + return new SpacedUpdate(callback, interval); + }, [callback, interval]); + + return spacedUpdate; } \ No newline at end of file