mirror of
https://github.com/discourse/discourse-table-builder.git
synced 2026-09-16 23:02:13 -04:00
WIP: New approach (see description)
- Add edit table button (beside expand table button) - Parse the table contents - Display the table as a spreadsheet using the Importabular library
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import { apiInitializer } from "discourse/lib/api";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import I18n from "I18n";
|
||||
import { iconNode } from "discourse-common/lib/icon-library";
|
||||
import { create } from "virtual-dom";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default apiInitializer("0.11.1", (api) => {
|
||||
const site = api.container.lookup("site:main");
|
||||
const currentUser = api.getCurrentUser();
|
||||
|
||||
function createButton() {
|
||||
const openPopupBtn = document.createElement("button");
|
||||
openPopupBtn.classList.add(
|
||||
"open-popup-link",
|
||||
"btn-default",
|
||||
"btn",
|
||||
"btn-icon-text"
|
||||
);
|
||||
const expandIcon = create(
|
||||
iconNode("pencil-alt", { class: "edit-table-icon" })
|
||||
);
|
||||
const openPopupText = document.createTextNode(
|
||||
I18n.t(themePrefix("discourse_table_builder.edit.btn_edit"))
|
||||
);
|
||||
openPopupBtn.append(expandIcon, openPopupText);
|
||||
return openPopupBtn;
|
||||
}
|
||||
|
||||
function generateModal(event) {
|
||||
const table = event.target.nextElementSibling;
|
||||
const tempTable = table.cloneNode(true);
|
||||
const postId = this.id;
|
||||
|
||||
return ajax(`/posts/${postId}`, {
|
||||
type: "GET",
|
||||
cache: false,
|
||||
})
|
||||
.then((result) => {
|
||||
const attrs = {
|
||||
widget: this,
|
||||
raw: result.raw,
|
||||
};
|
||||
|
||||
showModal("table-editor-modal", {
|
||||
model: attrs,
|
||||
}).set("tableHtml", tempTable);
|
||||
return result.raw;
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
|
||||
function generatePopups(tables, attrs) {
|
||||
tables.forEach((table) => {
|
||||
if (site.isMobileDevice) {
|
||||
return;
|
||||
}
|
||||
|
||||
const popupBtn = createButton();
|
||||
table.parentNode.classList.add("fullscreen-table-wrapper");
|
||||
table.parentNode.insertBefore(popupBtn, table);
|
||||
popupBtn.addEventListener("click", generateModal.bind(attrs), false);
|
||||
});
|
||||
}
|
||||
|
||||
api.decorateCookedElement(
|
||||
(post, helper) => {
|
||||
const postOwner = helper.widget.attrs.username;
|
||||
|
||||
if (postOwner !== currentUser.username) {
|
||||
return;
|
||||
}
|
||||
|
||||
schedule("afterRender", () => {
|
||||
const tables = post.querySelectorAll("table");
|
||||
generatePopups(tables, helper.widget.attrs);
|
||||
});
|
||||
},
|
||||
{
|
||||
onlyStream: true,
|
||||
id: "edit-table",
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
{{#if context._canEditPost}}
|
||||
<DButton
|
||||
@class="btn-flat"
|
||||
@title={{theme-prefix "discourse_table_builder.edit.btn_edit"}}
|
||||
@label={{theme-prefix "discourse_table_builder.edit.btn_edit"}}
|
||||
@icon="pencil-alt"
|
||||
@action={{action "showEditTableModal"}}
|
||||
/>
|
||||
{{/if}}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { action } from "@ember/object";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default {
|
||||
setupComponent(args, component) {
|
||||
console.log(args, component, this);
|
||||
},
|
||||
|
||||
@action
|
||||
showEditTableModal() {
|
||||
const selection = window.getSelection();
|
||||
const selectedTable = selection.focusNode;
|
||||
const { context } = this.args;
|
||||
|
||||
// TODO: Improve table checking logic AND change so it only shows button when selected content is a table
|
||||
if (
|
||||
selectedTable.nodeName === "DIV" &&
|
||||
selectedTable.classList.contains("md-table")
|
||||
) {
|
||||
// ? TODO: simply pass quoteState object only?
|
||||
const attrs = {
|
||||
table: context.quoteState.buffer,
|
||||
postId: context.quoteState.postId,
|
||||
};
|
||||
showModal("table-editor-modal", {
|
||||
model: attrs,
|
||||
});
|
||||
} else {
|
||||
console.warn("This is not a table:", selection);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,9 +1,46 @@
|
||||
import Controller from "@ember/controller";
|
||||
import { action } from "@ember/object";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { A } from "@ember/array";
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import { tableToObj } from "../lib/utilities";
|
||||
|
||||
export default class extends Controller {
|
||||
onShow() {
|
||||
// ? TODO move to component (read about not allowing Controllers to do DOM manipulation)
|
||||
this._super(...arguments);
|
||||
|
||||
loadScript(settings.theme_uploads.importabular).then(() => {
|
||||
this.buildTable(this.tableHtml);
|
||||
});
|
||||
}
|
||||
|
||||
buildTable(table) {
|
||||
const tableObject = tableToObj(table);
|
||||
const headings = [];
|
||||
const tableData = [];
|
||||
tableObject.forEach((object) => {
|
||||
// Build Headings
|
||||
if (!headings.includes(...Object.keys(object))) {
|
||||
headings.push(...Object.keys(object));
|
||||
}
|
||||
|
||||
tableData.push([...Object.values(object)]);
|
||||
});
|
||||
|
||||
const columns = headings.map((heading) => {
|
||||
return {
|
||||
label: heading,
|
||||
};
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars, no-undef
|
||||
const sheet = new Importabular({
|
||||
node: document.getElementById("table-editor-spreadsheet"),
|
||||
columns,
|
||||
data: tableData,
|
||||
width: "100vw",
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
cancelTableEdit() {
|
||||
this.send("closeModal");
|
||||
@@ -12,7 +49,6 @@ export default class extends Controller {
|
||||
@action
|
||||
editTable() {
|
||||
// TODO: insert table edit submission logic
|
||||
console.log("Table has been successfully edited");
|
||||
this.send("closeModal");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// SRC: https://gist.github.com/mattheo-gist/4151867
|
||||
/* eslint-disable */
|
||||
|
||||
export function tableToObj(table) {
|
||||
var rows = table.rows;
|
||||
var propCells = rows[0].cells;
|
||||
var propNames = [];
|
||||
var results = [];
|
||||
var obj, row, cells;
|
||||
|
||||
// Use the first row for the property names
|
||||
// Could use a header section but result is the same if
|
||||
// there is only one header row
|
||||
for (var i = 0, iLen = propCells.length; i < iLen; i++) {
|
||||
propNames.push(propCells[i].textContent || propCells[i].innerText);
|
||||
}
|
||||
|
||||
// Use the rows for data
|
||||
// Could use tbody rows here to exclude header & footer
|
||||
// but starting from 1 gives required result
|
||||
for (var j = 1, jLen = rows.length; j < jLen; j++) {
|
||||
cells = rows[j].cells;
|
||||
obj = {};
|
||||
|
||||
for (var k = 0; k < iLen; k++) {
|
||||
obj[propNames[k]] = cells[k].textContent || cells[k].innerText;
|
||||
}
|
||||
results.push(obj);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@@ -3,7 +3,10 @@
|
||||
@class="table-editor-modal"
|
||||
>
|
||||
{{! TODO: Parse .md to json and fill table }}
|
||||
{{model.table}}
|
||||
{{! TODO: Fix instances where multiple tables in a single post }}
|
||||
|
||||
{{! TODO better id name }}
|
||||
<div class="table-spreadsheet-container" id="table-editor-spreadsheet"></div>
|
||||
|
||||
</DModalBody>
|
||||
|
||||
@@ -11,7 +14,7 @@
|
||||
<DButton
|
||||
@class="btn-primary btn-edit-table"
|
||||
@label={{theme-prefix "discourse_table_builder.edit.modal.create"}}
|
||||
@icon="plus"
|
||||
@icon="pencil-alt"
|
||||
@action={{action "editTable"}}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user