mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 09:58:32 +02:00
quick search widget WIP
This commit is contained in:
parent
2ac78c2e03
commit
9447d3f9b5
87
src/public/app/widgets/quick_search.js
Normal file
87
src/public/app/widgets/quick_search.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import BasicWidget from "./basic_widget.js";
|
||||||
|
import server from "../services/server.js";
|
||||||
|
import linkService from "../services/link.js";
|
||||||
|
import treeCache from "../services/tree_cache.js";
|
||||||
|
import utils from "../services/utils.js";
|
||||||
|
|
||||||
|
const TPL = `
|
||||||
|
<div class="quick-search input-group input-group-sm" style="width: 250px;">
|
||||||
|
<style>
|
||||||
|
.quick-search .dropdown-menu {
|
||||||
|
max-height: 600px;
|
||||||
|
max-width: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
box-shadow: 10px 10px 93px -25px black;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<input type="text" class="form-control form-control-sm search-string" placeholder="Quick search">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-outline-secondary search-button" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="bx bx-search"></span>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
|
<a class="dropdown-item" href="#">Action</a>
|
||||||
|
<a class="dropdown-item" href="#">Another action</a>
|
||||||
|
<a class="dropdown-item" href="#">Something else here</a>
|
||||||
|
<div role="separator" class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item" href="#">Separated link</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
const MAX_DISPLAYED_NOTES = 20;
|
||||||
|
|
||||||
|
export default class QuickSearchWidget extends BasicWidget {
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL);
|
||||||
|
this.overflowing();
|
||||||
|
|
||||||
|
this.$searchString = this.$widget.find('.search-string');
|
||||||
|
this.$dropdownMenu = this.$widget.find('.dropdown-menu');
|
||||||
|
this.$dropdownToggle = this.$widget.find('.search-button');
|
||||||
|
|
||||||
|
this.$widget.find('.input-group-append').on('show.bs.dropdown', () => this.search());
|
||||||
|
|
||||||
|
utils.bindElShortcut(this.$searchString, 'return', () => {
|
||||||
|
this.$dropdownToggle.dropdown('show');
|
||||||
|
|
||||||
|
this.$searchString.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
async search() {
|
||||||
|
this.$dropdownMenu.empty();
|
||||||
|
this.$dropdownMenu.append('<span class="dropdown-item disabled"><span class="bx bx-loader bx-spin"></span> Searching ...</span>');
|
||||||
|
|
||||||
|
const resultNoteIds = await server.get('quick-search/' + encodeURIComponent(this.$searchString.val()));
|
||||||
|
|
||||||
|
const displayedNoteIds = resultNoteIds.slice(0, Math.min(MAX_DISPLAYED_NOTES, resultNoteIds.length));
|
||||||
|
|
||||||
|
this.$dropdownMenu.empty();
|
||||||
|
|
||||||
|
this.$dropdownMenu.append('<div class="dropdown-item"><button class="btn btn-sm">Show in full search</button></div>');
|
||||||
|
|
||||||
|
if (displayedNoteIds.length === 0) {
|
||||||
|
this.$dropdownMenu.append('<span class="dropdown-item disabled">No results found</span>');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const note of await treeCache.getNotes(displayedNoteIds)) {
|
||||||
|
const $link = await linkService.createNoteLink(note.noteId, {showNotePath: true});
|
||||||
|
$link.addClass('dropdown-item');
|
||||||
|
|
||||||
|
this.$dropdownMenu.append($link);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultNoteIds.length > MAX_DISPLAYED_NOTES) {
|
||||||
|
this.$dropdownMenu.append(`<span class="dropdown-item disabled">... and ${resultNoteIds.length - MAX_DISPLAYED_NOTES} more results.</span>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$dropdownToggle.dropdown('update');
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import BasicWidget from "./basic_widget.js";
|
import BasicWidget from "./basic_widget.js";
|
||||||
import HistoryNavigationWidget from "./history_navigation.js";
|
import HistoryNavigationWidget from "./history_navigation.js";
|
||||||
import protectedSessionService from "../services/protected_session.js";
|
import protectedSessionService from "../services/protected_session.js";
|
||||||
|
import QuickSearchWidget from "./quick_search.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="standard-top-widget">
|
<div class="standard-top-widget">
|
||||||
@ -13,7 +14,7 @@ const TPL = `
|
|||||||
height: 35px;
|
height: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.standard-top-widget button {
|
.standard-top-widget button:not(.search-button) {
|
||||||
padding: 1px 5px 1px 5px;
|
padding: 1px 5px 1px 5px;
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
@ -84,6 +85,11 @@ export default class StandardTopWidget extends BasicWidget {
|
|||||||
|
|
||||||
this.$widget.prepend(historyNavigationWidget.render());
|
this.$widget.prepend(historyNavigationWidget.render());
|
||||||
|
|
||||||
|
const quickSearchWidget = new QuickSearchWidget();
|
||||||
|
this.child(quickSearchWidget);
|
||||||
|
|
||||||
|
this.$widget.append(quickSearchWidget.render());
|
||||||
|
|
||||||
this.$enterProtectedSessionButton = this.$widget.find(".enter-protected-session-button");
|
this.$enterProtectedSessionButton = this.$widget.find(".enter-protected-session-button");
|
||||||
this.$enterProtectedSessionButton.on('click', protectedSessionService.enterProtectedSession);
|
this.$enterProtectedSessionButton.on('click', protectedSessionService.enterProtectedSession);
|
||||||
|
|
||||||
|
@ -200,6 +200,19 @@ async function searchFromRelation(note, relationName) {
|
|||||||
return typeof result[0] === 'string' ? result : result.map(item => item.noteId);
|
return typeof result[0] === 'string' ? result : result.map(item => item.noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function quickSearch(req) {
|
||||||
|
const {searchString} = req.params;
|
||||||
|
|
||||||
|
const searchContext = new SearchContext({
|
||||||
|
fastSearch: false,
|
||||||
|
includeArchivedNotes: false,
|
||||||
|
fuzzyAttributeSearch: false
|
||||||
|
});
|
||||||
|
|
||||||
|
return searchService.findNotesWithQuery(searchString, searchContext)
|
||||||
|
.map(sr => sr.noteId);
|
||||||
|
}
|
||||||
|
|
||||||
function getRelatedNotes(req) {
|
function getRelatedNotes(req) {
|
||||||
const attr = req.body;
|
const attr = req.body;
|
||||||
|
|
||||||
@ -276,5 +289,6 @@ function formatValue(val) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
searchFromNote,
|
searchFromNote,
|
||||||
searchAndExecute,
|
searchAndExecute,
|
||||||
getRelatedNotes
|
getRelatedNotes,
|
||||||
|
quickSearch
|
||||||
};
|
};
|
||||||
|
@ -262,6 +262,7 @@ function register(app) {
|
|||||||
route(POST, '/api/sender/image', [auth.checkToken, uploadMiddleware], senderRoute.uploadImage, apiResultHandler);
|
route(POST, '/api/sender/image', [auth.checkToken, uploadMiddleware], senderRoute.uploadImage, apiResultHandler);
|
||||||
route(POST, '/api/sender/note', [auth.checkToken], senderRoute.saveNote, apiResultHandler);
|
route(POST, '/api/sender/note', [auth.checkToken], senderRoute.saveNote, apiResultHandler);
|
||||||
|
|
||||||
|
apiRoute(GET, '/api/quick-search/:searchString', searchRoute.quickSearch);
|
||||||
apiRoute(GET, '/api/search-note/:noteId', searchRoute.searchFromNote);
|
apiRoute(GET, '/api/search-note/:noteId', searchRoute.searchFromNote);
|
||||||
apiRoute(POST, '/api/search-and-execute-note/:noteId', searchRoute.searchAndExecute);
|
apiRoute(POST, '/api/search-and-execute-note/:noteId', searchRoute.searchAndExecute);
|
||||||
apiRoute(POST, '/api/search-related', searchRoute.getRelatedNotes);
|
apiRoute(POST, '/api/search-related', searchRoute.getRelatedNotes);
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const cls = require('../cls');
|
||||||
|
|
||||||
class SearchContext {
|
class SearchContext {
|
||||||
constructor(params = {}) {
|
constructor(params = {}) {
|
||||||
this.fastSearch = !!params.fastSearch;
|
this.fastSearch = !!params.fastSearch;
|
||||||
this.ancestorNoteId = params.ancestorNoteId;
|
this.ancestorNoteId = params.ancestorNoteId || cls.getHoistedNoteId();
|
||||||
this.ancestorDepth = params.ancestorDepth;
|
this.ancestorDepth = params.ancestorDepth;
|
||||||
this.includeArchivedNotes = !!params.includeArchivedNotes;
|
this.includeArchivedNotes = !!params.includeArchivedNotes;
|
||||||
this.orderBy = params.orderBy;
|
this.orderBy = params.orderBy;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user