Grid 2: Refactored grid editor to use new model and property editors w/ @lars-erik

This commit is contained in:
Shannon
2018-05-21 11:51:57 +02:00
parent 782fc26723
commit c2e89ed955
5 changed files with 269 additions and 284 deletions
@@ -10,6 +10,38 @@
return {
mapGridValueToPersistableModel: function (model) {
// fixme - we now really only need to remove the properties collections of items
var persistableModel = {
rows: _.map(model.sections[0].rows,
function (row) {
return {
alias: row.name,
settings: {}, // fixme - add the settings
cells: _.map(row.cells,
function (cell) {
return {
settings: {},
items: _.map(cell.items,
function (item) {
return {
type: item.type,
values: _.object(_.map(item.properties,
function (prop) {
return [prop.alias, prop.value];
}))
};
})
};
})
};
})
};
return persistableModel;
},
formatChangePasswordModel: function(model) {
if (!model) {
return null;
@@ -1,6 +1,6 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.Grid2Controller",
function ($scope, $http, assetsService, localizationService, $rootScope, dialogService, gridResource, mediaResource, imageHelper, $timeout, umbRequestHelper, angularHelper) {
function ($scope, $http, assetsService, localizationService, $rootScope, dialogService, gridResource, mediaResource, imageHelper, $timeout, umbRequestHelper, angularHelper, umbDataFormatter) {
// Grid status variables
var placeHolder = "";
@@ -81,7 +81,7 @@ angular.module("umbraco")
var notIncludedRte = [];
var cancelMove = false;
var startingArea;
var startingCell;
$scope.sortableOptionsCell = {
distance: 10,
@@ -113,24 +113,24 @@ angular.module("umbraco")
},
over: function (event, ui) {
var area = $(event.target).scope().area;
var allowedEditors = area.allowed;
var cell = $(event.target).scope().cell;
var allowedEditors = cell.allowed;
if (($.inArray(ui.item.scope().control.editor.alias, allowedEditors) < 0 && allowedEditors) ||
(startingArea != area && area.maxItems != '' && area.maxItems > 0 && area.maxItems < area.controls.length + 1)) {
(startingCell != cell && cell.maxItems != '' && cell.maxItems > 0 && cell.maxItems < cell.controls.length + 1)) {
$scope.$apply(function () {
$(event.target).scope().area.dropNotAllowed = true;
$(event.target).scope().cell.dropNotAllowed = true;
});
ui.placeholder.hide();
cancelMove = true;
}
else {
if ($(event.target).scope().area.controls.length == 0){
if ($(event.target).scope().cell.controls.length == 0){
$scope.$apply(function () {
$(event.target).scope().area.dropOnEmpty = true;
$(event.target).scope().cell.dropOnEmpty = true;
});
ui.placeholder.hide();
} else {
@@ -142,8 +142,8 @@ angular.module("umbraco")
out: function(event, ui) {
$scope.$apply(function () {
$(event.target).scope().area.dropNotAllowed = false;
$(event.target).scope().area.dropOnEmpty = false;
$(event.target).scope().cell.dropNotAllowed = false;
$(event.target).scope().cell.dropOnEmpty = false;
});
},
@@ -171,9 +171,9 @@ angular.module("umbraco")
start: function (e, ui) {
//Get the starting area for reference
var area = $(e.target).scope().area;
startingArea = area;
//Get the starting cell for reference
var cell = $(e.target).scope().cell;
startingCell = cell;
// fade out control when sorting
ui.item.context.style.display = "block";
@@ -227,7 +227,7 @@ angular.module("umbraco")
$scope.$apply(function () {
var cell = $(e.target).scope().area;
var cell = $(e.target).scope().cell;
cell.hasActiveChild = hasActiveChild(cell, cell.controls);
cell.active = false;
});
@@ -258,35 +258,22 @@ angular.module("umbraco")
// *********************************************
// Add items overlay menu
// *********************************************
$scope.openEditorOverlay = function(event, area, index, key) {
$scope.openEditorOverlay = function (event, cell, index, key) {
$scope.editorOverlay = {
view: "itempicker",
filter: area.$allowedEditors.length > 15,
filter: cell.$allowedEditors.length > 15,
title: localizationService.localize("grid_insertControl"),
availableItems: area.$allowedEditors,
availableItems: cell.$allowedEditors,
event: event,
show: true,
submit: function(model) {
$scope.addControl(model.selectedItem, area, index);
$scope.addControl(model.selectedItem, cell, index);
$scope.editorOverlay.show = false;
$scope.editorOverlay = null;
}
};
};
// *********************************************
// Template management functions
// *********************************************
$scope.addTemplate = function (template) {
$scope.model.value = angular.copy(template);
//default row data
_.forEach($scope.model.value.sections, function (section) {
$scope.initSection(section);
});
};
// *********************************************
// Row management function
@@ -300,28 +287,43 @@ angular.module("umbraco")
rows[index].active = false;
};
function getAllowedLayouts(section) {
function getAllowedRowLayouts(section) {
var layouts = $scope.model.config.items.layouts;
var rowLayouts = $scope.model.config.items.layouts;
// fixme - remove when refactored datatype
_.forEach(rowLayouts, function(rowLayout) {
if (!rowLayout.cells) {
rowLayout.cells = rowLayout.areas;
delete rowLayout.areas;
}
});
return rowLayouts;
//fixme - this is probably not necessary any more
//This will occur if it is a new section which has been
// created from a 'template'
if (section.allowed && section.allowed.length > 0) {
return _.filter(layouts, function (layout) {
return _.filter(rowLayouts, function (layout) {
return _.indexOf(section.allowed, layout.name) >= 0;
});
}
else {
return layouts;
return rowLayouts;
}
}
$scope.addRow = function (section, layout) {
$scope.addRow = function (section, rowLayout) {
//copy the selected layout into the rows collection
var row = angular.copy(layout);
var row = angular.copy(rowLayout);
//fixme - we've got discrepancies between alias/name on row layouts and rows.
row.alias = row.name;
// Init row value
row = initRow(row);
@@ -492,7 +494,7 @@ angular.module("umbraco")
}
// *********************************************
// Area management functions
// cell management functions
// *********************************************
$scope.clickCell = function(index, cells, row) {
@@ -558,31 +560,28 @@ angular.module("umbraco")
s4() + "-" + s4() + s4() + s4();
};
})();
$scope.setUniqueId = function (cell, index) {
return guid();
};
$scope.addControl = function (editor, cell, index, initialize) {
initialize = (initialize !== false);
var newControl = {
value: null,
editor: editor,
var newItem = {
type: editor.key,
values: {},
//editor: editor,
$initializing: initialize
};
if (index === undefined) {
index = cell.controls.length;
index = cell.items.length;
}
newControl.active = true;
newItem.active = true;
//populate control
initControl(newControl, index + 1);
initItem(newItem, index + 1);
cell.controls.push(newControl);
cell.items.push(newItem);
};
@@ -625,7 +624,7 @@ angular.module("umbraco")
// *********************************************
// Initialization
// Initialization - this runs ONE time
// these methods are called from ng-init on the template
// so we can controll their first load data
//
@@ -636,80 +635,49 @@ angular.module("umbraco")
// *********************************************
// Init template + sections
// *********************************************
$scope.initContent = function () {
var clear = true;
function initContent () {
//fixme - config will be different we need to wait for that
////settings indicator shortcut
//if (($scope.model.config.items.config && $scope.model.config.items.config.length > 0) || ($scope.model.config.items.styles && $scope.model.config.items.styles.length > 0)) {
// $scope.hasSettings = true;
//}
//settings indicator shortcut
if (($scope.model.config.items.config && $scope.model.config.items.config.length > 0) || ($scope.model.config.items.styles && $scope.model.config.items.styles.length > 0)) {
$scope.hasSettings = true;
}
//ensure the grid has a column value set,
//if nothing is found, set it to 12
//ensure the grid has a column value set, if nothing is found, set it to 12
if (!$scope.model.config.items.columns){
$scope.model.config.items.columns = 12;
} else if (angular.isString($scope.model.config.items.columns)) {
$scope.model.config.items.columns = parseInt($scope.model.config.items.columns);
}
if ($scope.model.value && $scope.model.value.sections && $scope.model.value.sections.length > 0 && $scope.model.value.sections[0].rows && $scope.model.value.sections[0].rows.length > 0) {
//fixme - we are currently still using "layouts" but we will eventually get rid of them. So we need to wrap
//our current value with the layout JS since we only persist with rows.
var section = {
grid: $scope.model.config.items.columns,
rows: []
};
var sectionWrapper = {
name: "1 column layout",
sections: [
section
]
};
if ($scope.model.value.name && angular.isArray($scope.model.config.items.templates)) {
//This will occur if it is an existing value, in which case
// we need to determine which layout was applied by looking up
// the name
// TODO: We need to change this to an immutable ID!!
var found = _.find($scope.model.config.items.templates, function (t) {
return t.name === $scope.model.value.name;
});
if (found && angular.isArray(found.sections) && found.sections.length === $scope.model.value.sections.length) {
//Cool, we've found the template associated with our current value with matching sections counts, now we need to
// merge this template data on to our current value (as if it was new) so that we can preserve what is and isn't
// allowed for this template based on the current config.
_.each(found.sections, function (templateSection, index) {
angular.extend($scope.model.value.sections[index], angular.copy(templateSection));
});
}
}
_.forEach($scope.model.value.sections, function (section, index) {
if (section.grid > 0) {
$scope.initSection(section);
//we do this to ensure that the grid can be reset by deleting the last row
if (section.rows.length > 0) {
clear = false;
}
} else {
$scope.model.value.sections.splice(index, 1);
}
});
} else if ($scope.model.config.items.templates && $scope.model.config.items.templates.length === 1) {
$scope.addTemplate($scope.model.config.items.templates[0]);
clear = false;
}
if (clear) {
$scope.model.value = undefined;
}
angular.extend(sectionWrapper.sections[0], $scope.model.value);
$scope.model.value = sectionWrapper;
initSection(section);
};
$scope.initSection = function (section) {
function initSection(section) {
section.$percentage = $scope.percentage(section.grid);
section.$allowedLayouts = getAllowedLayouts(section);
section.allowedRowLayouts = getAllowedRowLayouts(section);
if (!section.rows || section.rows.length === 0) {
section.rows = [];
if(section.$allowedLayouts.length === 1){
$scope.addRow(section, section.$allowedLayouts[0]);
if (section.allowedRowLayouts.length === 1){
$scope.addRow(section, section.allowedRowLayouts[0]);
}
} else {
_.forEach(section.rows, function (row, index) {
@@ -738,63 +706,70 @@ angular.module("umbraco")
//merge the layout data with the original config data
//if there are no config info on this, splice it out
var original = _.find($scope.model.config.items.layouts, function (o) { return o.name === row.name; });
var original = _.find($scope.model.config.items.layouts, function (o) { return o.name === row.alias; });
if (!original) {
return null;
} else {
//make a copy to not touch the original config
original = angular.copy(original);
original.styles = row.styles;
original.config = row.config;
original.hasConfig = gridItemHasConfig(row.styles, row.config);
//sync area configuration
_.each(original.areas, function (area, areaIndex) {
if (area.grid > 0) {
var currentArea = row.areas[areaIndex];
//sync cell configuration
_.each(original.cells, function (cell, cellIndex) {
if (currentArea) {
area.config = currentArea.config;
area.styles = currentArea.styles;
area.hasConfig = gridItemHasConfig(currentArea.styles, currentArea.config);
if (cell.grid > 0) {
var currentCell = row.cells[cellIndex];
if (currentCell) {
cell.config = currentCell.config;
cell.styles = currentCell.styles;
cell.hasConfig = gridItemHasConfig(currentCell.styles, currentCell.config);
}
//set editor permissions
if (area.allowed && area.allowed.length !== 0) {
area.$allowedEditors = _.filter($scope.availableEditors, function (editor) {
return _.indexOf(area.allowed, editor.udi) >= 0;
if (!cell.allowed || cell.allowAll === true) {
cell.$allowedEditors = $scope.availableEditors;
cell.$allowsRTE = true;
} else {
cell.$allowedEditors = _.filter($scope.availableEditors, function (editor) {
return _.indexOf(cell.allowed, editor.alias) >= 0;
});
}
else {
area.$allowedEditors = $scope.availableEditors;
if (_.indexOf(cell.allowed, "rte") >= 0) {
cell.$allowsRTE = true;
}
}
//copy over existing controls into the new areas
if (row.areas.length > areaIndex && row.areas[areaIndex].controls) {
area.controls = currentArea.controls;
//copy over existing controls into the new cells
if (row.cells.length > cellIndex && row.cells[cellIndex].items) {
cell.items = currentCell.items;
_.forEach(area.controls, function (control, controlIndex) {
initControl(control, controlIndex);
_.forEach(cell.items, function (item, itemIndex) {
initItem(item, itemIndex);
});
} else {
//if empty
area.controls = [];
cell.items = [];
//if only one allowed editor
if(area.$allowedEditors.length === 1){
$scope.addControl(area.$allowedEditors[0], area, 0, false);
if (cell.$allowedEditors.length === 1){
$scope.addControl(cell.$allowedEditors[0], cell, 0, false);
}
}
//set width
area.$percentage = $scope.percentage(area.grid);
area.$uniqueId = $scope.setUniqueId();
cell.$percentage = $scope.percentage(cell.grid);
cell.$uniqueId = guid();
} else {
original.areas.splice(areaIndex, 1);
original.cells.splice(cellIndex, 1);
}
});
@@ -802,10 +777,10 @@ angular.module("umbraco")
original.$initialized = true;
//set a disposable unique ID
original.$uniqueId = $scope.setUniqueId();
original.$uniqueId = guid();
//set a no disposable unique ID (util for row styling)
original.id = !row.id ? $scope.setUniqueId() : row.id;
original.id = !row.id ? guid() : row.id;
return original;
}
@@ -816,23 +791,29 @@ angular.module("umbraco")
// *********************************************
// Init control
// *********************************************
function initControl(control, index) {
control.$index = index;
control.$uniqueId = guid();
function initItem(item, index) {
item.$index = index;
item.$uniqueId = guid();
//create the properties collection which will be bound here
control.properties = [];
item.properties = [];
//get a scaffold for the current doc type
gridResource.getScaffold(control.editor.key).then(function (c) {
gridResource.getScaffold(item.type).then(function (c) {
if (c.tabs && c.tabs.length) {
control.properties = c.tabs[0].properties;
_.each(control.properties, function (p) {
item.properties = c.tabs[0].properties;
_.each(item.properties, function (p) {
p.$uniqueId = guid();
p.hideLabel = true;
p.value = item.values[p.alias];
return;
//fixme - we probably don't need to do this any more
//now we need to re-assign the view and set the boolean if it's a preview or not
if (control.editor.views && control.editor.views[p.alias]) {
p.view = control.editor.views[p.alias].view;
if (item.editor.views && item.editor.views[p.alias]) {
p.view = item.editor.views[p.alias].view;
}
});
}
@@ -840,96 +821,68 @@ angular.module("umbraco")
};
/** Called once to initialize the editor */
function init() {
gridResource.getGridContentTypes().then(function (response) {
$scope.availableEditors = response;
gridResource.getGridContentTypes().then(function (response) {
$scope.availableEditors = response;
//Localize the grid editor names
angular.forEach($scope.availableEditors, function (value, key) {
//If no translation is provided, keep using the editor name from the manifest
if (localizationService.dictionary.hasOwnProperty("grid_" + value.alias)) {
value.name = localizationService.localize("grid_" + value.alias);
}
});
$scope.contentReady = true;
//Init grid
initContent();
//Localize the grid editor names
angular.forEach($scope.availableEditors, function (value, key) {
//If no translation is provided, keep using the editor name from the manifest
if (localizationService.dictionary.hasOwnProperty("grid_" + value.alias)) {
value.name = localizationService.localize("grid_" + value.alias);
}
});
$scope.contentReady = true;
// *********************************************
// Init grid
// *********************************************
$scope.initContent();
});
$scope.mapToPersistableModel = function(fullModel) {
var persistableModel = {
rows: _.map(fullModel.sections[0].rows,
function (row) {
return {
alias: row.name,
settings: {}, // fixme - add the settings
cells: _.map(row.areas,
function (cell) {
return {
settings: {},
items: _.map(cell.controls,
function (item) {
return {
type: item.editor.key,
values: _.object(_.map(item.properties,
function (prop) {
return [prop.alias, prop.value];
}))
};
})
};
})
};
})
};
return persistableModel;
}
init();
//Clean the grid value before submitting to the server, we don't need
// all of that grid configuration in the value to be stored!! All of that
// needs to be merged in at runtime to ensure that the real config values are used
// if they are ever updated.
var unsubscribe = $scope.$on("formSubmitting", function () {
$scope.model.value = umbDataFormatter.mapGridValueToPersistableModel($scope.model.value);
if ($scope.model.value && $scope.model.value.sections) {
_.each($scope.model.value.sections, function(section) {
if (section.rows) {
_.each(section.rows, function (row) {
if (row.areas) {
_.each(row.areas, function (area) {
//if ($scope.model.value && $scope.model.value.sections) {
// _.each($scope.model.value.sections, function(section) {
// if (section.rows) {
// _.each(section.rows, function (row) {
// if (row.cells) {
// _.each(row.cells, function (cell) {
//Remove the 'editors' - these are the allowed editors, these will
// be injected at runtime to this editor, it should not be persisted
// //Remove the 'editors' - these are the allowed editors, these will
// // be injected at runtime to this editor, it should not be persisted
if (area.editors) {
delete area.editors;
}
// if (cell.editors) {
// delete cell.editors;
// }
if (area.controls) {
_.each(area.controls, function (control) {
if (control.editor) {
//replace
var alias = control.editor.alias;
control.editor = {
alias: alias
};
}
});
}
});
}
});
}
});
}
// if (cell.controls) {
// _.each(cell.controls, function (control) {
// if (control.editor) {
// //replace
// var alias = control.editor.alias;
// control.editor = {
// alias: alias
// };
// }
// });
// }
// });
// }
// });
// }
// });
//}
});
//when the scope is destroyed we need to unsubscribe
@@ -114,93 +114,93 @@
<div class="tb">
<div>
<!-- Areas in row -->
<!-- Cells in row -->
<div class="umb-cell td mainTd"
ng-repeat="area in row.areas"
ng-style="{width: area.$percentage + '%'}"
ng-repeat="cell in row.cells"
ng-style="{width: cell.$percentage + '%'}"
ng-class="{
'-has-config': area.hasConfig,
'-active': area.active,
'-active-child': area.hasActiveChild}"
ng-model="area.controls"
ng-click="clickCell($index, row.areas, row)"
on-outside-click="clickOutsideCell($index, row.areas, row)"
bind-click-on="{{area.active}}">
'-has-config': cell.hasConfig,
'-active': cell.active,
'-active-child': cell.hasActiveChild}"
ng-model="cell.items"
ng-click="clickCell($index, row.cells, row)"
on-outside-click="clickOutsideCell($index, row.cells, row)"
bind-click-on="{{cell.active}}">
<!-- Cell -->
<div class="umb-cell-content"
ng-class="
{'-active': area.active,
'-has-editors' : area.controls.length>
{'-active': cell.active,
'-has-editors' : cell.items.length>
0,
'-collapsed': sortMode}">
<!-- disable drop overlay -->
<div class="drop-overlay -disable" ng-if="area.dropNotAllowed">
<div class="drop-overlay -disable" ng-if="cell.dropNotAllowed">
<i class="icon-delete drop-icon"></i>
<localize key="grid_contentNotAllowed" />
</div>
<!-- allow drop overlay -->
<div class="drop-overlay -allow" ng-if="area.dropOnEmpty">
<div class="drop-overlay -allow" ng-if="cell.dropOnEmpty">
<i class="icon-download drop-icon"></i>
<localize key="grid_contentAllowed" />
</div>
<div class="umb-grid-has-config" ng-if="area.hasConfig && !sortMode">
<div class="umb-grid-has-config" ng-if="cell.hasConfig && !sortMode">
<localize key="grid_settingsApplied" />
</div>
<div class="cell-tools" ng-if="area.active && !sortMode">
<div class="cell-tool" ng-click="editGridItemSettings(area, 'cell')">
<div class="cell-tools" ng-if="cell.active && !sortMode">
<div class="cell-tool" ng-click="editGridItemSettings(cell, 'cell')">
<i class="icon-settings"></i>
</div>
</div>
<div class="umb-cell-inner" ui-sortable="sortableOptionsCell" ng-model="area.controls">
<div class="umb-cell-inner" ui-sortable="sortableOptionsCell" ng-model="cell.items">
<!-- Control placeholder -->
<div class="umb-cell-placeholder" ng-if="area.controls.length === 0" ng-click="openEditorOverlay($event, area, 0, area.$uniqueId);">
<!-- Item placeholder -->
<div class="umb-cell-placeholder" ng-if="cell.items.length === 0" ng-click="openEditorOverlay($event, cell, 0, cell.$uniqueId);">
<div class="cell-tools-add -center">
<localize ng-if="!sortMode" key="grid_addElement" />
<localize ng-if="sortMode" key="grid_dropElement" />
</div>
</div>
<!-- for each control in areas -->
<!-- for each control in cells -->
<div class="umb-control"
ng-repeat="control in area.controls"
ng-click="clickControl($index, area.controls, area)"
ng-class="{'-active': control.active}"
on-outside-click="clickOutsideControl($index, area.controls, area)"
bind-click-on="{{control.active}}"
umb-set-dirty-on-change="{{control.value}}">
ng-repeat="item in cell.items"
ng-click="clickControl($index, cell.items, cell)"
ng-class="{'-active': item.active}"
on-outside-click="clickOutsideControl($index, cell.items, cell)"
bind-click-on="{{item.active}}"
umb-set-dirty-on-change="{{item.value}}">
<div class="umb-control-click-overlay" ng-show="!control.active && !sortMode"></div>
<div class="umb-control-click-overlay" ng-show="!item.active && !sortMode"></div>
<div class="umb-control-collapsed umb-control-handle" ng-show="sortMode">
{{control.editor.name}}
{{item.editor.name}}
</div>
<div class="umb-control-inner" ng-hide="sortMode">
<div class="umb-control-bar umb-control-handle">
<div class="umb-control-title" ng-if="control.active">
{{control.editor.name}}
<div class="umb-control-title" ng-if="item.active">
{{item.editor.name}}
</div>
<div class="umb-tools" ng-if="control.active">
<div class="umb-tools" ng-if="item.active">
<div class="umb-control-tool" ng-if="control.editor.config.settings">
<i class="umb-control-tool-icon icon-settings" ng-click="editGridItemSettings(control, 'control')"></i>
<div class="umb-control-tool" ng-if="item.editor.config.settings">
<i class="umb-control-tool-icon icon-settings" ng-click="editGridItemSettings(item, 'control')"></i>
</div>
<div class="umb-control-tool">
<i class="umb-control-tool-icon icon-trash" ng-click="togglePrompt(control)"></i>
<umb-confirm-action ng-if="control.deletePrompt"
<i class="umb-control-tool-icon icon-trash" ng-click="togglePrompt(item)"></i>
<umb-confirm-action ng-if="item.deletePrompt"
direction="left"
on-confirm="removeControl(area, $index)"
on-confirm="removeControl(cell, $index)"
on-cancel="hidePrompt(control)">
</umb-confirm-action>
</div>
@@ -210,8 +210,8 @@
</div>
<!-- Rendering the editor for specific control -->
<div ng-if="control">
<div ng-repeat="property in control.properties">
<div ng-if="item">
<div ng-repeat="property in item.properties">
<umb-property property="property">
<umb-property-editor model="property"></umb-property-editor>
</umb-property>
@@ -222,10 +222,10 @@
</div>
</div>
<!-- Controls repeat end -->
<!-- if area is empty tools -->
<div class="umb-grid-add-more-content" ng-if="area.controls.length > 0 && !sortMode && (area.maxItems == undefined || area.maxItems == '' || area.maxItems == 0 || area.maxItems > area.controls.length)">
<div class="cell-tools-add -bar newbtn" ng-click="openEditorOverlay($event, area, 0, area.$uniqueId);"><localize key="grid_addElement" /></div>
<!-- items repeat end -->
<!-- if cell is empty tools -->
<div class="umb-grid-add-more-content" ng-if="cell.items.length > 0 && !sortMode && (cell.maxItems == undefined || cell.maxItems == '' || cell.maxItems == 0 || cell.maxItems > cell.items.length)">
<div class="cell-tools-add -bar newbtn" ng-click="openEditorOverlay($event, cell, 0, cell.$uniqueId);"><localize key="grid_addElement" /></div>
</div>
</div>
@@ -264,13 +264,13 @@
<p ng-hide="section.rows.length > 0"><strong><localize key="grid_addRows" /></strong></p>
<div class="preview-rows columns"
ng-repeat="layout in section.$allowedLayouts"
ng-show="layout.areas.length > 0"
ng-click="addRow(section, layout)">
ng-repeat="rowLayout in section.allowedRowLayouts"
ng-show="rowLayout.cells.length > 0"
ng-click="addRow(section, rowLayout)">
<div class="preview-row">
<div class="preview-col" ng-style="{width: percentage(area.grid) + '%'}" ng-repeat="area in layout.areas">
<div class="preview-col" ng-style="{width: percentage(cell.grid) + '%'}" ng-repeat="cell in rowLayout.cells">
<div class="preview-cell">
</div>
@@ -282,7 +282,7 @@
<div class="preview-overlay">
</div>
<small>{{layout.label || layout.name}}</small>
<small>{{rowLayout.label || rowLayout.name}}</small>
</div> <!-- .templates-preview-rows -->
@@ -296,7 +296,7 @@
</div>
<pre>
<code>{{model | json}}</code>
<code>{{model.value | json}}</code>
</pre>
<umb-overlay ng-if="editorOverlay.show"
@@ -1,12 +1,12 @@
<div ng-controller="Umbraco.PropertyEditors.textboxController">
<input type="text" name="textbox" ng-model="model.value" id="{{model.alias}}"
class="umb-property-editor umb-textstring textstring"
ng-required="model.validation.mandatory"
ng-trim="false"
ng-keyup="model.change()" />
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
<div class="help" ng-if="model.maxlength">
<strong>{{model.count}}</strong>
<localize key="textbox_characters_left">characters left</localize>
</div>
<textarea rows="1"
umb-auto-resize
umb-auto-focus
ng-required="model.validation.mandatory"
ng-keyup="model.change()"
ng-trim="false"
class="textstring input-block-level" id="{{model.$uniqueId}}_text"
ng-model="model.value"
localize="placeholder" placeholder="@grid_placeholderWriteHere"
style="font-size: 36px; line-height: 45px; font-weight: bold;"></textarea>
</div>
@@ -29,7 +29,7 @@ namespace Umbraco.Web.Models.ContentEditing
public IDictionary<string, GridEditorPath> Views { get; set; }
}
[DataContract(Name = "contentType", Namespace = "")]
[DataContract(Name = "editorPath", Namespace = "")]
public class GridEditorPath
{
public GridEditorPath()