ember-paper
Version:
The Ember approach to Material Design.
193 lines (162 loc) • 5.6 kB
JavaScript
/* eslint-disable ember/require-computed-property-dependencies, prettier/prettier */
/**
* @module ember-paper
*/
import Mixin from '@ember/object/mixin';
import { htmlSafe } from '@ember/template';
import { computed } from '@ember/object';
import { schedule, later } from '@ember/runloop';
import { nextTick, computeTimeout } from 'ember-css-transitions/utils/transition-utils';
import { getOwner } from '@ember/application';
/**
* @class Translate3dMixin
* @extends Ember.Mixin
*/
export default Mixin.create({
attributeBindings: ['translateStyle:style'],
classNameBindings: ['transformIn:md-transition-in'],
fromStyle: computed('defaultedOpenFrom', function() {
return this.toTransformCss(this.calculateZoomToOrigin(this.element, this.defaultedOpenFrom));
}),
centerStyle: computed(function() {
return this.toTransformCss('');
}),
translateStyle: computed('fromStyle', 'centerStyle', 'transformStyleApply', function() {
if (this.transformStyleApply === 'from') {
return htmlSafe(this.fromStyle);
} else if (this.transformStyleApply === 'main') {
return htmlSafe(this.centerStyle);
} else {
return htmlSafe('');
}
}),
onTranslateFromEnd() {},
onTranslateToEnd() {},
didInsertElement() {
this._super(...arguments);
schedule('afterRender', () => {
// Set translate3d style to start at the `from` origin
this.set('transformStyleApply', 'from');
// Wait while CSS takes affect
// Set the `main` styles and run the transition-in styles
nextTick().then(() => {
if (this.isDestroyed) {
return;
}
later(() => {
if (!this.isDestroying && !this.isDestroyed) {
this.onTranslateFromEnd();
}
}, computeTimeout(this.element) || 0);
if (!this.isDestroying && !this.isDestroyed) {
this.set('transformStyleApply', 'main');
this.set('transformIn', true);
}
});
});
},
/**
* Specific reversal of the request translate animation above...
*
* @protected
*/
willDestroyElement() {
this._super(...arguments);
let config = getOwner(this).resolveRegistration('config:environment');
let containerClone = this.element.parentNode.cloneNode(true);
let dialogClone = containerClone.querySelector('md-dialog');
let parent = this.defaultedParent;
if (config.environment === 'test' && !this.parent) {
parent = '#ember-testing';
}
let parentNode = document.querySelector(parent);
if (parentNode && parentNode.parentNode !== null) {
parentNode.parentNode.appendChild(containerClone);
}
let toStyle = this.toTransformCss(this.calculateZoomToOrigin(this.element, this.defaultedCloseTo));
let origin = typeof this.origin === 'string'
? document.querySelector(this.origin)
: this.origin;
nextTick().then(() => {
dialogClone.classList.remove('md-transition-in');
dialogClone.classList.add('md-transition-out');
dialogClone.style.cssText = toStyle;
nextTick().then(() => {
later(() => {
if (containerClone.parentNode !== null) {
containerClone.parentNode.removeChild(containerClone);
}
this.onTranslateToEnd(origin);
}, computeTimeout(dialogClone) || 0);
});
});
},
/**
* Calculate the zoom transform from dialog to origin.
*
* We use this to set the dialog position immediately;
* then the md-transition-in actually translates back to
* `translate3d(0,0,0) scale(1.0)`...
*
* NOTE: all values are rounded to the nearest integer
*
* @public
*/
calculateZoomToOrigin(element, originator) {
let zoomStyle;
originator = typeof originator === 'string'
? document.querySelector(originator)
: originator;
if (originator) {
let originBnds = this.copyRect(originator.getBoundingClientRect());
let dialogRect = this.copyRect(element.getBoundingClientRect());
let dialogCenterPt = this.centerPointFor(dialogRect);
let originCenterPt = this.centerPointFor(originBnds);
zoomStyle = {
centerX: originCenterPt.x - dialogCenterPt.x,
centerY: originCenterPt.y - dialogCenterPt.y,
scaleX: Math.min(0.5, originBnds.width / dialogRect.width),
scaleY: Math.min(0.5, originBnds.height / dialogRect.height)
};
} else {
zoomStyle = { centerX: 0, centerY: 0, scaleX: 0.5, scaleY: 0.5 };
}
return `translate3d(${zoomStyle.centerX}px, ${zoomStyle.centerY}px, 0 ) scale(${zoomStyle.scaleX}, ${zoomStyle.scaleY})`;
},
/**
* Convert the translate CSS value to key/value pair(s).
*
* @public
*/
toTransformCss(transform) {
return `transform: ${transform};`;
},
/**
* Clone the Rect and calculate the height/width if needed
*
* @public
*/
copyRect(source, destination) {
if (!source) {
return null;
}
destination = destination || {};
'left top right bottom width height'.split(' ').forEach((key) => {
destination[key] = Math.round(source[key]);
});
destination.width = destination.width || (destination.right - destination.left);
destination.height = destination.height || (destination.bottom - destination.top);
return destination;
},
/**
* Calculate 'rounded' center point of Rect
*
* @public
*/
centerPointFor(targetRect) {
return {
x: Math.round(targetRect.left + (targetRect.width / 2)),
y: Math.round(targetRect.top + (targetRect.height / 2))
};
}
});