From 2d1bda54be09472ac422bcb288dd6d7b45c4e91a Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 10 Aug 2018 13:53:19 +1000 Subject: [PATCH] Fixes: Deal with invariant property value syncing, componentizes umb-image-gravity, gets the image cropper syncing data correctly, ensures resizing for image cropper is done when variants change. --- .../components/content/edit.controller.js | 11 +- .../content/umbvariantcontent.directive.js | 7 +- .../imaging/umbimagecrop.directive.js | 2 +- .../imaging/umbimagegravity.directive.js | 296 +++++++++++------- .../upload/umbpropertyfileupload.directive.js | 2 +- .../src/common/resources/content.resource.js | 64 ++-- .../services/umbrequesthelper.service.js | 2 + .../src/less/property-editors.less | 4 +- .../mediapicker/mediapicker.html | 2 +- .../src/views/components/content/edit.html | 3 +- .../components/imaging/umb-image-gravity.html | 7 +- .../imagecropper/imagecropper.controller.js | 17 + .../imagecropper/imagecropper.html | 4 +- src/Umbraco.Web/UI/JavaScript/JsInitialize.js | 6 +- 14 files changed, 268 insertions(+), 159 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 5de2b1a0d0..6b80ca8a2c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -32,6 +32,9 @@ "rightIsOpen": false }; + $scope.initVariant = initVariant; + $scope.splitViewChanged = splitViewChanged; + function init(content) { if (infiniteMode) { @@ -61,6 +64,12 @@ setActiveCulture(); } + /** This is called when the split view changes based on the umb-variant-content */ + function splitViewChanged() { + //send an event downwards + $scope.$broadcast("editors.content.splitViewChanged", { editors: $scope.editors }); + } + /** * The content item(s) are loaded into an array and this will set the active content item based on the current culture (query string). * If the content item is invariant, then only one item exists in the array. @@ -556,8 +565,6 @@ }); }; - $scope.initVariant = initVariant; - /* publish method used in infinite editing */ $scope.publishAndClose = function (content) { $scope.publishAndCloseButtonState = "busy"; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js index 299c56cc7f..cdc23ee985 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js @@ -53,6 +53,7 @@ $timeout(function () { editor.collapsed = false; editor.loading = false; + scope.onSplitViewChanged(); }, 100); }; @@ -97,6 +98,7 @@ return e === scope.editor; }); scope.editors.splice(index, 1); + scope.onSplitViewChanged(); }, 400); }; @@ -110,13 +112,16 @@ }, scope: { + //TODO: This should be turned into a proper component + page: "=", content: "=", editor: "=", editors: "=", //TODO: I don't like having this callback defined and would like to make this directive a bit less // coupled but right now don't have time - initVariant: "&" + initVariant: "&", + onSplitViewChanged: "&" } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js index a287ec0476..cdcdb7313c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js @@ -264,7 +264,7 @@ angular.module("umbraco.directives") }); //ie hack - if(window.navigator.userAgent.indexOf("MSIE ")){ + if(window.navigator.userAgent.indexOf("MSIE ") >= 0){ var ranger = element.find("input"); ranger.bind("change",function(){ scope.$apply(function(){ diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js index 73f3aba239..2048194f04 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js @@ -1,139 +1,197 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbImageGravity -* @restrict E -* @function -* @description -**/ -angular.module("umbraco.directives") - .directive('umbImageGravity', function ($timeout, localizationService, $log) { - return { - restrict: 'E', - replace: true, - templateUrl: 'views/components/imaging/umb-image-gravity.html', - scope: { - src: '=', - center: "=", - onImageLoaded: "&?" - }, - link: function(scope, element, attrs) { +(function() { + 'use strict'; - //Internal values for keeping track of the dot and the size of the editor - scope.dimensions = { - width: 0, - height: 0, - left: 0, - top: 0 - }; + function umbImageGravityController($scope, $element, $timeout) { - scope.loaded = false; + var vm = this; - //elements - var $viewport = element.find(".viewport"); - var $image = element.find("img"); - var $overlay = element.find(".overlay"); + //Internal values for keeping track of the dot and the size of the editor + vm.dimensions = { + width: 0, + height: 0, + left: 0, + top: 0 + }; - scope.style = function () { - if(scope.dimensions.width <= 0){ - setDimensions(); - } + var htmlImage = null; //DOM element reference + var htmlOverlay = null; //DOM element reference + var draggable = null; - return { - 'top': scope.dimensions.top + 'px', - 'left': scope.dimensions.left + 'px' - }; - }; + vm.loaded = false; + vm.$onInit = onInit; + vm.$onChanges = onChanges; + vm.$postLink = postLink; + vm.$onDestroy = onDestroy; + vm.style = style; + vm.setFocalPoint = setFocalPoint; - scope.setFocalPoint = function(event) { + /** Sets the css style for the Dot */ + function style() { - scope.$emit("imageFocalPointStart"); + if (vm.dimensions.width <= 0) { + //this initializes the dimensions since when the image element first loads + //there will be zero dimensions + setDimensions(); + } - var offsetX = event.offsetX - 10; - var offsetY = event.offsetY - 10; + return { + 'top': vm.dimensions.top + 'px', + 'left': vm.dimensions.left + 'px' + }; + }; - calculateGravity(offsetX, offsetY); + function setFocalPoint (event) { - lazyEndEvent(); + $scope.$emit("imageFocalPointStart"); - }; + var offsetX = event.offsetX - 10; + var offsetY = event.offsetY - 10; - var setDimensions = function(){ - scope.dimensions.width = $image.width(); - scope.dimensions.height = $image.height(); + calculateGravity(offsetX, offsetY); - if(scope.center){ - scope.dimensions.left = scope.center.left * scope.dimensions.width -10; - scope.dimensions.top = scope.center.top * scope.dimensions.height -10; - }else{ - scope.center = { left: 0.5, top: 0.5 }; - } - }; + lazyEndEvent(); - var calculateGravity = function(offsetX, offsetY){ - scope.dimensions.left = offsetX; - scope.dimensions.top = offsetY; + }; - scope.center.left = (scope.dimensions.left+10) / scope.dimensions.width; - scope.center.top = (scope.dimensions.top+10) / scope.dimensions.height; - }; + /** Initializes the component */ + function onInit() { + if (!vm.center) { + vm.center = { left: 0.5, top: 0.5 }; + } + } - var lazyEndEvent = _.debounce(function(){ - scope.$apply(function(){ - scope.$emit("imageFocalPointStop"); - }); - }, 2000); + /** Called when the component has linked everything and the DOM is available */ + function postLink() { + //elements + htmlImage = $element.find("img"); + htmlOverlay = $element.find(".overlay"); - - //Drag and drop positioning, using jquery ui draggable - //TODO ensure that the point doesnt go outside the box - $overlay.draggable({ - containment: "parent", - start: function(){ - scope.$apply(function(){ - scope.$emit("imageFocalPointStart"); - }); - }, - stop: function() { - scope.$apply(function(){ - var offsetX = $overlay[0].offsetLeft; - var offsetY = $overlay[0].offsetTop; - calculateGravity(offsetX, offsetY); - }); - - lazyEndEvent(); - } - }); - - //// INIT ///// - $image.load(function() { - $timeout(function() { - setDimensions(); - scope.loaded = true; - if (scope.onImageLoaded) { - scope.onImageLoaded(); - } - }); - }); - - $(window).on('resize.umbImageGravity', function(){ - scope.$apply(function(){ - $timeout(function(){ - setDimensions(); - }); - // Make sure we can find the offset values for the overlay(dot) before calculating - // fixes issue with resize event when printing the page (ex. hitting ctrl+p inside the rte) - if($overlay.is(':visible')) { - var offsetX = $overlay[0].offsetLeft; - var offsetY = $overlay[0].offsetTop; - calculateGravity(offsetX, offsetY); - } - }); + //Drag and drop positioning, using jquery ui draggable + draggable = htmlOverlay.draggable({ + containment: "parent", + start: function () { + $scope.$apply(function () { + $scope.$emit("imageFocalPointStart"); + }); + }, + stop: function () { + $scope.$apply(function () { + var offsetX = htmlOverlay[0].offsetLeft; + var offsetY = htmlOverlay[0].offsetTop; + calculateGravity(offsetX, offsetY); }); - scope.$on('$destroy', function() { - $(window).off('.umbImageGravity'); - }); + lazyEndEvent(); + } + }); - } - }; - }); + $(window).on('resize.umbImageGravity', function () { + $scope.$apply(function () { + resized(); + }); + }); + + //if any ancestor directive emits this event, we need to resize + $scope.$on("editors.content.splitViewChanged", function () { + $timeout(resized, 200); + }); + + //listen for the image DOM element loading + htmlImage.on("load", function () { + $timeout(function () { + setDimensions(); + vm.loaded = true; + if (vm.onImageLoaded) { + vm.onImageLoaded(); + } + }, 100); + }); + } + + function onDestroy() { + $(window).off('resize.umbImageGravity'); + if (htmlOverlay) { + //TODO: This should be destroyed but this will throw an exception: + // "cannot call methods on draggable prior to initialization; attempted to call method 'destroy'" + // I've tried lots of things and cannot get this to work, we weren't destroying before so hopefully + // there's no mem leaks? + //htmlOverlay.draggable("destroy"); + } + if (htmlImage) { + htmlImage.off("load"); + } + } + + /** Called when we need to resize based on window or DOM dimensions to re-center the focal point */ + function resized() { + $timeout(function () { + setDimensions(); + }); + // Make sure we can find the offset values for the overlay(dot) before calculating + // fixes issue with resize event when printing the page (ex. hitting ctrl+p inside the rte) + if (htmlOverlay.is(':visible')) { + var offsetX = htmlOverlay[0].offsetLeft; + var offsetY = htmlOverlay[0].offsetTop; + calculateGravity(offsetX, offsetY); + } + } + + /** Watches the one way binding changes */ + function onChanges(changes) { + if (changes.center && !changes.center.isFirstChange() && !angular.equals(changes.center.currentValue, changes.center.previousValue)) { + //when center changes update the dimensions + setDimensions(); + } + } + + /** Sets the width/height/left/top dimentions based on the image size and the "center" value */ + function setDimensions() { + if (htmlImage) { + vm.dimensions.width = htmlImage.width(); + vm.dimensions.height = htmlImage.height(); + vm.dimensions.left = vm.center.left * vm.dimensions.width - 10; + vm.dimensions.top = vm.center.top * vm.dimensions.height - 10; + } + return vm.dimensions.width; + }; + + /** + * based on the offset selected calculates the "center" value and calls the callback + * @param {any} offsetX + * @param {any} offsetY + */ + function calculateGravity(offsetX, offsetY) { + + vm.onValueChanged({ + left: (offsetX + 10) / vm.dimensions.width, + top: (offsetY + 10) / vm.dimensions.height + }); + + //vm.center.left = (offsetX + 10) / scope.dimensions.width; + //vm.center.top = (offsetY + 10) / scope.dimensions.height; + }; + + var lazyEndEvent = _.debounce(function () { + $scope.$apply(function () { + $scope.$emit("imageFocalPointStop"); + }); + }, 2000); + + } + + var umbImageGravityComponent = { + templateUrl: 'views/components/imaging/umb-image-gravity.html', + bindings: { + src: "<", + center: "<", + onImageLoaded: "&?", + onValueChanged: "&" + }, + controllerAs: 'vm', + controller: umbImageGravityController + }; + + angular.module("umbraco.directives") + .component('umbImageGravity', umbImageGravityComponent); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js index b8afc4b35e..4e250d8dee 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js @@ -276,7 +276,7 @@ controller: umbPropertyFileUploadController }; - angular.module("umbraco") + angular.module("umbraco.directives") .component('umbPropertyFileUpload', umbPropertyFileUploadComponent); })(); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 33840e87d9..5bd44d6891 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -345,12 +345,15 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { getBlueprintById: function (id) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "contentApiBaseUrl", - "GetBlueprintById", - [{ id: id }])), - 'Failed to retrieve data for content id ' + id); + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetBlueprintById", + [{ id: id }])), + 'Failed to retrieve data for content id ' + id) + .then(function(result) { + return $q.when(umbDataFormatter.formatContentGetData(result)); + }); }, getNotifySettingsById: function (id) { @@ -405,12 +408,19 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }); return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "contentApiBaseUrl", - "GetByIds", - idQuery)), - 'Failed to retrieve data for content with multiple ids'); + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetByIds", + idQuery)), + 'Failed to retrieve data for content with multiple ids') + .then(function (result) { + //each item needs to be re-formatted + _.each(result, function(r) { + umbDataFormatter.formatContentGetData(r) + }); + return $q.when(result); + }); }, @@ -449,23 +459,29 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { getScaffold: function (parentId, alias) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "contentApiBaseUrl", - "GetEmpty", - [{ contentTypeAlias: alias }, { parentId: parentId }])), - 'Failed to retrieve data for empty content item type ' + alias); + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetEmpty", + [{ contentTypeAlias: alias }, { parentId: parentId }])), + 'Failed to retrieve data for empty content item type ' + alias) + .then(function(result) { + return $q.when(umbDataFormatter.formatContentGetData(result)); + }); }, getBlueprintScaffold: function (parentId, blueprintId) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "contentApiBaseUrl", - "GetEmpty", - [{ blueprintId: blueprintId }, { parentId: parentId }])), - 'Failed to retrieve blueprint for id ' + blueprintId); + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetEmpty", + [{ blueprintId: blueprintId }, { parentId: parentId }])), + 'Failed to retrieve blueprint for id ' + blueprintId) + .then(function(result) { + return $q.when(umbDataFormatter.formatContentGetData(result)); + }); }, /** diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js index f63dc609c3..24fb11d656 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js @@ -248,6 +248,8 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ formHelper.showNotifications(response.data); + //TODO: We need to pass the result through umbDataFormatter.formatContentGetData! + //the data returned is the up-to-date data so the UI will refresh return $q.resolve(response.data); }, function (response) { diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 9b21a1667c..9ff3ff9733 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -570,7 +570,7 @@ ul.color-picker li { .imagecropper .umb-cropper__container { position: relative; margin-bottom: 10px; - max-width: 100%; + max-width: 100%; border: 1px solid @gray-10; @media (min-width: 769px) { @@ -583,7 +583,7 @@ ul.color-picker li { top: 3px; right: 3px; cursor: pointer; - z-index: 1; + z-index: @zindexCropperOverlay + 1; } .umb-close-cropper:hover { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html index f17a08dd47..d0982fb95f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html @@ -177,4 +177,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html index 14bbc8b576..41ddd04584 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html @@ -13,7 +13,8 @@ content="content" editor="editor" editors="editors" - init-variant="initVariant(variant)"> + init-variant="initVariant(variant)" + on-split-view-changed="splitViewChanged()"> diff --git a/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html b/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html index 21500e7c84..8ea809ef8b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html @@ -1,9 +1,10 @@
-
+ +
- + -
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js index 1c9f835142..8816893018 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js @@ -9,9 +9,26 @@ angular.module('umbraco') $scope.crop = crop; $scope.done = done; $scope.clear = clear; + $scope.focalPointChanged = focalPointChanged; //declare a special method which will be called whenever the value has changed from the server $scope.model.onValueChanged = onValueChanged; + /** + * Called when the umgImageGravity component updates the focal point value + * @param {any} left + * @param {any} top + */ + function focalPointChanged(left, top) { + //update the model focalpoint value + $scope.model.value.focalPoint = { + left: left, + top: top + }; + + //set form to dirty to track changes + $scope.imageCropperForm.$setDirty(); + } + /** * Used to assign a new model value * @param {any} src diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html index bc41218592..dd74dd9e9f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html @@ -35,8 +35,10 @@
+
{{model.value.focalPoint | json}}
+ center="model.value.focalPoint" + on-value-changed="focalPointChanged(left, top)"> Remove file
diff --git a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js index 5dfa53b9d8..c3f4937927 100644 --- a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js +++ b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js @@ -1,5 +1,8 @@ [ 'lib/jquery/jquery.min.js', + 'lib/jquery-ui/jquery-ui.min.js', + 'lib/jquery-ui-touch-punch/jquery.ui.touch-punch.js', + 'lib/angular/angular.js', 'lib/underscore/underscore-min.js', @@ -7,9 +10,6 @@ 'lib/animejs/anime.min.js', - 'lib/jquery-ui/jquery-ui.min.js', - 'lib/jquery-ui-touch-punch/jquery.ui.touch-punch.js', - 'lib/angular-route/angular-route.js', 'lib/angular-cookies/angular-cookies.js', 'lib/angular-touch/angular-touch.js',