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 (
+
+ |
+
+ |
+
+ {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