import { action } from "@ember/object"; import loadScript from "discourse/lib/load-script"; import { arrayToTable, findTableRegex, tokenRange, } from "../../discourse-table-builder/lib/utilities"; import Component from "@glimmer/component"; import { ajax } from "discourse/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; import I18n from "I18n"; import { schedule } from "@ember/runloop"; import { tracked } from "@glimmer/tracking"; export default class SpreadsheetEditor extends Component { @tracked showEditReason = false; @tracked loading = null; spreadsheet = null; defaultColWidth = 150; isEditingTable = !!this.args.model.tableTokens; // Getters: 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 createSpreadsheet(spreadsheet) { this.spreadsheet = spreadsheet; schedule("afterRender", () => { this.loadLibraries().then(() => { if (this.isEditingTable) { this.buildPopulatedTable(this.args.model.tableTokens); } else { this.buildNewTable(); } }); }); } @action showEditReasonField() { this.showEditReason = !this.showEditReason; } @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.args.model.toolbarEvent.addText(markdownTable); return this.args.closeModal(); } else { return this.updateTable(markdownTable); } } // Helper Methods: loadLibraries() { this.loading = true; return loadScript(settings.theme_uploads_local.jsuites) .then(() => { return loadScript(settings.theme_uploads_local.jspreadsheet); }) .finally(() => (this.loading = false)); } buildNewTable() { const data = [ ["", "", ""], ["", "", ""], ["", "", ""], ["", "", ""], ["", "", ""], ["", "", ""], ]; const columns = [ { title: I18n.t( themePrefix("discourse_table_builder.default_header.col_1") ), width: this.defaultColWidth, }, { title: I18n.t( themePrefix("discourse_table_builder.default_header.col_2") ), width: this.defaultColWidth, }, { title: I18n.t( themePrefix("discourse_table_builder.default_header.col_3") ), width: this.defaultColWidth, }, { title: I18n.t( themePrefix("discourse_table_builder.default_header.col_4") ), width: this.defaultColWidth, }, ]; return this.buildSpreadsheet(data, columns); } extractTableContent(data) { return data .flat() .filter((t) => t.type === "inline") .map((t) => t.content); } buildPopulatedTable(tableTokens) { const contentRows = tokenRange(tableTokens, "tr_open", "tr_close"); const rows = []; let headings; const rowWidthFactor = 8; contentRows.forEach((row, index) => { if (index === 0) { // headings headings = this.extractTableContent(row).map((heading) => { return { title: heading, width: Math.max( heading.length * rowWidthFactor, this.defaultColWidth ), align: "left", }; }); } else { // rows: rows.push(this.extractTableContent(row)); } }); return this.buildSpreadsheet(rows, headings); } buildSpreadsheet(data, columns, opts = {}) { const postNumber = this.args.model?.post_number; const exportFileName = postNumber ? `post-${postNumber}-table-export` : `post-table-export`; // eslint-disable-next-line no-undef this.spreadsheet = jspreadsheet(this.spreadsheet, { data, columns, defaultColAlign: "left", wordWrap: true, csvFileName: exportFileName, text: this.localeMapping(), ...opts, }); } buildUpdatedPost(tableIndex, raw, newRaw) { const tableToEdit = raw.match(findTableRegex()); let editedTable; if (tableToEdit.length) { editedTable = raw.replace(tableToEdit[tableIndex], newRaw); } else { return raw; } // replace null characters editedTable = editedTable.replace(/\0/g, "\ufffd"); return editedTable; } updateTable(markdownTable) { const tableIndex = this.args.model.tableIndex; const postId = this.args.model.post.id; const newRaw = markdownTable; const editReason = this.editReason || I18n.t(themePrefix("discourse_table_builder.edit.default_edit_reason")); const raw = this.args.model.post.raw; const newPostRaw = this.buildUpdatedPost(tableIndex, raw, newRaw); return this.sendTableUpdate(postId, newPostRaw, editReason); } sendTableUpdate(postId, raw, edit_reason) { return ajax(`/posts/${postId}.json`, { type: "PUT", data: { post: { raw, edit_reason, }, }, }) .catch(popupAjaxError) .finally(() => { this.args.closeModal(); }); } buildTableMarkdown(headers, data) { const table = []; data.forEach((row) => { const result = {}; headers.forEach((_key, index) => { const columnKey = `col${index}`; return (result[columnKey] = row[index]); }); table.push(result); }); return arrayToTable(table, headers); } localeMapping() { return { noRecordsFound: prefixedLocale("no_records_found"), show: prefixedLocale("show"), entries: prefixedLocale("entries"), insertANewColumnBefore: prefixedLocale("context_menu.col.before"), insertANewColumnAfter: prefixedLocale("context_menu.col.after"), deleteSelectedColumns: prefixedLocale("context_menu.col.delete"), renameThisColumn: prefixedLocale("context_menu.col.rename"), orderAscending: prefixedLocale("context_menu.order.ascending"), orderDescending: prefixedLocale("context_menu.order.descending"), insertANewRowBefore: prefixedLocale("context_menu.row.before"), insertANewRowAfter: prefixedLocale("context_menu.row.after"), deleteSelectedRows: prefixedLocale("context_menu.row.delete"), copy: prefixedLocale("context_menu.copy"), paste: prefixedLocale("context_menu.paste"), saveAs: prefixedLocale("context_menu.save"), about: prefixedLocale("about"), areYouSureToDeleteTheSelectedRows: prefixedLocale( "prompts.delete_selected_rows" ), areYouSureToDeleteTheSelectedColumns: prefixedLocale( "prompts.delete_selected_cols" ), thisActionWillDestroyAnyExistingMergedCellsAreYouSure: prefixedLocale( "prompts.will_destroy_merged_cells" ), thisActionWillClearYourSearchResultsAreYouSure: prefixedLocale( "prompts.will_clear_search_results" ), thereIsAConflictWithAnotherMergedCell: prefixedLocale( "prompts.conflict_with_merged_cells" ), invalidMergeProperties: prefixedLocale("invalid_merge_props"), cellAlreadyMerged: prefixedLocale("cells_already_merged"), noCellsSelected: prefixedLocale("no_cells_selected"), }; } } function prefixedLocale(localeString) { return I18n.t( themePrefix(`discourse_table_builder.spreadsheet.${localeString}`) ); }