Skip to content

Commit

Permalink
Rewrite webview as a class to better manage state
Browse files Browse the repository at this point in the history
  • Loading branch information
roman-r-m committed Feb 7, 2021
1 parent 9e14d2d commit 5af0bbf
Showing 1 changed file with 89 additions and 71 deletions.
160 changes: 89 additions & 71 deletions src/webview/index.ts
Original file line number Diff line number Diff line change
@@ -1,96 +1,114 @@
import { GotoMessage, SearchResult } from "src/common";

declare const webviewApi: any;

function debounce(func: Function, timeout = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}
class SearchDialog {

declare const webviewApi: any;
let selectedIndex = -1;

root: Element;
results: SearchResult[];
selectedIndex: number = -1;
search: Function;

const getResources = debounce(async query => {
const results: SearchResult[] = await webviewApi.postMessage({
type: 'search',
query: query
});
constructor() {
this.search = debounce(this.getResources);
const queryInput = document.getElementById('query-input') as HTMLInputElement;
queryInput.addEventListener('input', e => {
e.preventDefault();
this.getResources(queryInput.value);
});

const searchResults = document.getElementById('search-results');
searchResults.innerText = '';

if (results.length > 0) {
selectedIndex = -1;
for (let i = 0; i < results.length; i++) {
const searchResult = results[i];
const row = document.createElement('div');
row.setAttribute('class', 'search-result-row');
searchResults.appendChild(row);
const root = document.getElementById('joplin-plugin-content');
root.addEventListener('keydown', evt => this.onKey(evt));
}

const resourceName = document.createElement('div');
resourceName.setAttribute('class', 'resource-name-cell');
resourceName.innerText = searchResult.title;
row.appendChild(resourceName);
onKey(event: KeyboardEvent): any {
const key = event.key;
switch (key) {
case 'Up':
case 'Down':
case 'ArrowUp':
case 'ArrowDown': {
const results = document.getElementById('search-results');
const newIndex = key === 'ArrowUp' || key === 'Up' ? this.selectedIndex - 1 : this.selectedIndex + 1;
const max = results.children.length;
if (max > 0) {
if (this.selectedIndex >= 0) {
results.children[this.selectedIndex].removeAttribute('selected');
}
this.selectedIndex = newIndex < 0 ? max - 1 : (newIndex % results.children.length);
results.children[this.selectedIndex].setAttribute('selected', 'true');
}
event.preventDefault();
break;
}
case 'Enter':
this.select(this.selectedIndex);
break;
}
}

const includedIn = document.createElement('div');
includedIn.setAttribute('class', 'referencing-notes-cell');
row.appendChild(includedIn);
async getResources(query: string) {
this.results = await webviewApi.postMessage({
type: 'search',
query: query
});

const referencingNotesList = document.createElement('div');
referencingNotesList.setAttribute('class', 'referencing-notes-list')
includedIn.appendChild(referencingNotesList);
// TODO compare new with existing and only redraw on change?
this.redraw();
}

searchResult.notes.forEach(n =>{
const noteLink = document.createElement('a');
noteLink.setAttribute('href', '#');
noteLink.addEventListener('click', ev => {
webviewApi.postMessage({
type: 'goto',
resourceId: searchResult.id,
noteId: n.id
} as GotoMessage);
});
noteLink.innerText = n.title;
referencingNotesList.appendChild(noteLink);
});
select(index: number) {
if (index >= 0 && index < this.results.length) {
const result = this.results[index];
webviewApi.postMessage({
type: 'goto',
resourceId: result.id,
noteId: result.notes[0].id //TODO
} as GotoMessage);
}
}
});
redraw() {
const searchResults = document.getElementById('search-results');
searchResults.innerText = '';

if (this.results.length > 0) {
this.selectedIndex = -1;
for (let i = 0; i < this.results.length; i++) {
const searchResult = this.results[i];
const row = document.createElement('div');
row.setAttribute('class', 'search-result-row');
searchResults.appendChild(row);

const queryInput = document.getElementById('query-input') as HTMLInputElement;
queryInput.addEventListener('input', e => {
e.preventDefault();
console.log(JSON.stringify(e));
getResources(queryInput.value);
});
const resourceName = document.createElement('div');
resourceName.setAttribute('class', 'resource-name-cell');
resourceName.innerText = searchResult.title;
row.appendChild(resourceName);

const root = document.getElementById('joplin-plugin-content');
const results = document.getElementById('search-results');
// document.addEventListener('click', e => {
// if (!root.contains(e.target)) {
// close the dialog
// }
// });
const includedIn = document.createElement('div');
includedIn.setAttribute('class', 'referencing-notes-cell');
row.appendChild(includedIn);

const referencingNotesList = document.createElement('div');
referencingNotesList.setAttribute('class', 'referencing-notes-list')
includedIn.appendChild(referencingNotesList);

root.addEventListener('keydown', evt => {
switch (evt.key) {
case 'Up':
case 'Down':
case 'ArrowUp':
case 'ArrowDown': {
const newIndex = evt.key === 'ArrowUp' || evt.key === 'Up' ? selectedIndex - 1 : selectedIndex + 1;
if (results.children.length > 0) {
if (selectedIndex > 0) {
results.children[selectedIndex].removeAttribute('selected');
searchResult.notes.forEach(n => {
const noteLink = document.createElement('a');
noteLink.setAttribute('href', '#');
noteLink.addEventListener('click', _e => this.select(i));
noteLink.innerText = `In: ${n.title}`;
referencingNotesList.appendChild(noteLink);
});
}
selectedIndex = (newIndex % results.children.length);
results.children[selectedIndex].setAttribute('selected', 'true');
}
evt.preventDefault();
break;
}
}
});
}

new SearchDialog();

0 comments on commit 5af0bbf

Please sign in to comment.