import linkService from "./link.js";
import noteContentRenderer from "./note_content_renderer.js";
import froca from "./froca.js";
import attributeRenderer from "./attribute_renderer.js";
import libraryLoader from "./library_loader.js";
const TPL = `
`;
class NoteListRenderer {
/*
* We're using noteIds so that it's not necessary to load all notes at once when paging
*/
constructor($parent, parentNote, noteIds, showNotePath = false) {
this.$noteList = $(TPL);
// note list must be added to the DOM immediatelly, otherwise some functionality scripting (canvas) won't work
$parent.empty();
this.parentNote = parentNote;
const includedNoteIds = this.getIncludedNoteIds();
this.noteIds = noteIds.filter(noteId => !includedNoteIds.has(noteId) && noteId !== 'hidden');
if (this.noteIds.length === 0) {
return;
}
$parent.append(this.$noteList);
this.page = 1;
this.pageSize = parseInt(parentNote.getLabelValue('pageSize'));
if (!this.pageSize || this.pageSize < 1) {
this.pageSize = 20;
}
this.viewType = parentNote.getLabelValue('viewType');
if (!['list', 'grid'].includes(this.viewType)) {
// when not explicitly set decide based on note type
this.viewType = parentNote.type === 'search' ? 'list' : 'grid';
}
this.$noteList.addClass(this.viewType + '-view');
this.showNotePath = showNotePath;
}
/** @returns {Set} list of noteIds included (images, included notes) into a parent note and which
* don't have to be shown in the note list. */
getIncludedNoteIds() {
const includedLinks = this.parentNote
? this.parentNote.getRelations().filter(rel => rel.name === 'imageLink' || rel.name === 'includeNoteLink')
: [];
return new Set(includedLinks.map(rel => rel.value));
}
async renderList() {
if (this.noteIds.length === 0) {
this.$noteList.hide();
return;
}
const highlightedTokens = this.parentNote.highlightedTokens || [];
if (highlightedTokens.length > 0) {
await libraryLoader.requireLibrary(libraryLoader.MARKJS);
this.highlightRegex = new RegExp(highlightedTokens.join("|"), 'gi');
} else {
this.highlightRegex = null;
}
this.$noteList.show();
const $container = this.$noteList.find('.note-list-container').empty();
const startIdx = (this.page - 1) * this.pageSize;
const endIdx = startIdx + this.pageSize;
const pageNoteIds = this.noteIds.slice(startIdx, Math.min(endIdx, this.noteIds.length));
const pageNotes = await froca.getNotes(pageNoteIds);
for (const note of pageNotes) {
const $card = await this.renderNote(note, this.parentNote.hasLabel('expanded'));
$container.append($card);
}
this.renderPager();
return this.$noteList;
}
renderPager() {
const $pager = this.$noteList.find('.note-list-pager').empty();
const pageCount = Math.ceil(this.noteIds.length / this.pageSize);
$pager.toggle(pageCount > 1);
let lastPrinted;
for (let i = 1; i <= pageCount; i++) {
if (pageCount < 20 || i <= 5 || pageCount - i <= 5 || Math.abs(this.page - i) <= 2) {
lastPrinted = true;
$pager.append(
i === this.page
? $('').text(i).css('text-decoration', 'underline').css('font-weight', "bold")
: $('')
.text(i)
.on('click', () => {
this.page = i;
this.renderList();
}),
" "
);
}
else if (lastPrinted) {
$pager.append("... ");
lastPrinted = false;
}
}
}
async renderNote(note, expand = false) {
const $expander = $('');
const {$renderedAttributes} = await attributeRenderer.renderNormalAttributes(note);
const notePath = this.parentNote.type === 'search'
? note.noteId // for search note parent we want to display non-search path
: this.parentNote.noteId + '/' + note.noteId;
const $card = $('')
.attr('data-note-id', note.noteId)
.append(
$('