import linkService from "./link.js"; import noteContentRenderer from "./note_content_renderer.js"; import treeCache from "./tree_cache.js"; import attributeService from "./attributes.js"; const TPL = `
     
`; class NoteListRenderer { constructor(parentNote, notes) { this.$noteList = $(TPL); this.parentNote = parentNote; this.notes = notes; this.page = 1; this.pageSize = 6; this.viewType = parentNote.getLabelValue('viewType'); if (!['list', 'grid'].includes(this.viewType)) { this.viewType = 'list'; // default } this.$noteList.addClass(this.viewType + '-view'); this.$noteList.find('.list-view-button').on('click', () => this.toggleViewType('list')); this.$noteList.find('.grid-view-button').on('click', () => this.toggleViewType('grid')); this.$noteList.find('.expand-children-button').on('click', async () => { for (let i = 1; i < 30; i++) { // protection against infinite cycle const $unexpandedCards = this.$noteList.find('.note-book-card:not(.expanded)'); if ($unexpandedCards.length === 0) { break; } await this.toggleCards($unexpandedCards, true); if (!this.parentNote.hasLabel('expanded')) { await attributeService.addLabel(this.parentNote.noteId, 'expanded'); } } }); this.$noteList.find('.collapse-all-button').on('click', async () => { const $expandedCards = this.$noteList.find('.note-book-card.expanded'); await this.toggleCards($expandedCards, false); // owned is important - we shouldn't remove inherited expanded labels for (const expandedAttr of this.parentNote.getOwnedLabels('expanded')) { await attributeService.removeAttributeById(this.parentNote.noteId, expandedAttr.attributeId); } }); } async toggleViewType(type) { if (type !== 'list' && type !== 'grid') { throw new Error(`Invalid view type ${type}`); } this.viewType = type; this.$noteList .removeClass('grid-view') .removeClass('list-view') .addClass(this.viewType + '-view'); await attributeService.setLabel(this.parentNote.noteId, 'viewType', type); await this.renderList(); } async renderList() { const $container = this.$noteList.find('.note-list-container').empty(); const imageLinks = this.parentNote ? this.parentNote.getRelations('imageLink') : []; const startIdx = (this.page - 1) * this.pageSize; const endIdx = startIdx + this.pageSize; const pageNotes = this.notes.slice(startIdx, Math.min(endIdx, this.notes.length)); for (const note of pageNotes) { // image is already visible in the parent note so no need to display it separately in the book if (imageLinks.find(rel => rel.value === note.noteId)) { continue; } const $card = await this.renderNote(note); $container.append($card); } this.renderPager(); return this.$noteList; } renderPager() { const $pager = this.$noteList.find('.note-list-pager').empty(); const pageCount = Math.ceil(this.notes.length / this.pageSize); $pager.toggle(pageCount > 1); for (let i = 1; i <= pageCount; i++) { $pager.append( i === this.page ? $('').text(i).css('text-decoration', 'underline').css('font-weight', "bold") : $('') .text(i) .on('click', () => { this.page = i; this.renderList(); }), "   " ); } } async toggleCards(cards, expand) { for (const card of cards) { const $card = $(card); const noteId = $card.attr('data-note-id'); const note = await treeCache.getNote(noteId); await this.toggleContent($card, note, expand); } } // TODO: we should also render (promoted) attributes async renderNote(note) { const notePath = /*this.notePath + '/' + */ note.noteId; const $expander = $(''); const $card = $('
') .attr('data-note-id', note.noteId) .append( $('
') .append($expander) .append(await linkService.createNoteLink(notePath, {showTooltip: false})) ); $expander.on('click', () => this.toggleContent($card, note, !$card.hasClass("expanded"))); await this.toggleContent($card, note, this.parentNote.hasLabel('expanded')); return $card; } async toggleContent($card, note, expand) { if (this.viewType === 'list' && ((expand && $card.hasClass("expanded")) || (!expand && !$card.hasClass("expanded")))) { return; } const $expander = $card.find('> .note-book-title .note-expander'); if (expand || this.viewType === 'grid') { $card.addClass("expanded"); $expander.addClass("bx-chevron-down").removeClass("bx-chevron-right"); } else { $card.removeClass("expanded"); $expander.addClass("bx-chevron-right").removeClass("bx-chevron-down"); } if ((expand || this.viewType === 'grid') && $card.find('.note-book-content').length === 0) { $card.append(await this.renderNoteContent(note)); } } async renderNoteContent(note) { const $content = $('
'); try { const {renderedContent, type} = await noteContentRenderer.getRenderedContent(note); $content.append(renderedContent); $content.addClass("type-" + type); } catch (e) { console.log(`Caught error while rendering note ${note.noteId} of type ${note.type}: ${e.message}, stack: ${e.stack}`); $content.append("rendering error"); } if (this.viewType === 'list') { const imageLinks = note.getRelations('imageLink'); const childNotes = (await note.getChildNotes()) .filter(childNote => !imageLinks.find(rel => rel.value === childNote.noteId)); for (const childNote of childNotes) { $content.append(await this.renderNote(childNote)); } } return $content; } } export default NoteListRenderer;