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.

This commit is contained in:
Shannon
2018-08-10 13:53:19 +10:00
parent 3395e4c150
commit 2d1bda54be
14 changed files with 268 additions and 159 deletions

View File

@@ -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";

View File

@@ -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: "&"
}
};

View File

@@ -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(){

View File

@@ -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);
})();

View File

@@ -276,7 +276,7 @@
controller: umbPropertyFileUploadController
};
angular.module("umbraco")
angular.module("umbraco.directives")
.component('umbPropertyFileUpload', umbPropertyFileUploadComponent);
})();

View File

@@ -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));
});
},
/**

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -177,4 +177,4 @@
</umb-editor-view>
</div>
</div>

View File

@@ -13,7 +13,8 @@
content="content"
editor="editor"
editors="editors"
init-variant="initVariant(variant)">
init-variant="initVariant(variant)"
on-split-view-changed="splitViewChanged()">
</umb-variant-content>
</div>
</div>

View File

@@ -1,9 +1,10 @@
<div class="umb-cropper-gravity">
<div class="gravity-container" ng-show="loaded">
<div class="gravity-container" ng-show="vm.loaded">
<div class="viewport">
<img ng-src="{{src}}" style="max-width: 100%; max-height: 100%" ng-click="setFocalPoint($event)" draggable="false" />
<img ng-src="{{vm.src}}" style="max-width: 100%; max-height: 100%" ng-click="vm.setFocalPoint($event)" draggable="false" />
<div class="overlay" ng-style="style()">
<div class="overlay" ng-style="vm.style()">
</div>

View File

@@ -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

View File

@@ -35,8 +35,10 @@
</div>
<div ng-if="!currentCrop" class="umb-cropper-imageholder clearfix">
<pre><code>{{model.value.focalPoint | json}}</code></pre>
<umb-image-gravity src="imageSrc"
center="model.value.focalPoint">
center="model.value.focalPoint"
on-value-changed="focalPointChanged(left, top)">
</umb-image-gravity>
<a href class="btn btn-link btn-crop-delete" ng-click="clear()"><i class="icon-delete red"></i> <localize key="content_uploadClear">Remove file</localize></a>
</div>