chore(react/ribbon): dynamic rendering of search options

This commit is contained in:
Elian Doran 2025-08-24 15:59:22 +03:00
parent 759398d804
commit b6d5a6ec2e
No known key found for this signature in database
10 changed files with 72 additions and 115 deletions

View File

@ -1,4 +1,4 @@
import { ComponentChildren } from "preact"; import { ComponentChildren, VNode } from "preact";
import { t } from "../../services/i18n"; import { t } from "../../services/i18n";
import Button from "../react/Button"; import Button from "../react/Button";
import { TabContext } from "./ribbon-interface"; import { TabContext } from "./ribbon-interface";
@ -18,53 +18,72 @@ import appContext from "../../components/app_context";
import server from "../../services/server"; import server from "../../services/server";
interface SearchOption { interface SearchOption {
searchOption: string; attributeName: string;
attributeType: "label" | "relation";
icon: string; icon: string;
label: string; label: string;
tooltip?: string; tooltip?: string;
// TODO: Make mandatory once all components are ported.
component?: (props: SearchOptionProps) => VNode;
}
interface SearchOptionProps {
note: FNote;
refreshResults: () => void;
attributeName: string;
attributeType: "label" | "relation";
} }
const SEARCH_OPTIONS: SearchOption[] = [ const SEARCH_OPTIONS: SearchOption[] = [
{ {
searchOption: "searchString", attributeName: "searchString",
attributeType: "label",
icon: "bx bx-text", icon: "bx bx-text",
label: t("search_definition.search_string") label: t("search_definition.search_string"),
component: SearchStringOption
}, },
{ {
searchOption: "searchScript", attributeName: "searchScript",
attributeType: "relation",
icon: "bx bx-code", icon: "bx bx-code",
label: t("search_definition.search_script") label: t("search_definition.search_script")
}, },
{ {
searchOption: "ancestor", attributeName: "ancestor",
attributeType: "relation",
icon: "bx bx-filter-alt", icon: "bx bx-filter-alt",
label: t("search_definition.ancestor") label: t("search_definition.ancestor")
}, },
{ {
searchOption: "fastSearch", attributeName: "fastSearch",
attributeType: "label",
icon: "bx bx-run", icon: "bx bx-run",
label: t("search_definition.fast_search"), label: t("search_definition.fast_search"),
tooltip: t("search_definition.fast_search_description") tooltip: t("search_definition.fast_search_description")
}, },
{ {
searchOption: "includeArchivedNotes", attributeName: "includeArchivedNotes",
attributeType: "label",
icon: "bx bx-archive", icon: "bx bx-archive",
label: t("search_definition.include_archived"), label: t("search_definition.include_archived"),
tooltip: t("search_definition.include_archived_notes_description") tooltip: t("search_definition.include_archived_notes_description")
}, },
{ {
searchOption: "orderBy", attributeName: "orderBy",
attributeType: "label",
icon: "bx bx-arrow-from-top", icon: "bx bx-arrow-from-top",
label: t("search_definition.order_by") label: t("search_definition.order_by")
}, },
{ {
searchOption: "limit", attributeName: "limit",
attributeType: "label",
icon: "bx bx-stop", icon: "bx bx-stop",
label: t("search_definition.limit"), label: t("search_definition.limit"),
tooltip: t("search_definition.limit_description") tooltip: t("search_definition.limit_description")
}, },
{ {
searchOption: "debug", attributeName: "debug",
attributeType: "label",
icon: "bx bx-bug", icon: "bx bx-bug",
label: t("search_definition.debug"), label: t("search_definition.debug"),
tooltip: t("search_definition.debug_description") tooltip: t("search_definition.debug_description")
@ -97,6 +116,7 @@ export default function SearchDefinitionTab({ note, ntxId }: TabContext) {
return ( return (
<div className="search-definition-widget"> <div className="search-definition-widget">
<div className="search-settings"> <div className="search-settings">
{note &&
<table className="search-setting-table"> <table className="search-setting-table">
<tr> <tr>
<td className="title-column">{t("search_definition.add_search_option")}</td> <td className="title-column">{t("search_definition.add_search_option")}</td>
@ -111,10 +131,17 @@ export default function SearchDefinitionTab({ note, ntxId }: TabContext) {
</td> </td>
</tr> </tr>
<tbody className="search-options"> <tbody className="search-options">
<SearchStringOption {SEARCH_OPTIONS.map(({ attributeType, attributeName, component }) => {
refreshResults={refreshResults} const attr = note.getAttribute(attributeType, attributeName);
note={note} if (attr && component) {
/> return component({
attributeName,
attributeType,
note,
refreshResults
});
}
})}
</tbody> </tbody>
<tbody className="action-options"> <tbody className="action-options">
@ -134,6 +161,7 @@ export default function SearchDefinitionTab({ note, ntxId }: TabContext) {
</tr> </tr>
</tbody> </tbody>
</table> </table>
}
</div> </div>
</div> </div>
) )
@ -163,7 +191,7 @@ function SearchOption({ note, title, children, help, attributeName, attributeTyp
) )
} }
function SearchStringOption({ note, refreshResults }: { note: FNote, refreshResults: () => void }) { function SearchStringOption({ note, refreshResults }: SearchOptionProps) {
const currentValue = useRef(""); const currentValue = useRef("");
const spacedUpdate = useSpacedUpdate(async () => { const spacedUpdate = useSpacedUpdate(async () => {
const searchString = currentValue.current; const searchString = currentValue.current;

View File

@ -153,24 +153,6 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
this.$searchOptions.empty(); this.$searchOptions.empty();
for (const OptionClass of OPTION_CLASSES) {
const { attributeType, optionName } = OptionClass;
const attr = this.note.getAttribute(attributeType as AttributeType, optionName);
this.$widget.find(`[data-search-option-add='${optionName}'`).toggle(!attr);
if (attr) {
const searchOption = new OptionClass(attr, this.note).setParent(this);
this.child(searchOption);
const renderedEl = searchOption.render();
if (renderedEl) {
this.$searchOptions.append(renderedEl);
}
}
}
const actions = bulkActionService.parseActions(this.note); const actions = bulkActionService.parseActions(this.note);
const renderedEls = actions const renderedEls = actions
.map((action) => renderReactWidget(this, action.doRender())) .map((action) => renderReactWidget(this, action.doRender()))

View File

@ -51,13 +51,6 @@ const TPL = /*html*/`
</tr>`; </tr>`;
export default class Ancestor extends AbstractSearchOption { export default class Ancestor extends AbstractSearchOption {
static get optionName() {
return "ancestor";
}
static get attributeType() {
return "relation";
}
static async create(noteId: string) { static async create(noteId: string) {
await AbstractSearchOption.setAttribute(noteId, "relation", "ancestor", "root"); await AbstractSearchOption.setAttribute(noteId, "relation", "ancestor", "root");
} }

View File

@ -20,13 +20,6 @@ const TPL = /*html*/`
</tr>`; </tr>`;
export default class Debug extends AbstractSearchOption { export default class Debug extends AbstractSearchOption {
static get optionName() {
return "debug";
}
static get attributeType() {
return "label";
}
static async create(noteId: string) { static async create(noteId: string) {
await AbstractSearchOption.setAttribute(noteId, "label", "debug"); await AbstractSearchOption.setAttribute(noteId, "label", "debug");
} }

View File

@ -19,12 +19,6 @@ const TPL = /*html*/`
</tr>`; </tr>`;
export default class FastSearch extends AbstractSearchOption { export default class FastSearch extends AbstractSearchOption {
static get optionName() {
return "fastSearch";
}
static get attributeType() {
return "label";
}
static async create(noteId: string) { static async create(noteId: string) {
await AbstractSearchOption.setAttribute(noteId, "label", "fastSearch"); await AbstractSearchOption.setAttribute(noteId, "label", "fastSearch");

View File

@ -13,12 +13,6 @@ const TPL = /*html*/`
</tr>`; </tr>`;
export default class IncludeArchivedNotes extends AbstractSearchOption { export default class IncludeArchivedNotes extends AbstractSearchOption {
static get optionName() {
return "includeArchivedNotes";
}
static get attributeType() {
return "label";
}
static async create(noteId: string) { static async create(noteId: string) {
await AbstractSearchOption.setAttribute(noteId, "label", "includeArchivedNotes"); await AbstractSearchOption.setAttribute(noteId, "label", "includeArchivedNotes");

View File

@ -26,13 +26,6 @@ export default class Limit extends AbstractSearchOption {
private $limit!: JQuery<HTMLElement>; private $limit!: JQuery<HTMLElement>;
static get optionName() {
return "limit";
}
static get attributeType() {
return "label";
}
static async create(noteId: string) { static async create(noteId: string) {
await AbstractSearchOption.setAttribute(noteId, "label", "limit", "10"); await AbstractSearchOption.setAttribute(noteId, "label", "limit", "10");
} }

View File

@ -37,13 +37,6 @@ const TPL = /*html*/`
export default class OrderBy extends AbstractSearchOption { export default class OrderBy extends AbstractSearchOption {
static get optionName() {
return "orderBy";
}
static get attributeType() {
return "label";
}
static async create(noteId: string) { static async create(noteId: string) {
await AbstractSearchOption.setAttribute(noteId, "label", "orderBy", "relevancy"); await AbstractSearchOption.setAttribute(noteId, "label", "orderBy", "relevancy");
await AbstractSearchOption.setAttribute(noteId, "label", "orderDirection", "asc"); await AbstractSearchOption.setAttribute(noteId, "label", "orderDirection", "asc");

View File

@ -33,12 +33,6 @@ const TPL = /*html*/`
</tr>`; </tr>`;
export default class SearchScript extends AbstractSearchOption { export default class SearchScript extends AbstractSearchOption {
static get optionName() {
return "searchScript";
}
static get attributeType() {
return "relation";
}
static async create(noteId: string) { static async create(noteId: string) {
await AbstractSearchOption.setAttribute(noteId, "relation", "searchScript", "root"); await AbstractSearchOption.setAttribute(noteId, "relation", "searchScript", "root");

View File

@ -11,13 +11,6 @@ export default class SearchString extends AbstractSearchOption {
private $searchString!: JQuery<HTMLElement>; private $searchString!: JQuery<HTMLElement>;
private spacedUpdate!: SpacedUpdate; private spacedUpdate!: SpacedUpdate;
static get optionName() {
return "searchString";
}
static get attributeType() {
return "label";
}
static async create(noteId: string) { static async create(noteId: string) {
await AbstractSearchOption.setAttribute(noteId, "label", "searchString"); await AbstractSearchOption.setAttribute(noteId, "label", "searchString");
} }