Selecting history nodes

This commit is contained in:
Magnus Åhall 2026-06-06 21:27:42 +02:00
parent aeca9d8559
commit 28111cc8eb
2 changed files with 140 additions and 48 deletions

View file

@ -21,7 +21,8 @@ html {
} }
button { button {
font-size: 1e m; font-size: 1em;
padding: 4px 8px;
} }
/* ------------------------------------- * /* ------------------------------------- *
@ -546,7 +547,7 @@ n2-pagehistory {
padding: 32px; padding: 32px;
margin-bottom: 32px; margin-bottom: 32px;
border-radius: 8px; border-radius: 8px;
background-color: #f8f8f8; background-color: #fafafa;
box-shadow: box-shadow:
rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.4) 0px 2px 4px,
@ -585,62 +586,72 @@ n2-pagehistory {
gap: 1px; gap: 1px;
border: 1px solid var(--line-color); border: 1px solid var(--line-color);
&>div>div { n2-pagehistorynode>* {
padding: 8px 12px; padding: 8px 12px;
background-color: #fff; background-color: #fff;
white-space: nowrap; white-space: nowrap;
}
&.index { n2-pagehistorynode {
&.selected .el-index:after {
position: absolute;
left: -20px;
content: '>';
color: var(--color1);
font-weight: bold;
margin-right: 8px;
}
.el-index {
position: relative;
text-align: right; text-align: right;
} }
&.updated { .el-updated {
white-space: initial; white-space: initial;
} }
.date { .el-date {
white-space: nowrap; white-space: nowrap;
font-weight: bold; font-weight: bold;
} }
.time { .el-time {
white-space: nowrap; white-space: nowrap;
color: #555; color: #555;
} }
&.name { .el-name {
white-space: initial; white-space: initial;
/*overflow-wrap: anywhere;*/ /*overflow-wrap: anywhere;*/
word-break: break-all; word-break: break-all;
color: var(--color1); color: var(--color1);
} }
} }
.history-node {
display: contents;
}
}
.el-pagination {
grid-column: 1 / -1;
margin-top: 16px;
display: grid;
grid-template-columns: repeat(3, min-content);
grid-gap: 16px;
align-items: center;
white-space: nowrap;
user-select: none;
.el-prev,
.el-next {
font-weight: bold;
cursor: pointer;
border: 1px solid #aaa;
background-color: #eee;
padding: 8px 16px;
border-radius: 4px;
}
} }
} }
.el-pagination {
grid-column: 1 / -1;
margin-top: 16px;
display: grid;
grid-template-columns: repeat(3, min-content);
grid-gap: 16px;
align-items: center;
white-space: nowrap;
user-select: none;
.el-prev,
.el-next {
font-weight: bold;
cursor: pointer;
border: 1px solid #aaa;
background-color: #eee;
padding: 8px 16px;
border-radius: 4px;
}
}
}

View file

