From 21114cbbd774114fa90be616311e3ca280c49b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 14 Jan 2020 10:54:11 +0100 Subject: [PATCH 01/10] gulp support for less files in views folder --- src/Umbraco.Web.UI.Client/gulp/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/config.js b/src/Umbraco.Web.UI.Client/gulp/config.js index 59e8bf6c05..a807d63f5f 100755 --- a/src/Umbraco.Web.UI.Client/gulp/config.js +++ b/src/Umbraco.Web.UI.Client/gulp/config.js @@ -17,7 +17,7 @@ module.exports = { installer: { files: "./src/less/installer.less", watch: "./src/less/**/*.less", out: "installer.css" }, nonodes: { files: "./src/less/pages/nonodes.less", watch: "./src/less/**/*.less", out: "nonodes.style.min.css"}, preview: { files: "./src/less/canvas-designer.less", watch: "./src/less/**/*.less", out: "canvasdesigner.css" }, - umbraco: { files: "./src/less/belle.less", watch: "./src/less/**/*.less", out: "umbraco.css" }, + umbraco: { files: "./src/less/belle.less", watch: "./src/**/*.less", out: "umbraco.css" }, rteContent: { files: "./src/less/rte-content.less", watch: "./src/less/**/*.less", out: "rte-content.css" } }, From 678e31834d966adc59b6f423d403e1dfc88a3744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 09:06:27 +0100 Subject: [PATCH 02/10] registrer Block List Editor --- src/Umbraco.Core/Constants-PropertyEditors.cs | 5 ++++ .../BlockListPropertyEditor.cs | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs diff --git a/src/Umbraco.Core/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs index eb2b3525a7..dcd7eb9d05 100644 --- a/src/Umbraco.Core/Constants-PropertyEditors.cs +++ b/src/Umbraco.Core/Constants-PropertyEditors.cs @@ -36,6 +36,11 @@ namespace Umbraco.Core /// public static class Aliases { + /// + /// Block List. + /// + public const string BlockList = "Umbraco.BlockList"; + /// /// CheckBox List. /// diff --git a/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs new file mode 100644 index 0000000000..da44151ec9 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs @@ -0,0 +1,30 @@ +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Represents a block list property editor. + /// + [DataEditor( + Constants.PropertyEditors.Aliases.BlockList, + "Block List", + "blocklist", + ValueType = ValueTypes.Json, + Group = Constants.PropertyEditors.Groups.Lists, + Icon = "icon-thumbnail-list")] + public class BlockListPropertyEditor : DataEditor + { + public BlockListPropertyEditor(ILogger logger) + : base(logger) + { } + + #region Pre Value Editor + //protected override IConfigurationEditor CreateConfigurationEditor() => new BlockEditorListConfigurationEditor(); + + #endregion + + } +} From 897517d4ec0675cf10c375dd96cf3ad29df5f32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 09:07:13 +0100 Subject: [PATCH 03/10] do not set focus if already set + clear timeout if running --- .../components/forms/focuswhen.directive.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/focuswhen.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/focuswhen.directive.js index d8dbcc1012..dda5c51175 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/focuswhen.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/focuswhen.directive.js @@ -2,13 +2,20 @@ angular.module("umbraco.directives").directive('focusWhen', function ($timeout) return { restrict: 'A', link: function (scope, elm, attrs, ctrl) { + + var delayTimer; + attrs.$observe("focusWhen", function (newValue) { - if (newValue === "true") { - $timeout(function () { - elm.trigger("focus"); - }); + if (newValue === "true" && document.activeelement !== elm[0]) { + delayTimer = $timeout(function () { + elm[0].focus(); + }); } }); + + scope.$on('$destroy', function() { + $timeout.cancel(delayTimer); + }); } }; }); From 85d4cd9be3995f10fc4699c259267decbaa49a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 09:07:41 +0100 Subject: [PATCH 04/10] compile JS after HTML, cause HTML is embeded in JS. --- src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js index 24a6e65540..42f25ccb23 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js @@ -12,6 +12,7 @@ var processLess = require('../util/processLess'); //const { less } = require('./less'); //const { views } = require('./views'); +var {js} = require('./js'); function watchTask(cb) { @@ -39,6 +40,7 @@ function watchTask(cb) { viewWatcher.on('change', function(path, stats) { console.log("copying " + group.files + " to " + config.root + config.targets.views + group.folder); src(group.files).pipe( dest(config.root + config.targets.views + group.folder) ); + js(); }); } }); From 4caad6e42953c15d5124f6c7a70c9bcba464f055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 09:09:14 +0100 Subject: [PATCH 05/10] ability to turn on/off focus outlines --- .../src/main.controller.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/main.controller.js b/src/Umbraco.Web.UI.Client/src/main.controller.js index 93870f8a56..3601cb9652 100644 --- a/src/Umbraco.Web.UI.Client/src/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/main.controller.js @@ -32,13 +32,17 @@ function MainController($scope, $location, appState, treeService, notificationsS // For more information about this approach, see https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2 function handleFirstTab(evt) { if (evt.keyCode === 9) { - $scope.tabbingActive = true; - $scope.$digest(); - window.removeEventListener('keydown', handleFirstTab); - window.addEventListener('mousedown', disableTabbingActive); + enableTabbingActive(); } } + function enableTabbingActive() { + $scope.tabbingActive = true; + $scope.$digest(); + window.addEventListener('mousedown', disableTabbingActive); + window.removeEventListener("keydown", handleFirstTab); + } + function disableTabbingActive(evt) { $scope.tabbingActive = false; $scope.$digest(); @@ -48,6 +52,12 @@ function MainController($scope, $location, appState, treeService, notificationsS window.addEventListener("keydown", handleFirstTab); + $scope.$on("showFocusOutline", function() { + $scope.tabbingActive = true; + window.addEventListener('mousedown', disableTabbingActive); + window.removeEventListener("keydown", handleFirstTab); + }); + $scope.removeNotification = function (index) { notificationsService.remove(index); From 761b5ecc01323f8393f77c6d63996177eab1e9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 09:09:54 +0100 Subject: [PATCH 06/10] Add BlockListPropertyEditor to csproj --- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 5d29e53d4a..997b13ce8d 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -888,6 +888,7 @@ + From 034567bb0ee3e918f715b47ca3a2605ef059b7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 11:11:05 +0100 Subject: [PATCH 07/10] reverted gulp change --- src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js index 42f25ccb23..3c90003e30 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js @@ -9,11 +9,6 @@ var MergeStream = require('merge-stream'); var processJs = require('../util/processJs'); var processLess = require('../util/processLess'); -//const { less } = require('./less'); -//const { views } = require('./views'); - -var {js} = require('./js'); - function watchTask(cb) { var watchInterval = 500; @@ -40,7 +35,6 @@ function watchTask(cb) { viewWatcher.on('change', function(path, stats) { console.log("copying " + group.files + " to " + config.root + config.targets.views + group.folder); src(group.files).pipe( dest(config.root + config.targets.views + group.folder) ); - js(); }); } }); From 382ead27f20ee688a223405177fddf39850ad993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 13:30:50 +0100 Subject: [PATCH 08/10] Run JS when Views has been updated, cause we have embeded templates. --- src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js index 3c90003e30..a94314abd6 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/watchTask.js @@ -9,6 +9,8 @@ var MergeStream = require('merge-stream'); var processJs = require('../util/processJs'); var processLess = require('../util/processLess'); +var {js} = require('./js'); + function watchTask(cb) { var watchInterval = 500; @@ -33,8 +35,15 @@ function watchTask(cb) { if(group.watch !== false) { viewWatcher = watch(group.files, { ignoreInitial: true, interval: watchInterval }); viewWatcher.on('change', function(path, stats) { + console.log("copying " + group.files + " to " + config.root + config.targets.views + group.folder); - src(group.files).pipe( dest(config.root + config.targets.views + group.folder) ); + + return MergeStream( + src(group.files) + .pipe( dest(config.root + config.targets.views + group.folder) ) + , js() + ); + }); } }); From 2583d0c7d03727eaab5bb8ca3b4b5fe9b0e4a6f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 20 Jan 2020 17:34:49 +0100 Subject: [PATCH 09/10] BlockList PropertyEditor --- src/Umbraco.Web.UI.Client/src/less/belle.less | 9 + .../imageblock/imageblock.editor.html | 3 + .../imageblock/imageblock.editor.less | 12 + .../labelblock/labelblock.editor.html | 4 + .../labelblock/labelblock.editor.less | 34 ++ .../textareablock.editor.controller.js | 29 + .../textareablock/textareablock.editor.html | 15 + .../textareablock/textareablock.editor.less | 27 + .../elementeditor/elementeditor.controller.js | 23 + .../elementeditor/elementeditor.html | 84 +++ .../blocklist/blocklist.component.html | 104 ++++ .../blocklist/blocklist.component.js | 553 ++++++++++++++++++ .../blocklist/blocklist.component.less | 217 +++++++ .../propertyeditors/blocklist/blocklist.html | 1 + 14 files changed, 1115 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index b5e032f9fb..78a10da78d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -195,6 +195,10 @@ @import "components/contextdialogs/umb-dialog-datatype-delete.less"; +// Property Editors +@import "../views/propertyeditors/blocklist/blocklist.component.less"; + + // Utilities @import "utilities/layout/_display.less"; @import "utilities/theme/_opacity.less"; @@ -218,6 +222,11 @@ // Used for prevalue editors @import "components/prevalues/multivalues.less"; +// Block Elements +@import "../views/blockelements/labelblock/labelblock.editor.less"; +@import "../views/blockelements/textareablock/textareablock.editor.less"; +@import "../views/blockelements/imageblock/imageblock.editor.less"; + // Dashboards @import "dashboards/getstarted.less"; @import "dashboards/umbraco-forms.less"; diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html new file mode 100644 index 0000000000..c77f19ba67 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.html @@ -0,0 +1,3 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less new file mode 100644 index 0000000000..99b1bb53f2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/imageblock/imageblock.editor.less @@ -0,0 +1,12 @@ +.blockelement-imageblock-editor { + + padding-top: 2px; + padding-bottom: 2px; + + img { + width: 100%; + border: none; + resize: none; + border-radius: @baseBorderRadius; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html new file mode 100644 index 0000000000..f0678c76e9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.html @@ -0,0 +1,4 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less new file mode 100644 index 0000000000..610773528b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/labelblock/labelblock.editor.less @@ -0,0 +1,34 @@ +.blockelement-labelblock-editor { + + width: 100%; + min-height: 48px; + border: 1px solid @gray-9; + border-radius: @baseBorderRadius; + + color: @ui-action-discreet-type; + + text-align: left; + padding-left: 20px; + padding-bottom: 2px; + margin-bottom: 2px; + margin-top: 2px; + + user-select: none; + + transition: border-color 120ms; + + i { + font-size: 22px; + display: inline-block; + vertical-align: middle; + } + span { + display: inline-block; + vertical-align: middle; + } + + &:hover { + color: @ui-action-discreet-type-hover; + border-color: @gray-8; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js new file mode 100644 index 0000000000..761fc57e03 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.controller.js @@ -0,0 +1,29 @@ +//used for the media picker dialog +angular.module("umbraco") +.controller("Umbraco.Editors.TextAreaBlockElementEditorController", + function ($scope) { + + var vm = this; + + vm.firstProperty = $scope.block.content.tabs[0].properties[0]; +/* + vm.submitOnEnter = function($event) { + if($event && $event.keyCode === 13 && !$event.shiftKey && !$event.ctrlKey) { + var target = $event.target; + if(target.selectionStart === target.selectionEnd && target.selectionEnd === target.textLength) { + //&& (target.textLength === 0 || /\r|\n/.test(target.value.charAt(target.textLength - 1))) + $scope.$emit("showFocusOutline"); + $scope.blockApi.showCreateOptionsFor($scope.block, $event); + } + } + } +*/ + vm.onBlur = function() { + if (vm.firstProperty.value === null || vm.firstProperty.value === "") { + $scope.blockApi.deleteBlock($scope.block); + } + } + + } + +); diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html new file mode 100644 index 0000000000..405c8634b4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.html @@ -0,0 +1,15 @@ + +
+ + + + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less new file mode 100644 index 0000000000..492025274a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/blockelements/textareablock/textareablock.editor.less @@ -0,0 +1,27 @@ +.blockelement-textareablock-editor { + + width: 100%; + padding-bottom: 24px; + padding-top: 24px; + + padding-left: 24px; + padding-right: 24px; + + min-height: 64px; + box-sizing: border-box; + + textarea { + display: block; + width: 100%; + max-width: 640px; + margin-left: auto; + margin-right: auto; + border: none; + resize: none; + overflow: auto; + + font-size: 18px; + font-family: Georgia,Cambria,"Times New Roman",Times,serif; + line-height: 1.25; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js new file mode 100644 index 0000000000..d421418297 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.controller.js @@ -0,0 +1,23 @@ +//used for the media picker dialog +angular.module("umbraco") +.controller("Umbraco.Editors.ElementEditorController", + function ($scope) { + + var vm = this; + + vm.content = $scope.model.block.content; + + vm.saveAndClose = function() { + if ($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + + vm.close = function() { + if ($scope.model && $scope.model.close) { + $scope.model.close($scope.model); + } + } + + } +); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html new file mode 100644 index 0000000000..71aab124e6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/elementeditor/elementeditor.html @@ -0,0 +1,84 @@ +
+ + + + + + +
+ + + +
+ +
+
+ +
+ +
+
{{ group.label }}
+
+ +
+ + +
+ + +
+ +
+
+ +
+ + + + + +
+
+
+
+ +
+ + + + + + + + + + + + + + + + +
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html new file mode 100644 index 0000000000..e38858c8b7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.html @@ -0,0 +1,104 @@ +
+ + +
+ +
+ +
+ + + + +
+ +
+
+
+ +

No editor

+
+ +
+ + +
+ +
+ +
+
+ + + + + +
+ + + + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js new file mode 100644 index 0000000000..6283bd1026 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -0,0 +1,553 @@ +(function () { + 'use strict'; + + angular + .module('umbraco') + .component('blockListPropertyEditor', { + templateUrl: 'views/propertyeditors/blocklist/blocklist.component.html', + controller: BlockListController, + controllerAs: 'vm', + bindings: { + + }, + require: { + umbProperty: '?^umbProperty', + propertyForm: '?^propertyForm' + } + }); + + function BlockListController($scope, $interpolate, editorService, clipboardService, localizationService, overlayService) { + + var vm = this; + var model = $scope.$parent.$parent.model; + + $scope.moveFocusToBlock = null; + + vm.quickMenuVisible = false; + vm.quickMenuIndex = 0; + + vm.quickMenuAddNewBlock = function(type) { + addNewBlock(vm.quickMenuIndex, type); + vm.quickMenuVisible = false; + } + + vm.availableBlockTypes = [ + { + alias: "pageModule", + name: "Module", + icon: "icon-document", + prototype_paste_data: { + + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "{{pageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + editor: "views/blockelements/labelblock/labelblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + } + }, + { + alias: "contentTypeAlias", + name: "contentTypeName", + icon: "icon-text", + prototype_paste_data: { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "Label", + editor: "views/blockelements/textareablock/textareablock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + } + }, + { + alias: "contentTypeAlias", + name: "contentTypeName", + icon: "icon-picture", + prototype_paste_data: { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "Label", + editor: "views/blockelements/imageblock/imageblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Let's have a chat", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ], + temp_image: "/umbraco/assets/img/login.jpg" + } + } + } + ]; + + // var defaultBlockType... + + // TODO: get icon, properties etc. from available types? + vm.blocks = [ + { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "{{pageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + key: 1, + editor: "views/blockelements/labelblock/labelblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "The purpose of lorem ipsum is to create a natural looking block of text (sentence, paragraph, page, etc.) that doesn't distract from the layout. A practice not without controversy, laying out pages with meaningless filler text can be very useful when the focus is meant to be on design, not content.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + }, + { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "{{pageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + key: 2, + editor: "views/blockelements/labelblock/labelblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + }, + { + + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "{{pageTitle | truncate:true:36}}", + labelInterpolate: $interpolate("{{pageTitle | truncate:true:36}}"), + key: 3, + editor: "views/blockelements/labelblock/labelblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ] + } + } + ]; + + function setDirty() { + if (vm.propertyForm) { + vm.propertyForm.$setDirty(); + } + }; + + function addNewBlock(index, type) { + + var block = angular.copy(type.prototype_paste_data); + + vm.blocks.splice(index, 0, block); + $scope.moveFocusToBlock = block; + + } + /* + function moveFocusToNextBlock(blockModel, $event) { + var index = vm.blocks.indexOf(blockModel); + if(index < vm.blocks.length) { + var nextBlock = vm.blocks[index+1]; + $scope.moveFocusToBlock = nextBlock; + } else { + showCreateOptions(blockModel, $event); + } + } + */ + + vm.showCreateOptionsFor = function(blockModel, $event) { + var index = vm.blocks.indexOf(blockModel); + $event.preventDefault(); + showCreateOptionsAt(index); + } + function showCreateOptionsAt(index) { + vm.quickMenuIndex = index; + vm.quickMenuVisible = true; + window.addEventListener("keydown", handleTypingInCreateOptions); + } + + function handleTypingInCreateOptions(event) { + if (event.ctrlKey || event.metaKey || event.altKey) + return; + + if ( + (event.keyCode === 13) // enter + || + (event.keyCode >= 48 && event.keyCode <= 90)// 0 to z + || + (event.keyCode >= 96 && event.keyCode <= 111)// numpads + || + (event.keyCode >= 186 && event.keyCode <= 222)// semi-colon and a lot of other special characters + ) { + // Continue writting... needs to know default text-element. if we have one. + } + } + + function hideCreateOptions() { + vm.quickMenuVisible = false; + window.removeEventListener("keydown", handleTypingInCreateOptions); + } + + vm.onCreateOptionsBlur = function($event) { + + if(!$($event.relatedTarget).is(".umb-block-list__block--create-bar > button")) { + hideCreateOptions(); + } + + } + + vm.getBlockLabel = function(block) { + + var name = ""; + + var props = new Object(); + + var tab = block.content.tabs[0]; + // TODO: need to look up all tabs... + for(const property of tab.properties) { + props[property.alias] = property.value; + } + + if(block.labelInterpolate) { + return block.labelInterpolate(props); + } + + return "block.label"; + } + + vm.deleteBlock = function(block) { + var index = vm.blocks.indexOf(block); + if(index !== -1) { + vm.blocks.splice(index, 1); + } + if(vm.quickMenuIndex > index) { + vm.quickMenuIndex--; + } + } + + vm.editBlock = function(blockModel) { + + var elementEditor = { + block: blockModel, + view: "views/common/infiniteeditors/elementeditor/elementeditor.html", + size: "large", + submit: function(model) { + blockModel.content = model.block.content; + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + + // open property settings editor + editorService.open(elementEditor); + } + + vm.showCreateDialog = function (createIndex, $event) { + + if (vm.blockTypePicker) { + return; + } + + if (vm.availableBlockTypes.length === 0) { + return; + } + + vm.blockTypePicker = { + show: true, + size: vm.availableBlockTypes.length > 6 ? "medium" : "small", + filter: vm.availableBlockTypes.length > 12 ? true : false, + orderBy: "$index", + view: "itempicker", + event: $event, + availableItems: vm.availableBlockTypes, + submit: function (model) { + if (model && model.selectedItem) { + addNewBlock(createIndex, model.selectedItem); + } + vm.blockTypePicker.close(); + }, + close: function () { + vm.blockTypePicker.show = false; + vm.blockTypePicker = null; + } + }; + + }; + + vm.requestCopyBlock = function(block) { + console.log("copy") + } + vm.requestDeleteBlock = function(block) { + localizationService.localizeMany(["content_nestedContentDeleteItem", "general_delete", "general_cancel", "contentTypeEditor_yesDelete"]).then(function (data) { + const overlay = { + title: data[1], + content: data[0], + closeButtonLabel: data[2], + submitButtonLabel: data[3], + submitButtonStyle: "danger", + close: function () { + overlayService.close(); + }, + submit: function () { + vm.deleteBlock(block); + overlayService.close(); + } + }; + + overlayService.open(overlay); + }); + } + + vm.showCopy = clipboardService.isSupported(); + + + + vm.sorting = false; + vm.sortableOptions = { + axis: "y", + cursor: "grabbing", + handle: '.umb-block-list__block', + cancel: 'input,textarea,select,option', + classes: '.blockelement--dragging', + distance: 5, + tolerance: "pointer", + scroll: true, + start: function (ev, ui) { + $scope.$apply(function () { + vm.sorting = true; + }); + }, + update: function (ev, ui) { + setDirty(); + }, + stop: function (ev, ui) { + $scope.$apply(function () { + vm.sorting = false; + }); + } + }; + + $scope.blockApi = { + showCreateOptionsFor: vm.showCreateOptionsFor, + removeBlock: vm.removeBlock + } + + + var copyAllEntriesAction = { + labelKey: 'clipboard_labelForCopyAllEntries', + labelTokens: [model.label], + icon: 'documents', + method: function () {}, + isDisabled: true + } + + var propertyActions = [ + copyAllEntriesAction + ]; + + this.$onInit = function () { + if (this.umbProperty) { + this.umbProperty.setPropertyActions(propertyActions); + } + }; + + + } + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less new file mode 100644 index 0000000000..a26dda4202 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.less @@ -0,0 +1,217 @@ +@umb-block-list__item_minimum_height: 48px; + +.umb-block-list { + padding-bottom:10px; +} + +.umb-block-list__wrapper { + position: relative; + max-width: 1024px; + > .ui-sortable > .ui-sortable-helper > .umb-block-list__block > .umb-block-list__block--content > * { + box-shadow: 0px 5px 10px 0 rgba(0,0,0,.2); + } +} + +.umb-block-list__block { + position: relative; + width: 100%; + cursor: grab; + + .umb-block-list__block--head { + opacity: 0; + transition: opacity 120ms; + } + .umb-block-list__block--actions { + opacity: 0; + transition: opacity 120ms; + } + + &:hover, &:focus, &:focus-within { + .umb-block-list__block--head { + opacity: 1; + } + + .umb-block-list__block--actions { + opacity: 1; + } + } + + &:focus, &:focus-within { + .umb-block-list__block--head { + &::before { + background-color: @blueMid; + } + } + } +} + +.umb-block-list__block--head { + position: absolute; + top: 0; + left: -180px;// 160px from control-header + 20px from spacing. + bottom: 0; + width: 180px;// 160px from control-header + 20px from spacing. + user-select: none; + padding-top: 6px; + padding-right: 14px; + box-sizing: border-box; + color: @gray-5; + background-color: rgba(255, 255, 255, .96); + box-shadow: 0 0 6px 6px rgba(255, 255, 255, .96); + text-align: right; + &::before { + content: ''; + position: absolute; + top: 6px; + bottom: 6px; + right: 4px; + width: 1px; + background-color: @gray-10; + } + + small { + text-align: left; + margin-left: 4px; + margin-bottom: 4px; + } +} +label.umb-block-list__block--head { + cursor: grab; +} + +.umb-block-list__block--actions { + position: absolute; + top: 10px; + right: 10px; + font-size: 0; + background-color: rgba(255, 255, 255, .96); + border-radius: 14px; + padding-left: 5px; + padding-right: 5px; + .action { + display: inline-block; + color: @ui-action-discreet-type; + font-size: 18px; + padding: 5px; + &:hover { + color: @ui-action-discreet-type-hover; + } + } +} + +.umb-block-list__block--content { + position: relative; + width: 100%; + min-height: @umb-block-list__item_minimum_height; + background-color: @white; + border-radius: @baseBorderRadius; +} + + +.umb-block-list__block--create-button { + position: absolute; + width: 100%; + z-index:1; + opacity: 0; + outline: none; + height: 20px; + margin-top: -10px; + padding-top: 10px; + margin-bottom: -10px; + transition: opacity 240ms; + + &::before { + content: ''; + position: absolute; + background-color: @ui-outline; + border-radius: 2px; + top:9px; + right: 0; + left: 0; + height: 2px; + animation: umb-block-list__block--create-button 800ms ease-in-out infinite; + @keyframes umb-block-list__block--create-button { + 0% { opacity: 0.5; } + 50% { opacity: 1; } + 100% { opacity: 0.5; } + } + } + &::after { + content: "+"; + margin-left: auto; + margin-right: auto; + margin-top: -16px; + width: 28px; + height: 25px; + padding-bottom: 3px; + border-radius: 3em; + border: 2px solid @ui-outline; + display: flex; + justify-content: center; + align-items: center; + color: @ui-outline; + font-size: 20px; + font-weight: 800; + background-color: rgba(255, 255, 255, .96); + box-shadow: 0 0 0 2px rgba(255, 255, 255, .96); + transform: scale(0); + transition: transform 240ms ease-in; + } + &:focus { + &::after { + border: 2px solid @ui-outline; + } + } + &:hover, &:focus { + opacity: 1; + transition-duration: 120ms; + &::after { + transform: scale(1); + transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275); + } + } +} +/* +.umb-block-list__block--create-bar { + button { + display: inline-block; + width: 120px; + height: 120px; + border-radius: @baseBorderRadius; + text-align: center; + font-size: 12px; + i { + font-size: 30px; + line-height: 20px; + margin-bottom: 10px; + display: block; + } + } +} +*/ +.umb-block-list__create-button { + display: flex; + width: 100%; + align-items: center; + justify-content: center; + border: 1px dashed @ui-action-discreet-border; + color: @ui-action-discreet-type; + font-weight: bold; + margin: 2px 0; + padding: 5px 15px; + box-sizing: border-box; + border-radius: @baseBorderRadius; +} + +.umb-block-list__create-button:hover { + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-border-hover; + text-decoration: none; +} + +.umb-block-list__create-button.--disabled, +.umb-block-list__create-button.--disabled:hover { + color: @gray-7; + border-color: @gray-7; + cursor: default; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html new file mode 100644 index 0000000000..198dab4f5f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.html @@ -0,0 +1 @@ + From f988a6a31b1a81459f0afb4c77e860cbd45a5bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 27 Jan 2020 07:37:29 +0100 Subject: [PATCH 10/10] more demo content --- .../blocklist/blocklist.component.js | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js index 6283bd1026..5fc17fcafd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklist.component.js @@ -84,8 +84,8 @@ }, { alias: "contentTypeAlias", - name: "contentTypeName", - icon: "icon-text", + name: "Text", + icon: "icon-info", prototype_paste_data: { elementType: { alias: 'contentTypeAlias', @@ -132,7 +132,7 @@ }, { alias: "contentTypeAlias", - name: "contentTypeName", + name: "Image", icon: "icon-picture", prototype_paste_data: { elementType: { @@ -178,6 +178,55 @@ temp_image: "/umbraco/assets/img/login.jpg" } } + }, + { + alias: "contentTypeAlias", + name: "Inline editing", + icon: "icon-picture", + prototype_paste_data: { + elementType: { + alias: 'contentTypeAlias', + icon: "icon-document", + label: "Text" + }, + label: "Label", + editor: "views/blockelements/imageblock/imageblock.editor.html", + content: { + variants: [ + { + language: { + isDefault: true + } + } + ], + tabs: [ + { + id: 1234, + label: "Group 1", + properties: [ + { + label: "Page Title", + description: "The title of the page", + view: "textbox", + config: {maxChars: 500}, + hideLabel: false, + validation: {mandatory: true, mandatoryMessage: "", pattern: null, patternMessage: ""}, + readonly: false, + id: 441, + dataTypeKey: "0cc0eba1-9960-42c9-bf9b-60e150b429ae", + value: "Let's have a chat", + alias: "pageTitle", + editor: "Umbraco.TextBox", + isSensitive: false, + culture: null, + segment: null + } + ] + } + ], + temp_image: "/umbraco/assets/img/demo.png" + } + } } ];