And the following code in a single javascript code-block inside page Chart
:const DELIMITER = '|'
const DFLT = {
color: '#36A2EB|#FF6384|#4BC0C0|#FF9F40|#9966FF|#FFCD56|#C9CBCF'.split('|'),
width: ['0']
}
const LS = logseq.api
const Module = logseq.Module
const Kits = Module.Kits
const Msg = Module.Msg
function advancedQuery(content, queryWord){
const queryStart = content.indexOf("[", queryWord + 5)
if (queryStart < 0) return
var queryEnd = queryStart + 1
var n = 1
while (n > 0) {
const close = content.indexOf("]", queryEnd)
if (close < 0) return
const open = content.indexOf("[", queryEnd)
if (close < open || open < 0) {
queryEnd = close + 1
n -= 1
continue
}
queryEnd = open + 1
n += 1
}
return content.slice(queryStart, queryEnd)
}
function simpleQuery(content, queryWord){
const queryStart = queryWord + 6
const queryEnd = content.indexOf("}}", queryStart)
return content.slice(queryStart, queryEnd)
}
function getResults(content, queryWord){
if (queryWord < 0) return
if (content[queryWord - 1] === ":") {
const query = advancedQuery(content, queryWord)
if (query) return LS.datascript_query(query).flat()
} else if (content.slice(queryWord - 2, queryWord) === "{{") {
const query = simpleQuery(content, queryWord)
if (query) return LS.custom_query(query)
}
}
function Plotter(div){
return {div, dataset: div.dataset, data: [],
prop: div.dataset.prop.split(DELIMITER),
__proto__: Plotter.prototype};
}
Plotter.prototype.attemptPlot = function(){
let msg = "No results"
try {
this.findResults()
if (this.res) {
this.fillData()
this.readDataset(this.dataset)
this.plot()
}
} catch(e) {
msg = "Error: " + e.message
this.data.length = 0
}
if (this.data.length > 0) return
const spanMsg = Kits.createElementOfClass("span", "text-sm", msg)
this.div.appendChild(spanMsg)
}
Plotter.prototype.fillData = function(){
this.prop.forEach(
(p)=>this.data.push(this.res.map( (r)=>{
const v = Number(r.properties?.[p] ?? r[p] ?? r.data)
return (isNaN(v) ? 0 : v)
}))
)
}
Plotter.prototype.findResults = function(){
const parent = this.div.closest("div.ls-block")
const child = parent.querySelector("div.ls-block")
if (!child) return Msg.warning("Missing child")
const blockId = child.getAttribute("blockid")
const block = LS.get_block(blockId)
const content = block.content.replace(/\n?.*[:][:].*\n?/g, "\n").trim()
const queryWord = content.indexOf("query")
this.res = getResults(content, queryWord)
?? Msg.warning("Missing query")
}
Plotter.prototype.getDatasets = function(){
return this.data.map( (data, i)=>{
const color = this.color[i] ?? this.color[0]
return {
backgroundColor: color,
borderColor: color,
borderWidth: Number(this.width[i] ?? this.width[0]),
data,
label: this.title[i] ?? this.prop[i]
}
})
}
Plotter.prototype.labelForResult = function(r){
return r.label ?? r.originalName ?? r['original-name'] ??
LS.get_page(r.page.id).originalName
}
Plotter.prototype.plot = function(){
const dataset = this.dataset
const canvas = document.createElement('canvas')
this.div.appendChild(canvas)
new Chart(canvas, {
data: {
datasets: this.getDatasets(),
labels: this.res.map(this.labelForResult, this)
},
options: {
indexAxis: dataset.indexAxis ?? 'x',
maintainAspectRatio: dataset.aspectRatio || false
},
type: dataset.type
})
}
Plotter.prototype.readDataset = function(dataset){
this.color = (!dataset.color) ? DFLT.color
: dataset.color.split(DELIMITER)
this.title = (dataset.title === '$2') ? []
: dataset.title.split(DELIMITER)
this.width = (!dataset.width) ? DFLT.width
: dataset.width.split(DELIMITER)
}
logseq.kits.setStatic(function chart(div){
const plotter = Plotter(div)
if (window.Chart) return plotter.attemptPlot()
const script = document.createElement('script')
script.src = "https://cdn.jsdelivr.net/npm/chart.js"
script.type = 'text/javascript'
document.body.appendChild(script)
script.addEventListener("load", (e)=>{
Msg.info("chart.js loaded")
plotter.attemptPlot()
})
Msg.info("Loading chart.js ...")
})