diff --git a/src/Limbo.Umbraco.Tables/PropertyEditors/TableConfiguration.cs b/src/Limbo.Umbraco.Tables/PropertyEditors/TableConfiguration.cs index 5618a4a..39acbb0 100644 --- a/src/Limbo.Umbraco.Tables/PropertyEditors/TableConfiguration.cs +++ b/src/Limbo.Umbraco.Tables/PropertyEditors/TableConfiguration.cs @@ -49,4 +49,11 @@ public class TableConfiguration { [ConfigurationField("cacheLevel", "Cache Level", "/App_Plugins/Limbo.Umbraco.Tables/Views/CacheLevel.html", Description = "Select the cache level of the underlying property value converter.")] public PropertyCacheLevel? CacheLevel { get; set; } + + /// <summary> + /// Gets or sets whether the label of the property editor should be hidden. + /// </summary> + [ConfigurationField("enableDevMode", "Developer mode?", "boolean", Description = "Enable a property action to edit the raw data for the editor value.")] + public bool EnableDevMode { get; set; } + } \ No newline at end of file diff --git a/src/Limbo.Umbraco.Tables/wwwroot/Lang/da-DK.xml b/src/Limbo.Umbraco.Tables/wwwroot/Lang/da-DK.xml index b285f60..08604c9 100644 --- a/src/Limbo.Umbraco.Tables/wwwroot/Lang/da-DK.xml +++ b/src/Limbo.Umbraco.Tables/wwwroot/Lang/da-DK.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<language alias="en" intName="English (US)" localName="English (US)" lcid="" culture="en-US"> +<language alias="da" intName="Danish" localName="Danish" lcid="" culture="da-DK"> <area alias="limboTables"> <key alias="addRow">Tilføj række</key> <key alias="addColumn">Tilføj kolonne</key> @@ -10,5 +10,6 @@ <key alias="useLastRowAsFooter">Brug sidste række som footer</key> <key alias="addCellContent">Tilføj cellens indhold</key> <key alias="editCellContent">Redigér cellens indhold</key> + <key alias="editRawValue">Redigér råværdi</key> </area> </language> \ No newline at end of file diff --git a/src/Limbo.Umbraco.Tables/wwwroot/Lang/en-US.xml b/src/Limbo.Umbraco.Tables/wwwroot/Lang/en-US.xml index 37d79ee..4adad06 100644 --- a/src/Limbo.Umbraco.Tables/wwwroot/Lang/en-US.xml +++ b/src/Limbo.Umbraco.Tables/wwwroot/Lang/en-US.xml @@ -10,5 +10,6 @@ <key alias="useLastRowAsFooter">Use last row as footer</key> <key alias="addCellContent">Add cell content</key> <key alias="editCellContent">Edit cell content</key> + <key alias="editRawValue">Edit raw value</key> </area> </language> \ No newline at end of file diff --git a/src/Limbo.Umbraco.Tables/wwwroot/Scripts/Controllers/TableDataEditor.js b/src/Limbo.Umbraco.Tables/wwwroot/Scripts/Controllers/TableDataEditor.js index 367e212..0fe2aa7 100644 --- a/src/Limbo.Umbraco.Tables/wwwroot/Scripts/Controllers/TableDataEditor.js +++ b/src/Limbo.Umbraco.Tables/wwwroot/Scripts/Controllers/TableDataEditor.js @@ -1,250 +1,359 @@ angular.module("umbraco").controller("Limbo.Umbraco.Tables.Editor.Controller", function ($scope, $routeParams, editorService, localizationService) { - const vm = this; - - if ($scope.model.config.hideLabel === true) $scope.model.hideLabel = true; - - vm.allowUseFirstRowAsHeader = $scope.model.config.allowUseFirstRowAsHeader === true; - vm.allowUseFirstColumnAsHeader = $scope.model.config.allowUseFirstColumnAsHeader === true; - vm.allowUseLastRowAsFooter = $scope.model.config.allowUseLastRowAsFooter === true; - - vm.addRow = function () { - - const row = {}; - - vm.table.rows.push(row); - - if (vm.table.columns.length === 0) { - vm.addColumn(); - return; - } - - vm.addEmptyCells(); - - } - - vm.addColumn = function () { - - const column = {}; - - vm.table.columns.push(column); - - vm.addEmptyCells(); - - } - - vm.addEmptyCells = function () { - - if (vm.table.cells.length === 0) { - vm.addNewRows(vm.table.rows.length); - return; - } - - // Get column difference - const firstCell = vm.table.cells[0]; - const diffColumns = vm.table.columns.length - firstCell.length; - - if (diffColumns < 0) { - // Remove columns - vm.table.cells.forEach((row) => { - row.splice(row.length - diffColumns, diffColumns); - }); - } else if (diffColumns > 0) { - // Add columns - vm.table.cells.forEach((row, index) => { - for (let x = 0; x < diffColumns; x++) { - row.push(vm.getEmptyCell(index, (vm.table.columns.length - 1) + x)); - } - }); - } - - // Get row difference - const diffRows = vm.table.rows.length - vm.table.cells.length; - - if (diffRows < 0) { - // Remove rows - vm.table.cells.splice(diffRows, diffRows); - } else if (diffRows > 0) { - vm.addNewRows(diffRows); - } - - } - - vm.addNewRows = function (count) { - for (let i = 0; i < count; i++) { - const rows = []; - for (let column = 0; column < vm.table.columns.length; column++) { - const cell = vm.getEmptyCell((vm.table.rows.length - 1) + i, column); - rows.push(cell); - } - vm.table.cells.push(rows); - } - } - - vm.getEmptyCell = function (rowIndex, columnIndex) { - return { - rowIndex: rowIndex, - columnIndex: columnIndex, - value: "", - type: "td", - scope: null - }; - } - - vm.reIndexCells = function () { - vm.table.cells.forEach(function (row, rowIndex) { - row.forEach(function (cell, colIndex) { - if (vm.table.useFirstRowAsHeader && vm.table.useFirstColumnAsHeader) { - if (rowIndex === 0 && colIndex !== 0) { - cell.type = "th"; - cell.scope = "col"; - } - if (rowIndex !== 0 && colIndex === 0) { - cell.type = "th"; - cell.scope = "row"; - } - } else if (vm.table.useFirstRowAsHeader && rowIndex === 0) { - cell.type = "th"; - cell.scope = "col"; - } else if (vm.table.useFirstColumnAsHeader && colIndex === 0) { - cell.type = "th"; - cell.scope = "row"; - } else { - cell.type = "td"; - cell.scope = null; - } - cell.columnIndex = colIndex; - cell.rowIndex = rowIndex; - }); - }); - } - - vm.removeColumn = function (index) { - - if (vm.table.columns.length === 1) { - return; - } - - vm.table.columns.splice(index, 1); - vm.table.cells.forEach((row) => { - row.splice(index, 1); - }); - - vm.reIndexCells(); - - } - - vm.removeRow = function (index) { - - if (vm.table.rows.length === 1) { - return; - } - - vm.table.rows.splice(index, 1); - vm.table.cells.splice(index, 1); - vm.reIndexCells(); - - } - - vm.editCell = function (cell) { - - const o = { - view: "/App_Plugins/Limbo.Umbraco.Tables/Views/TableOverlay.html", - show: true, - title: "Edit cell content", - size: "medium", - prop: { - alias: "value", - label: "", - view: "rte", - config: { - editor: $scope.model.config.rte, - overlaySize: $scope.model.config.overlaySize - }, - value: cell.value - }, - submit: function (model) { - cell.value = model.prop.value?.markup ? model.prop.value.markup : model.prop.value; - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - - editorService.open(o); - - localizationService.localize("limboTables_editCellContent").then(function (value) { - o.title = value; - }); - - } - - vm.getCssClass = function () { - return ""; - } - - vm.getTableClas = function () { - return ""; - } - - vm.getRowClass = function (rowIndex) { - return ""; - } - - vm.getColumnClass = function (cell) { - return ""; - } - - vm.showRowAndColumnSettings = function () { - return ""; - } - - - function loadTable() { - if (!$scope.model.value || !($scope.model.value instanceof Object)) return; - vm.table = $scope.model.value; - } - - function save() { - - // Re-index all the cells - vm.reIndexCells(); - - // Determine whether all cells are currently empty - let allEmpty = true; + const vm = this; + + if ($scope.model.config.hideLabel === true) $scope.model.hideLabel = true; + + vm.allowUseFirstRowAsHeader = $scope.model.config.allowUseFirstRowAsHeader === true; + vm.allowUseFirstColumnAsHeader = $scope.model.config.allowUseFirstColumnAsHeader === true; + vm.allowUseLastRowAsFooter = $scope.model.config.allowUseLastRowAsFooter === true; + + vm.addRow = function () { + + const row = {}; + + vm.table.rows.push(row); + + if (vm.table.columns.length === 0) { + vm.addColumn(); + return; + } + + vm.addEmptyCells(); + + } + + vm.addColumn = function () { + + + const column = {}; + + vm.table.columns.push(column); + + vm.addEmptyCells(); + + } + + vm.addEmptyCells = function () { + + if (vm.table.cells.length === 0) { + vm.addNewRows(vm.table.rows.length); + return; + } + + // Get column difference + const firstCell = vm.table.cells[0]; + const diffColumns = vm.table.columns.length - firstCell.length; + + if (diffColumns < 0) { + // Remove columns + vm.table.cells.forEach((row) => { + row.splice(row.length - diffColumns, diffColumns); + }); + } else if (diffColumns > 0) { + // Add columns + vm.table.cells.forEach((row, index) => { + for (let x = 0; x < diffColumns; x++) { + row.push(vm.getEmptyCell(index, (vm.table.columns.length - 1) + x)); + } + }); + } + + // Get row difference + const diffRows = vm.table.rows.length - vm.table.cells.length; + + if (diffRows < 0) { + // Remove rows + vm.table.cells.splice(diffRows, diffRows); + } else if (diffRows > 0) { + vm.addNewRows(diffRows); + } + + } + + vm.addNewRows = function (count) { + for (let i = 0; i < count; i++) { + const rows = []; + for (let column = 0; column < vm.table.columns.length; column++) { + const cell = vm.getEmptyCell((vm.table.rows.length - 1) + i, column); + rows.push(cell); + } + vm.table.cells.push(rows); + } + } + + vm.getEmptyCell = function (rowIndex, columnIndex) { + return { + rowIndex: rowIndex, + columnIndex: columnIndex, + value: "", + type: "td", + scope: null + }; + } + + vm.reIndexCells = function () { + vm.table.cells.forEach(function (row, rowIndex) { + row.forEach(function (cell, colIndex) { + if (vm.table.useFirstRowAsHeader && vm.table.useFirstColumnAsHeader) { + if (rowIndex === 0 && colIndex !== 0) { + cell.type = "th"; + cell.scope = "col"; + } + if (rowIndex !== 0 && colIndex === 0) { + cell.type = "th"; + cell.scope = "row"; + } + } else if (vm.table.useFirstRowAsHeader && rowIndex === 0) { + cell.type = "th"; + cell.scope = "col"; + } else if (vm.table.useFirstColumnAsHeader && colIndex === 0) { + cell.type = "th"; + cell.scope = "row"; + } else { + cell.type = "td"; + cell.scope = null; + } + cell.columnIndex = colIndex; + cell.rowIndex = rowIndex; + }); + }); + } + + vm.removeColumn = function (index) { + + if (vm.table.columns.length === 1) { + return; + } + + vm.table.columns.splice(index, 1); + vm.table.cells.forEach((row) => { + row.splice(index, 1); + }); + + vm.reIndexCells(); + + } + + vm.removeRow = function (index) { + + if (vm.table.rows.length === 1) { + return; + } + + vm.table.rows.splice(index, 1); + vm.table.cells.splice(index, 1); + vm.reIndexCells(); + + } + + vm.editCell = function (cell) { + + const o = { + view: "/App_Plugins/Limbo.Umbraco.Tables/Views/TableOverlay.html", + show: true, + title: "Edit cell content", + size: "medium", + prop: { + alias: "value", + label: "", + view: "rte", + config: { + editor: $scope.model.config.rte, + overlaySize: $scope.model.config.overlaySize + }, + value: cell.value + }, + submit: function (model) { + cell.value = model.prop.value?.markup ? model.prop.value.markup : model.prop.value; + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + + editorService.open(o); + + localizationService.localize("limboTables_editCellContent").then(function (value) { + o.title = value; + }); + + } + + vm.getCssClass = function () { + return ""; + } + + vm.getTableClas = function () { + return ""; + } + + vm.getRowClass = function (rowIndex) { + return ""; + } + + vm.getColumnClass = function (cell) { + return ""; + } + + vm.showRowAndColumnSettings = function () { + return ""; + } + + + function loadTable() { + + if (!$scope.model.value || !($scope.model.value instanceof Object)) return; + + const t = $scope.model.value; + + // If the current model doesn't contain a "cells" array or the array is empty, it means + // that we have an invalid model, which means we can stop further parsing of the model + if (!Array.isArray(t.cells) || t.cells.length === 0) { + vm.table = null; + return; + } + + // If the "rows" array is missing, we can create it from the "cells" array. The "rows" + // array would for instance be missing if the value originates from the "Imulus.TableEditor", + // in which the data format is slightly different than this package. + let rows; + if (Array.isArray(t.rows)) { + rows = t.rows; + } else { + rows = []; + for (let i = 0; i < t.cells.length; i++) { + rows.push({}); + } + } + + // Similar to the "rows" array, we need to create the "columns" array is missing in the + // current table model + let columns; + if (Array.isArray(t.columns)) { + columns = t.columns; + } else { + columns = []; + if (rows.length > 0) { + for (let i = 0; i < t.cells[0].length; i++) { + columns.push({}); + } + } + } + + // Initialize the overall table object used by the property editor + vm.table = { + useFirstRowAsHeader: t.useFirstRowAsHeader === true, + useFirstColumnAsHeader: t.useFirstColumnAsHeader === true, + useLastRowAsFooter: t.useLastRowAsFooter === true, + columns, + rows, + cells: t.cells + }; + + } + + //editRawValue capabilities adapted from https://github.com/leekelleher/umbraco-contentment/blob/dev/v4.x/src/Umbraco.Community.Contentment/DataEditors/Notes/notes.js + // & from https://github.com/leekelleher/umbraco-contentment/blob/dev/v4.x/src/Umbraco.Community.Contentment/DataEditors/_/_dev-mode.js */ + function editRawTableData() { + const o = { + view: "/App_Plugins/Limbo.Umbraco.Tables/Views/_json-editor.html", + show: true, + title: "Edit raw value", + size: "medium", + value: Utilities.toJson($scope.model.value, true), + ace: { + showGutter: true, + useWrapMode: true, + useSoftTabs: true, + theme: "chrome", + mode: "javascript", + advanced: { + fontSize: "14px", + wrap: true + } + //onLoad: function (_editor) { + // $timeout(() => _editor.focus()); + //} + }, + submit: function (value) { + + if (value == "") { + $scope.model.value = value; + initTable(); + } else { + $scope.model.value = Utilities.fromJson(value); + loadTable(); + } + + editorService.close(); + + }, + + close: function () { + editorService.close(); + } + }; + + editorService.open(o); + + localizationService.localize("limboTables_editRawValue").then(function (value) { + o.title = value; + }); + }; + + + function save() { + + // Re-index all the cells + vm.reIndexCells(); + + // Determine whether all cells are currently empty + let allEmpty = true; for (let i = 0; i < vm.table.cells.length; i++) { if (!allEmpty) break; - for (let j = 0; j < vm.table.cells[i].length; j++) { - if (vm.table.cells[i][j].value) { - allEmpty = false; + for (let j = 0; j < vm.table.cells[i].length; j++) { + if (vm.table.cells[i][j].value) { + allEmpty = false; break; } } } - // Persist the table model back to the property value, but not if all cells are empty - $scope.model.value = allEmpty ? "" : vm.table; + // Persist the table model back to the property value, but not if all cells are empty + $scope.model.value = allEmpty ? "" : vm.table; } - function initTable() { - vm.table = { - rows: [], - columns: [], - cells: [], - useFirstRowAsHeader: false, - useFirstColumnAsHeader: false - }; - vm.addRow(); - } + function initTable() { + vm.table = { + rows: [], + columns: [], + cells: [], + useFirstRowAsHeader: false, + useFirstColumnAsHeader: false + }; + vm.addRow(); + } + + function init() { + initTable(); + if ($routeParams.id !== "-1") loadTable(); + + if ($scope.model.config.enableDevMode === true && $scope.umbProperty) { + $scope.umbProperty.setPropertyActions([ + { + labelKey: "limboTables_editRawValue", + icon: "brackets", + method: () => editRawTableData() + } + ]); + } - function init() { - initTable(); - if ($routeParams.id !== "-1") loadTable(); - $scope.$on("formSubmitting", save); - } + $scope.$on("formSubmitting", save); + } - init(); + init(); }); \ No newline at end of file diff --git a/src/Limbo.Umbraco.Tables/wwwroot/Views/_json-editor.html b/src/Limbo.Umbraco.Tables/wwwroot/Views/_json-editor.html new file mode 100644 index 0000000..7054ff3 --- /dev/null +++ b/src/Limbo.Umbraco.Tables/wwwroot/Views/_json-editor.html @@ -0,0 +1,35 @@ +<div> + <umb-editor-view> + + <umb-editor-header name="model.title" + name-locked="true" + hide-alias="true" + hide-icon="true" + hide-description="true"> + </umb-editor-header> + + <umb-editor-container> + + <div auto-scale="85" umb-ace-editor="model.ace" model="model.value"></div> + + </umb-editor-container> + + <umb-editor-footer> + <umb-editor-footer-content-right> + <umb-button type="button" + button-style="link" + shortcut="esc" + label-key="general_close" + action="model.close()"> + </umb-button> + <umb-button type="button" + button-style="success" + shortcut="ctrl+s" + label-key="bulk_done" + action="model.submit(model.value)"> + </umb-button> + </umb-editor-footer-content-right> + </umb-editor-footer> + + </umb-editor-view> +</div>