BlockList PreValue Editor opens configurations as overlay

This commit is contained in:
Niels Lyngsø
2020-04-08 11:58:25 +02:00
parent fb175c5af8
commit b26db075a3
21 changed files with 1022 additions and 424 deletions
@@ -9,12 +9,11 @@ function elementTypeResource($q, $http, umbRequestHelper) {
getAll: function () {
// TODO: Change this into a real api (ElementTypeApi). This is a temporary fix to get data.
var url = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + "/backoffice/UmbracoApi/NestedContent/GetContentTypes";
return umbRequestHelper.resourcePromise(
$http.get(url),
'Failed to retrieve content types'
);
var url = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + "/backoffice/UmbracoApi/ElementType/GetAll";
return umbRequestHelper.resourcePromise(
$http.get(url),
'Failed to retrieve element types'
);
/*
return umbRequestHelper.resourcePromise(
@@ -198,8 +198,11 @@
// Property Editors
@import "../views/propertyeditors/blockeditor/blockcard/umb-block-card-grid.less";
@import "../views/propertyeditors/blockeditor/blockcard/blockcard.component.less";
@import "../views/propertyeditors/blocklist/blocklist.component.less";
@import "../views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.less";
@import "../views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.less";
@import "../views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.less";
@import "../views/propertyeditors/notsupported/notsupported.less";
@@ -202,6 +202,7 @@
@ui-icon: @blueNight;
@ui-icon-hover: @blueMid;
@ui-drop-area-color: @blueMidLight;
// Scaffolding
@@ -252,6 +253,7 @@
// Disabled this to keep consistency throughout the backoffice UI. Untill a better solution is thought up, this will do.
@baseBorderRadius: 3px; // 2px;
@doubleBorderRadius: 6px;
@borderRadiusLarge: 3px; // 6px;
@borderRadiusSmall: 3px; // 3px;
@@ -0,0 +1,11 @@
<div class="__showcase" ng-style="{'background-color':vm.blockConfigModel.backgroundColor, 'background-image':'url('+vm.blockConfigModel.thumbnail+')'}">
<i ng-if="vm.blockConfigModel.thumbnail == null && vm.elementTypeModel.icon" class="__icon {{ vm.elementTypeModel.icon }}" ng-style="{'color':vm.blockConfigModel.iconColor}" aria-hidden="true"></i>
</div>
<div class="__info">
<div class="__name" ng-bind="vm.elementTypeModel.name"></div>
<div class="__subname" ng-if="vm.elementTypeModel.description" ng-bind="vm.elementTypeModel.description"></div>
</div>
<ng-transclude></ng-transclude>
@@ -0,0 +1,28 @@
(function () {
"use strict";
angular
.module("umbraco")
.component("umbBlockCard", {
templateUrl: "views/propertyeditors/blockeditor/blockcard/blockcard.component.html",
controller: BlockCardController,
controllerAs: "vm",
transclude: true,
bindings: {
blockConfigModel: "<",
elementTypeModel: "<"
}
});
function BlockCardController() {
var vm = this;
vm.$onInit = function() {
console.log(vm.blockConfigModel);
console.log(vm.elementTypeModel);
}
}
})();
@@ -0,0 +1,120 @@
.umb-block-card,
umb-block-card {
position: relative;
display: inline-block;
width: 100%;
height: auto;
margin-right: 20px;
margin-bottom: 20px;
background-color: white;
border-radius: @doubleBorderRadius;
box-shadow: 0 1px 2px rgba(0,0,0,.2);
overflow: hidden;
cursor: pointer;
&.--isOpen {
&::after {
content: "";
position: absolute;
border: 2px solid @ui-active-border;
border-radius: @doubleBorderRadius;
top:0;
bottom: 0;
left: 0;
right: 0;
}
}
&.--sortable-placeholder {
&::after {
content: "";
position: absolute;
background-color:rgba(@ui-drop-area-color, .05);
border: 2px solid rgba(@ui-drop-area-color, .1);
border-radius: @doubleBorderRadius;
box-shadow: 0 0 4px rgba(@ui-drop-area-color, 0.05);
top:0;
bottom: 0;
left: 0;
right: 0;
animation: umb-block-card--sortable-placeholder 400ms ease-in-out alternate infinite;
@keyframes umb-block-card--sortable-placeholder {
0% { opacity: 1; }
100% { opacity: 0.5; }
}
}
box-shadow: none;
}
.__showcase {
position: relative;
width: 100%;
padding-bottom: 10/16*100%;
background-color: @gray-11;
.__icon {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size:42px;
}
}
.__info {
width: 100%;
background-color: #fff;
padding-bottom: 6px;
.__name {
font-weight: bold;
font-size: 14px;
color: @ui-action-type;
margin-left: 16px;
margin-top: 8px;
margin-bottom: -1px;
}
.__subname {
color: @gray-4;
font-size: 12px;
margin-left: 16px;
margin-bottom: -1px;
}
}
&:hover {
.__info {
.__name {
color: @ui-action-type-hover;
}
}
}
.__actions {
position: absolute;
top: 10px;
right: 0;
opacity: 0;
transition: opacity 120ms;
.__action {
display: inline-block;
border-radius: 50%;
width: 28px;
height: 28px;
margin-right: 10px;
background-color: white;
color:@ui-action-type;
&:hover {
color: @ui-action-type-hover;
}
}
}
&:hover, &:focus, &:focus-within {
.__actions {
opacity: 1;
}
}
}
@@ -0,0 +1,15 @@
.umb-block-card-grid {
/* FlexBox Fallback */
display: flex;
flex-wrap: wrap;
> * {
flex: 1 1 240px;
}
/* Grid Setup */
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: minmax(200px, auto);
grid-gap: 20px;
}
@@ -87,10 +87,9 @@
right: 0;
left: 0;
height: 2px;
animation: umb-block-list__block--create-button_before 800ms ease-in-out infinite;
animation: umb-block-list__block--create-button_before 400ms ease-in-out alternate infinite;
@keyframes umb-block-list__block--create-button_before {
0% { opacity: 0.5; }
50% { opacity: 1; }
0% { opacity: 1; }
100% { opacity: 0.5; }
}
}
@@ -0,0 +1,208 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.BlockList.BlockConfigurationController
* @function
*
* @description
* The controller for the content type editor property settings dialog
*/
(function () {
"use strict";
function TransferProperties(fromObject, toObject) {
for (var p in fromObject) {
toObject[p] = fromObject[p];
}
}
function BlockConfigurationController($scope, elementTypeResource, overlayService, localizationService, editorService) {
var vm = this;
vm.enableAddBlock = true;
vm.openBlock = null;
function evaluateStatus() {
if (!vm.elementTypes) return;// cancel if elementTypes isnt loaded jet.
vm.enableAddBlock = vm.getAvailableElementTypes().length > 0;
}
function onInit() {
if (!$scope.model.value) {
$scope.model.value = [];
}
loadElementTypes();
}
function loadElementTypes() {
return elementTypeResource.getAll().then(function (elementTypes) {
vm.elementTypes = elementTypes;
evaluateStatus();
});
}
vm.requestRemoveBlockByIndex = function (index) {
localizationService.localizeMany(["general_delete", "blockEditor_confirmDeleteBlockMessage", "blockEditor_confirmDeleteBlockNotice"]).then(function (data) {
var contentElementType = vm.getElementTypeByAlias($scope.model.value[index].contentTypeAlias);
overlayService.confirmDelete({
title: data[0],
content: localizationService.tokenReplace(data[1], [contentElementType.name]),
confirmMessage: data[2],
close: function () {
overlayService.close();
},
submit: function () {
vm.removeBlockByIndex(index);
overlayService.close();
}
});
});
}
vm.removeBlockByIndex = function (index) {
$scope.model.value.splice(index, 1);
};
vm.sortableOptions = {
"ui-floating": true,
items: "umb-block-card",
cursor: "grabbing",
placeholder: 'umb-block-card --sortable-placeholder'
};
vm.getAvailableElementTypes = function () {
return vm.elementTypes.filter(function (type) {
return !$scope.model.value.find(function (entry) {
return type.alias === entry.contentTypeAlias;
});
});
};
vm.getElementTypeByAlias = function(alias) {
return _.find(vm.elementTypes, function (type) {
return type.alias === alias;
});
};
vm.openAddDialog = function ($event, entry) {
//we have to add the alias to the objects (they are stored as contentTypeAlias)
var selectedItems = _.each($scope.model.value, function (obj) {
obj.alias = obj.contentTypeAlias;
return obj;
});
var availableItems = vm.getAvailableElementTypes()
var elemTypeSelectorOverlay = {
view: "itempicker",
title: "no title jet",
availableItems: availableItems,
selectedItems: selectedItems,
createNewItem: {
action: function() {
overlayService.close();
vm.createElementTypeAndAdd(vm.addBlockFromElementTypeAlias);
},
icon: "icon-add",
name: "Create new"
},
position: "target",
event: $event,
size: availableItems.length < 7 ? "small" : "medium",
submit: function (overlay) {
vm.addBlockFromElementTypeAlias(overlay.selectedItem.alias);
overlayService.close();
},
close: function () {
overlayService.close();
}
};
overlayService.open(elemTypeSelectorOverlay);
};
vm.createElementTypeAndAdd = function(callback) {
const editor = {
create: true,
infiniteMode: true,
isElement: true,
submit: function (model) {
loadElementTypes().then( function () {
callback(model.documentTypeAlias);
});
editorService.close();
},
close: function () {
editorService.close();
}
};
editorService.documentTypeEditor(editor);
}
vm.addBlockFromElementTypeAlias = function(alias) {
var entry = {
"contentTypeAlias": alias,
"view": null,
"labelTemplate": "",
"settingsElementTypeAlias": null
};
$scope.model.value.push(entry);
};
vm.openBlockOverlay = function (block) {
localizationService.localize("blockEditor_blockConfigurationOverlayTitle", [vm.getElementTypeByAlias(block.contentTypeAlias).name]).then(function (data) {
var clonedBlockData = angular.copy(block);
vm.openBlock = block;
var overlayModel = {
block: clonedBlockData,
title: data,
view: "views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html",
size: "small",
submit: function(overlayModel) {
TransferProperties(overlayModel.block, block);// transfer properties back to block object. (Doing this cause we dont know if block object is added to model jet, therefor we cant use index or replace the object.)
overlayModel.close();
},
close: function() {
editorService.close();
vm.openBlock = null;
}
};
// open property settings editor
editorService.open(overlayModel);
});
};
onInit();
$scope.$watchCollection('model.value', function(newVal, oldVal) {
evaluateStatus();
});
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockList.BlockConfigurationController", BlockConfigurationController);
})();
@@ -0,0 +1,30 @@
<div
class="umb-block-list-block-configuration"
ng-controller="Umbraco.PropertyEditors.BlockList.BlockConfigurationController as vm"
>
<div
class="umb-block-card-grid"
ui-sortable="vm.sortableOptions"
ng-model="model.value"
>
<umb-block-card
block-config-model="block"
element-type-model="vm.getElementTypeByAlias(block.contentTypeAlias)"
ng-repeat="block in model.value"
ng-class="{'--isOpen':vm.openBlock === block}"
ng-click="vm.openBlockOverlay(block)">
<div class="__actions">
<button type="button" class="btn-reset __action umb-outline" ng-click="vm.requestRemoveBlockByIndex($index); $event.stopPropagation();">
<i class="icon icon-trash" aria-hidden="true"></i>
<localize key="general_delete" class="sr-only">Delete</localize>
</button>
</div>
</umb-block-card>
<button id="{{model.alias}}" type="button" class="btn-reset __add-button" ng-click="vm.openAddDialog($event)" ng-disabled="!vm.enableAddBlock">
<localize key="general_add">Add</localize>
</button>
</div>
</div>
@@ -0,0 +1,28 @@
.umb-block-list-block-configuration {
.__add-button {
position: relative;
display: inline-flex;
width: 100%;
height: auto;
margin-right: 20px;
margin-bottom: 20px;
color: @ui-action-discreet-type;
border: 1px dashed @ui-action-discreet-border;
border-radius: @doubleBorderRadius;
align-items: center;
justify-content: center;
padding: 5px 15px;
box-sizing: border-box;
font-weight: bold;
}
.__add-button:hover {
color: @ui-action-discreet-type-hover;
border-color: @ui-action-discreet-border-hover;
}
}
@@ -0,0 +1,190 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.BlockList.BlockConfigurationOverlayController
* @function
*
* @description
* The controller for the content type editor property settings dialog
*/
(function () {
"use strict";
function BlockConfigurationOverlayController($scope, overlayService, localizationService, editorService, elementTypeResource) {
var vm = this;
vm.block = $scope.model.block;
loadElementTypes();
function loadElementTypes() {
return elementTypeResource.getAll().then(function (elementTypes) {
vm.elementTypes = elementTypes;
});
}
vm.getElementTypeByAlias = function(alias) {
return _.find(vm.elementTypes, function (type) {
return type.alias === alias;
});
};
vm.openElementType = function(elementTypeAlias) {
var elementTypeId = vm.getElementTypeByAlias(elementTypeAlias).id;
const editor = {
id: elementTypeId,
submit: function (model) {
loadElementTypes();
editorService.close();
},
close: function () {
editorService.close();
}
};
editorService.documentTypeEditor(editor);
}
vm.addSettingsForBlock = function ($event, block) {
var elemTypeSelectorOverlay = {
view: "itempicker",
title: "Pick settings (missing translation)",
availableItems: vm.elementTypes,
position: "target",
event: $event,
size: vm.elementTypes.length < 7 ? "small" : "medium",
createNewItem: {
action: function() {
overlayService.close();
vm.createElementTypeAndAdd((alias) => {
vm.applySettingsToBlock(block, alias);
});
},
icon: "icon-add",
name: "Create new"
},
submit: function (overlay) {
vm.applySettingsToBlock(block, overlay.selectedItem.alias);
overlayService.close();
},
close: function () {
overlayService.close();
}
};
overlayService.open(elemTypeSelectorOverlay);
};
vm.applySettingsToBlock = function(block, alias) {
block.settingsElementTypeAlias = alias;
};
vm.requestRemoveSettingsForBlock = function(block) {
localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) {
var settingsElementType = vm.getElementTypeByAlias(entry.settingsElementTypeAlias);
overlayService.confirmRemove({
title: data[0],
content: localizationService.tokenReplace(data[1], [settingsElementType.name]),
close: function () {
overlayService.close();
},
submit: function () {
vm.removeSettingsForEntry(entry);
overlayService.close();
}
});
});
};
vm.removeSettingsForEntry = function(entry) {
entry.settingsElementTypeAlias = null;
};
vm.addViewForBlock = function(block) {
const filePicker = {
title: "Select view (TODO need translation)",
section: "settings",
treeAlias: "files",
entityType: "file",
isDialog: true,
filter: function (i) {
return i.name.indexOf(".html" !== -1);
},
select: function (file) {
console.log(file);
block.view = file.name;
editorService.close();
},
close: function () {
editorService.close();
}
};
editorService.treePicker(filePicker);
}
vm.requestRemoveViewForBlock = function(block) {
localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) {
overlayService.confirmRemove({
title: data[0],
content: localizationService.tokenReplace(data[1], [block.view]),
close: function () {
overlayService.close();
},
submit: function () {
vm.removeViewForBlock(block);
overlayService.close();
}
});
});
};
vm.removeViewForBlock = function(block) {
block.view = null;
};
vm.addThumbnailForBlock = function(block) {
const thumbnailPicker = {
title: "Select thumbnail (TODO need translation)",
section: "settings",
treeAlias: "files",
entityType: "file",
isDialog: true,
filter: function (i) {
return (i.name.indexOf(".jpg") !== -1 || i.name.indexOf(".jpeg") !== -1 || i.name.indexOf(".png") !== -1 || i.name.indexOf(".svg") !== -1 || i.name.indexOf(".webp") !== -1 || i.name.indexOf(".gif") !== -1);
},
select: function (file) {
block.thumbnail = file.name;
editorService.close();
},
close: function () {
editorService.close();
}
};
editorService.treePicker(thumbnailPicker);
}
vm.removeThumbnailForBlock = function(entry) {
entry.thumbnail = null;
};
vm.submit = function () {
if ($scope.model && $scope.model.submit) {
$scope.model.submit($scope.model);
}
}
vm.close = function() {
if ($scope.model && $scope.model.close) {
// TODO: If content has changed, we should notify user.
$scope.model.close($scope.model);
}
}
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockList.BlockConfigurationOverlayController", BlockConfigurationOverlayController);
})();
@@ -0,0 +1,310 @@
<div class="umb-block-list-block-configuration-overlay" ng-controller="Umbraco.PropertyEditors.BlockList.BlockConfigurationOverlayController as vm">
<form novalidate name="blockListBlockConfigurationForm" val-form-manager>
<umb-editor-view ng-if="!page.loading">
<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 class="form-vertical">
<div class="umb-group-panel">
<div class="umb-group-panel__header">
<div id="group-{{group.id}}">Block Editor</div>
</div>
<div class="umb-group-panel__content">
<!-- label -->
<div class="control-group umb-control-group -no-border">
<div class="umb-el-wrap">
<label class="control-label" for="label"><localize key="blockEditor_labelLabelTemplate">label</localize></label>
<div class="controls">
<input type="text" name="label" ng-model="vm.block.label" style="width:100%" umb-auto-focus/>
</div>
</div>
</div>
<!-- custom view -->
<div class="control-group umb-control-group -no-border">
<div class="umb-el-wrap">
<label class="control-label" for="iconcolor"><localize key="blockEditor_labelCustomView">Custom view</localize></label>
<div class="controls">
<div class="__settings-input --hasValue" ng-if="vm.block.view !== null" >
<umb-node-preview icon="'icon-document'" name="vm.block.view"></umb-node-preview>
<div class="__control-actions">
<button type="button" class="btn-reset __control-actions-btn --remove umb-outline" ng-click="vm.requestRemoveViewForBlock(vm.block)">
<i class="icon icon-wrong"></i>
</button>
</div>
</div>
<button type="button" class="btn-reset __settings-input --noValue umb-outline" ng-if="vm.block.view === null" ng-click="vm.addViewForBlock(vm.block)">
<localize key="blockEditor_addCustomView">Add custom view</localize>
</button>
</div>
</div>
</div>
<!-- editor overlay size -->
<div class="control-group umb-control-group -no-border">
<div class="umb-el-wrap">
<label class="control-label" for="editorSize"><localize key="blockEditor_labelEditorSize">Editor size</localize></label>
<div class="controls">
<select id="editorSize" ng-model="vm.block.editorSize">
<option value="small">Small</option>
<option value="medium">Medium</option>
<option value="large">Large</option>
</select>
</div>
</div>
</div>
</div>
</div>
<div class="umb-group-panel">
<div class="umb-group-panel__header">
<div id="group-{{group.id}}">Data Settings</div>
</div>
<div class="umb-group-panel__content">
<!-- Content Element Type -->
<div class="control-group umb-control-group -no-border">
<div class="umb-el-wrap">
<label class="control-label"><localize key="blockEditor_labelContentElementType">Content ElementType</localize></label>
<div class="controls">
<div class="__settings-input --hasValue" ng-if="vm.block.contentTypeAlias !== null" >
{{ contentPreview = vm.getElementTypeByAlias(vm.block.contentTypeAlias); "" }}
<umb-node-preview icon="contentPreview.icon" name="contentPreview.name"></umb-node-preview>
<div class="__control-actions">
<button type="button" class="btn-reset __control-actions-btn --open umb-outline" ng-click="vm.openElementType(vm.block.contentTypeAlias)">
<i class="icon icon-edit"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Settings Element Type -->
<div class="control-group umb-control-group -no-border">
<div class="umb-el-wrap">
<label class="control-label"><localize key="blockEditor_labelSettingsElementType">Settings Element Type</localize></label>
<div class="controls">
<div class="__settings-input --hasValue" ng-if="vm.block.settingsElementTypeAlias !== null">
{{ settingsPreview = vm.getElementTypeByAlias(vm.block.settingsElementTypeAlias); "" }}
<umb-node-preview icon="settingsPreview.icon" name="settingsPreview.name"></umb-node-preview>
<div class="__control-actions">
<button type="button" class="btn-reset __control-actions-btn --open umb-outline" ng-click="vm.openElementType(vm.block.settingsElementTypeAlias)">
<i class="icon icon-edit"></i>
</button>
<button type="button" class="btn-reset __control-actions-btn --remove umb-outline" ng-click="vm.requestRemoveSettingsForBlock(vm.block)">
<i class="icon icon-wrong"></i>
</button>
</div>
</div>
<button type="button" class="btn-reset __settings-input --noValue umb-outline" ng-if="vm.block.settingsElementTypeAlias === null" ng-click="vm.addSettingsForBlock($event, vm.block)">
<localize key="blockEditor_addSettingsElementType">Add settings</localize>
</button>
</div>
</div>
</div>
<!-- Recursive
<div class="control-group umb-control-group">
<div class="umb-el-wrap">
<div class="controls">
<label class="control-label">
<localize key="templateEditor_recursive">Recursive</localize>
</label>
<div class="flex">
<umb-checkbox name="recursive"
disabled="!vm.field"
model="vm.recursive"
text="Yes, make it recursive"
label-key="templateEditor_recursiveDescr">
</umb-checkbox>
</div>
</div>
</div>
</div>
-->
</div>
</div>
<div class="umb-group-panel">
<div class="umb-group-panel__header">
<div id="group-{{group.id}}">Showcase</div>
</div>
<div class="umb-group-panel__content">
<!-- backgroundColor -->
<div class="control-group umb-control-group -no-border">
<div class="umb-el-wrap">
<label class="control-label" for="backgroundColor"><localize key="blockEditor_labelBackgroundColor">Background Color</localize></label>
<div class="controls">
<input type="text" name="backgroundColor" ng-model="vm.block.backgroundColor"/>
</div>
</div>
</div>
<!-- iconColor -->
<div class="control-group umb-control-group -no-border">
<div class="umb-el-wrap">
<label class="control-label" for="iconColor"><localize key="blockEditor_labelIconColor">Icon Color</localize></label>
<div class="controls">
<input type="text" name="iconColor" ng-model="vm.block.iconColor"/>
</div>
</div>
</div>
<!-- thumbnail -->
<div class="control-group umb-control-group -no-border">
<div class="umb-el-wrap">
<label class="control-label" for="iconcolor"><localize key="blockEditor_thumbnail">Thumbnail</localize></label>
<div class="controls">
<div class="__settings-input --hasValue" ng-if="vm.block.thumbnail !== null" >
<umb-node-preview icon="'icon-document'" name="vm.block.thumbnail"></umb-node-preview>
<div class="__control-actions">
<button type="button" class="btn-reset __control-actions-btn --remove umb-outline" ng-click="vm.removeThumbnailForBlock(vm.block)">
<i class="icon icon-wrong"></i>
</button>
</div>
</div>
<button type="button" class="btn-reset __settings-input --noValue umb-outline" ng-if="vm.block.thumbnail === null" ng-click="vm.addThumbnailForBlock(vm.block)">
<localize key="blockEditor_addThumbnail">Add thumbnail</localize>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</umb-editor-container>
<umb-editor-footer>
<umb-editor-footer-content-right>
<umb-button
action="vm.close()"
button-style="link"
label-key="general_close"
type="button">
</umb-button>
<umb-button
action="vm.submit()"
button-style="primary"
state="submitButtonState"
label-key="buttons_submitChanges"
type="button">
</umb-button>
</umb-editor-footer-content-right>
</umb-editor-footer>
</umb-editor-view>
</form>
</div>
<!--
<div class="umb-table">
<div class="umb-table-head">
<div class="umb-table-row">
<div class="umb-table-cell not-fixed umb-table__name">
<localize key="blockEditor_labelContentElementType">Custom view</localize>
</div>
<div class="umb-table-cell">
<localize key="blockEditor_labelLabelTemplate">Label</localize>
<button type="button" class="btn-reset">
<i class="icon icon-help-alt"></i>
</button>
</div>
<div class="umb-table-cell">
<localize key="blockEditor_labelCustomView">Custom view</localize>
</div>
<div class="umb-table-cell">
<localize key="blockEditor_labelSettingsElementType">Custom view</localize>
</div>
<div class="umb-table-cell action-cell">
</div>
</div>
</div>
<div class="umb-table-body" ui-sortable="vm.sortableOptions" ng-model="model.value">
<div class="umb-table-row block-entry" ng-repeat="entry in model.value">
<div class="umb-table-cell not-fixed umb-table__name">
{{ contentPreview = vm.getElementTypeByAlias(entry.contentTypeAlias); "" }}
<umb-node-preview icon="contentPreview.icon" name="contentPreview.name"></umb-node-preview>
<div class="__control-actions">
<button type="button" class="btn-reset __control-actions-btn --open umb-outline" ng-click="vm.openElementType(entry.contentTypeAlias)">
<i class="icon icon-edit"></i>
</button>
</div>
</div>
<div class="umb-table-cell">
<input type="text" ng-model="entry.label" class="text-input"/>
</div>
<div class="umb-table-cell">
<div class="__settings-input --hasValue" ng-if="entry.view !== null" >
<umb-node-preview icon="'icon-document'" name="entry.view"></umb-node-preview>
<div class="__control-actions">
<button type="button" class="btn-reset __control-actions-btn --remove umb-outline" ng-click="vm.requestRemoveViewForEntry(entry)">
<i class="icon icon-wrong"></i>
</button>
</div>
</div>
<button type="button" class="btn-reset __settings-input --noValue umb-outline" ng-if="entry.view === null" ng-click="vm.addViewForEntry(entry)">
<localize key="blockEditor_addCustomView">Add custom view</localize>
</button>
</div>
<div class="umb-table-cell">
<div class="__settings-input --hasValue" ng-if="entry.settingsElementTypeAlias !== null" >
{{ settingsPreview = vm.getElementTypeByAlias(entry.settingsElementTypeAlias); "" }}
<umb-node-preview icon="settingsPreview.icon" name="settingsPreview.name"></umb-node-preview>
<div class="__control-actions">
<button type="button" class="btn-reset __control-actions-btn --open umb-outline" ng-click="vm.openElementType(entry.settingsElementTypeAlias)">
<i class="icon icon-edit"></i>
</button>
<button type="button" class="btn-reset __control-actions-btn --remove umb-outline" ng-click="vm.requestRemoveSettingsForEntry(entry)">
<i class="icon icon-wrong"></i>
</button>
</div>
</div>
<button type="button" class="btn-reset __settings-input --noValue umb-outline" ng-if="entry.settingsElementTypeAlias === null" ng-click="vm.addSettingsForEntry($event, entry)">
<localize key="blockEditor_addSettingsElementType">Add settings</localize>
</button>
</div>
<div class="umb-table-cell action-cell --noOverflow">
<button type="button" class="btn-icon umb-outline" ng-click="vm.requestRemoveEntryByIndex($index)">
<i class="icon icon-trash" aria-hidden="true"></i>
<localize key="general_delete" class="sr-only">Delete</localize>
</button>
</div>
</div>
</div>
</div>
<button type="button" class="btn-reset add-button" ng-click="vm.openAddDialog($event)" ng-disabled="!vm.enableAddEntry">
<localize key="general_add">Add</localize>
</button>
-->
@@ -1,46 +1,11 @@
.umb-block-list-element-type-picker {
.umb-block-list-block-configuration-overlay {
.block-entry {
cursor: grab;
background-color: white;
border-radius: @baseBorderRadius;
}
.umb-table {
border:1px solid @gray-11;
}
.umb-table-head {
button {
margin-left: 5px;
color: @ui-action-discreet-type;
&:hover {
color: @ui-action-discreet-type-hover;
}
}
}
.umb-table-cell {
padding-left: 10px;
padding-right: 0;
&.action-cell {
padding-right: 15px;
}
}
.action-cell {
flex: 0 0 30px;
}
.text-input {
width: 100%;
}
.umb-node-preview {
flex-grow: 1;
}
.cell-actions {
.__control-actions {
position: absolute;
display: flex;
align-items: center;
@@ -51,14 +16,14 @@
opacity: 0;
transition: opacity 120ms;
}
.umb-table-cell:hover,
.umb-table-cell:focus,
.umb-table-cell:focus-within {
.cell-actions {
.control-group:hover,
.control-group:focus,
.control-group:focus-within {
.__control-actions {
opacity: 1;
}
}
.cell-actions-btn {
.__control-actions-btn {
position: relative;
color: @ui-action-discreet-type;
height: 30px;
@@ -76,14 +41,15 @@
border-bottom: none;
}
.settings-input {
.__settings-input {
position: relative;
padding: 5px 8px;
margin-bottom: 10px;
color: @ui-action-discreet-type;
border: 1px dashed @ui-action-discreet-border;
width: 100%;
font-weight: bold;
display: flex;
display: inline-flex;
flex-flow: row nowrap;
localize {
@@ -93,6 +59,7 @@
.umb-node-preview {
padding: 3px 0;
margin-left: 5px;
overflow: hidden;
}
&.--noValue {
@@ -112,7 +79,7 @@
}
}
.add-button {
.__add-button {
width:100%;
color: @ui-action-discreet-type;
border: 1px dashed @ui-action-discreet-border;
@@ -126,7 +93,7 @@
font-weight: bold;
}
.add-button:hover {
.__add-button:hover {
color: @ui-action-discreet-type-hover;
border-color: @ui-action-discreet-border-hover;
}
@@ -1,285 +0,0 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.PropertySettingsController
* @function
*
* @description
* The controller for the content type editor property settings dialog
*/
(function () {
"use strict";
function ElementTypePickerController($scope, elementTypeResource, overlayService, localizationService, editorService) {
var vm = this;
vm.enableAddEntry = true;
function evaluateStatus() {
if (!vm.elementTypes) return;// cancel if elementTypes isnt loaded jet.
vm.enableAddEntry = vm.getAvailableElementTypes().length > 0;
}
function onInit() {
if (!$scope.model.value) {
$scope.model.value = [];
}
localizationService.localize("content_nestedContentSelectElementTypeModalTitle").then(function (value) {
//selectElementTypeModalTitle = value;
});
loadElementTypes();
}
function loadElementTypes() {
return elementTypeResource.getAll().then(function (elementTypes) {
vm.elementTypes = elementTypes;
evaluateStatus();
});
}
vm.requestRemoveEntryByIndex = function (index) {
localizationService.localizeMany(["general_delete", "blockEditor_confirmDeleteBlockMessage", "blockEditor_confirmDeleteBlockNotice"]).then(function (data) {
var contentElementType = vm.getElementTypeByAlias($scope.model.value[index].contentTypeAlias);
overlayService.confirmDelete({
title: data[0],
content: localizationService.tokenReplace(data[1], [contentElementType.name]),
confirmMessage: data[2],
close: function () {
overlayService.close();
},
submit: function () {
vm.removeEntryByIndex(index);
overlayService.close();
}
});
});
}
vm.removeEntryByIndex = function (index) {
$scope.model.value.splice(index, 1);
};
vm.sortableOptions = {
axis: "y",
cursor: "grabbing",
placeholder: 'sortable-placeholder',
forcePlaceholderSize: true
};
vm.getAvailableElementTypes = function () {
return vm.elementTypes.filter(function (type) {
return !$scope.model.value.find(function (entry) {
return type.alias === entry.contentTypeAlias;
});
});
};
vm.getElementTypeByAlias = function(alias) {
return _.find(vm.elementTypes, function (type) {
return type.alias === alias;
});
};
vm.openAddDialog = function ($event, entry) {
//we have to add the alias to the objects (they are stored as contentTypeAlias)
var selectedItems = _.each($scope.model.value, function (obj) {
obj.alias = obj.contentTypeAlias;
return obj;
});
var availableItems = vm.getAvailableElementTypes()
var elemTypeSelectorOverlay = {
view: "itempicker",
title: "no title jet",
availableItems: availableItems,
selectedItems: selectedItems,
createNewItem: {
action: function() {
overlayService.close();
vm.createElementTypeAndAdd(vm.addEntryFromElementTypeAlias);
},
icon: "icon-add",
name: "Create new"
},
position: "target",
event: $event,
size: availableItems.length < 7 ? "small" : "medium",
submit: function (overlay) {
vm.addEntryFromElementTypeAlias(overlay.selectedItem.alias);
overlayService.close();
},
close: function () {
overlayService.close();
}
};
overlayService.open(elemTypeSelectorOverlay);
};
vm.createElementTypeAndAdd = function(callback) {
const editor = {
create: true,
infiniteMode: true,
isElement: true,
submit: function (model) {
console.log(model)
loadElementTypes().then( function () {
callback(model.documentTypeAlias);
});
editorService.close();
},
close: function () {
editorService.close();
}
};
editorService.documentTypeEditor(editor);
}
vm.addEntryFromElementTypeAlias = function(alias) {
var entry = {
"contentTypeAlias": alias,
"view": null,
"labelTemplate": "",
"settingsElementTypeAlias": null
};
$scope.model.value.push(entry);
};
vm.requestRemoveSettingsForEntry = function(entry) {
localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) {
var settingsElementType = vm.getElementTypeByAlias(entry.settingsElementTypeAlias);
overlayService.confirmRemove({
title: data[0],
content: localizationService.tokenReplace(data[1], [settingsElementType.name]),
close: function () {
overlayService.close();
},
submit: function () {
vm.removeSettingsForEntry(entry);
overlayService.close();
}
});
});
};
vm.removeSettingsForEntry = function(entry) {
entry.settingsElementTypeAlias = null;
};
vm.addSettingsForEntry = function ($event, entry) {
var elemTypeSelectorOverlay = {
view: "itempicker",
title: "Pick settings (missing translation)",
availableItems: vm.elementTypes,
position: "target",
event: $event,
size: vm.elementTypes.length < 7 ? "small" : "medium",
createNewItem: {
action: function() {
overlayService.close();
vm.createElementTypeAndAdd((alias) => {
vm.addSettingsAtEntry(entry, alias);
});
},
icon: "icon-add",
name: "Create new"
},
submit: function (overlay) {
vm.addSettingsAtEntry(entry, overlay.selectedItem.alias);
overlayService.close();
},
close: function () {
overlayService.close();
}
};
overlayService.open(elemTypeSelectorOverlay);
};
vm.addSettingsAtEntry = function(entry, alias) {
entry.settingsElementTypeAlias = alias;
};
vm.openElementType = function(elementTypeAlias) {
var elementTypeId = vm.getElementTypeByAlias(elementTypeAlias).id;
const editor = {
id: elementTypeId,
submit: function (model) {
loadElementTypes();
editorService.close();
},
close: function () {
editorService.close();
}
};
editorService.documentTypeEditor(editor);
}
vm.requestRemoveViewForEntry = function(entry) {
localizationService.localizeMany(["general_remove", "defaultdialogs_confirmremoveusageof"]).then(function (data) {
overlayService.confirmRemove({
title: data[0],
content: localizationService.tokenReplace(data[1], [entry.view]),
close: function () {
overlayService.close();
},
submit: function () {
vm.removeViewForEntry(entry);
overlayService.close();
}
});
});
};
vm.removeViewForEntry = function(entry) {
entry.view = null;
};
vm.addViewForEntry = function(entry) {
const filePicker = {
title: "Select view (TODO need translation)",
section: "settings",
treeAlias: "files",
entityType: "file",
isDialog: true,
filter: function (i) {
if (i.name.indexOf(".html") !== -1) {
return true;
}
},
select: function (file) {
console.log(file);
entry.view = file.name;
editorService.close();
},
close: function () {
editorService.close();
}
};
editorService.treePicker(filePicker);
}
onInit();
$scope.$watchCollection('model.value', function(newVal, oldVal) {
evaluateStatus();
});
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockList.ElementTypePickerController", ElementTypePickerController);
})();
@@ -1,81 +0,0 @@
<div id="{{model.alias}}" class="umb-block-list-element-type-picker" ng-controller="Umbraco.PropertyEditors.BlockList.ElementTypePickerController as vm">
<div class="umb-table">
<div class="umb-table-head">
<div class="umb-table-row">
<div class="umb-table-cell not-fixed umb-table__name">
<localize key="blockEditor_labelContentElementType">Custom view</localize>
</div>
<div class="umb-table-cell">
<localize key="blockEditor_labelLabelTemplate">Label</localize>
<button type="button" class="btn-reset">
<i class="icon icon-help-alt"></i>
</button>
</div>
<div class="umb-table-cell">
<localize key="blockEditor_labelCustomView">Custom view</localize>
</div>
<div class="umb-table-cell">
<localize key="blockEditor_labelSettingsElementType">Custom view</localize>
</div>
<div class="umb-table-cell action-cell">
</div>
</div>
</div>
<div class="umb-table-body" ui-sortable="vm.sortableOptions" ng-model="model.value">
<div class="umb-table-row block-entry" ng-repeat="entry in model.value">
<div class="umb-table-cell not-fixed umb-table__name">
{{ contentPreview = vm.getElementTypeByAlias(entry.contentTypeAlias); "" }}
<umb-node-preview icon="contentPreview.icon" name="contentPreview.name"></umb-node-preview>
<div class="cell-actions">
<button type="button" class="btn-reset cell-actions-btn --open umb-outline" ng-click="vm.openElementType(entry.contentTypeAlias)">
<i class="icon icon-edit"></i>
</button>
</div>
</div>
<div class="umb-table-cell">
<input type="text" ng-model="entry.label" class="text-input"/>
</div>
<div class="umb-table-cell">
<div class="settings-input --hasValue" ng-if="entry.view !== null" >
<umb-node-preview icon="'icon-document'" name="entry.view"></umb-node-preview>
<div class="cell-actions">
<button type="button" class="btn-reset cell-actions-btn --remove umb-outline" ng-click="vm.requestRemoveViewForEntry(entry)">
<i class="icon icon-wrong"></i>
</button>
</div>
</div>
<button type="button" class="btn-reset settings-input --noValue umb-outline" ng-if="entry.view === null" ng-click="vm.addViewForEntry(entry)">
<localize key="blockEditor_addCustomView">Add custom view</localize>
</button>
</div>
<div class="umb-table-cell">
<div class="settings-input --hasValue" ng-if="entry.settingsElementTypeAlias !== null" >
{{ settingsPreview = vm.getElementTypeByAlias(entry.settingsElementTypeAlias); "" }}
<umb-node-preview icon="settingsPreview.icon" name="settingsPreview.name"></umb-node-preview>
<div class="cell-actions">
<button type="button" class="btn-reset cell-actions-btn --open umb-outline" ng-click="vm.openElementType(entry.settingsElementTypeAlias)">
<i class="icon icon-edit"></i>
</button>
<button type="button" class="btn-reset cell-actions-btn --remove umb-outline" ng-click="vm.requestRemoveSettingsForEntry(entry)">
<i class="icon icon-wrong"></i>
</button>
</div>
</div>
<button type="button" class="btn-reset settings-input --noValue umb-outline" ng-if="entry.settingsElementTypeAlias === null" ng-click="vm.addSettingsForEntry($event, entry)">
<localize key="blockEditor_addSettingsElementType">Add settings</localize>
</button>
</div>
<div class="umb-table-cell action-cell --noOverflow">
<button type="button" class="btn-icon umb-outline" ng-click="vm.requestRemoveEntryByIndex($index)">
<i class="icon icon-trash" aria-hidden="true"></i>
<localize key="general_delete" class="sr-only">Delete</localize>
</button>
</div>
</div>
</div>
</div>
<button type="button" class="btn-reset add-button" ng-click="vm.openAddDialog($event)" ng-disabled="!vm.enableAddEntry">
<localize key="general_add">Add</localize>
</button>
</div>
@@ -2403,14 +2403,20 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="formsDescription">Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it!</key>
</area>
<area alias="blockEditor">
<key alias="labelBackgroundColor">Background color</key>
<key alias="labelIconColor">Icon color</key>
<key alias="labelContentElementType">Content model</key>
<key alias="labelLabelTemplate">Label</key>
<key alias="labelCustomView">Custom view</key>
<key alias="labelSettingsElementType">Settings model</key>
<key alias="labelEditorSize">Overlay editor size</key>
<key alias="addCustomView">Add custom view</key>
<key alias="addSettingsElementType">Add settings</key>
<key alias="labelTemplatePlaceholder">Overwrite label template</key>
<key alias="confirmDeleteBlockMessage"><![CDATA[Are you sure you want to delete block of <strong>%0%</strong>.]]></key>
<key alias="confirmDeleteBlockNotice">Content using this block will be lost.</key>
<key alias="blockConfigurationOverlayTitle"><![CDATA[Configuration of '%0%']]></key>
<key alias="thumbnail">Thumbnail</key>
<key alias="addThumbnail">Add thumbnail</key>
</area>
</language>
@@ -2414,14 +2414,20 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="formsDescription">Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it!</key>
</area>
<area alias="blockEditor">
<key alias="labelBackgroundColor">Background color</key>
<key alias="labelIconColor">Icon color</key>
<key alias="labelContentElementType">Content model</key>
<key alias="labelLabelTemplate">Label</key>
<key alias="labelCustomView">Custom view</key>
<key alias="labelSettingsElementType">Settings model</key>
<key alias="labelEditorSize">Overlay editor size</key>
<key alias="addCustomView">Add custom view</key>
<key alias="addSettingsElementType">Add settings</key>
<key alias="labelTemplatePlaceholder">Overwrite label template</key>
<key alias="confirmDeleteBlockMessage"><![CDATA[Are you sure you want to delete block of <strong>%0%</strong>.]]></key>
<key alias="confirmDeleteBlockNotice">Content using this block will be lost.</key>
<key alias="blockConfigurationOverlayTitle"><![CDATA[Configuration of '%0%']]></key>
<key alias="thumbnail">Thumbnail</key>
<key alias="addThumbnail">Add thumbnail</key>
</area>
</language>
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Services;
using Umbraco.Web.Editors;
using Umbraco.Web.Mvc;
namespace Umbraco.Web.Editors
{
[PluginController("UmbracoApi")]
public class ElementTypeController : UmbracoAuthorizedJsonController
{
[System.Web.Http.HttpGet]
public IEnumerable<object> GetAll()
{
return Services.ContentTypeService
.GetAllElementTypes()
.OrderBy(x => x.SortOrder)
.Select(x => new
{
id = x.Id,
key = x.Key,
name = x.Name,
description = x.Description,
alias = x.Alias,
icon = x.Icon
});
}
}
}
@@ -10,7 +10,7 @@ namespace Umbraco.Web.PropertyEditors
public class BlockListConfiguration
{
[ConfigurationField("blocks", "Available Blocks", "views/propertyeditors/blocklist/prevalue/blocklist.elementtypepicker.html", Description = "Define the available blocks.")]
[ConfigurationField("blocks", "Available Blocks", "views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html", Description = "Define the available blocks.")]
public BlockConfiguration[] Blocks { get; set; }
@@ -28,7 +28,16 @@ namespace Umbraco.Web.PropertyEditors
public class BlockConfiguration
{
// TODO: rename this to contentElementTypeAlias, I would like this to be specific, since we have the settings.
[JsonProperty("backgroundColor")]
public string BackgroundColor { get; set; }
[JsonProperty("iconColor")]
public string IconColor { get; set; }
[JsonProperty("thumbnail")]
public string Thumbnail { get; set; }
[JsonProperty("contentTypeAlias")]
public string Alias { get; set; }
@@ -40,6 +49,9 @@ namespace Umbraco.Web.PropertyEditors
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("editorSize")]
public string EditorSize { get; set; }
}
[ConfigurationField("useInlineEditingAsDefault", "Inline editing mode", "boolean", Description = "Use the inline editor as the default block view")]
+1
View File
@@ -149,6 +149,7 @@
<Compile Include="Dashboards\RedirectUrlDashboard.cs" />
<Compile Include="Dashboards\SettingsDashboards.cs" />
<Compile Include="Editors\BackOfficePreviewModel.cs" />
<Compile Include="Editors\ElementTypeController.cs" />
<Compile Include="Editors\Filters\ContentSaveModelValidator.cs" />
<Compile Include="Editors\Filters\MediaSaveModelValidator.cs" />
<Compile Include="Editors\ImageUrlGeneratorController.cs" />