From ade77e5fb818b1868cc2e508dea1d5819f30872c Mon Sep 17 00:00:00 2001 From: zadam Date: Thu, 26 May 2022 16:29:54 +0200 Subject: [PATCH] find widget readonly notes --- .../widgets/containers/scrolling_container.js | 7 ++- src/public/app/widgets/find.js | 12 ++-- src/public/app/widgets/find_in_code.js | 5 +- src/public/app/widgets/find_in_html.js | 63 ++++++++++++++++++- src/public/app/widgets/find_in_text.js | 8 +-- .../widgets/type_widgets/read_only_text.js | 1 + 6 files changed, 78 insertions(+), 18 deletions(-) diff --git a/src/public/app/widgets/containers/scrolling_container.js b/src/public/app/widgets/containers/scrolling_container.js index 6717ec992..59d7b5167 100644 --- a/src/public/app/widgets/containers/scrolling_container.js +++ b/src/public/app/widgets/containers/scrolling_container.js @@ -5,6 +5,7 @@ export default class ScrollingContainer extends Container { super(); this.css('overflow', 'auto'); + this.css('position', 'relative'); } setNoteContextEvent({noteContext}) { @@ -35,7 +36,7 @@ export default class ScrollingContainer extends Container { const promise = super.handleEventInChildren(name, data); - // there seems to be some asynchronicity and we need to wait a bit before scrolling + // there seems to be some asynchronicity, and we need to wait a bit before scrolling promise.then(() => setTimeout(() => this.$widget.scrollTop(scrollTop), 500)); return promise; @@ -44,4 +45,8 @@ export default class ScrollingContainer extends Container { return super.handleEventInChildren(name, data); } } + + scrollContainerToCommand({position}) { + this.$widget.scrollTop(position); + } } diff --git a/src/public/app/widgets/find.js b/src/public/app/widgets/find.js index aa873e4c5..fd8c793c2 100644 --- a/src/public/app/widgets/find.js +++ b/src/public/app/widgets/find.js @@ -123,7 +123,11 @@ export default class FindWidget extends NoteContextAwareWidget { await this.findNext(e?.shiftKey ? -1 : 1); e.preventDefault(); return false; - } else if (e.key === 'Escape') { + } + }); + + this.$findBox.keydown(async e => { + if (e.key === 'Escape') { await this.closeSearch(); } }); @@ -239,11 +243,9 @@ export default class FindWidget extends NoteContextAwareWidget { const totalFound = parseInt(this.$totalFound.text()); const currentFound = parseInt(this.$currentFound.text()) - 1; - if (totalFound > 0) { - await this.handler.cleanup(totalFound, currentFound); - } - this.searchTerm = null; + + await this.handler.findBoxClosed(totalFound, currentFound); } } diff --git a/src/public/app/widgets/find_in_code.js b/src/public/app/widgets/find_in_code.js index f3fecb044..1c244bd1d 100644 --- a/src/public/app/widgets/find_in_code.js +++ b/src/public/app/widgets/find_in_code.js @@ -169,7 +169,7 @@ export default class FindInCode { codeEditor.scrollIntoView(pos.from); } - async cleanup(totalFound, currentFound) { + async findBoxClosed(totalFound, currentFound) { const codeEditor = await this.getCodeEditor(); if (totalFound > 0) { @@ -190,10 +190,7 @@ export default class FindInCode { // Restore the highlightSelectionMatches setting codeEditor.setOption("highlightSelectionMatches", this.oldHighlightSelectionMatches); this.findResult = null; - } - async close() { - const codeEditor = await this.getCodeEditor(); codeEditor.focus(); } } diff --git a/src/public/app/widgets/find_in_html.js b/src/public/app/widgets/find_in_html.js index fbcaeb7fd..25e40c394 100644 --- a/src/public/app/widgets/find_in_html.js +++ b/src/public/app/widgets/find_in_html.js @@ -3,6 +3,7 @@ // for consistency import libraryLoader from "../services/library_loader.js"; import utils from "../services/utils.js"; +import appContext from "../services/app_context.js"; const FIND_RESULT_SELECTED_CSS_CLASSNAME = "ck-find-result_selected"; const FIND_RESULT_CSS_CLASSNAME = "ck-find-result"; @@ -11,6 +12,8 @@ export default class FindInHtml { constructor(parent) { /** @property {FindWidget} */ this.parent = parent; + this.currentIndex = 0; + this.$results = null; } async getInitialSearchTerm() { @@ -22,15 +25,69 @@ export default class FindInHtml { const $content = await this.parent.noteContext.getContentElement(); - $content.markRegExp(new RegExp(utils.escapeRegExp(searchTerm), "gi")); + const wholeWordChar = wholeWord ? "\\b" : ""; + const regExp = new RegExp(wholeWordChar + utils.escapeRegExp(searchTerm) + wholeWordChar, matchCase ? "g" : "gi"); + + return new Promise(res => { + $content.unmark({ + done: () => { + $content.markRegExp(regExp, { + element: "span", + className: FIND_RESULT_CSS_CLASSNAME, + separateWordSearch: false, + caseSensitive: matchCase, + done: async () => { + this.$results = $content.find("." + FIND_RESULT_CSS_CLASSNAME); + this.currentIndex = 0; + await this.jumpTo(); + + res({ + totalFound: this.$results.length, + currentFound: 1 + }); + } + }); + } + }); + }); } async findNext(direction, currentFound, nextFound) { + if (this.$results.length) { + this.currentIndex += direction; + + if (this.currentIndex < 0) { + this.currentIndex = this.$results.length - 1; + } + + if (this.currentIndex > this.$results.length - 1) { + this.currentIndex = 0; + } + + await this.jumpTo(); + } } - async cleanup(totalFound, currentFound) { + async findBoxClosed(totalFound, currentFound) { + const $content = await this.parent.noteContext.getContentElement(); + $content.unmark(); } - async close() { + async jumpTo() { + if (this.$results.length) { + const offsetTop = 100; + const $current = this.$results.eq(this.currentIndex); + this.$results.removeClass(FIND_RESULT_SELECTED_CSS_CLASSNAME); + + if ($current.length) { + $current.addClass(FIND_RESULT_SELECTED_CSS_CLASSNAME); + const position = $current.position().top - offsetTop; + + const $content = await this.parent.noteContext.getContentElement(); + const $contentWiget = appContext.getComponentByEl($content); + + $contentWiget.triggerCommand("scrollContainerTo", {position}); + } + } } } diff --git a/src/public/app/widgets/find_in_text.js b/src/public/app/widgets/find_in_text.js index 6de137e3a..97ebaea36 100644 --- a/src/public/app/widgets/find_in_text.js +++ b/src/public/app/widgets/find_in_text.js @@ -91,9 +91,10 @@ export default class FindInText { } } - async cleanup(totalFound, currentFound) { + async findBoxClosed(totalFound, currentFound) { + const textEditor = await this.getTextEditor(); + if (totalFound > 0) { - const textEditor = await this.getTextEditor(); // Clear the markers and set the caret to the // current occurrence const model = textEditor.model; @@ -112,10 +113,7 @@ export default class FindInText { } this.findResult = null; - } - async close() { - const textEditor = await this.getTextEditor(); textEditor.focus(); } } diff --git a/src/public/app/widgets/type_widgets/read_only_text.js b/src/public/app/widgets/type_widgets/read_only_text.js index 2fa0b5e81..a1a804450 100644 --- a/src/public/app/widgets/type_widgets/read_only_text.js +++ b/src/public/app/widgets/type_widgets/read_only_text.js @@ -33,6 +33,7 @@ const TPL = ` padding-top: 10px; font-family: var(--detail-font-family); min-height: 50px; + position: relative; } .note-detail-readonly-text p:first-child, .note-detail-readonly-text::before {