chore(react/ribbon): port search string

This commit is contained in:
Elian Doran 2025-08-24 15:29:07 +03:00
parent 0c8bfc39ef
commit c1b30db3d1
No known key found for this signature in database
6 changed files with 72 additions and 50 deletions

View File

@ -2,6 +2,7 @@ import server from "./server.js";
import froca from "./froca.js";
import type FNote from "../entities/fnote.js";
import type { AttributeRow } from "./load_results.js";
import { AttributeType } from "@triliumnext/commons";
async function addLabel(noteId: string, name: string, value: string = "", isInheritable = false) {
await server.put(`notes/${noteId}/attribute`, {
@ -25,6 +26,14 @@ async function removeAttributeById(noteId: string, attributeId: string) {
await server.remove(`notes/${noteId}/attributes/${attributeId}`);
}
export async function removeOwnedAttributesByNameOrType(note: FNote, type: AttributeType, name: string) {
for (const attr of note.getOwnedAttributes()) {
if (attr.type === type && attr.name === name) {
await server.remove(`notes/${note.noteId}/attributes/${attr.attributeId}`);
}
}
}
/**
* Removes a label identified by its name from the given note, if it exists. Note that the label must be owned, i.e.
* it will not remove inherited attributes.

View File

@ -3,16 +3,20 @@ interface FormTextAreaProps {
currentValue: string;
onBlur?(newValue: string): void;
rows: number;
className?: string;
placeholder?: string;
}
export default function FormTextArea({ id, onBlur, rows, currentValue }: FormTextAreaProps) {
export default function FormTextArea({ id, onBlur, rows, currentValue, className, placeholder }: FormTextAreaProps) {
return (
<textarea
id={id}
rows={rows}
className={`form-control ${className ?? ""}`}
onBlur={(e) => {
onBlur?.(e.currentTarget.value);
}}
style={{ width: "100%" }}
placeholder={placeholder}
>{currentValue}</textarea>
)
}

View File

@ -1,6 +1,14 @@
import { ComponentChildren } from "preact";
import { t } from "../../services/i18n";
import Button from "../react/Button";
import { TabContext } from "./ribbon-interface";
import Dropdown from "../react/Dropdown";
import ActionButton from "../react/ActionButton";
import FormTextArea from "../react/FormTextArea";
import { AttributeType, OptionNames } from "@triliumnext/commons";
import { removeOwnedAttributesByNameOrType } from "../../services/attributes";
import { note } from "mermaid/dist/rendering-util/rendering-elements/shapes/note.js";
import FNote from "../../entities/fnote";
interface SearchOption {
searchOption: string;
@ -73,8 +81,58 @@ export default function SearchDefinitionTab({ note }: TabContext) {
))}
</td>
</tr>
<tbody className="search-options">
<SearchStringOption />
</tbody>
</table>
</div>
</div>
)
}
function SearchOption({ note, title, children, help, attributeName, attributeType }: {
note: FNote;
title: string,
children: ComponentChildren,
help: ComponentChildren,
attributeName: string,
attributeType: AttributeType
}) {
return (
<tr>
<td className="title-column">{title}</td>
<td>{children}</td>
<td className="button-column">
{help && <Dropdown buttonClassName="bx bx-help-circle icon-action" hideToggleArrow>{help}</Dropdown>}
<ActionButton
icon="bx bx-x"
className="search-option-del"
onClick={() => removeOwnedAttributesByNameOrType(note, attributeType, attributeName)}
/>
</td>
</tr>
)
}
function SearchStringOption() {
return <SearchOption
title={t("search_string.title_column")}
help={<>
<strong>{t("search_string.search_syntax")}</strong> - {t("search_string.also_see")} <a href="#" data-help-page="search.html">{t("search_string.complete_help")}</a>
<ul style="marigin-bottom: 0;">
<li>{t("search_string.full_text_search")}</li>
<li><code>#abc</code> - {t("search_string.label_abc")}</li>
<li><code>#year = 2019</code> - {t("search_string.label_year")}</li>
<li><code>#rock #pop</code> - {t("search_string.label_rock_pop")}</li>
<li><code>#rock or #pop</code> - {t("search_string.label_rock_or_pop")}</li>
<li><code>#year &lt;= 2000</code> - {t("search_string.label_year_comparison")}</li>
<li><code>note.dateCreated &gt;= MONTH-1</code> - {t("search_string.label_date_created")}</li>
</ul>
</>}
>
<FormTextArea
className="search-string"
placeholder={t("search_string.placeholder")}
/>
</SearchOption>
}

View File

@ -163,10 +163,6 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
this.triggerEvent("searchRefreshed", { ntxId: this.noteContext?.ntxId });
}
async refreshSearchDefinitionCommand() {
await this.refresh();
}
async refreshWithNote(note: FNote) {
if (!this.note) {
return;

View File

@ -49,21 +49,4 @@ export default abstract class AbstractSearchOption extends Component {
}
abstract doRender(): JQuery<HTMLElement>;
async deleteOption() {
// TODO: Find a better pattern.
await this.deleteAttribute((this.constructor as any).attributeType, (this.constructor as any).optionName);
await ws.waitForMaxKnownEntityChangeId();
await this.triggerCommand("refreshSearchDefinition");
}
async deleteAttribute(type: AttributeType, name: string) {
for (const attr of this.note.getOwnedAttributes()) {
if (attr.type === type && attr.name === name) {
await server.remove(`notes/${this.note.noteId}/attributes/${attr.attributeId}`);
}
}
}
}

View File

@ -6,34 +6,6 @@ import appContext, { type EventData } from "../../components/app_context.js";
import { t } from "../../services/i18n.js";
import { Tooltip } from "bootstrap";
const TPL = /*html*/`
<tr>
<td class="title-column">${t("search_string.title_column")}</td>
<td>
<textarea class="form-control search-string" placeholder="${t("search_string.placeholder")}" autofocus></textarea>
</td>
<td class="button-column">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
<strong>${t("search_string.search_syntax")}</strong> - ${t("search_string.also_see")} <a href="#" data-help-page="search.html">${t("search_string.complete_help")}</a>
<ul style="marigin-bottom: 0;">
<li>${t("search_string.full_text_search")}</li>
<li><code>#abc</code> - ${t("search_string.label_abc")}</li>
<li><code>#year = 2019</code> - ${t("search_string.label_year")}</li>
<li><code>#rock #pop</code> - ${t("search_string.label_rock_pop")}</li>
<li><code>#rock or #pop</code> - ${t("search_string.label_rock_or_pop")}</li>
<li><code>#year &lt;= 2000</code> - ${t("search_string.label_year_comparison")}</li>
<li><code>note.dateCreated >= MONTH-1</code> - ${t("search_string.label_date_created")}</li>
</ul>
</div>
</div>
<span class="bx bx-x icon-action search-option-del"></span>
</td>
</tr>`;
export default class SearchString extends AbstractSearchOption {
private $searchString!: JQuery<HTMLElement>;