Allow search query results in tables

This commit is contained in:
Peter Stuifzand 2020-11-08 17:01:47 +01:00
parent f3737f96bd
commit 8513e4b96c
2 changed files with 146 additions and 75 deletions

View File

@ -142,6 +142,37 @@ function showSearchResultsExtended(element, template, searchTool, query, input,
}) })
} }
function formatLineResult(hit) {
return [
{
text: "[[" + hit.title + "]]",
indented: 0,
fold: 'open',
hidden: false,
fleeting: true
},
{
text: hit.line,
indented: 1,
fold: 'open',
hidden: false,
fleeting: true
}
]
}
function formatTitleResult(hit) {
return [
{
text: "[[" + hit.title + "]]",
indented: 0,
fold: 'open',
hidden: false,
fleeting: true
}
]
}
function renderGraphs() { function renderGraphs() {
$('code.language-dot').each(function (i, code) { $('code.language-dot').each(function (i, code) {
if (!code.innerText) { if (!code.innerText) {
@ -163,11 +194,23 @@ function renderGraphs() {
function el(tag, children = []) { function el(tag, children = []) {
let el = document.createElement(tag) let el = document.createElement(tag)
_.each(children, item => { _.each(children, item => {
el.appendChild(item) if (item !== undefined) el.appendChild(item)
}) })
return el return el
} }
function mflatten(rowData) {
return Promise.all(rowData.reduce(function (a, b) {
return a.concat(b);
}, []));
}
function flatten(rowData) {
return rowData.reduce(function (a, b) {
return a.concat(b);
}, []);
}
function Editor(holder, input) { function Editor(holder, input) {
function renderInline(cellText) { function renderInline(cellText) {
if (!cellText) return document.createTextNode('') if (!cellText) return document.createTextNode('')
@ -179,38 +222,57 @@ function Editor(holder, input) {
return span[0]; return span[0];
} }
function promiseMapAll(p, f) {
return p.then(all => Promise.all(_.map(all, f)))
}
function transformTable(editor, id, element) { function transformTable(editor, id, element) {
editor.treeForId(id).then(tree => { editor.treeForId(id)
let header = _.find(tree[0].children, c => c.text === 'headers') || [] .then(tree => {
let rows = _.find(tree[0].children, c => c.text === 'rows') || [] let header = _.find(tree[0].children, c => c.text === 'headers') || []
let rows = _.find(tree[0].children, c => c.text === 'rows') || []
let rowData = _.map(rows.children, row => { let collection = _.map(rows.children, child => {
// FIXME: Use a real parse link function let res = child.text.match(/{{query(!?):\s*([^}]+)}}/)
let page = row.text.substring(2).substring(0, row.text.length - 4) if (res && res[1] === '!') {
return fetch('/' + page + '?format=metakv') return search.startQuery(res[2])
.then(res => res.json()) .then(hits => _.uniqBy(_.flatMap(hits, formatTitleResult), _.property('text')))
.then(res => res.meta) }
.then(rowData => { return Promise.resolve().then(() => [child])
return el("tr", [ });
el("td", [renderInline(row.text)]),
..._.map(header.children, col => { Promise.all(collection)
let td = el("td") .then(_.flatten)
let value = rowData[_.snakeCase(_.trim(col.text))]; .then(_.partialRight(_.map, _.property('text')))
if (col.children && col.children.length > 0) { .then(_.uniq)
value = col.children[0].text .then(rowTexts => {
} return _.map(rowTexts, rowText => {
transform(value ? value.replace(/^:/, '=') : '', $(td), id, editor, rowData) let page = rowText.substring(2).substring(0, rowText.length - 4)
return td return fetch('/' + page + '?format=metakv')
}) .then(res => res.ok ? res.json() : {meta: {}})
]) .then(res => res.meta)
.then(rowData => {
rowData.title = rowData.title || rowText
return el("tr", [
el("td", [renderInline(rowText)]),
..._.map(header.children, col => {
let td = el("td")
let value = rowData[_.snakeCase(_.trim(col.text))];
if (col.children && col.children.length > 0) {
value = col.children[0].text
}
transform(value ? value.replace(/^:/, '=') : '', $(td), id, editor, rowData)
return td
})
])
})
})
}) })
}) .then(mflatten)
.then(trs => {
Promise.all(rowData) return el("table", [
.then(trs => { el("thead", [
return el("table", [ el("tr", [
el("thead", [
el("tr", [
el("th", [ el("th", [
document.createTextNode("Title") document.createTextNode("Title")
]), ]),
@ -219,13 +281,21 @@ function Editor(holder, input) {
document.createTextNode(col.text) document.createTextNode(col.text)
]) ])
}) })
] ]),
), ]),
]), el("tbody", trs)
el("tbody", trs) ])
]) })
}).then(table => element.html(table)) .then(table => {
}) table.classList.add('table')
table.classList.add('wiki-table')
let div = el('div', [table])
div.classList.add('table-wrapper')
return element.html(div);
})
})
} }
function transformMathExpression(converted, scope) { function transformMathExpression(converted, scope) {
@ -242,7 +312,7 @@ function Editor(holder, input) {
if (parsedExpr.isAssignmentNode) { if (parsedExpr.isAssignmentNode) {
converted = parsedExpr.object.name + " = <i>" + evaluated.toString() + "</i>" converted = parsedExpr.object.name + " = <i>" + evaluated.toString() + "</i>"
} else { } else {
converted = expr + " = <i>" + evaluated.toString() + "</i>" converted = "<span class='expression'>" + expr + " = </span><i>" + evaluated.toString() + "</i>"
} }
} catch (e) { } catch (e) {
converted = converted + ' <span style="background: red; color: white;">' + e.message + '</span>'; converted = converted + ' <span style="background: red; color: white;">' + e.message + '</span>';
@ -331,11 +401,11 @@ function Editor(holder, input) {
).save().then(() => indicator.done()) ).save().then(() => indicator.done())
}) })
// editor.on('rendered', function () { // editor.on('rendered', function () {
// PrismJS.highlightAll() // PrismJS.highlightAll()
// mermaid.init() // mermaid.init()
// renderGraphs(); // renderGraphs();
// }) // })
menu.connectContextMenu(editor) menu.connectContextMenu(editor)
@ -522,47 +592,45 @@ function Editor(holder, input) {
editor.on('stop-editing', function (input, id) { editor.on('stop-editing', function (input, id) {
let $input = $(input); let $input = $(input);
$input.parents('.list-item').removeClass('active'); $input.parents('.list-item').removeClass('active')
$('#link-complete').off() $('#link-complete').off()
// PrismJS.highlightAll() // PrismJS.highlightAll()
// mermaid.init() // mermaid.init()
// renderGraphs(); // renderGraphs();
if ($input.val()) { if (!$input.val()) return
let query = $input.val();
let res = query.match(/{{query: ([^}]+)}}/) let query = $input.val()
if (res) {
search.startQuery(res[1]) Promise.any([
.then(hits => { match(query, /{{query(!?):\s*([^}]+)}}/)
let results = _.flatMap(hits, hit => { ])
return [ .then(res => {
{ if (res[1] === '!') {
text: "[[" + hit.title + "]]", return search.startQuery(res[2])
indented: 0, .then(hits => _.uniqBy(_.flatMap(hits, formatTitleResult), _.property('text')))
fold: 'open', .then(results => editor.replaceChildren(id, results))
hidden: false, .finally(() => editor.render())
fleeting: true } else {
}, return search.startQuery(res[2])
{ .then(hits => _.flatMap(hits, formatLineResult))
text: hit.line, .then(results => editor.replaceChildren(id, results))
indented: 1, .finally(() => editor.render())
fold: 'open', }
hidden: false, })
fleeting: true .catch(() => console.log('match error'))
}
]
})
editor.replaceChildren(id, results)
})
.then(function () {
editor.render()
})
}
}
}); });
return editor return editor
}) })
} }
function match(s, re) {
return new Promise((resolve, reject) => {
let res = s.match(re)
if (res) resolve(res)
else reject()
});
}
let searchInput = document.getElementById('search-input'); let searchInput = document.getElementById('search-input');
_.tap(search.search(searchInput), searcher => { _.tap(search.search(searchInput), searcher => {

View File

@ -457,6 +457,9 @@ input.input-line, input.input-line:active {
width: 400px; width: 400px;
} }
} }
.wiki-table .expression {
display: none;
}
.todo--todo { .todo--todo {
} }