From 8513e4b96ce5bec8a8e215950032cb46f688cfa9 Mon Sep 17 00:00:00 2001 From: Peter Stuifzand Date: Sun, 8 Nov 2020 17:01:47 +0100 Subject: [PATCH] Allow search query results in tables --- editor/src/editor.js | 218 +++++++++++++++++++++++++++-------------- editor/src/styles.scss | 3 + 2 files changed, 146 insertions(+), 75 deletions(-) diff --git a/editor/src/editor.js b/editor/src/editor.js index 7588cb6..4763127 100644 --- a/editor/src/editor.js +++ b/editor/src/editor.js @@ -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() { $('code.language-dot').each(function (i, code) { if (!code.innerText) { @@ -163,11 +194,23 @@ function renderGraphs() { function el(tag, children = []) { let el = document.createElement(tag) _.each(children, item => { - el.appendChild(item) + if (item !== undefined) el.appendChild(item) }) 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 renderInline(cellText) { if (!cellText) return document.createTextNode('') @@ -179,38 +222,57 @@ function Editor(holder, input) { return span[0]; } + function promiseMapAll(p, f) { + return p.then(all => Promise.all(_.map(all, f))) + } + function transformTable(editor, id, element) { - editor.treeForId(id).then(tree => { - let header = _.find(tree[0].children, c => c.text === 'headers') || [] - let rows = _.find(tree[0].children, c => c.text === 'rows') || [] + editor.treeForId(id) + .then(tree => { + 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 => { - // FIXME: Use a real parse link function - let page = row.text.substring(2).substring(0, row.text.length - 4) - return fetch('/' + page + '?format=metakv') - .then(res => res.json()) - .then(res => res.meta) - .then(rowData => { - return el("tr", [ - el("td", [renderInline(row.text)]), - ..._.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 - }) - ]) + let collection = _.map(rows.children, child => { + let res = child.text.match(/{{query(!?):\s*([^}]+)}}/) + if (res && res[1] === '!') { + return search.startQuery(res[2]) + .then(hits => _.uniqBy(_.flatMap(hits, formatTitleResult), _.property('text'))) + } + return Promise.resolve().then(() => [child]) + }); + + Promise.all(collection) + .then(_.flatten) + .then(_.partialRight(_.map, _.property('text'))) + .then(_.uniq) + .then(rowTexts => { + return _.map(rowTexts, rowText => { + let page = rowText.substring(2).substring(0, rowText.length - 4) + 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 + }) + ]) + }) + }) }) - }) - - Promise.all(rowData) - .then(trs => { - return el("table", [ - el("thead", [ - el("tr", [ + .then(mflatten) + .then(trs => { + return el("table", [ + el("thead", [ + el("tr", [ el("th", [ document.createTextNode("Title") ]), @@ -219,13 +281,21 @@ function Editor(holder, input) { document.createTextNode(col.text) ]) }) - ] - ), - ]), - el("tbody", trs) - ]) - }).then(table => element.html(table)) - }) + ]), + ]), + el("tbody", trs) + ]) + }) + .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) { @@ -242,7 +312,7 @@ function Editor(holder, input) { if (parsedExpr.isAssignmentNode) { converted = parsedExpr.object.name + " = " + evaluated.toString() + "" } else { - converted = expr + " = " + evaluated.toString() + "" + converted = "" + expr + " = " + evaluated.toString() + "" } } catch (e) { converted = converted + ' ' + e.message + ''; @@ -331,11 +401,11 @@ function Editor(holder, input) { ).save().then(() => indicator.done()) }) - // editor.on('rendered', function () { - // PrismJS.highlightAll() - // mermaid.init() - // renderGraphs(); - // }) +// editor.on('rendered', function () { +// PrismJS.highlightAll() +// mermaid.init() +// renderGraphs(); +// }) menu.connectContextMenu(editor) @@ -522,47 +592,45 @@ function Editor(holder, input) { editor.on('stop-editing', function (input, id) { let $input = $(input); - $input.parents('.list-item').removeClass('active'); + $input.parents('.list-item').removeClass('active') $('#link-complete').off() // PrismJS.highlightAll() // mermaid.init() // renderGraphs(); - if ($input.val()) { - let query = $input.val(); - let res = query.match(/{{query: ([^}]+)}}/) - if (res) { - search.startQuery(res[1]) - .then(hits => { - let results = _.flatMap(hits, hit => { - return [ - { - text: "[[" + hit.title + "]]", - indented: 0, - fold: 'open', - hidden: false, - fleeting: true - }, - { - text: hit.line, - indented: 1, - fold: 'open', - hidden: false, - fleeting: true - } - ] - }) - editor.replaceChildren(id, results) - }) - .then(function () { - editor.render() - }) - } - } + if (!$input.val()) return + + let query = $input.val() + + Promise.any([ + match(query, /{{query(!?):\s*([^}]+)}}/) + ]) + .then(res => { + if (res[1] === '!') { + return search.startQuery(res[2]) + .then(hits => _.uniqBy(_.flatMap(hits, formatTitleResult), _.property('text'))) + .then(results => editor.replaceChildren(id, results)) + .finally(() => editor.render()) + } else { + return search.startQuery(res[2]) + .then(hits => _.flatMap(hits, formatLineResult)) + .then(results => editor.replaceChildren(id, results)) + .finally(() => editor.render()) + } + }) + .catch(() => console.log('match error')) }); 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'); _.tap(search.search(searchInput), searcher => { diff --git a/editor/src/styles.scss b/editor/src/styles.scss index 28035e2..4e7bebf 100644 --- a/editor/src/styles.scss +++ b/editor/src/styles.scss @@ -457,6 +457,9 @@ input.input-line, input.input-line:active { width: 400px; } } +.wiki-table .expression { + display: none; +} .todo--todo { }