@ -1,5 +1,7 @@
import { CustomHTMLElement } from './lib/custom_html_element.mjs' import { CustomHTMLElement } from './lib/custom_html_element.mjs'
import { Node } from './page_node.mjs' import { Node } from './page_node.mjs'
import { MarkedPosition } from './marked_position.mjs'
export class N2PageHistory extends CustomHTMLElement { export class N2PageHistory extends CustomHTMLElement {
static PAGESIZE = 15 static PAGESIZE = 15
@ -47,11 +49,14 @@ export class N2PageHistory extends CustomHTMLElement {
<div data-el="next">&gt;</div> <div data-el="next">&gt;</div>
</div> </div>
</div> </div>
<div data-el="markdown-content"></div>
` `
}// }}} }// }}}
constructor() {// {{{ constructor() {// {{{
super() super()
this.selectedNode = null
this.setAttribute('tabindex', '-1') this.setAttribute('tabindex', '-1')
this.addEventListener('keydown', event => this.keyHandler(event)) this.addEventListener('keydown', event => this.keyHandler(event))
@ -72,6 +77,10 @@ export class N2PageHistory extends CustomHTMLElement {
await this.useNode(event.detail.data) await this.useNode(event.detail.data)
this.render() this.render()
}) })
_mbus.subscribe('HISTORY_NODE_SELECTED', (event) => {
this.selectedNode = event.detail.data.historyNode
})
}// }}} }// }}}
async render(keepFetchHistoryProgress) {// {{{ async render(keepFetchHistoryProgress) {// {{{
this.elNodeName.innerText = this.node.get('Name') this.elNodeName.innerText = this.node.get('Name')
@ -88,24 +97,26 @@ export class N2PageHistory extends CustomHTMLElement {
let i = 0 let i = 0
let divs = nodes.map(n => { let divs = nodes.map(n => {
i++ i++
const index = 1 + this.nodesTotal - (N2PageHistory.PAGESIZE * (this.page - 1) + i)
const date = n.get('Updated').slice(0, 10) const div = new N2PageHistoryNode(n, index)
const time = n.get('Updated').slice(11, 19) div.render()
const div = document.createElement('div')
div.innerHTML = `
<div class="index">${1 + this.nodesTotal - (N2PageHistory.PAGESIZE * (this.page - 1) + i)}</div>
<div class="updated"><span class="date">${date}</span> <span class="time">${time}</span></div>
<div class="size">${this.formatSize(n.get('Content').length)}</div>
<div class="name">${n.get('Name')}</div>
`
div.classList.add('history-node')
return div return div
}) })
this.elNodes.replaceChildren(...divs) this.elNodes.replaceChildren(...divs)
if (!keepFetchHistoryProgress) if (!keepFetchHistoryProgress)
this.elFetchHistoryProgress.innerText = '' this.elFetchHistoryProgress.innerText = ''
// Select the first node.
if (!this.selectedNode) {
this.elNodes.firstElementChild?.select()
}
// Any selected history node is rendered with markdown.
/*
this.marked = new MarkedPosition()
this.elNodeMarkdown.innerHTML = this.marked.parse(this.elNodeContent.value)
*/
}// }}} }// }}}
async useNode(node) {// {{{ async useNode(node) {// {{{
@ -124,18 +135,33 @@ export class N2PageHistory extends CustomHTMLElement {
case 'ArrowRight': case 'ArrowRight':
this.nextPage() this.nextPage()
break break
case 'ArrowUp':
const prevNode = this.selectedNode?.previousElementSibling
if (prevNode)
prevNode.select()
break
case 'ArrowDown':
const nextNode = this.selectedNode?.nextElementSibling
if (nextNode)
nextNode.select()
break
} }
}// }}} }// }}}
prevPage() {// {{{ prevPage() {// {{{
if (this.page == 1) if (this.page == 1)
return return
// Selecting a node on another page is wrong.
this.selectedNode = null
this.page-- this.page--
this.render() this.render()
}// }}} }// }}}
nextPage() {// {{{ nextPage() {// {{{
if (this.page >= this.pages) if (this.page >= this.pages)
return return
// Selecting a node on another page is wrong.
this.selectedNode = null
this.page++ this.page++
this.render() this.render()
}// }}} }// }}}
@ -199,6 +225,61 @@ export class N2PageHistory extends CustomHTMLElement {
return json return json
}// }}} }// }}}
}
customElements.define('n2-pagehistory', N2PageHistory)
class N2PageHistoryNode extends CustomHTMLElement {
static {// {{{
this.tmpl = document.createElement('template')
this.tmpl.innerHTML = `
<div data-el="index"></div>
<div data-el="updated"><span data-el="date"></span> <span data-el="time"></span></div>
<div data-el="size"></div>
<div data-el="name"></div>
`
}// }}}
constructor(node, index) {// {{{
super()
this.node = node
this.index = index
this.style.display = 'contents'
this.selected = false
this.addEventListener('click', () => this.select())
// Another history node has been selected.
_mbus.subscribe('HISTORY_NODE_SELECTED', (event) => {
if (this.node.get('Updated') == event.detail.data.historyNode.node.get('Updated'))
return
this.selected = false
this.render()
})
}// }}}
select() {// {{{
this.selected = true
// Other nodes are told to unselect and rerender.
_mbus.dispatch('HISTORY_NODE_SELECTED', { historyNode: this })
this.render()
}// }}}
render() {// {{{
const date = this.node.get('Updated').slice(0, 10)
const time = this.node.get('Updated').slice(11, 19)
if (this.selected)
this.classList.add('selected')
else
this.classList.remove('selected')
this.elIndex.innerText = this.index
this.elDate.innerText = date
this.elTime.innerText = time
this.elSize.innerText = this.node.get('Content').length
this.elName.innerText = this.node.get('Name')
}// }}}
formatSize(s) {// {{{ formatSize(s) {// {{{
let div = 1 let div = 1
let unit = 'B' let unit = 'B'
@ -215,4 +296,4 @@ export class N2PageHistory extends CustomHTMLElement {
}).format(Math.round(s / div)) + ' ' + unit }).format(Math.round(s / div)) + ' ' + unit
}// }}} }// }}}
} }
customElements.define('n2-pagehistory', N2PageHistory) customElements.define('n2-pagehistorynode', N2PageHistoryNode)