FEATURE: Use spreadsheet editor for building tables
This commit is contained in:
parent
a50cd73dcf
commit
3eee3da853
|
@ -2,6 +2,5 @@
|
||||||
@import "vendor/jspreadsheet-datatables";
|
@import "vendor/jspreadsheet-datatables";
|
||||||
@import "vendor/jspreadsheet-theme";
|
@import "vendor/jspreadsheet-theme";
|
||||||
@import "vendor/jsuites";
|
@import "vendor/jsuites";
|
||||||
|
@import "post/table-edit-decorator";
|
||||||
@import "table-builder";
|
@import "modal/insert-table-modal";
|
||||||
@import "table-editor";
|
|
||||||
|
|
|
@ -3,22 +3,24 @@ import { action } from "@ember/object";
|
||||||
import showModal from "discourse/lib/show-modal";
|
import showModal from "discourse/lib/show-modal";
|
||||||
|
|
||||||
export default apiInitializer("0.11.1", (api) => {
|
export default apiInitializer("0.11.1", (api) => {
|
||||||
api.modifyClass("component:d-editor", {
|
api.modifyClass("controller:composer", {
|
||||||
pluginId: "discourse-table-builder",
|
pluginId: "discourse-table-builder",
|
||||||
|
|
||||||
@action
|
@action
|
||||||
showTableBuilder(event) {
|
showTableBuilder() {
|
||||||
showModal("table-builder-modal").set("toolbarEvent", event);
|
showModal("insert-table-modal").setProperties({
|
||||||
|
toolbarEvent: this.toolbarEvent,
|
||||||
|
tableHtml: null,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
api.onToolbarCreate((toolbar) => {
|
api.addToolbarPopupMenuOptionsCallback(() => {
|
||||||
toolbar.addButton({
|
return {
|
||||||
id: "table-builder",
|
id: "table-builder",
|
||||||
group: "insertions",
|
action: "showTableBuilder",
|
||||||
icon: "table",
|
icon: "table",
|
||||||
sendAction: (event) => toolbar.context.send("showTableBuilder", event),
|
label: themePrefix("discourse_table_builder.composer.button"),
|
||||||
title: themePrefix("discourse_table_builder.composer.button"),
|
};
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -35,7 +35,7 @@ export default apiInitializer("0.11.1", (api) => {
|
||||||
|
|
||||||
return ajax(`/posts/${this.id}`, { type: "GET" })
|
return ajax(`/posts/${this.id}`, { type: "GET" })
|
||||||
.then((post) => {
|
.then((post) => {
|
||||||
showModal("table-editor-modal", {
|
showModal("insert-table-modal", {
|
||||||
model: post,
|
model: post,
|
||||||
}).setProperties({
|
}).setProperties({
|
||||||
tableHtml: tempTable,
|
tableHtml: tempTable,
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
import GlimmerComponent from "discourse/components/glimmer";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
|
|
||||||
export default class BodyRow extends GlimmerComponent {
|
|
||||||
get disableRemoveRow() {
|
|
||||||
if (this.args.allRows.length > 1) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
addBodyValue() {
|
|
||||||
const columnId = this.args.columnId;
|
|
||||||
const rowId = this.args.row.id;
|
|
||||||
const value = this.bodyRowValue;
|
|
||||||
|
|
||||||
this.args.setRowValue(columnId, rowId, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
addRow() {
|
|
||||||
const columnId = this.args.columnId;
|
|
||||||
const rowId = this.args.allRows.length + 1;
|
|
||||||
|
|
||||||
this.args.addRow(columnId, rowId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
removeRow() {
|
|
||||||
this.args.removeRow(this.args.columnId, this.args.row);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
import GlimmerComponent from "discourse/components/glimmer";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
|
|
||||||
export default class HeaderColumn extends GlimmerComponent {
|
|
||||||
@tracked alignment;
|
|
||||||
|
|
||||||
get disableRemoveColumn() {
|
|
||||||
if (this.args.tableItems.length > 1) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
addColumn() {
|
|
||||||
const newColumnId = this.args.tableItems.length + 1;
|
|
||||||
this.args.addColumn(newColumnId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
removeColumn() {
|
|
||||||
this.args.removeColumn(this.args.column);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
addColumnHeader() {
|
|
||||||
const index = this.args.columnId - 1;
|
|
||||||
this.args.setColumnHeader(index, this.columnHeaderValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
addRow(columnId, rowId) {
|
|
||||||
this.args.addRow(columnId, rowId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
removeRow(columnId, row) {
|
|
||||||
this.args.removeRow(columnId, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setRowValue(columnId, rowId, value) {
|
|
||||||
this.args.setRowValue(columnId, rowId, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
alignColumn(alignment) {
|
|
||||||
this.args.alignColumn(this.args.columnId, alignment);
|
|
||||||
this.alignment = alignment;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,3 @@
|
||||||
// import Controller from "@ember/controller";
|
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import loadScript from "discourse/lib/load-script";
|
import loadScript from "discourse/lib/load-script";
|
||||||
import { arrayToTable, findTableRegex, tableToObj } from "../lib/utilities";
|
import { arrayToTable, findTableRegex, tableToObj } from "../lib/utilities";
|
||||||
|
@ -6,26 +5,115 @@ import Component from "@ember/component";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
import { schedule } from "@ember/runloop";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: "",
|
tagName: "",
|
||||||
showEditReason: false,
|
showEditReason: false,
|
||||||
|
spreadsheet: null,
|
||||||
|
|
||||||
|
// Lifecycle Methods:
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
this.loadLibraries().then(() => {
|
schedule("afterRender", () => {
|
||||||
this.buildTable(this.tableHtml);
|
this.loadLibraries().then(() => {
|
||||||
|
if (this.isEditingTable) {
|
||||||
|
this.buildPopulatedTable(this.tableHtml);
|
||||||
|
} else {
|
||||||
|
this.buildNewTable();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
this.spreadsheet?.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Getters:
|
||||||
|
get isEditingTable() {
|
||||||
|
if (this.tableHtml) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
get modalAttributes() {
|
||||||
|
if (this.isEditingTable) {
|
||||||
|
return {
|
||||||
|
title: themePrefix("discourse_table_builder.edit.modal.title"),
|
||||||
|
insertTable: {
|
||||||
|
title: themePrefix("discourse_table_builder.edit.modal.create"),
|
||||||
|
icon: "pencil-alt",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
title: themePrefix("discourse_table_builder.modal.title"),
|
||||||
|
insertTable: {
|
||||||
|
title: themePrefix("discourse_table_builder.modal.create"),
|
||||||
|
icon: "plus",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Actions:
|
||||||
|
@action
|
||||||
|
showEditReasonField() {
|
||||||
|
if (this.showEditReason) {
|
||||||
|
return this.set("showEditReason", false);
|
||||||
|
} else {
|
||||||
|
return this.set("showEditReason", true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
cancelTableInsertion() {
|
||||||
|
this.triggerModalClose();
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
insertTable() {
|
||||||
|
const updatedHeaders = this.spreadsheet.getHeaders().split(","); // keys
|
||||||
|
const updatedData = this.spreadsheet.getData(); // values
|
||||||
|
const markdownTable = this.buildTableMarkdown(updatedHeaders, updatedData);
|
||||||
|
|
||||||
|
if (!this.isEditingTable) {
|
||||||
|
this.toolbarEvent.addText(markdownTable);
|
||||||
|
return this.triggerModalClose();
|
||||||
|
} else {
|
||||||
|
return this.updateTable(markdownTable);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Helper Methods:
|
||||||
loadLibraries() {
|
loadLibraries() {
|
||||||
return loadScript(settings.theme_uploads.jsuites).then(() => {
|
return loadScript(settings.theme_uploads.jsuites).then(() => {
|
||||||
return loadScript(settings.theme_uploads.jspreadsheet);
|
return loadScript(settings.theme_uploads.jspreadsheet);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
buildTable(table) {
|
buildNewTable() {
|
||||||
|
const data = [
|
||||||
|
["", "", ""],
|
||||||
|
["", "", ""],
|
||||||
|
["", "", ""],
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ title: "Column 1", width: 150 },
|
||||||
|
{ title: "Column 2", width: 150 },
|
||||||
|
{ title: "Column 3", width: 150 },
|
||||||
|
];
|
||||||
|
|
||||||
|
return this.buildSpreadsheet(data, columns);
|
||||||
|
},
|
||||||
|
|
||||||
|
buildPopulatedTable(table) {
|
||||||
const tableObject = tableToObj(table);
|
const tableObject = tableToObj(table);
|
||||||
const headings = [];
|
const headings = [];
|
||||||
const tableData = [];
|
const tableData = [];
|
||||||
|
@ -47,47 +135,20 @@ export default Component.extend({
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return this.buildSpreadsheet(tableData, columns);
|
||||||
|
},
|
||||||
|
|
||||||
|
buildSpreadsheet(data, columns, opts = {}) {
|
||||||
const spreadsheetContainer = document.querySelector("#spreadsheet");
|
const spreadsheetContainer = document.querySelector("#spreadsheet");
|
||||||
|
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
this.spreadsheet = jspreadsheet(spreadsheetContainer, {
|
this.spreadsheet = jspreadsheet(spreadsheetContainer, {
|
||||||
data: tableData,
|
data,
|
||||||
columns,
|
columns,
|
||||||
|
...opts,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@action
|
|
||||||
showEditReasonField() {
|
|
||||||
if (this.showEditReason) {
|
|
||||||
return this.set("showEditReason", false);
|
|
||||||
} else {
|
|
||||||
return this.set("showEditReason", true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
@action
|
|
||||||
cancelTableEdit() {
|
|
||||||
this.triggerModalClose();
|
|
||||||
},
|
|
||||||
|
|
||||||
@action
|
|
||||||
editTable() {
|
|
||||||
const updatedHeaders = this.spreadsheet.getHeaders().split(","); // keys
|
|
||||||
const updatedData = this.spreadsheet.getData(); // values
|
|
||||||
|
|
||||||
const markdownTable = this.buildTableMarkdown(updatedHeaders, updatedData);
|
|
||||||
const tableId = this.get("tableId");
|
|
||||||
const postId = this.model.id;
|
|
||||||
const newRaw = markdownTable;
|
|
||||||
const editReason =
|
|
||||||
this.get("editReason") ||
|
|
||||||
I18n.t(themePrefix("discourse_table_builder.edit.default_edit_reason"));
|
|
||||||
const raw = this.model.raw;
|
|
||||||
const newPostRaw = this.buildUpdatedPost(tableId, raw, newRaw);
|
|
||||||
|
|
||||||
this.updateTable(postId, newPostRaw, editReason);
|
|
||||||
},
|
|
||||||
|
|
||||||
buildUpdatedPost(tableId, raw, newRaw) {
|
buildUpdatedPost(tableId, raw, newRaw) {
|
||||||
const tableToEdit = raw.match(findTableRegex());
|
const tableToEdit = raw.match(findTableRegex());
|
||||||
let editedTable;
|
let editedTable;
|
||||||
|
@ -101,7 +162,20 @@ export default Component.extend({
|
||||||
return editedTable;
|
return editedTable;
|
||||||
},
|
},
|
||||||
|
|
||||||
updateTable(postId, raw, edit_reason) {
|
updateTable(markdownTable) {
|
||||||
|
const tableId = this.get("tableId");
|
||||||
|
const postId = this.model.id;
|
||||||
|
const newRaw = markdownTable;
|
||||||
|
const editReason =
|
||||||
|
this.get("editReason") ||
|
||||||
|
I18n.t(themePrefix("discourse_table_builder.edit.default_edit_reason"));
|
||||||
|
const raw = this.model.raw;
|
||||||
|
const newPostRaw = this.buildUpdatedPost(tableId, raw, newRaw);
|
||||||
|
|
||||||
|
return this.sendTableUpdate(postId, newPostRaw, editReason);
|
||||||
|
},
|
||||||
|
|
||||||
|
sendTableUpdate(postId, raw, edit_reason) {
|
||||||
return ajax(`/posts/${postId}.json`, {
|
return ajax(`/posts/${postId}.json`, {
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
data: {
|
data: {
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
import Controller from "@ember/controller";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
import { A } from "@ember/array";
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
@tracked tableItems = A([
|
|
||||||
{ column: 1, rows: A([{ id: 1 }]) },
|
|
||||||
{ column: 2, rows: A([{ id: 1 }]) },
|
|
||||||
]);
|
|
||||||
|
|
||||||
resetData() {
|
|
||||||
this.tableItems = A([
|
|
||||||
{ column: 1, rows: A([{ id: 1 }]) },
|
|
||||||
{ column: 2, rows: A([{ id: 1 }]) },
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
cancelTableCreation() {
|
|
||||||
this.send("closeModal");
|
|
||||||
}
|
|
||||||
|
|
||||||
createDivider(alignment) {
|
|
||||||
switch (alignment) {
|
|
||||||
case "left":
|
|
||||||
return ":--";
|
|
||||||
break;
|
|
||||||
case "right":
|
|
||||||
return "--:";
|
|
||||||
break;
|
|
||||||
case "center":
|
|
||||||
return ":--:";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return "--";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTable(table) {
|
|
||||||
const headings = [];
|
|
||||||
const divider = [];
|
|
||||||
const rows = [];
|
|
||||||
|
|
||||||
table.forEach((item) => {
|
|
||||||
headings.push(item.header);
|
|
||||||
divider.push(this.createDivider(item.alignment));
|
|
||||||
item.rows.forEach((r) => rows.push(r));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make an object for each row rather than by column
|
|
||||||
const rowItems = rows.reduce((row, { id, content }) => {
|
|
||||||
row[id] ??= { id, content: [] };
|
|
||||||
if (Array.isArray(content)) {
|
|
||||||
row[id].content = row[id].value.concat(content);
|
|
||||||
} else {
|
|
||||||
row[id].content.push(content);
|
|
||||||
}
|
|
||||||
return row;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const header = `|${headings.join("|")}|\n`;
|
|
||||||
const tableDivider = `|${divider.join("|")}|\n`;
|
|
||||||
|
|
||||||
let rowValues;
|
|
||||||
Object.values(rowItems).forEach((item) => {
|
|
||||||
item.content.forEach((line) => {
|
|
||||||
if (line === undefined) {
|
|
||||||
line = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rowValues) {
|
|
||||||
rowValues += `${line}|`;
|
|
||||||
} else {
|
|
||||||
rowValues = `|${line}|`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
rowValues += "\n";
|
|
||||||
});
|
|
||||||
|
|
||||||
let tableMarkdown = header + tableDivider + rowValues;
|
|
||||||
|
|
||||||
this.toolbarEvent.addText(tableMarkdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
createTable() {
|
|
||||||
this.buildTable(this.tableItems);
|
|
||||||
this.resetData();
|
|
||||||
this.send("closeModal");
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
removeColumn(column) {
|
|
||||||
this.tableItems.removeObject(column);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
addColumn(columnId) {
|
|
||||||
this.tableItems.pushObject({
|
|
||||||
column: columnId,
|
|
||||||
rows: A([{ id: 1 }]),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setColumnHeader(index, value) {
|
|
||||||
this.tableItems[index].header = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
addRow(columnId, rowId) {
|
|
||||||
this.tableItems.find((item) => {
|
|
||||||
if (item.column === columnId) {
|
|
||||||
item.rows.pushObject({ id: rowId });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
removeRow(columnId, row) {
|
|
||||||
this.tableItems.find((item) => {
|
|
||||||
if (item.column === columnId) {
|
|
||||||
if (item.rows.length === 1) {
|
|
||||||
// do not allow deleting if only one row left
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
item.rows.removeObject(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setRowValue(columnId, rowId, value) {
|
|
||||||
const index = columnId - 1;
|
|
||||||
this.tableItems[index].rows.find((row) => {
|
|
||||||
if (row.id === rowId) {
|
|
||||||
row.content = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
alignColumn(columnId, alignment) {
|
|
||||||
this.tableItems.find((item) => {
|
|
||||||
if (item.column === columnId) {
|
|
||||||
item.alignment = alignment;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
<div class="body-row">
|
|
||||||
<TextField
|
|
||||||
@value={{bodyRowValue}}
|
|
||||||
@class="table-builder-input"
|
|
||||||
@placeholderKey={{theme-prefix "discourse_table_builder.modal.body"}}
|
|
||||||
{{on "change" this.addBodyValue}}
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@icon="plus"
|
|
||||||
@title={{theme-prefix "discourse_table_builder.modal.buttons.add_row"}}
|
|
||||||
@action={{action "addRow"}}
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@icon="trash-alt"
|
|
||||||
@class="btn-danger"
|
|
||||||
@title={{theme-prefix "discourse_table_builder.modal.buttons.remove_row"}}
|
|
||||||
@action={{action "removeRow"}}
|
|
||||||
@disabled={{this.disableRemoveRow}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
|
@ -1,62 +0,0 @@
|
||||||
<div class="header-column">
|
|
||||||
<div class="header-row">
|
|
||||||
<TextField
|
|
||||||
@value={{columnHeaderValue}}
|
|
||||||
@class="table-builder-input"
|
|
||||||
@id="header-column-{{@columnId}}"
|
|
||||||
@placeholderKey={{theme-prefix "discourse_table_builder.modal.header"}}
|
|
||||||
{{on "change" this.addColumnHeader}}
|
|
||||||
/>
|
|
||||||
<div class="body-inputs">
|
|
||||||
{{#each @column.rows as |item|}}
|
|
||||||
<BodyRow
|
|
||||||
@addRow={{this.addRow}}
|
|
||||||
@columnId={{@columnId}}
|
|
||||||
@allRows={{@column.rows}}
|
|
||||||
@row={{item}}
|
|
||||||
@removeRow={{this.removeRow}}
|
|
||||||
@setRowValue={{this.setRowValue}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="column-action-buttons column-aligned-{{this.alignment}}">
|
|
||||||
<DButton
|
|
||||||
@icon="align-left"
|
|
||||||
@class="btn-align-left"
|
|
||||||
@title={{theme-prefix "discourse_table_builder.modal.buttons.align_left"}}
|
|
||||||
@action={{action "alignColumn" "left"}}
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@icon="align-center"
|
|
||||||
@class="btn-align-center"
|
|
||||||
@title={{theme-prefix
|
|
||||||
"discourse_table_builder.modal.buttons.align_center"
|
|
||||||
}}
|
|
||||||
@action={{action "alignColumn" "center"}}
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@icon="align-right"
|
|
||||||
@class="btn-align-right"
|
|
||||||
@title={{theme-prefix
|
|
||||||
"discourse_table_builder.modal.buttons.align_right"
|
|
||||||
}}
|
|
||||||
@action={{action "alignColumn" "right"}}
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@icon="trash-alt"
|
|
||||||
@class="btn-danger"
|
|
||||||
@action={{action "removeColumn"}}
|
|
||||||
@title={{theme-prefix
|
|
||||||
"discourse_table_builder.modal.buttons.remove_column"
|
|
||||||
}}
|
|
||||||
@disabled={{this.disableRemoveColumn}}
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@icon="plus"
|
|
||||||
@action={{action "addColumn"}}
|
|
||||||
@title={{theme-prefix "discourse_table_builder.modal.buttons.add_column"}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,38 +1,62 @@
|
||||||
<DModalBody
|
<DModalBody @title={{this.modalAttributes.title}} @class="insert-table-modal">
|
||||||
@title={{theme-prefix "discourse_table_builder.edit.modal.title"}}
|
{{#if this.isEditingTable}}
|
||||||
@class="table-editor-modal"
|
<div class="edit-reason">
|
||||||
>
|
<DButton
|
||||||
<div class="edit-reason">
|
@icon="info-circle"
|
||||||
<DButton
|
@title={{theme-prefix
|
||||||
@icon="info-circle"
|
"discourse_table_builder.edit.modal.trigger_reason"
|
||||||
@title={{theme-prefix
|
|
||||||
"discourse_table_builder.edit.modal.trigger_reason"
|
|
||||||
}}
|
|
||||||
@action={{action "showEditReasonField"}}
|
|
||||||
@class="btn btn-icon btn-flat no-text btn-edit-reason"
|
|
||||||
/>
|
|
||||||
{{#if showEditReason}}
|
|
||||||
<TextField
|
|
||||||
@value={{this.editReason}}
|
|
||||||
@placeholderKey={{theme-prefix
|
|
||||||
"discourse_table_builder.edit.modal.reason"
|
|
||||||
}}
|
}}
|
||||||
|
@action={{action "showEditReasonField"}}
|
||||||
|
@class="btn btn-icon btn-flat no-text btn-edit-reason"
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{#if showEditReason}}
|
||||||
</div>
|
<TextField
|
||||||
|
@value={{this.editReason}}
|
||||||
|
@placeholderKey={{theme-prefix
|
||||||
|
"discourse_table_builder.edit.modal.reason"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div id="spreadsheet" tabindex="1" class="jexcel_container"></div>
|
<div id="spreadsheet" tabindex="1" class="jexcel_container"></div>
|
||||||
</DModalBody>
|
</DModalBody>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<DButton
|
<div class="primary-actions">
|
||||||
@class="btn-edit-table"
|
<DButton
|
||||||
@label={{theme-prefix "discourse_table_builder.edit.modal.create"}}
|
@class="btn-insert-table"
|
||||||
@icon="pencil-alt"
|
@label={{this.modalAttributes.insertTable.title}}
|
||||||
@action={{action "editTable"}}
|
@icon={{this.modalAttributes.insertTable.icon}}
|
||||||
/>
|
@action={{action "insertTable"}}
|
||||||
<DButton
|
/>
|
||||||
@class="btn-flat"
|
<DButton
|
||||||
@label={{theme-prefix "discourse_table_builder.edit.modal.cancel"}}
|
@class="btn-flat"
|
||||||
@action={{action "cancelTableEdit"}}
|
@label={{theme-prefix "discourse_table_builder.edit.modal.cancel"}}
|
||||||
/>
|
@action={{action "cancelTableInsertion"}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="secondary-actions">
|
||||||
|
<DPopover>
|
||||||
|
<DButton class="trigger" @icon="question" />
|
||||||
|
<ul>
|
||||||
|
<h4>{{theme-i18n "discourse_table_builder.modal.help.title"}}</h4>
|
||||||
|
<li>
|
||||||
|
<kbd>
|
||||||
|
{{theme-i18n "discourse_table_builder.modal.help.enter_key"}}
|
||||||
|
</kbd>
|
||||||
|
{{theme-i18n "discourse_table_builder.modal.help.new_row"}}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<kbd>
|
||||||
|
{{theme-i18n "discourse_table_builder.modal.help.tab_key"}}
|
||||||
|
</kbd>
|
||||||
|
{{theme-i18n "discourse_table_builder.modal.help.new_col"}}
|
||||||
|
</li>
|
||||||
|
<li>{{theme-i18n "discourse_table_builder.modal.help.options"}}</li>
|
||||||
|
</ul>
|
||||||
|
</DPopover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,4 +3,5 @@
|
||||||
@tableHtml={{this.tableHtml}}
|
@tableHtml={{this.tableHtml}}
|
||||||
@tableId={{this.tableId}}
|
@tableId={{this.tableId}}
|
||||||
@triggerModalClose={{action "closeEditModal"}}
|
@triggerModalClose={{action "closeEditModal"}}
|
||||||
|
@toolbarEvent={{this.toolbarEvent}}
|
||||||
/>
|
/>
|
|
@ -1,40 +0,0 @@
|
||||||
<DModalBody
|
|
||||||
@title={{theme-prefix "discourse_table_builder.modal.title"}}
|
|
||||||
@class="table-builder-modal"
|
|
||||||
>
|
|
||||||
<form id="insert-markdown-table-form" {{action "createTable" on="submit"}}>
|
|
||||||
<section class="header-section">
|
|
||||||
<div class="table-header-fields">
|
|
||||||
{{#each this.tableItems as |item|}}
|
|
||||||
<HeaderColumn
|
|
||||||
@column={{item}}
|
|
||||||
@columnId={{item.column}}
|
|
||||||
@tableItems={{this.tableItems}}
|
|
||||||
@addColumn={{this.addColumn}}
|
|
||||||
@removeColumn={{this.removeColumn}}
|
|
||||||
@setColumnHeader={{this.setColumnHeader}}
|
|
||||||
@addRow={{this.addRow}}
|
|
||||||
@removeRow={{this.removeRow}}
|
|
||||||
@setRowValue={{this.setRowValue}}
|
|
||||||
@alignColumn={{this.alignColumn}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</form>
|
|
||||||
</DModalBody>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<DButton
|
|
||||||
@class="btn-primary btn-build-table"
|
|
||||||
@label={{theme-prefix "discourse_table_builder.modal.create"}}
|
|
||||||
@icon="plus"
|
|
||||||
@action={{action "createTable"}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
@class="btn-flat"
|
|
||||||
@label={{theme-prefix "discourse_table_builder.modal.cancel"}}
|
|
||||||
@action={{action "cancelTableCreation"}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
|
@ -7,16 +7,13 @@ en:
|
||||||
title: "Table Builder"
|
title: "Table Builder"
|
||||||
create: "Build Table"
|
create: "Build Table"
|
||||||
cancel: "cancel"
|
cancel: "cancel"
|
||||||
header: "Header"
|
help:
|
||||||
body: "Body"
|
title: "Using the Spreadsheet Editor"
|
||||||
buttons:
|
enter_key: "Enter"
|
||||||
add_column: "Add Column"
|
tab_key: "Tab"
|
||||||
remove_column: "Remove Column"
|
new_row: "at the end of a row to insert a new row."
|
||||||
add_row: "Add Row"
|
new_col: "at the end of a column to insert a new column."
|
||||||
remove_row: "Remove Row"
|
options: "Right-click on cells to access more options in a dropdown menu."
|
||||||
align_left: "Align Left"
|
|
||||||
align_center: "Align Center"
|
|
||||||
align_right: "Align Right"
|
|
||||||
edit:
|
edit:
|
||||||
btn_edit: "Edit Table"
|
btn_edit: "Edit Table"
|
||||||
modal:
|
modal:
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
.open-popup-link {
|
.btn-insert-table {
|
||||||
display: inline;
|
|
||||||
margin-inline: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-edit-table {
|
|
||||||
background: var(--tertiary);
|
background: var(--tertiary);
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
|
|
||||||
|
@ -23,7 +18,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-editor-modal {
|
.insert-table-modal-modal {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
@ -34,4 +29,20 @@
|
||||||
color: var(--primary-high);
|
color: var(--primary-high);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.secondary-actions .tippy-content {
|
||||||
|
h4 {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
margin-block: 0.25rem;
|
||||||
|
color: var(--primary-high);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
.open-popup-link {
|
||||||
|
display: inline;
|
||||||
|
margin-inline: 0.25em;
|
||||||
|
}
|
|
@ -1,77 +0,0 @@
|
||||||
.table-builder-modal-modal {
|
|
||||||
.modal-inner-container {
|
|
||||||
--modal-max-width: 90vw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-header-fields {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
|
|
||||||
.table-builder-input {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-column {
|
|
||||||
border: 1px solid var(--primary-low-mid);
|
|
||||||
padding: 1rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column-action-buttons {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
gap: 0.5rem;
|
|
||||||
margin-top: auto;
|
|
||||||
border-top: 1px solid var(--primary-low-mid);
|
|
||||||
padding-top: 1rem;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.column-aligned {
|
|
||||||
&-right .btn-align-right {
|
|
||||||
background: var(--primary-high);
|
|
||||||
.d-icon {
|
|
||||||
color: var(--primary-very-low);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-left .btn-align-left {
|
|
||||||
background: var(--primary-high);
|
|
||||||
.d-icon {
|
|
||||||
color: var(--primary-very-low);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-center .btn-align-center {
|
|
||||||
background: var(--primary-high);
|
|
||||||
.d-icon {
|
|
||||||
color: var(--primary-very-low);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.body-inputs {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.table-builder-input {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.body-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
gap: 0.5rem;
|
|
||||||
|
|
||||||
input {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue