cartodb/lib/assets/javascripts/builder/components/scroll/scroll-view.js
2020-06-15 10:58:47 +08:00

142 lines
4.3 KiB
JavaScript
Executable File

var CoreView = require('backbone/core-view');
var Ps = require('perfect-scrollbar');
var template = require('./scroll.tpl');
var MutationObserver = window.MutationObserver;
module.exports = CoreView.extend({
tagName: 'div',
className: 'ScrollView',
initialize: function (opts) {
if (!opts.createContentView) throw new Error('A factory createContentView function is required');
this.options = opts || {};
this._type = opts.type || 'vertical'; // vertical or horizontal
this._maxScroll = 0;
this._bindedCheckShadows = this._checkShadows.bind(this);
},
render: function () {
this.clearSubViews();
this._html();
var view = this.options.createContentView.call(this);
this._contentContainer().append(view.render().el);
this.addView(view);
this._applyScroll();
this._updateScrollWhenExist();
return this;
},
_html: function () {
this.$el.html(template({
type: this._type
}));
(this._type === 'horizontal') && this.$el.addClass('ScrollView--horizontal');
},
_contentContainer: function () {
return this.$('.js-content');
},
_wrapperContainer: function () {
return this.$('.js-wrapper');
},
_updateScrollWhenExist: function () {
// Phantom doesn't provide this api for window.
if (!MutationObserver) return;
// even with the changes in PS, we need to check when this element is added to the dom
// in order to trigger manually an update.
var element = document.body;
var self = this._wrapperContainer().get(0);
var onMutationObserver = function () {
if (element.contains(self)) {
Ps.update(self);
observer.disconnect();
}
};
var observer = new MutationObserver(onMutationObserver);
onMutationObserver();
var config = { subtree: true, childList: true };
observer.observe(element, config);
},
_applyScroll: function () {
Ps.initialize(this._wrapperContainer().get(0), {
wheelSpeed: 2,
wheelPropagation: true,
stopPropagationOnClick: false,
minScrollbarLength: 20,
suppressScrollX: this._type === 'vertical',
suppressScrollY: this._type === 'horizontal'
});
this._bindScroll();
},
_bindScroll: function () {
this._wrapperContainer()
.on('ps-dom-change', this._bindedCheckShadows)
.on('ps-x-reach-start', this._bindedCheckShadows)
.on('ps-x-reach-end', this._bindedCheckShadows)
.on('ps-y-reach-start', this._bindedCheckShadows)
.on('ps-y-reach-end', this._bindedCheckShadows)
.on('ps-scroll-x', this._bindedCheckShadows)
.on('ps-scroll-y', this._bindedCheckShadows);
},
_unbindScroll: function () {
this._wrapperContainer()
.off('ps-dom-change', this._bindedCheckShadows)
.off('ps-x-reach-start', this._bindedCheckShadows)
.off('ps-x-reach-end', this._bindedCheckShadows)
.off('ps-y-reach-start', this._bindedCheckShadows)
.off('ps-y-reach-end', this._bindedCheckShadows)
.off('ps-scroll-x', this._bindedCheckShadows)
.off('ps-scroll-y', this._bindedCheckShadows);
},
_checkShadows: function () {
var currentPos;
var max;
var width;
var height;
var maxPos;
if (this._type === 'horizontal') {
currentPos = this._wrapperContainer().scrollLeft();
max = this._wrapperContainer().get(0).scrollWidth;
width = this._wrapperContainer().outerWidth();
maxPos = max - width;
this.$('> .js-leftShadow').toggleClass('is-visible', currentPos > 0);
this.$('> .js-rightShadow').toggleClass('is-visible', currentPos < maxPos);
} else {
currentPos = this._wrapperContainer().scrollTop();
max = this._wrapperContainer().get(0).scrollHeight;
height = this._wrapperContainer().outerHeight();
maxPos = max - height;
// We use the direct descendant selector to avoid affect others nested view with scroll
this.$('> .js-topShadow').toggleClass('is-visible', currentPos > 0);
this.$('> .js-bottomShadow').toggleClass('is-visible', currentPos < maxPos);
}
},
destroyScroll: function () {
if (this._wrapperContainer().length) {
this._unbindScroll();
Ps.destroy(this._wrapperContainer().get(0));
}
},
clean: function () {
this.destroyScroll();
CoreView.prototype.clean.apply(this, arguments);
}
});