mirror of
https://github.com/zadam/trilium.git
synced 2025-11-11 15:55:52 +01:00
feat(react/widgets): port search result
This commit is contained in:
@@ -11,7 +11,6 @@ import NoteListWidget from "../widgets/note_list.js";
|
|||||||
import SqlResultWidget from "../widgets/sql_result.js";
|
import SqlResultWidget from "../widgets/sql_result.js";
|
||||||
import SqlTableSchemasWidget from "../widgets/sql_table_schemas.js";
|
import SqlTableSchemasWidget from "../widgets/sql_table_schemas.js";
|
||||||
import NoteIconWidget from "../widgets/note_icon.jsx";
|
import NoteIconWidget from "../widgets/note_icon.jsx";
|
||||||
import SearchResultWidget from "../widgets/search_result.js";
|
|
||||||
import ScrollingContainer from "../widgets/containers/scrolling_container.js";
|
import ScrollingContainer from "../widgets/containers/scrolling_container.js";
|
||||||
import RootContainer from "../widgets/containers/root_container.js";
|
import RootContainer from "../widgets/containers/root_container.js";
|
||||||
import WatchedFileUpdateStatusWidget from "../widgets/watched_file_update_status.js";
|
import WatchedFileUpdateStatusWidget from "../widgets/watched_file_update_status.js";
|
||||||
@@ -42,6 +41,7 @@ import { applyModals } from "./layout_commons.js";
|
|||||||
import Ribbon from "../widgets/ribbon/Ribbon.jsx";
|
import Ribbon from "../widgets/ribbon/Ribbon.jsx";
|
||||||
import FloatingButtons from "../widgets/FloatingButtons.jsx";
|
import FloatingButtons from "../widgets/FloatingButtons.jsx";
|
||||||
import { DESKTOP_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
|
import { DESKTOP_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
|
||||||
|
import SearchResult from "../widgets/search_result.jsx";
|
||||||
|
|
||||||
export default class DesktopLayout {
|
export default class DesktopLayout {
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ export default class DesktopLayout {
|
|||||||
.child(new SqlTableSchemasWidget())
|
.child(new SqlTableSchemasWidget())
|
||||||
.child(new NoteDetailWidget())
|
.child(new NoteDetailWidget())
|
||||||
.child(new NoteListWidget(false))
|
.child(new NoteListWidget(false))
|
||||||
.child(new SearchResultWidget())
|
.child(<SearchResult />)
|
||||||
.child(new SqlResultWidget())
|
.child(new SqlResultWidget())
|
||||||
.child(<ScrollPadding />)
|
.child(<ScrollPadding />)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ interface AlertProps {
|
|||||||
type: "info" | "danger" | "warning";
|
type: "info" | "danger" | "warning";
|
||||||
title?: string;
|
title?: string;
|
||||||
children: ComponentChildren;
|
children: ComponentChildren;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Alert({ title, type, children }: AlertProps) {
|
export default function Alert({ title, type, children, className }: AlertProps) {
|
||||||
return (
|
return (
|
||||||
<div className={`alert alert-${type}`}>
|
<div className={`alert alert-${type} ${className ?? ""}`}>
|
||||||
{title && <h4>{title}</h4>}
|
{title && <h4>{title}</h4>}
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
16
apps/client/src/widgets/search_result.css
Normal file
16
apps/client/src/widgets/search_result.css
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
.search-result-widget {
|
||||||
|
flex-grow: 100000;
|
||||||
|
flex-shrink: 100000;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
contain: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-widget .note-list {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-no-results, .search-not-executed-yet {
|
||||||
|
margin: 20px;
|
||||||
|
padding: 20px !important;
|
||||||
|
}
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
import { t } from "../services/i18n.js";
|
|
||||||
import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
|
||||||
import NoteListRenderer from "../services/note_list_renderer.js";
|
|
||||||
import type FNote from "../entities/fnote.js";
|
|
||||||
import type { EventData } from "../components/app_context.js";
|
|
||||||
|
|
||||||
const TPL = /*html*/`
|
|
||||||
<div class="search-result-widget">
|
|
||||||
<style>
|
|
||||||
.search-result-widget {
|
|
||||||
flex-grow: 100000;
|
|
||||||
flex-shrink: 100000;
|
|
||||||
min-height: 0;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-result-widget .note-list {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-no-results, .search-not-executed-yet {
|
|
||||||
margin: 20px;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="search-no-results alert alert-info">
|
|
||||||
${t("search_result.no_notes_found")}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="search-not-executed-yet alert alert-info">
|
|
||||||
${t("search_result.search_not_executed")}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="search-result-widget-content">
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
export default class SearchResultWidget extends NoteContextAwareWidget {
|
|
||||||
|
|
||||||
private $content!: JQuery<HTMLElement>;
|
|
||||||
private $noResults!: JQuery<HTMLElement>;
|
|
||||||
private $notExecutedYet!: JQuery<HTMLElement>;
|
|
||||||
|
|
||||||
isEnabled() {
|
|
||||||
return super.isEnabled() && this.note?.type === "search";
|
|
||||||
}
|
|
||||||
|
|
||||||
doRender() {
|
|
||||||
this.$widget = $(TPL);
|
|
||||||
this.contentSized();
|
|
||||||
this.$content = this.$widget.find(".search-result-widget-content");
|
|
||||||
this.$noResults = this.$widget.find(".search-no-results");
|
|
||||||
this.$notExecutedYet = this.$widget.find(".search-not-executed-yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
async refreshWithNote(note: FNote) {
|
|
||||||
const noResults = note.getChildNoteIds().length === 0 && !!note.searchResultsLoaded;
|
|
||||||
|
|
||||||
this.$content.empty();
|
|
||||||
this.$noResults.toggle(noResults);
|
|
||||||
this.$notExecutedYet.toggle(!note.searchResultsLoaded);
|
|
||||||
|
|
||||||
if (noResults || !note.searchResultsLoaded) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const noteListRenderer = new NoteListRenderer({
|
|
||||||
$parent: this.$content,
|
|
||||||
parentNote: note,
|
|
||||||
showNotePath: true
|
|
||||||
});
|
|
||||||
await noteListRenderer.renderList();
|
|
||||||
}
|
|
||||||
|
|
||||||
searchRefreshedEvent({ ntxId }: EventData<"searchRefreshed">) {
|
|
||||||
if (!this.isNoteContext(ntxId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
notesReloadedEvent({ noteIds }: EventData<"notesReloaded">) {
|
|
||||||
if (this.noteId && noteIds.includes(this.noteId)) {
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
63
apps/client/src/widgets/search_result.tsx
Normal file
63
apps/client/src/widgets/search_result.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { useEffect, useRef, useState } from "preact/hooks";
|
||||||
|
import { t } from "../services/i18n";
|
||||||
|
import Alert from "./react/Alert";
|
||||||
|
import { useNoteContext, useNoteProperty, useTriliumEvent } from "./react/hooks";
|
||||||
|
import "./search_result.css";
|
||||||
|
import NoteListRenderer from "../services/note_list_renderer";
|
||||||
|
|
||||||
|
enum SearchResultState {
|
||||||
|
NO_RESULTS,
|
||||||
|
NOT_EXECUTED,
|
||||||
|
GOT_RESULTS
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SearchResult() {
|
||||||
|
const { note, ntxId } = useNoteContext();
|
||||||
|
const [ state, setState ] = useState<SearchResultState>();
|
||||||
|
const searchContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
searchContainerRef.current?.replaceChildren();
|
||||||
|
|
||||||
|
if (!note?.searchResultsLoaded) {
|
||||||
|
setState(SearchResultState.NOT_EXECUTED);
|
||||||
|
} else if (note.getChildNoteIds().length === 0) {
|
||||||
|
setState(SearchResultState.NO_RESULTS);
|
||||||
|
} else if (searchContainerRef.current) {
|
||||||
|
setState(SearchResultState.GOT_RESULTS);
|
||||||
|
|
||||||
|
const noteListRenderer = new NoteListRenderer({
|
||||||
|
$parent: $(searchContainerRef.current),
|
||||||
|
parentNote: note,
|
||||||
|
showNotePath: true
|
||||||
|
});
|
||||||
|
noteListRenderer.renderList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => refresh(), [ note ]);
|
||||||
|
useTriliumEvent("searchRefreshed", ({ ntxId: eventNtxId }) => {
|
||||||
|
if (eventNtxId === ntxId) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
useTriliumEvent("notesReloaded", ({ noteIds }) => {
|
||||||
|
if (note?.noteId && noteIds.includes(note.noteId)) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="search-result-widget">
|
||||||
|
{state === SearchResultState.NOT_EXECUTED && (
|
||||||
|
<Alert type="info" className="search-not-executed-yet">{t("search_result.search_not_executed")}</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{state === SearchResultState.NO_RESULTS && (
|
||||||
|
<Alert type="info" className="search-no-results">{t("search_result.no_notes_found")}</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div ref={searchContainerRef} className="search-results" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user