findwidget in read only notes WIP

This commit is contained in:
zadam 2022-05-25 23:38:06 +02:00
parent 37cb5f5e9a
commit f250b72563
13 changed files with 148 additions and 62 deletions

7
libraries/jquery.mark.es6.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -16,7 +16,7 @@ async function convertMarkdownToHtml(text) {
const result = writer.render(parsed);
appContext.triggerCommand('executeInTextEditor', {
appContext.triggerCommand('executeWithTextEditor', {
callback: textEditor => {
const viewFragment = textEditor.data.processor.toView(result);
const modelFragment = textEditor.data.toModel(viewFragment);

View File

@ -61,10 +61,13 @@ const EXCALIDRAW = {
"node_modules/react/umd/react.production.min.js",
"node_modules/react-dom/umd/react-dom.production.min.js",
"node_modules/@excalidraw/excalidraw/dist/excalidraw.production.min.js",
],
// css: [
// "stylesheets/somestyle.css"
// ]
]
};
const MARKJS = {
js: [
"libraries/jquery.mark.es6.min.js"
]
};
async function requireLibrary(library) {
@ -118,5 +121,6 @@ export default {
WHEEL_ZOOM,
FORCE_GRAPH,
MERMAID,
EXCALIDRAW
EXCALIDRAW,
MARKJS
}

View File

@ -228,7 +228,7 @@ class NoteContext extends Component {
}
async getTextEditor(callback) {
return new Promise(resolve => appContext.triggerCommand('executeInTextEditor', {
return new Promise(resolve => appContext.triggerCommand('executeWithTextEditor', {
callback,
resolve,
ntxId: this.ntxId
@ -236,7 +236,14 @@ class NoteContext extends Component {
}
async getCodeEditor() {
return new Promise(resolve => appContext.triggerCommand('executeInCodeEditor', {
return new Promise(resolve => appContext.triggerCommand('executeWithCodeEditor', {
resolve,
ntxId: this.ntxId
}));
}
async getContentElement() {
return new Promise(resolve => appContext.triggerCommand('executeWithContentElement', {
resolve,
ntxId: this.ntxId
}));

View File

@ -365,6 +365,10 @@ function sleep(time_ms) {
});
}
function escapeRegExp(str) {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
export default {
reloadFrontendApp,
parseDate,
@ -410,4 +414,5 @@ export default {
filterAttributeName,
isValidAttributeName,
sleep,
escapeRegExp
};

View File

@ -6,6 +6,7 @@
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import FindInText from "./find_in_text.js";
import FindInCode from "./find_in_code.js";
import FindInHtml from "./find_in_html.js";
const findWidgetDelayMillis = 200;
const waitForEnter = (findWidgetDelayMillis < 0);
@ -86,6 +87,13 @@ export default class FindWidget extends NoteContextAwareWidget {
this.textHandler = new FindInText(this);
this.codeHandler = new FindInCode(this);
this.htmlHandler = new FindInHtml(this);
}
async noteSwitched() {
await super.noteSwitched();
await this.closeSearch();
}
doRender() {
@ -125,6 +133,42 @@ export default class FindWidget extends NoteContextAwareWidget {
return this.$widget;
}
async findInTextEvent() {
if (!this.isActiveNoteContext()) {
return;
}
if (!['text', 'code'].includes(this.note.type) || !this.$findBox.is(":hidden")) {
return;
}
const readOnly = await this.noteContext.isReadOnly();
if (readOnly) {
this.handler = this.htmlHandler;
} else {
this.handler = this.note.type === "code"
? this.codeHandler
: this.textHandler;
}
this.$findBox.show();
this.$input.focus();
this.$totalFound.text(0);
this.$currentFound.text(0);
const searchTerm = await this.handler.getInitialSearchTerm();
this.$input.val(searchTerm || "");
// Directly perform the search if there's some text to
// find, without delaying or waiting for enter
if (searchTerm !== "") {
this.$input.select();
await this.performFind();
}
}
startSearch() {
// XXX This should clear the previous search immediately in all cases
// (the search is stale when waitforenter but also while the
@ -172,35 +216,6 @@ export default class FindWidget extends NoteContextAwareWidget {
}
}
async findInTextEvent() {
if (!this.isActiveNoteContext()) {
return;
}
// Only writeable text and code supported
const readOnly = await this.noteContext.isReadOnly();
if (readOnly || !['text', 'code'].includes(this.note.type) || !this.$findBox.is(":hidden")) {
return;
}
this.$findBox.show();
this.$input.focus();
this.$totalFound.text(0);
this.$currentFound.text(0);
const searchTerm = await this.handler.getInitialSearchTerm();
this.$input.val(searchTerm || "");
// Directly perform the search if there's some text to
// find, without delaying or waiting for enter
if (searchTerm !== "") {
this.$input.select();
await this.performFind();
}
}
/** Perform the find and highlight the find results. */
async performFind() {
const searchTerm = this.$input.val();
@ -216,33 +231,23 @@ export default class FindWidget extends NoteContextAwareWidget {
}
async closeSearch() {
this.$findBox.hide();
if (this.$findBox.is(":visible")) {
this.$findBox.hide();
// Restore any state, if there's a current occurrence clear markers
// and scroll to and select the last occurrence
const totalFound = parseInt(this.$totalFound.text());
const currentFound = parseInt(this.$currentFound.text()) - 1;
// Restore any state, if there's a current occurrence clear markers
// and scroll to and select the last occurrence
const totalFound = parseInt(this.$totalFound.text());
const currentFound = parseInt(this.$currentFound.text()) - 1;
if (totalFound > 0) {
await this.handler.cleanup(totalFound, currentFound);
}
if (totalFound > 0) {
await this.handler.cleanup(totalFound, currentFound);
}
this.searchTerm = null;
}
async entitiesReloadedEvent({loadResults}) {
if (loadResults.isNoteContentReloaded(this.noteId)) {
this.refresh();
this.searchTerm = null;
}
}
isEnabled() {
return super.isEnabled() && ['text', 'code'].includes(this.note.type);
}
get handler() {
return this.note.type === "code"
? this.codeHandler
: this.textHandler;
}
}

View File

@ -1,13 +1,14 @@
// ck-find-result and ck-find-result_selected are the styles ck-editor
// uses for highlighting matches, use the same one on CodeMirror
// for consistency
import utils from "../services/utils.js";
const FIND_RESULT_SELECTED_CSS_CLASSNAME = "ck-find-result_selected";
const FIND_RESULT_CSS_CLASSNAME = "ck-find-result";
const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
export default class FindInCode {
constructor(parent) {
/** @property {FindWidget} */
this.parent = parent;
}
@ -53,7 +54,7 @@ export default class FindInCode {
}
if (searchTerm !== "") {
searchTerm = escapeRegExp(searchTerm);
searchTerm = utils.escapeRegExp(searchTerm);
// Find and highlight matches
// Find and highlight matches

View File

@ -0,0 +1,36 @@
// ck-find-result and ck-find-result_selected are the styles ck-editor
// uses for highlighting matches, use the same one on CodeMirror
// for consistency
import libraryLoader from "../services/library_loader.js";
import utils from "../services/utils.js";
const FIND_RESULT_SELECTED_CSS_CLASSNAME = "ck-find-result_selected";
const FIND_RESULT_CSS_CLASSNAME = "ck-find-result";
export default class FindInHtml {
constructor(parent) {
/** @property {FindWidget} */
this.parent = parent;
}
async getInitialSearchTerm() {
return ""; // FIXME
}
async performFind(searchTerm, matchCase, wholeWord) {
await libraryLoader.requireLibrary(libraryLoader.MARKJS);
const $content = await this.parent.noteContext.getContentElement();
$content.markRegExp(new RegExp(utils.escapeRegExp(searchTerm), "gi"));
}
async findNext(direction, currentFound, nextFound) {
}
async cleanup(totalFound, currentFound) {
}
async close() {
}
}

View File

@ -1,5 +1,6 @@
export default class FindInText {
constructor(parent) {
/** @property {FindWidget} */
this.parent = parent;
}

View File

@ -171,7 +171,7 @@ export default class EditableCodeTypeWidget extends TypeWidget {
}
}
async executeInCodeEditorEvent({resolve, ntxId}) {
async executeWithCodeEditorEvent({resolve, ntxId}) {
if (!this.isNoteContext(ntxId)) {
return;
}

View File

@ -229,7 +229,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
return !selection.isCollapsed;
}
async executeInTextEditorEvent({callback, resolve, ntxId}) {
async executeWithTextEditorEvent({callback, resolve, ntxId}) {
if (!this.isNoteContext(ntxId)) {
return;
}

View File

@ -30,4 +30,14 @@ export default class ReadOnlyCodeTypeWidget extends TypeWidget {
this.$content.text(noteComplement.content);
}
async executeWithContentElementEvent({resolve, ntxId}) {
if (!this.isNoteContext(ntxId)) {
return;
}
await this.initialized;
resolve(this.$content);
}
}

View File

@ -114,4 +114,14 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget {
async refreshIncludedNoteEvent({noteId}) {
this.refreshIncludedNote(this.$content, noteId);
}
async executeWithContentElementEvent({resolve, ntxId}) {
if (!this.isNoteContext(ntxId)) {
return;
}
await this.initialized;
resolve(this.$content);
}
}