From 3234de9f9279323d2fdfa8718a58c45a14329f39 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 27 Jul 2016 11:59:18 -0700 Subject: [PATCH 1/5] Add category headers to groups --- .../resources/describe-settings.json | 22 ++-- domain-server/resources/web/css/style.css | 4 +- .../resources/web/settings/js/settings.js | 116 +++++++++++++----- 3 files changed, 101 insertions(+), 41 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index bb067877aa..c1fe975624 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -455,12 +455,13 @@ "name": "group_permissions", "type": "table", "caption": "Permissions for Users in Groups", - "can_add_new_rows": true, + "categorize_by_key": "permissions_id", + "can_add_new_categories": true, "groups": [ { - "label": "Group", - "span": 2 + "label": "Rank", + "span": 1 }, { "label": "Permissions ?", @@ -471,7 +472,9 @@ "columns": [ { "name": "permissions_id", - "label": "Group Name" + "label": "Group Name", + "readonly": true, + "hidden": true }, { "name": "rank_id", @@ -487,7 +490,7 @@ }, { "name": "rank_name", - "label": "Rank Name", + "label": "", "readonly": true }, { @@ -548,8 +551,8 @@ "groups": [ { - "label": "Group", - "span": 2 + "label": "Rank", + "span": 1 }, { "label": "Permissions ?", @@ -560,7 +563,8 @@ "columns": [ { "name": "permissions_id", - "label": "Group Name" + "label": "Group Name", + "hidden": true }, { "name": "rank_id", @@ -574,7 +578,7 @@ }, { "name": "rank_name", - "label": "Rank Name", + "label": "", "readonly": true }, { diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index b019f59b4e..87764237f5 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -20,7 +20,9 @@ body { top: 40px; } -.table .value-row td, .table .inputs td { +.table .value-row td, +.table .value-category td, +.table .inputs td { vertical-align: middle; } diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index df4509a1ac..8e5ac25d9a 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -7,10 +7,13 @@ var Settings = { TRIGGER_CHANGE_CLASS: 'trigger-change', DATA_ROW_CLASS: 'value-row', DATA_COL_CLASS: 'value-col', + DATA_CATEGORY_CLASS: 'value-category', ADD_ROW_BUTTON_CLASS: 'add-row', ADD_ROW_SPAN_CLASSES: 'glyphicon glyphicon-plus add-row', DEL_ROW_BUTTON_CLASS: 'del-row', DEL_ROW_SPAN_CLASSES: 'glyphicon glyphicon-remove del-row', + DEL_CATEGORY_BUTTON_CLASS: 'del-category', + DEL_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-remove del-category', MOVE_UP_BUTTON_CLASS: 'move-up', MOVE_UP_SPAN_CLASSES: 'glyphicon glyphicon-chevron-up move-up', MOVE_DOWN_BUTTON_CLASS: 'move-down', @@ -164,19 +167,23 @@ $(document).ready(function(){ }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.ADD_ROW_BUTTON_CLASS, function(){ - addTableRow(this); + addTableRow($(this).closest('tr')); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.DEL_ROW_BUTTON_CLASS, function(){ - deleteTableRow(this); + deleteTableRow($(this).closest('tr')); + }); + + $('#' + Settings.FORM_ID).on('click', '.' + Settings.DEL_CATEGORY_BUTTON_CLASS, function(){ + deleteTableCategory($(this).closest('tr')); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){ - moveTableRow(this, true); + moveTableRow($(this).closest('tr'), true); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.MOVE_DOWN_BUTTON_CLASS, function(){ - moveTableRow(this, false); + moveTableRow($(this).closest('tr'), false); }); $('#' + Settings.FORM_ID).on('keyup', function(e){ @@ -924,7 +931,6 @@ $('body').on('click', '.save-button', function(e){ function makeTable(setting, keypath, setting_value, isLocked) { var isArray = !_.has(setting, 'key'); - var isHash = !isArray; if (!isArray && setting.can_order) { setting.can_order = false; @@ -974,25 +980,60 @@ function makeTable(setting, keypath, setting_value, isLocked) { html += "" + setting.key.label + "" // Key } + var numVisibleColumns = 0; _.each(setting.columns, function(col) { + if (!col.hidden) numVisibleColumns++; html += "" + col.label + "" // Data }) if (!isLocked && !setting.read_only) { if (setting.can_order) { + numVisibleColumns++; html += ""; } - html += "" + numVisibleColumns++; + html += ""; } // populate rows in the table from existing values var row_num = 1; if (keypath.length > 0 && _.size(setting_value) > 0) { + var rowIsObject = setting.columns.length > 1; + _.each(setting_value, function(row, rowIndexOrName) { - html += "" + var categoryKey = setting.categorize_by_key; + var isCategorized = !!categoryKey; + var categoryData = ""; + if (isCategorized && isArray) { + var predicate = {}; + var categoryValue = row[categoryKey]; + predicate[categoryKey] = categoryValue; + categoryValue = rowIsObject ? row[categoryKey] : row; + categoryData = "data-category='" + categoryValue + "'"; + + if (_.findIndex(setting_value, predicate) === rowIndexOrName) { + html += + "" + + "" + + categoryValue + + "" + + ((setting.can_add_new_categories) ? ( + "" + + "" + + "" + ) : ( + "" + )) + + ""; + } + } + + html += + "" if (setting.numbered === true) { html += "" + row_num + "" @@ -1006,8 +1047,8 @@ function makeTable(setting, keypath, setting_value, isLocked) { _.each(setting.columns, function(col) { + var colValue, colName; if (isArray) { - rowIsObject = setting.columns.length > 1; colValue = rowIsObject ? row[col.name] : row; colName = keypath + "[" + rowIndexOrName + "]" + (rowIsObject ? "." + col.name : ""); } else { @@ -1019,18 +1060,25 @@ function makeTable(setting, keypath, setting_value, isLocked) { || (nonDeletableRowKey === col.name && nonDeletableRowValues.indexOf(colValue) !== -1); if (isArray && col.type === "checkbox" && col.editable) { - html += "" - + ""; + html += + "" + + "" + + ""; } else if (isArray && col.type === "time" && col.editable) { - html += "" - + ""; + html += + "" + + "" + + ""; } else { // Use a hidden input so that the values are posted. - html += "" + colValue + ""; + html += + "" + + colValue + + "" + + ""; } }) @@ -1138,9 +1186,7 @@ function badgeSidebarForDifferences(changedElement) { $("a[href='#" + panelParentID + "'] .badge").html(badgeValue); } -function addTableRow(add_glyphicon) { - var row = $(add_glyphicon).closest('tr') - +function addTableRow(row) { var table = row.parents('table') var isArray = table.data('setting-type') === 'array' @@ -1279,9 +1325,7 @@ function addTableRow(add_glyphicon) { row.parent().append(input_clone) } -function deleteTableRow(delete_glyphicon) { - var row = $(delete_glyphicon).closest('tr') - +function deleteTableRow(row) { var table = $(row).closest('table') var isArray = table.data('setting-type') === 'array' @@ -1298,12 +1342,13 @@ function deleteTableRow(delete_glyphicon) { row.remove() } else { // this is the last row, we can't remove it completely since we need to post an empty array - - row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS) - row.addClass('empty-array-row') - - row.html(""); + row + .removeClass(Settings.DATA_ROW_CLASS) + .removeClass(Settings.NEW_ROW_CLASS) + .removeAttr("data-category") + .addClass('empty-array-row') + .html(""); } } @@ -1311,9 +1356,18 @@ function deleteTableRow(delete_glyphicon) { badgeSidebarForDifferences($(table)) } -function moveTableRow(move_glyphicon, move_up) { - var row = $(move_glyphicon).closest('tr') +function deleteTableCategory(categoryHeaderRow) { + var categoryName = categoryHeaderRow.data("category"); + categoryHeaderRow + .closest("table") + .find("tr[data-category='" + categoryName + "']") + .each(function () { + deleteTableRow($(this)); + }); +} + +function moveTableRow(row, move_up) { var table = $(row).closest('table') var isArray = table.data('setting-type') === 'array' if (!isArray) { From 93f9f93b5a1f2a700ad6d3ab2e67034e3349ed99 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 27 Jul 2016 18:11:08 -0700 Subject: [PATCH 2/5] Enable adding groups --- .../resources/describe-settings.json | 2 + domain-server/resources/web/css/style.css | 13 ++ .../resources/web/settings/js/settings.js | 142 ++++++++++++++---- 3 files changed, 128 insertions(+), 29 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index c1fe975624..5b60b1a82c 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -457,6 +457,8 @@ "caption": "Permissions for Users in Groups", "categorize_by_key": "permissions_id", "can_add_new_categories": true, + "new_category_placeholder": "Add Group", + "new_category_needs_reload": true, "groups": [ { diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index 87764237f5..809e9251c4 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -3,6 +3,10 @@ body { padding-bottom: 30px; } +[hidden] { + display: none !important; +} + .table-lead .lead-line { background-color: black; } @@ -33,6 +37,15 @@ body { margin-right: auto; } +.table .value-category { + font-weight: bold; +} + +.table .value-category .message { + font-style: italic; + font-weight: normal; +} + .glyphicon-remove { font-size: 24px; } diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 8e5ac25d9a..33afc72891 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -12,6 +12,8 @@ var Settings = { ADD_ROW_SPAN_CLASSES: 'glyphicon glyphicon-plus add-row', DEL_ROW_BUTTON_CLASS: 'del-row', DEL_ROW_SPAN_CLASSES: 'glyphicon glyphicon-remove del-row', + ADD_CATEGORY_BUTTON_CLASS: 'add-category', + ADD_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-plus add-category', DEL_CATEGORY_BUTTON_CLASS: 'del-category', DEL_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-remove del-category', MOVE_UP_BUTTON_CLASS: 'move-up', @@ -174,6 +176,10 @@ $(document).ready(function(){ deleteTableRow($(this).closest('tr')); }); + $('#' + Settings.FORM_ID).on('click', '.' + Settings.ADD_CATEGORY_BUTTON_CLASS, function(){ + addTableCategory($(this).closest('tr')); + }); + $('#' + Settings.FORM_ID).on('click', '.' + Settings.DEL_CATEGORY_BUTTON_CLASS, function(){ deleteTableCategory($(this).closest('tr')); }); @@ -205,10 +211,11 @@ $(document).ready(function(){ } if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) { - sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click() + sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click(); + sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).click(); // set focus to the first input in the new row - $target.closest('table').find('tr.inputs input:first').focus() + $target.closest('table').find('tr.inputs input:first').focus(); } } @@ -1013,21 +1020,8 @@ function makeTable(setting, keypath, setting_value, isLocked) { predicate[categoryKey] = categoryValue; categoryValue = rowIsObject ? row[categoryKey] : row; categoryData = "data-category='" + categoryValue + "'"; - if (_.findIndex(setting_value, predicate) === rowIndexOrName) { - html += - "" + - "" + - categoryValue + - "" + - ((setting.can_add_new_categories) ? ( - "" + - "" + - "" - ) : ( - "" - )) + - ""; + html += makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, setting.can_add_new_categories, false); } } @@ -1043,7 +1037,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { html += "" + rowIndexOrName + "" } - var isNonDeletableRow = !setting.can_add_new_rows; + var isNonDeletableRow = !setting.can_add_new_rows && !setting.can_add_new_categories || setting.new_category_needs_reload; _.each(setting.columns, function(col) { @@ -1104,19 +1098,47 @@ function makeTable(setting, keypath, setting_value, isLocked) { } // populate inputs in the table for new values - if (!isLocked && !setting.read_only && setting.can_add_new_rows) { - html += makeTableInputs(setting) + if (!isLocked && !setting.read_only) { + if (setting.can_add_new_categories) { + html += makeTableCategoryInput(setting, numVisibleColumns); + } + if (setting.can_add_new_rows || setting.can_add_new_categories) { + html += makeTableInputs(setting); + } } html += "" return html; } +function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, canRemove, needsReload) { + var html = + "" + + "" + + "" + categoryValue + "" + + ((needsReload) ? ( + " - Reload to see contents." + ) : ( + "" + )) + + "" + + ((canRemove) ? ( + "" + + "" + + "" + ) : ( + "" + )) + + ""; + return html; +} + function makeTableInputs(setting) { - var html = "" + var html = ""; if (setting.numbered === true) { - html += "" + html += ""; } if (setting.key) { @@ -1142,12 +1164,28 @@ function makeTableInputs(setting) { html += "" } html += "" + "'>" html += "" return html } +function makeTableCategoryInput(setting, numVisibleColumns) { + var needsReload = setting.new_category_needs_reload; + var categoryKey = setting.categorize_by_key; + var placeholder = setting.new_category_placeholder ? setting.new_category_placeholder : ""; + var html = + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + return html; +} + function badgeSidebarForDifferences(changedElement) { // figure out which group this input is in var panelParentID = changedElement.closest('.panel').attr('id'); @@ -1187,10 +1225,11 @@ function badgeSidebarForDifferences(changedElement) { } function addTableRow(row) { - var table = row.parents('table') - var isArray = table.data('setting-type') === 'array' + var table = row.parents('table'); + var isArray = table.data('setting-type') === 'array'; + var keepField = row.data("keep-field"); - var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS) + var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS); if (!isArray) { // Check key spaces @@ -1307,10 +1346,12 @@ function addTableRow(row) { } else { console.log("Unknown table element") } - }) + }); - input_clone.find('input').each(function(){ - $(this).val($(this).attr('data-default')); + input_clone.children('td').each(function () { + if ($(this).attr("name") !== keepField) { + $(this).find("input").val($(this).attr('data-default')); + } }); if (isArray) { @@ -1322,7 +1363,7 @@ function addTableRow(row) { badgeSidebarForDifferences($(table)) - row.parent().append(input_clone) + row.after(input_clone) } function deleteTableRow(row) { @@ -1356,6 +1397,49 @@ function deleteTableRow(row) { badgeSidebarForDifferences($(table)) } +function addTableCategory($categoryInputRow) { + var $input = $categoryInputRow.find("input").first(); + var $rowInput = $categoryInputRow.next(".inputs").clone(); + if (!$rowInput) { + console.error("Error cloning inputs"); + } + + var needsReload = $categoryInputRow.data("needs-reload"); + var categoryKey = $categoryInputRow.data("key"); + var categoryValue = $input.prop("value"); + var width = 0; + $categoryInputRow + .children("td") + .each(function () { + width += $(this).prop("colSpan") || 1; + }); + + $input + .prop("value", "") + .focus(); + + $rowInput.find("td[name='" + categoryKey + "'] > input").first() + .prop("value", categoryValue); + $rowInput + .attr("data-category", categoryValue) + .addClass(Settings.NEW_ROW_CLASS); + + // TODO: create inputs on initial template load + + var $newCategoryRow = $(makeTableCategoryHeader(categoryKey, categoryValue, width, true, needsReload)); + $newCategoryRow.addClass(Settings.NEW_ROW_CLASS); + + $categoryInputRow + .before($newCategoryRow) + .before($rowInput); + + if (!needsReload) { + $rowInput.removeAttr("hidden"); + } else { + addTableRow($rowInput); + } +} + function deleteTableCategory(categoryHeaderRow) { var categoryName = categoryHeaderRow.data("category"); From aa909a13663d27351d001c20bbd33b06bbe2dc12 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Thu, 28 Jul 2016 13:47:21 -0700 Subject: [PATCH 3/5] Polish category/row addition and deletion --- .../resources/describe-settings.json | 11 +- domain-server/resources/web/css/style.css | 7 +- .../resources/web/settings/js/settings.js | 140 ++++++++++-------- 3 files changed, 93 insertions(+), 65 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 5b60b1a82c..f4ceb0f432 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -457,8 +457,9 @@ "caption": "Permissions for Users in Groups", "categorize_by_key": "permissions_id", "can_add_new_categories": true, + "can_add_new_rows": false, "new_category_placeholder": "Add Group", - "new_category_needs_reload": true, + "new_category_message": "Reload to see ranks", "groups": [ { @@ -548,8 +549,12 @@ { "name": "group_forbiddens", "type": "table", - "caption": "Permissions denied to Users in Groups", - "can_add_new_rows": true, + "caption": "Permissions Denied to Users in Groups", + "categorize_by_key": "permissions_id", + "can_add_new_categories": true, + "can_add_new_rows": false, + "new_category_placeholder": "Add Blacklist Group", + "new_category_message": "Reload to see ranks", "groups": [ { diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index 809e9251c4..0a3dc3dab5 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -41,11 +41,16 @@ body { font-weight: bold; } -.table .value-category .message { +.table .value-category [message]::after { + content: attr(message); font-style: italic; font-weight: normal; } +.table .value-category.inputs { + font-weight: normal; +} + .glyphicon-remove { font-size: 24px; } diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 33afc72891..4b258fa9fb 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -42,7 +42,7 @@ var viewHelpers = { form_group = "
"; setting_value = _(values).valueForKeyPath(keypath); - if (typeof setting_value == 'undefined' || setting_value === null) { + if (_.isUndefined(setting_value) || _.isNull(setting_value)) { if (_.has(setting, 'default')) { setting_value = setting.default; } else { @@ -56,11 +56,11 @@ var viewHelpers = { } function common_attrs(extra_classes) { - extra_classes = (typeof extra_classes !== 'undefined' ? extra_classes : ""); + extra_classes = (!_.isUndefined(extra_classes) ? extra_classes : ""); return " class='" + (setting.type !== 'checkbox' ? 'form-control' : '') + " " + Settings.TRIGGER_CHANGE_CLASS + " " + extra_classes + "' data-short-name='" + setting.name + "' name='" + keypath + "' " - + "id='" + (typeof setting.html_id !== 'undefined' ? setting.html_id : keypath) + "'"; + + "id='" + (!_.isUndefined(setting.html_id) ? setting.html_id : keypath) + "'"; } if (setting.type === 'checkbox') { @@ -937,6 +937,7 @@ $('body').on('click', '.save-button', function(e){ }); function makeTable(setting, keypath, setting_value, isLocked) { + // TODO: enforce category sorting var isArray = !_.has(setting, 'key'); if (!isArray && setting.can_order) { @@ -952,9 +953,10 @@ function makeTable(setting, keypath, setting_value, isLocked) { var nonDeletableRowKey = setting["non-deletable-row-key"]; var nonDeletableRowValues = setting["non-deletable-row-values"]; - html += ""; + html += "
"; if (setting.caption) { html += "" @@ -1012,22 +1014,20 @@ function makeTable(setting, keypath, setting_value, isLocked) { _.each(setting_value, function(row, rowIndexOrName) { var categoryKey = setting.categorize_by_key; - var isCategorized = !!categoryKey; - var categoryData = ""; - if (isCategorized && isArray) { - var predicate = {}; - var categoryValue = row[categoryKey]; - predicate[categoryKey] = categoryValue; + var isCategorized = !!categoryKey && isArray; + var categoryPair = {}; + var categoryValue = ""; + if (isCategorized) { categoryValue = rowIsObject ? row[categoryKey] : row; - categoryData = "data-category='" + categoryValue + "'"; - if (_.findIndex(setting_value, predicate) === rowIndexOrName) { - html += makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, setting.can_add_new_categories, false); + categoryPair[categoryKey] = categoryValue; + if (_.findIndex(setting_value, categoryPair) === rowIndexOrName) { + html += makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, setting.can_add_new_categories, ""); } } - html += - "" + html += ""; if (setting.numbered === true) { html += "" @@ -1037,7 +1037,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { html += "" } - var isNonDeletableRow = !setting.can_add_new_rows && !setting.can_add_new_categories || setting.new_category_needs_reload; + var isNonDeletableRow = !setting.can_add_new_rows; _.each(setting.columns, function(col) { @@ -1075,7 +1075,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { ""; } - }) + }); if (!isLocked && !setting.read_only) { if (setting.can_order) { @@ -1093,6 +1093,10 @@ function makeTable(setting, keypath, setting_value, isLocked) { html += "" + if (isCategorized && setting.can_add_new_rows && _.findLastIndex(setting_value, categoryPair) === rowIndexOrName) { + html += makeTableInputs(setting, categoryPair, categoryValue); + } + row_num++ }); } @@ -1103,7 +1107,7 @@ function makeTable(setting, keypath, setting_value, isLocked) { html += makeTableCategoryInput(setting, numVisibleColumns); } if (setting.can_add_new_rows || setting.can_add_new_categories) { - html += makeTableInputs(setting); + html += makeTableInputs(setting, {}, ""); } } html += "
" + setting.caption + "
" + row_num + "" + rowIndexOrName + "
" @@ -1111,16 +1115,11 @@ function makeTable(setting, keypath, setting_value, isLocked) { return html; } -function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, canRemove, needsReload) { +function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, canRemove, message) { var html = "" + "" + - "" + categoryValue + "" + - ((needsReload) ? ( - " - Reload to see contents." - ) : ( - "" - )) + + "" + categoryValue + "" + "" + ((canRemove) ? ( "" + @@ -1133,9 +1132,10 @@ function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, return html; } -function makeTableInputs(setting) { - var html = ""; +function makeTableInputs(setting, initialValues, categoryValue) { + var html = ""; if (setting.numbered === true) { html += ""; @@ -1148,15 +1148,21 @@ function makeTableInputs(setting) { } _.each(setting.columns, function(col) { + var defaultValue = _.has(initialValues, col.name) ? initialValues[col.name] : col.default; if (col.type === "checkbox") { - html += "" - + ""; + html += + "" + + "" + + ""; } else { - html += "\ - \ - " + html += + "" + + "" + + ""; } }) @@ -1171,12 +1177,14 @@ function makeTableInputs(setting) { } function makeTableCategoryInput(setting, numVisibleColumns) { - var needsReload = setting.new_category_needs_reload; + var canAddRows = setting.can_add_new_rows; var categoryKey = setting.categorize_by_key; - var placeholder = setting.new_category_placeholder ? setting.new_category_placeholder : ""; + var placeholder = setting.new_category_placeholder || ""; + var message = setting.new_category_message || ""; var html = - "" + - "" + + "" + + "" + "" + "" + "" + @@ -1366,35 +1374,40 @@ function addTableRow(row) { row.after(input_clone) } -function deleteTableRow(row) { - var table = $(row).closest('table') - var isArray = table.data('setting-type') === 'array' +function deleteTableRow($row) { + var $table = $row.closest('table'); + var categoryName = $row.data("category"); + var isArray = $table.data('setting-type') === 'array'; - row.empty(); + $row.empty(); if (!isArray) { - row.html(""); + $row.html(""); } else { - if (table.find('.' + Settings.DATA_ROW_CLASS).length > 1) { - updateDataChangedForSiblingRows(row) + if ($table.find('.' + Settings.DATA_ROW_CLASS + "[data-category='" + categoryName + "']").length <= 1) { + // This is the last row of the category, so delete the header + $table.find('.' + Settings.DATA_CATEGORY_CLASS + "[data-category='" + categoryName + "']").remove(); + } + + if ($table.find('.' + Settings.DATA_ROW_CLASS).length > 1) { + updateDataChangedForSiblingRows($row); // this isn't the last row - we can just remove it - row.remove() + $row.remove(); } else { // this is the last row, we can't remove it completely since we need to post an empty array - row + $row .removeClass(Settings.DATA_ROW_CLASS) .removeClass(Settings.NEW_ROW_CLASS) .removeAttr("data-category") .addClass('empty-array-row') - .html(""); } } // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated - badgeSidebarForDifferences($(table)) + badgeSidebarForDifferences($table); } function addTableCategory($categoryInputRow) { @@ -1404,7 +1417,8 @@ function addTableCategory($categoryInputRow) { console.error("Error cloning inputs"); } - var needsReload = $categoryInputRow.data("needs-reload"); + var canAddRows = $categoryInputRow.data("can-add-rows"); + var message = $categoryInputRow.data("message"); var categoryKey = $categoryInputRow.data("key"); var categoryValue = $input.prop("value"); var width = 0; @@ -1426,28 +1440,32 @@ function addTableCategory($categoryInputRow) { // TODO: create inputs on initial template load - var $newCategoryRow = $(makeTableCategoryHeader(categoryKey, categoryValue, width, true, needsReload)); + var $newCategoryRow = $(makeTableCategoryHeader(categoryKey, categoryValue, width, true, " - " + message)); $newCategoryRow.addClass(Settings.NEW_ROW_CLASS); $categoryInputRow .before($newCategoryRow) .before($rowInput); - if (!needsReload) { + if (canAddRows) { $rowInput.removeAttr("hidden"); } else { addTableRow($rowInput); } } -function deleteTableCategory(categoryHeaderRow) { - var categoryName = categoryHeaderRow.data("category"); +function deleteTableCategory($categoryHeaderRow) { + var categoryName = $categoryHeaderRow.data("category"); - categoryHeaderRow + $categoryHeaderRow .closest("table") .find("tr[data-category='" + categoryName + "']") .each(function () { - deleteTableRow($(this)); + if ($(this).hasClass(Settings.DATA_ROW_CLASS)) { + deleteTableRow($(this)); + } else { + $(this).remove(); + } }); } From 987a8c802f06e0443b1a0e0058e1910d10652803 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Thu, 28 Jul 2016 15:41:47 -0700 Subject: [PATCH 4/5] Add basic input validation --- .../resources/web/settings/js/settings.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 4b258fa9fb..e59405981f 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -1412,6 +1412,17 @@ function deleteTableRow($row) { function addTableCategory($categoryInputRow) { var $input = $categoryInputRow.find("input").first(); + var categoryValue = $input.prop("value"); + if (!categoryValue || $categoryInputRow.closest("table").find("tr[data-category='" + categoryValue + "']").length !== 0) { + $categoryInputRow.addClass("has-warning"); + + setTimeout(function () { + $categoryInputRow.removeClass("has-warning"); + }, 1000); + + return; + } + var $rowInput = $categoryInputRow.next(".inputs").clone(); if (!$rowInput) { console.error("Error cloning inputs"); @@ -1420,7 +1431,6 @@ function addTableCategory($categoryInputRow) { var canAddRows = $categoryInputRow.data("can-add-rows"); var message = $categoryInputRow.data("message"); var categoryKey = $categoryInputRow.data("key"); - var categoryValue = $input.prop("value"); var width = 0; $categoryInputRow .children("td") @@ -1438,8 +1448,6 @@ function addTableCategory($categoryInputRow) { .attr("data-category", categoryValue) .addClass(Settings.NEW_ROW_CLASS); - // TODO: create inputs on initial template load - var $newCategoryRow = $(makeTableCategoryHeader(categoryKey, categoryValue, width, true, " - " + message)); $newCategoryRow.addClass(Settings.NEW_ROW_CLASS); From bcff561eb89d124e0b5bc9a0a354a325f300c8ce Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Thu, 28 Jul 2016 17:14:48 -0700 Subject: [PATCH 5/5] Add category collapsing and expanding --- domain-server/resources/web/css/style.css | 17 ++++++-- .../resources/web/settings/js/settings.js | 40 ++++++++++++++++--- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css index 0a3dc3dab5..b66b7df258 100644 --- a/domain-server/resources/web/css/style.css +++ b/domain-server/resources/web/css/style.css @@ -37,8 +37,9 @@ body { margin-right: auto; } -.table .value-category { +.value-category:not(.inputs) { font-weight: bold; + background: #f5f5f5; } .table .value-category [message]::after { @@ -47,8 +48,18 @@ body { font-weight: normal; } -.table .value-category.inputs { - font-weight: normal; +.table .value-row.contracted, +.table .inputs.contracted { + display: none; +} + +.toggle-category { + cursor: pointer; +} + +.toggle-category-icon { + padding: 4px; + margin-right: 8px; } .glyphicon-remove { diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index e59405981f..06c6520c26 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -14,6 +14,11 @@ var Settings = { DEL_ROW_SPAN_CLASSES: 'glyphicon glyphicon-remove del-row', ADD_CATEGORY_BUTTON_CLASS: 'add-category', ADD_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-plus add-category', + TOGGLE_CATEGORY_COLUMN_CLASS: 'toggle-category', + TOGGLE_CATEGORY_SPAN_CLASS: 'toggle-category-icon', + TOGGLE_CATEGORY_SPAN_CLASSES: 'glyphicon toggle-category-icon', + TOGGLE_CATEGORY_EXPANDED_CLASS: 'glyphicon-triangle-bottom', + TOGGLE_CATEGORY_CONTRACTED_CLASS: 'glyphicon-triangle-right', DEL_CATEGORY_BUTTON_CLASS: 'del-category', DEL_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-remove del-category', MOVE_UP_BUTTON_CLASS: 'move-up', @@ -184,6 +189,10 @@ $(document).ready(function(){ deleteTableCategory($(this).closest('tr')); }); + $('#' + Settings.FORM_ID).on('click', '.' + Settings.TOGGLE_CATEGORY_COLUMN_CLASS, function(){ + toggleTableCategory($(this).closest('tr')); + }); + $('#' + Settings.FORM_ID).on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){ moveTableRow($(this).closest('tr'), true); }); @@ -937,9 +946,10 @@ $('body').on('click', '.save-button', function(e){ }); function makeTable(setting, keypath, setting_value, isLocked) { - // TODO: enforce category sorting var isArray = !_.has(setting, 'key'); - + var categoryKey = setting.categorize_by_key; + var isCategorized = !!categoryKey && isArray; + if (!isArray && setting.can_order) { setting.can_order = false; } @@ -1013,8 +1023,6 @@ function makeTable(setting, keypath, setting_value, isLocked) { var rowIsObject = setting.columns.length > 1; _.each(setting_value, function(row, rowIndexOrName) { - var categoryKey = setting.categorize_by_key; - var isCategorized = !!categoryKey && isArray; var categoryPair = {}; var categoryValue = ""; if (isCategorized) { @@ -1118,7 +1126,8 @@ function makeTable(setting, keypath, setting_value, isLocked) { function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, canRemove, message) { var html = "" + - "" + + "" + + "" + "" + categoryValue + "" + "" + ((canRemove) ? ( @@ -1418,7 +1427,7 @@ function addTableCategory($categoryInputRow) { setTimeout(function () { $categoryInputRow.removeClass("has-warning"); - }, 1000); + }, 400); return; } @@ -1477,6 +1486,25 @@ function deleteTableCategory($categoryHeaderRow) { }); } +function toggleTableCategory($categoryHeaderRow) { + var $icon = $categoryHeaderRow.find("." + Settings.TOGGLE_CATEGORY_SPAN_CLASS).first(); + var categoryName = $categoryHeaderRow.data("category"); + var wasExpanded = $icon.hasClass(Settings.TOGGLE_CATEGORY_EXPANDED_CLASS); + if (wasExpanded) { + $icon + .addClass(Settings.TOGGLE_CATEGORY_CONTRACTED_CLASS) + .removeClass(Settings.TOGGLE_CATEGORY_EXPANDED_CLASS); + } else { + $icon + .addClass(Settings.TOGGLE_CATEGORY_EXPANDED_CLASS) + .removeClass(Settings.TOGGLE_CATEGORY_CONTRACTED_CLASS); + } + $categoryHeaderRow + .closest("table") + .find("tr[data-category='" + categoryName + "']") + .toggleClass("contracted", wasExpanded); +} + function moveTableRow(row, move_up) { var table = $(row).closest('table') var isArray = table.data('setting-type') === 'array'