From c67b14b83708ae7a7a8a263adefbf15df20e36b3 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 4 Sep 2019 14:47:19 +0200 Subject: [PATCH 01/18] Fix mandatory validation clientside for content picker and MNTP (cherry picked from commit adeb8fc1f70abccf2aa2b361eaefd1e07da4183c) --- .../contentpicker/contentpicker.controller.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 6667c3b539..be99ffe8f9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -117,6 +117,12 @@ function contentPickerController($scope, entityResource, editorState, iconHelper } //merge the server config on top of the default config, then set the server config to use the result $scope.model.config = angular.extend(defaultConfig, $scope.model.config); + + // if the property is mandatory, set the minCount config to 1 (unless of course it is set to something already), + // that way the minCount/maxCount validation handles the mandatory as well + if ($scope.model.validation && $scope.model.validation.mandatory && !$scope.model.config.minNumber) { + $scope.model.config.minNumber = 1; + } } //Umbraco persists boolean for prevalues as "0" or "1" so we need to convert that! From 7b0b0f94957df059694c237998eb5d063d9bd29b Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Wed, 4 Sep 2019 10:06:14 +0100 Subject: [PATCH 02/18] Update umbracoUser2NodeNotify in SuperZero migration (cherry picked from commit 79bea8b4402dc9ecad20d6a01cc2dfadf8e9af04) --- src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/SuperZero.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/SuperZero.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/SuperZero.cs index 64ac20d175..9026f15fc1 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/SuperZero.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/SuperZero.cs @@ -30,6 +30,7 @@ Database.Execute("set identity_insert umbracoUser off;"); Database.Execute("update umbracoUser2UserGroup set userId=-1 where userId=0;"); + Database.Execute("update umbracoUser2NodeNotify set userId=-1 where userId=0;"); Database.Execute("update umbracoNode set nodeUser=-1 where nodeUser=0;"); Database.Execute("update umbracoUserLogin set userId=-1 where userId=0;"); Database.Execute($"update {Constants.DatabaseSchema.Tables.ContentVersion} set userId=-1 where userId=0;"); From 9de47d1f320110f15b4c2044174955a6b0fd10d4 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 5 Sep 2019 15:10:01 +0200 Subject: [PATCH 03/18] Merge branch 'v7/dev' into v8/dev # Conflicts: # src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.html (cherry picked from commit 1278644cbb891b70efa1e13ce119ac6bbbc13e75) --- .../src/views/common/infiniteeditors/embed/embed.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html index e48ec84b25..23559c6123 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html @@ -16,7 +16,7 @@ - + Date: Thu, 5 Sep 2019 15:27:33 +0200 Subject: [PATCH 04/18] v8: Highlight of old password in change password form (#6274) (cherry picked from commit f4f99d7a988ed4a2b2457804c16fd5571edaeb7a) --- .../src/views/common/overlays/user/user.html | 6 ++---- .../components/users/change-password.html | 21 +++++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html index 60477ae9b8..531feef892 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html @@ -26,8 +26,7 @@ action="togglePasswordFields()" button-style="action" label="Change password" - label-key="general_changePassword" - button-style="success"> + label-key="general_changePassword"> -
+ diff --git a/src/Umbraco.Web/Editors/MacrosController.cs b/src/Umbraco.Web/Editors/MacrosController.cs index d7d50236d5..429e8b6190 100644 --- a/src/Umbraco.Web/Editors/MacrosController.cs +++ b/src/Umbraco.Web/Editors/MacrosController.cs @@ -62,6 +62,11 @@ namespace Umbraco.Web.Editors return this.ReturnErrorResponse("Macro with this alias already exists"); } + if (name == null || name.Length > 255) + { + return this.ReturnErrorResponse("Name cannnot be more than 255 characters in length."); + } + try { var macro = new Macro @@ -149,6 +154,11 @@ namespace Umbraco.Web.Editors return this.ReturnErrorResponse($"No macro data found in request"); } + if (macroDisplay.Name == null || macroDisplay.Name.Length > 255) + { + return this.ReturnErrorResponse("Name cannnot be more than 255 characters in length."); + } + var macro = _macroService.GetById(int.Parse(macroDisplay.Id.ToString())); if (macro == null) From 2024681a2634d5e70e8fcaa1d63663b37bfde546 Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Thu, 29 Aug 2019 17:04:42 +0100 Subject: [PATCH 11/18] Friendly exception message when datatype with name more than 255 characters is saved (cherry picked from commit 79f8dc200a3d4f3262316de03783b8d286499fac) --- src/Umbraco.Core/Services/Implement/DataTypeService.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Core/Services/Implement/DataTypeService.cs b/src/Umbraco.Core/Services/Implement/DataTypeService.cs index 3552b2d8fc..dc998b18dd 100644 --- a/src/Umbraco.Core/Services/Implement/DataTypeService.cs +++ b/src/Umbraco.Core/Services/Implement/DataTypeService.cs @@ -349,6 +349,11 @@ namespace Umbraco.Core.Services.Implement throw new ArgumentException("Cannot save datatype with empty name."); } + if (dataType.Name != null && dataType.Name.Length > 255) + { + throw new InvalidOperationException("Name cannot be more than 255 characters in length."); + } + _dataTypeRepository.Save(dataType); saveEventArgs.CanCancel = false; From cafd07fd010090713d76763ef63a2f56e1c6d712 Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Thu, 29 Aug 2019 17:22:23 +0100 Subject: [PATCH 12/18] Better exception message on media type save (cherry picked from commit d2efcc822d7361fd915833cf7d09f6f4eb8c7a45) --- .../ContentTypeServiceBaseOfTRepositoryTItemTService.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index 1f1f0d9ac3..6ac8e1404a 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -390,6 +390,11 @@ namespace Umbraco.Core.Services.Implement if (string.IsNullOrWhiteSpace(item.Name)) throw new ArgumentException("Cannot save item with empty name."); + if (item.Name != null && item.Name.Length > 255) + { + throw new InvalidOperationException("Name cannot be more than 255 characters in length."); + } + scope.WriteLock(WriteLockIds); // validate the DAG transform, within the lock From 890281b271b4fda844dd078cc15ade48c30e79be Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Thu, 29 Aug 2019 17:41:55 +0100 Subject: [PATCH 13/18] better exception message on content type save (cherry picked from commit 7d3655c78c99c5ef6f235f0d092b6c08853e11ff) --- src/Umbraco.Core/Services/Implement/FileService.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Umbraco.Core/Services/Implement/FileService.cs b/src/Umbraco.Core/Services/Implement/FileService.cs index 79d5b35775..26a24e9b98 100644 --- a/src/Umbraco.Core/Services/Implement/FileService.cs +++ b/src/Umbraco.Core/Services/Implement/FileService.cs @@ -358,6 +358,11 @@ namespace Umbraco.Core.Services.Implement { "ContentTypeAlias", contentTypeAlias }, }; + if (contentTypeAlias != null && contentTypeAlias.Length > 255) + { + throw new InvalidOperationException("Name cannot be more than 255 characters in length."); + } + // check that the template hasn't been created on disk before creating the content type // if it exists, set the new template content to the existing file content string content = GetViewContent(contentTypeAlias); @@ -365,7 +370,10 @@ namespace Umbraco.Core.Services.Implement { template.Content = content; } + + + using (var scope = ScopeProvider.CreateScope()) { var saveEventArgs = new SaveEventArgs(template, true, evtMsgs, additionalData); From d044dd07c3ab92b3606d924ff7e6534866406c6d Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Thu, 29 Aug 2019 16:32:53 +0100 Subject: [PATCH 14/18] Show a friendly exception message when the media name exceeds 255 characters (cherry picked from commit e0d4168f3f70351226135904d8d84aea9f028ea4) --- .../Services/Implement/MediaService.cs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index ab075c4ade..e9fdedbf33 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -139,6 +139,10 @@ namespace Umbraco.Core.Services.Implement var parent = parentId > 0 ? GetById(parentId) : null; if (parentId > 0 && parent == null) throw new ArgumentException("No media with that id.", nameof(parentId)); + if (name != null && name.Length > 255) + { + throw new InvalidOperationException("Name cannot be more than 255 characters in length."); throw new InvalidOperationException("Name cannot be more than 255 characters in length."); + } var media = new Models.Media(name, parentId, mediaType); using (var scope = ScopeProvider.CreateScope()) @@ -168,6 +172,10 @@ namespace Umbraco.Core.Services.Implement var mediaType = GetMediaType(mediaTypeAlias); if (mediaType == null) throw new ArgumentException("No media type with that alias.", nameof(mediaTypeAlias)); + if (name != null && name.Length > 255) + { + throw new InvalidOperationException("Name cannot be more than 255 characters in length."); throw new InvalidOperationException("Name cannot be more than 255 characters in length."); + } var media = new Models.Media(name, -1, mediaType); using (var scope = ScopeProvider.CreateScope()) @@ -202,6 +210,10 @@ namespace Umbraco.Core.Services.Implement var mediaType = GetMediaType(mediaTypeAlias); if (mediaType == null) throw new ArgumentException("No media type with that alias.", nameof(mediaTypeAlias)); // causes rollback + if (name != null && name.Length > 255) + { + throw new InvalidOperationException("Name cannot be more than 255 characters in length."); throw new InvalidOperationException("Name cannot be more than 255 characters in length."); + } var media = new Models.Media(name, parent, mediaType); CreateMedia(scope, media, parent, userId, false); @@ -648,6 +660,11 @@ namespace Umbraco.Core.Services.Implement if (string.IsNullOrWhiteSpace(media.Name)) throw new ArgumentException("Media has no name.", nameof(media)); + if (media.Name != null && media.Name.Length > 255) + { + throw new InvalidOperationException("Name cannot be more than 255 characters in length."); throw new InvalidOperationException("Name cannot be more than 255 characters in length."); + } + scope.WriteLock(Constants.Locks.MediaTree); if (media.HasIdentity == false) media.CreatorId = userId; @@ -760,7 +777,7 @@ namespace Umbraco.Core.Services.Implement const int pageSize = 500; var page = 0; var total = long.MaxValue; - while(page * pageSize < total) + while (page * pageSize < total) { //get descendants - ordered from deepest to shallowest var descendants = GetPagedDescendants(media.Id, page, pageSize, out total, ordering: Ordering.By("Path", Direction.Descending)); @@ -945,7 +962,7 @@ namespace Umbraco.Core.Services.Implement // if media was trashed, and since we're not moving to the recycle bin, // indicate that the trashed status should be changed to false, else just // leave it unchanged - var trashed = media.Trashed ? false : (bool?) null; + var trashed = media.Trashed ? false : (bool?)null; PerformMoveLocked(media, parentId, parent, userId, moves, trashed); scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.RefreshBranch).ToEventArgs()); @@ -1009,7 +1026,7 @@ namespace Umbraco.Core.Services.Implement private void PerformMoveMediaLocked(IMedia media, int userId, bool? trash) { - if (trash.HasValue) ((ContentBase) media).Trashed = trash.Value; + if (trash.HasValue) ((ContentBase)media).Trashed = trash.Value; _mediaRepository.Save(media); } From c7ea93199f59f3ef64201f17fd347e5ba8434b6f Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 5 Sep 2019 16:45:33 +0200 Subject: [PATCH 15/18] Merge pull request #6222 from patrickdemooij9/temp-5724 5724: Unable to add multiple property groups (cherry picked from commit 3163b5f2fe5b81f716e3330d0662856ae88dba5c) # Conflicts: # src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js --- .../components/umbgroupsbuilder.directive.js | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js index c60d6aebdd..5d654da813 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js @@ -384,6 +384,8 @@ // activate group scope.activateGroup(group); + // push new init tab to the scope + addInitGroup(scope.model.groups); }; scope.activateGroup = function(selectedGroup) { @@ -402,7 +404,6 @@ scope.removeGroup = function(groupIndex) { scope.model.groups.splice(groupIndex, 1); - addInitGroup(scope.model.groups); }; scope.updateGroupTitle = function(group) { @@ -531,10 +532,8 @@ // set focus on init property var numberOfProperties = group.properties.length; group.properties[numberOfProperties - 1].focus = true; - - // push new init tab to the scope - addInitGroup(scope.model.groups); + notifyChanged(); }, close: function() { if(_.isEqual(oldPropertyModel, propertyModel) === false) { @@ -580,17 +579,7 @@ // remove property tab.properties.splice(propertyIndex, 1); - // if the last property in group is an placeholder - remove add new tab placeholder - if(tab.properties.length === 1 && tab.properties[0].propertyState === "init") { - - angular.forEach(scope.model.groups, function(group, index, groups){ - if(group.tabState === 'init') { - groups.splice(index, 1); - } - }); - - } - + notifyChanged(); }; function addInitProperty(group) { From 32941370065fcbea6857ad9c68a16a93c839812a Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 5 Sep 2019 18:01:07 +0200 Subject: [PATCH 16/18] V8: Make the color picker mandatory validation work (#5765) (cherry picked from commit 8aca254ed06a2c5372770a3940285f00bc3513b5) --- .../colorpicker/colorpicker.controller.js | 70 +++---------------- .../colorpicker/colorpicker.html | 5 +- 2 files changed, 11 insertions(+), 64 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js index d6fe709962..f37905d2c8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js @@ -1,4 +1,4 @@ -function ColorPickerController($scope) { +function ColorPickerController($scope, $timeout) { //setup the default config var config = { @@ -11,32 +11,7 @@ function ColorPickerController($scope) { //map back to the model $scope.model.config = config; - - // TODO: This isn't used - function convertArrayToDictionaryArray(model) { - //now we need to format the items in the dictionary because we always want to have an array - var newItems = []; - for (var i = 0; i < model.length; i++) { - newItems.push({ id: model[i], sortOrder: 0, value: model[i] }); - } - - return newItems; - } - - // TODO: This isn't used - function convertObjectToDictionaryArray(model) { - //now we need to format the items in the dictionary because we always want to have an array - var newItems = []; - var vals = _.values($scope.model.config.items); - var keys = _.keys($scope.model.config.items); - - for (var i = 0; i < vals.length; i++) { - var label = vals[i].value ? vals[i].value : vals[i]; - newItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: label }); - } - - return newItems; - } + $scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0; if ($scope.isConfigured) { @@ -79,27 +54,13 @@ function ColorPickerController($scope) { $scope.model.config.items = items; } - $scope.toggleItem = function (color) { - - var currentColor = ($scope.model.value && $scope.model.value.hasOwnProperty("value")) - ? $scope.model.value.value - : $scope.model.value; - - var newColor; - if (currentColor === color.value) { - // deselect - $scope.model.value = $scope.model.useLabel ? { value: "", label: "" } : ""; - newColor = ""; - } - else { - // select - $scope.model.value = $scope.model.useLabel ? { value: color.value, label: color.label } : color.value; - newColor = color.value; - } - + $scope.selectColor = function (color) { // this is required to re-validate - $scope.propertyForm.modelValue.$setViewValue(newColor); - }; + $timeout(function () { + var newColor = color ? color.value : null; + $scope.propertyForm.selectedColor.$setViewValue(newColor); + }); + } // Method required by the valPropertyValidator directive (returns true if the property editor has at least one color selected) $scope.validateMandatory = function () { @@ -116,21 +77,6 @@ function ColorPickerController($scope) { } $scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0; - // A color is active if it matches the value and label of the model. - // If the model doesn't store the label, ignore the label during the comparison. - $scope.isActiveColor = function (color) { - - // no value - if (!$scope.model.value) - return false; - - // Complex color (value and label)? - if (!$scope.model.value.hasOwnProperty("value")) - return $scope.model.value === color.value; - - return $scope.model.value.value === color.value && $scope.model.value.label === color.label; - }; - // Finds the color best matching the model's color, // and sets the model color to that one. This is useful when // either the value or label was changed on the data type. diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html index ef45e99638..9b0bdad661 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html @@ -9,9 +9,10 @@ colors="model.config.items" selected-color="model.value" size="m" - use-label="model.useLabel"> + use-label="model.useLabel" + on-select="selectColor(color)"> - + From 089e450e09bd5d9e40ead8af2edc88658671e534 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 5 Sep 2019 18:08:00 +0200 Subject: [PATCH 17/18] Make sure media picker items are sortable (#6037) (cherry picked from commit 6da0caa9027b5f8f863f38f0ee319792ee27578a) --- .../mediapicker/mediapicker.controller.js | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index f6447f8144..d7bf31bbe9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -149,14 +149,6 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl $scope.allowAddMedia = hasAccessToMedia; setupViewModel(); - - //When the model value changes sync the view model - $scope.$watch("model.value", - function (newVal, oldVal) { - if (newVal !== oldVal) { - setupViewModel(); - } - }); }); }); } @@ -241,25 +233,25 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl }; - - $scope.sortableOptions = { + containment: 'parent', + cursor: 'move', + tolerance: 'pointer', disabled: !multiPicker, items: "li:not(.add-wrapper)", cancel: ".unsortable", update: function (e, ui) { setDirty(); - var r = []; - // TODO: Instead of doing this with a half second delay would be better to use a watch like we do in the - // content picker. Then we don't have to worry about setting ids, render models, models, we just set one and let the - // watch do all the rest. - $timeout(function () { - angular.forEach($scope.mediaItems, function (value, key) { - r.push($scope.model.config.idType === "udi" ? value.udi : value.id); - }); - $scope.ids = r; + $timeout(function() { + // TODO: Instead of doing this with a timeout would be better to use a watch like we do in the + // content picker. Then we don't have to worry about setting ids, render models, models, we just set one and let the + // watch do all the rest. + $scope.ids = _.map($scope.mediaItems, + function (item) { + return $scope.model.config.idType === "udi" ? item.udi : item.id; + }); sync(); - }, 500, false); + }); } }; From 1723c39e009322b737c6195a9462a3484369a4b8 Mon Sep 17 00:00:00 2001 From: Nathan Woulfe Date: Fri, 6 Sep 2019 15:45:17 +1000 Subject: [PATCH 18/18] fix tray colour, add some structure to CSS (cherry picked from commit daadfe6e6cef7ce812445e15bd68465944494ba9) --- .../src/less/sections.less | 222 +++++++++--------- 1 file changed, 114 insertions(+), 108 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/sections.less b/src/Umbraco.Web.UI.Client/src/less/sections.less index ef6c5f5046..5551ba6376 100644 --- a/src/Umbraco.Web.UI.Client/src/less/sections.less +++ b/src/Umbraco.Web.UI.Client/src/less/sections.less @@ -2,129 +2,135 @@ // ------------------------- ul.sections { - margin: 0; - display: flex; - margin-left: -20px; -} - -ul.sections>li { - display: flex; - justify-content: center; - align-items: center; - position: relative; -} - -ul.sections>li>a { - color: @white; - height: @appHeaderHeight; + margin: 0; display: flex; - align-items: center; - justify-content: center; - position: relative; - padding: 0 10px; - text-decoration: none; - outline: none; - cursor: pointer; -} + margin-left: -20px; -ul.sections>li>a .section__name { - border-radius: 3px; - margin-top:1px; - padding: 3px 10px 4px 10px; - opacity: 0.8; - transition: opacity .1s linear, box-shadow .1s; -} + > li { + display: flex; + justify-content: center; + align-items: center; + position: relative; -ul.sections>li>a::after { - content: ""; - left: 10px; - right: 10px; - height: 4px; - bottom: 0; - transform: translateY(4px); - background-color: @pinkLight; - position: absolute; - border-radius: 3px 3px 0 0; - opacity: 0; - padding: 0 2px; - transition: transform 240ms ease-in-out; -} + > a { + color: @white; + height: @appHeaderHeight; + display: flex; + align-items: center; + justify-content: center; + position: relative; + padding: 0 10px; + text-decoration: none; + outline: none; + cursor: pointer; -ul.sections>li.current>a { - color:@pinkLight; -} -ul.sections>li.current>a::after { - opacity: 1; - transform: translateY(0px); -} -ul.sections > li.current > a .section__name, -ul.sections > li > a:hover .section__name { - opacity: 1; - -webkit-font-smoothing: subpixel-antialiased; -} + &::after { + content: ""; + left: 10px; + right: 10px; + height: 4px; + bottom: 0; + transform: translateY(4px); + background-color: @ui-active; + position: absolute; + border-radius: 3px 3px 0 0; + opacity: 0; + padding: 0 2px; + transition: transform 240ms ease-in-out; + } -ul.sections > li > a:focus .section__name { - .tabbing-active & { - - border: 1px solid; - border-color: @gray-9; + &:focus .section__name { + .tabbing-active & { + border: 1px solid; + border-color: @gray-9; + } + } + } + + .section__name { + border-radius: 3px; + margin-top: 1px; + padding: 3px 10px 4px 10px; + opacity: 0.8; + transition: opacity .1s linear, box-shadow .1s; + } + + &.current a { + color: @ui-active; + + &::after { + opacity: 1; + transform: translateY(0px); + } + } + + &.expand { + i { + height: 5px; + width: 5px; + border-radius: 50%; + background: @white; + display: inline-block; + margin: 0 5px 0 0; + opacity: 0.6; + transition: opacity .1s linear; + } + + &:hover i { + opacity:1; + } + } + + &.current .section__name, + a:hover .section__name { + opacity: 1; + -webkit-font-smoothing: subpixel-antialiased; + } } } - - /* Sections tray */ -ul.sections>li.expand i { - height: 5px; - width: 5px; - border-radius: 50%; - background: #fff; - display: inline-block; - margin: 0 5px 0 0; - opacity: 0.6; -} - ul.sections-tray { - position: absolute; - top: @appHeaderHeight; - left: 0; - margin: 0; + position: absolute; + top: @appHeaderHeight; + left: 0; + margin: 0; list-style: none; - background: @purple; - z-index: 10000; - border-radius: 0 0 3px 3px; -} + background: @blueExtraDark; + z-index: 10000; + border-radius: 0 0 3px 3px; -ul.sections-tray>li>a { - padding: 8px 24px; - color: @white; - text-decoration: none; - display: block; - position: relative; -} + li { -ul.sections-tray>li>a::after { - content: ""; - width: 4px; - height: 100%; - background-color: @ui-active; - position: absolute; - border-radius: 0 3px 3px 0; - opacity: 0; - transition: all .2s linear; - top: 0; - left: 0; -} + &.current a { + color: @ui-active; + opacity: 1; -ul.sections-tray>li.current>a::after { - opacity: 1; -} + &::after { + opacity: 1; + } + } -ul.sections-tray>li>a .section__name { - opacity: 0.6; -} + a { + padding: 8px 24px; + color: @white; + text-decoration: none; + display: block; + position: relative; -ul.sections-tray>li>a:hover .section__name { - opacity: 1; + &::after { + content: ""; + width: 4px; + height: 100%; + background-color: @ui-active; + position: absolute; + border-radius: 0 3px 3px 0; + opacity: 0; + transition: all .2s linear; + top: 0; + left: 0; + } + } + } }