diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js index edb08f1066..c852228205 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js @@ -206,6 +206,10 @@ removeEditor(args.editor); })); + evts.push(eventsService.on("appState.editors.closeAll", function (name, args) { + scope.editors = []; + })); + //ensure to unregister from all events! scope.$on('$destroy', function () { for (var e in evts) { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js index 726103caf9..948702a4e3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js @@ -12,7 +12,7 @@ * Another thing this directive does is to ensure that any .control-group that contains form elements that are invalid will * be marked with the 'error' css class. This ensures that labels included in that control group are styled correctly. **/ -function valFormManager(serverValidationManager, $rootScope, $log, $timeout, notificationsService, eventsService, $routeParams, navigationService) { +function valFormManager(serverValidationManager, $rootScope, $timeout, $location, overlayService, eventsService, $routeParams, navigationService, editorService, localizationService) { var SHOW_VALIDATION_CLASS_NAME = "show-validation"; var SAVING_EVENT_NAME = "formSubmitting"; @@ -44,6 +44,22 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not }, link: function (scope, element, attr, formCtrl) { + var labels = {}; + + var labelKeys = [ + "prompt_unsavedChanges", + "prompt_unsavedChangesWarning", + "prompt_discardChanges", + "prompt_stay" + ]; + + localizationService.localizeMany(labelKeys).then(function (values) { + labels.unsavedChangesTitle = values[0]; + labels.unsavedChangesContent = values[1]; + labels.discardChangesButton = values[2]; + labels.stayButton = values[3]; + }); + //watch the list of validation errors to notify the application of any validation changes scope.$watch(function () { //the validators are in the $error collection: https://docs.angularjs.org/api/ng/type/form.FormController#$error @@ -104,28 +120,62 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not formCtrl.$setPristine(); })); + var confirmed = false; + //This handles the 'unsaved changes' dialog which is triggered when a route is attempting to be changed but // the form has pending changes var locationEvent = $rootScope.$on('$locationChangeStart', function(event, nextLocation, currentLocation) { - if (!formCtrl.$dirty || isSavingNewItem) { + + var infiniteEditors = editorService.getEditors(); + + if (!formCtrl.$dirty && infiniteEditors.length === 0 || isSavingNewItem && infiniteEditors.length === 0) { + confirmed = true; return; } var nextPath = nextLocation.split("#")[1]; - if (nextPath) { + if (nextPath && !confirmed) { if (navigationService.isRouteChangingNavigation(currentLocation, nextLocation)) { - if (!notificationsService.hasView()) { - if (nextPath.indexOf("%253") || nextPath.indexOf("%252")) { - nextPath = decodeURIComponent(nextPath); - } - - var msg = { view: "confirmroutechange", args: { path: nextPath, listener: locationEvent } }; - notificationsService.add(msg); + if (nextPath.indexOf("%253") || nextPath.indexOf("%252")) { + nextPath = decodeURIComponent(nextPath); } + // Open discard changes overlay + var overlay = { + "view": "default", + "title": labels.unsavedChangesTitle, + "content": labels.unsavedChangesContent, + "disableBackdropClick": true, + "submitButtonLabel": labels.stayButton, + "closeButtonLabel": labels.discardChangesButton, + submit: function() { + overlayService.close(); + }, + close: function() { + // close all editors + editorService.closeAll(); + // allow redirection + navigationService.clearSearch(); + //we need to break the path up into path and query + var parts = nextPath.split("?"); + var query = {}; + if (parts.length > 1) { + _.each(parts[1].split("&"), function(q) { + var keyVal = q.split("="); + query[keyVal[0]] = keyVal[1]; + }); + } + $location.path(parts[0]).search(query); + overlayService.close(); + confirmed = true; + } + }; + + overlayService.open(overlay); + //prevent the route! event.preventDefault(); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js index 453785f537..eab167c2ec 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js @@ -53,7 +53,7 @@ * @methodOf umbraco.services.editorService * * @description - * Opens a media editor in infinite editing, the submit callback returns the updated content item + * Method to close the latest opened editor */ function close() { var length = editors.length; @@ -69,6 +69,26 @@ eventsService.emit("appState.editors.close", args); } + /** + * @ngdoc method + * @name umbraco.services.editorService#closeAll + * @methodOf umbraco.services.editorService + * + * @description + * Method to close all open editors + */ + function closeAll() { + + editors = []; + + var args = { + editors: editors, + editor: null + }; + + eventsService.emit("appState.editors.closeAll", args); + } + /** * @ngdoc method * @name umbraco.services.editorService#contentEditor @@ -454,6 +474,7 @@ getEditors: getEditors, open: open, close: close, + closeAll: closeAll, mediaEditor: mediaEditor, contentEditor: contentEditor, contentPicker: contentPicker, diff --git a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js index a202c6ca6b..16d51add92 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js @@ -10,17 +10,37 @@ function overlayService(eventsService, backdropService) { - function open(overlay) { + var currentOverlay = null; + + function open(newOverlay) { + + // prevent two open overlays at the same time + if(currentOverlay) { + return; + } + + var backdropOptions = {}; + var overlay = newOverlay; + + // set the default overlay position to center if(!overlay.position) { overlay.position = "center"; } + + // option to disable backdrop clicks + if(overlay.disableBackdropClick) { + backdropOptions.disableEventsOnClick = true; + } + overlay.show = true; - backdropService.open(); + backdropService.open(backdropOptions); + currentOverlay = overlay; eventsService.emit("appState.overlay", overlay); } function close() { backdropService.close(); + currentOverlay = null; eventsService.emit("appState.overlay", null); } diff --git a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js index a47f552c9d..24318cce02 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js @@ -8,7 +8,7 @@ * The main application controller * */ -function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale, localStorageService, tourService, editorService) { +function MainController($scope, $location, appState, treeService, notificationsService, userService, historyService, updateChecker, assetsService, eventsService, tmhDynamicLocale, localStorageService, editorService, overlayService) { //the null is important because we do an explicit bool check on this in the view $scope.authenticated = null; @@ -87,6 +87,8 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $ $location.path("/").search(""); historyService.removeAll(); treeService.clearCache(); + editorService.closeAll(); + overlayService.close(); //if the user changed, clearout local storage too - could contain sensitive data localStorageService.clearAll(); diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less index 72c1ea5209..3a39ec9e1c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less @@ -66,7 +66,7 @@ background: @white; position: absolute; padding: 0 20px; - z-index: @zindexFixedNavbar; + z-index: @zIndexEditor; border-bottom: 1px solid @gray-9; width: 100%; box-sizing: border-box; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less index 79d941bbac..640a276443 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less @@ -13,7 +13,7 @@ bottom: 0; left: 0; background: @gray-10; - z-index: 100; + z-index: @zIndexEditor; } .umb-editor--animating { @@ -43,5 +43,5 @@ right: 0; left: 0; background: rgba(0,0,0,0.2); - z-index: 100; + z-index: @zIndexEditor; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less index ee865f1321..3fbe32134b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less @@ -4,7 +4,7 @@ background: @white; z-index: @zindexUmbOverlay; animation: fadeIn 0.2s; - box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); + box-shadow: 0 10px 50px rgba(0,0,0,0.1), 0 6px 20px rgba(0,0,0,0.16); } .umb-overlay__form { diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index c7d2e8d6ed..c6e87a74fe 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -319,6 +319,7 @@ // ------------------------- // Used for a bird's eye view of components dependent on the z-axis // Try to avoid customizing these :) +@zIndexEditor: 100; @zIndexTree: 100; @zindexDropdown: 1000; @zindexPopover: 1010; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/default/default.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/default/default.html new file mode 100644 index 0000000000..04201dde70 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/default/default.html @@ -0,0 +1 @@ +
{{model.content}}