| Current Path : /proc/thread-self/cwd/static/frontend/Magento/indaco/it_IT/Magento_Ui/js/timeline/ |
| Current File : //proc/thread-self/cwd/static/frontend/Magento/indaco/it_IT/Magento_Ui/js/timeline/timeline-view.js |
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
/**
* @api
*/
define([
'ko',
'Magento_Ui/js/lib/view/utils/async',
'underscore',
'Magento_Ui/js/lib/view/utils/raf',
'uiRegistry',
'uiClass'
], function (ko, $, _, raf, registry, Class) {
'use strict';
var hasClassList = (function () {
var list = document.createElement('_').classList;
return !!list && !list.toggle('_test', false);
})();
/**
* Polyfill of the 'classList.toggle' method.
*
* @param {HTMLElement} elem
*/
function toggleClass(elem) {
var classList = elem.classList,
args = Array.prototype.slice.call(arguments, 1),
$elem;
if (hasClassList) {
classList.toggle.apply(classList, args);
} else {
$elem = $(elem);
$elem.toggleClass.apply($elem, args);
}
}
return Class.extend({
defaults: {
selectors: {
content: '.timeline-content',
timeUnit: '.timeline-unit',
item: '.timeline-item:not([data-role=no-data-msg])',
event: '.timeline-event'
}
},
/**
* Initializes TimelineView component.
*
* @returns {TimelineView} Chainable.
*/
initialize: function () {
_.bindAll(
this,
'refresh',
'initContent',
'initItem',
'initTimeUnit',
'getItemBindings',
'updateItemsPosition',
'onScaleChange',
'onEventElementRender',
'onWindowResize',
'onContentScroll',
'onDataReloaded',
'onToStartClick',
'onToEndClick'
);
this._super()
.initModel()
.waitContent();
return this;
},
/**
* Applies listeners for the model properties changes.
*
* @returns {TimelineView} Chainable.
*/
initModel: function () {
var model = registry.get(this.model);
model.on('scale', this.onScaleChange);
model.source.on('reloaded', this.onDataReloaded);
this.model = model;
return this;
},
/**
* Applies DOM watcher for the
* content element rendering.
*
* @returns {TimelineView} Chainable.
*/
waitContent: function () {
$.async({
selector: this.selectors.content,
component: this.model
}, this.initContent);
return this;
},
/**
* Initializes timelines' content element.
*
* @param {HTMLElement} content
* @returns {TimelineView} Chainable.
*/
initContent: function (content) {
this.$content = content;
$(content).on('scroll', this.onContentScroll);
$(window).on('resize', this.onWindowResize);
$.async(this.selectors.item, content, this.initItem);
$.async(this.selectors.event, content, this.onEventElementRender);
$.async(this.selectors.timeUnit, content, this.initTimeUnit);
this.refresh();
return this;
},
/**
* Initializes timeline item element,
* e.g. establishes event listeners and applies data bindings.
*
* @param {HTMLElement} elem
* @returns {TimelineView} Chainable.
*/
initItem: function (elem) {
$(elem)
.bindings(this.getItemBindings)
.on('click', '._toend', this.onToEndClick)
.on('click', '._tostart', this.onToStartClick);
return this;
},
/**
* Initializes timeline unit element.
*
* @param {HTMLElement} elem
* @returns {TimelineView} Chainable.
*/
initTimeUnit: function (elem) {
$(elem).bindings(this.getTimeUnitBindings());
return this;
},
/**
* Updates items positions in a
* loop if state of a view has changed.
*/
refresh: function () {
raf(this.refresh);
if (this._update) {
this._update = false;
this.updateItemsPosition();
}
},
/**
* Returns object width additional bindings
* for a timeline unit element.
*
* @returns {Object}
*/
getTimeUnitBindings: function () {
return {
style: {
width: ko.computed(function () {
return this.getTimeUnitWidth() + '%';
}.bind(this))
}
};
},
/**
* Returns object with additional
* bindings for a timeline item element.
*
* @param {Object} ctx
* @returns {Object}
*/
getItemBindings: function (ctx) {
return {
style: {
width: ko.computed(function () {
return this.getItemWidth(ctx.$row()) + '%';
}.bind(this)),
'margin-left': ko.computed(function () {
return this.getItemMargin(ctx.$row()) + '%';
}.bind(this))
}
};
},
/**
* Calculates width in percents of a timeline unit element.
*
* @returns {Number}
*/
getTimeUnitWidth: function () {
return 100 / this.model.scale;
},
/**
* Calculates width of a record in percents.
*
* @param {Object} record
* @returns {String}
*/
getItemWidth: function (record) {
var days = 0;
if (record) {
days = this.model.getDaysLength(record);
}
return this.getTimeUnitWidth() * days;
},
/**
* Calculates left margin value for provided record.
*
* @param {Object} record
* @returns {String}
*/
getItemMargin: function (record) {
var offset = 0;
if (record) {
offset = this.model.getStartDelta(record);
}
return this.getTimeUnitWidth() * offset;
},
/**
* Returns collection of currently available
* timeline item elements.
*
* @returns {Array<HTMLElement>}
*/
getItems: function () {
var items = this.$content.querySelectorAll(this.selectors.item);
return _.toArray(items);
},
/**
* Updates positions of timeline elements.
*
* @returns {TimelineView} Chainable.
*/
updateItemsPosition: function () {
this.getItems()
.forEach(this.updatePositionFor, this);
return this;
},
/**
* Updates position of provided timeline element.
*
* @param {HTMLElement} $elem
* @returns {TimelineView} Chainable.
*/
updatePositionFor: function ($elem) {
var $event = $elem.querySelector(this.selectors.event),
leftEdge = this.getLeftEdgeFor($elem),
rightEdge = this.getRightEdgeFor($elem);
if ($event) {
$event.style.left = Math.max(-leftEdge, 0) + 'px';
$event.style.right = Math.max(rightEdge, 0) + 'px';
}
toggleClass($elem, '_scroll-start', leftEdge < 0);
toggleClass($elem, '_scroll-end', rightEdge > 0);
return this;
},
/**
* Scrolls content area to the start of provided element.
*
* @param {HTMLElement} elem
* @returns {TimelineView}
*/
toStartOf: function (elem) {
var leftEdge = this.getLeftEdgeFor(elem);
this.$content.scrollLeft += leftEdge;
return this;
},
/**
* Scrolls content area to the end of provided element.
*
* @param {HTMLElement} elem
* @returns {TimelineView}
*/
toEndOf: function (elem) {
var rightEdge = this.getRightEdgeFor(elem);
this.$content.scrollLeft += rightEdge + 1;
return this;
},
/**
* Calculates location of the left edge of an element
* relative to the contents' left edge.
*
* @param {HTMLElement} elem
* @returns {Number}
*/
getLeftEdgeFor: function (elem) {
var leftOffset = elem.getBoundingClientRect().left;
return leftOffset - this.$content.getBoundingClientRect().left;
},
/**
* Calculates location of the right edge of an element
* relative to the contents' right edge.
*
* @param {HTMLElement} elem
* @returns {Number}
*/
getRightEdgeFor: function (elem) {
var elemWidth = elem.offsetWidth,
leftEdge = this.getLeftEdgeFor(elem);
return leftEdge + elemWidth - this.$content.offsetWidth;
},
/**
* 'To Start' button 'click' event handler.
*
* @param {jQueryEvent} event
*/
onToStartClick: function (event) {
var elem = event.originalEvent.currentTarget;
event.stopPropagation();
this.toStartOf(elem);
},
/**
* 'To End' button 'click' event handler.
*
* @param {jQueryEvent} event
*/
onToEndClick: function (event) {
var elem = event.originalEvent.currentTarget;
event.stopPropagation();
this.toEndOf(elem);
},
/**
* Handler of the scale value 'change' event.
*/
onScaleChange: function () {
this._update = true;
},
/**
* Callback function which is invoked
* when event element was rendered.
*/
onEventElementRender: function () {
this._update = true;
},
/**
* Window 'resize' event handler.
*/
onWindowResize: function () {
this._update = true;
},
/**
* Content container 'scroll' event handler.
*/
onContentScroll: function () {
this._update = true;
},
/**
* Data 'reload' event handler.
*/
onDataReloaded: function () {
this._update = true;
}
});
});