blob: 08593aac69756d523d9a83a45bbf0da417f91de2 [file] [log] [blame]
'use strict';
var _fullThrottle = require('../utils/full-throttle');
var _fullThrottle2 = _interopRequireDefault(_fullThrottle);
var _jsonUtils = require('../utils/json-utils');
var _constants = require('../utils/constants');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
(function () {
'use strict';
var MDL_LAYOUT_CONTENT = 'mdl-layout__content';
var IS_SCROLL_CLASS = 'mdlext-is-scroll';
/**
* @constructor
* @param {Element} element The element that will be upgraded.
*/
var MaterialExtStickyHeader = function MaterialExtStickyHeader(element) {
// Stores the element.
this.header_ = element;
// Heder listens to scroll events from content
this.content_ = null;
this.lastScrollTop_ = 0;
// Default config
this.config_ = {
visibleAtScrollEnd: false
};
this.mutationObserver_ = null;
this.drawing_ = false;
// Initialize instance.
this.init();
};
window['MaterialExtStickyHeader'] = MaterialExtStickyHeader;
/**
* Update header width
* @private
*/
MaterialExtStickyHeader.prototype.recalcWidth_ = function () {
this.header_.style.width = this.content_.clientWidth + 'px';
};
var throttleResize = (0, _fullThrottle2.default)(function (self) {
return self.recalcWidth_();
});
/**
* Adjust header width when window resizes or oreientation changes
* @param event
* @private
*/
MaterialExtStickyHeader.prototype.resizeHandler_ = function () /* event */{
throttleResize(this);
};
/**
* Update header position
* @private
*/
MaterialExtStickyHeader.prototype.reposition_ = function () {
var currentContentScrollTop = this.content_.scrollTop;
var scrollDiff = this.lastScrollTop_ - currentContentScrollTop;
if (currentContentScrollTop <= 0) {
// Scrolled to the top. Header sticks to the top
this.header_.style.top = '0';
this.header_.classList.remove(IS_SCROLL_CLASS);
} else if (scrollDiff > 0) {
if (scrollDiff >= this.header_.offsetHeight) {
// Scrolled up. Header slides in
var headerTop = parseInt(window.getComputedStyle(this.header_).getPropertyValue('top')) || 0;
if (headerTop != 0) {
this.header_.style.top = '0';
this.header_.classList.add(IS_SCROLL_CLASS);
}
this.lastScrollTop_ = currentContentScrollTop;
}
return;
} else if (scrollDiff < 0) {
// Scrolled down
this.header_.classList.add(IS_SCROLL_CLASS);
var _headerTop = parseInt(window.getComputedStyle(this.header_).getPropertyValue('top')) || 0;
if (this.content_.scrollHeight - this.content_.scrollTop <= this.content_.offsetHeight) {
// Bottom of content
if (_headerTop != 0) {
this.header_.style.top = this.config_.visibleAtScrollEnd ? '0' : '-' + this.header_.offsetHeight + 'px';
}
} else {
_headerTop += scrollDiff;
var offsetHeight = this.header_.offsetHeight;
this.header_.style.top = (Math.abs(_headerTop) > offsetHeight ? -offsetHeight : _headerTop) + 'px';
}
}
this.lastScrollTop_ = currentContentScrollTop;
};
var throttleScroll = (0, _fullThrottle2.default)(function (self) {
return self.reposition_();
});
/**
* Scroll header when content scrolls
* @param event
* @private
*/
MaterialExtStickyHeader.prototype.scrollHandler_ = function () /* event */{
throttleScroll(this);
};
/**
* Init header position
* @private
*/
MaterialExtStickyHeader.prototype.updatePosition_ = function () /* event */{
this.recalcWidth_();
this.reposition_();
};
/**
* Add mutation observer
* @private
*/
MaterialExtStickyHeader.prototype.addMutationObserver_ = function () {
var _this = this;
// jsdom does not support MutationObserver - so this is not testable
/* istanbul ignore next */
this.mutationObserver_ = new MutationObserver(function () /*mutations*/{
// Adjust header width if content changes (e.g. in a SPA)
_this.updatePosition_();
});
this.mutationObserver_.observe(this.content_, {
attributes: false,
childList: true,
characterData: false,
subtree: true
});
};
/**
* Removes event listeners
* @private
*/
MaterialExtStickyHeader.prototype.removeListeners_ = function () {
window.removeEventListener('resize', this.resizeHandler_);
window.removeEventListener('orientationchange', this.resizeHandler_);
if (this.content_) {
this.content_.removeEventListener('scroll', this.scrollHandler_);
}
if (this.mutationObserver_) {
this.mutationObserver_.disconnect();
this.mutationObserver_ = null;
}
};
/**
* Initialize component
*/
MaterialExtStickyHeader.prototype.init = function () {
if (this.header_) {
this.removeListeners_();
if (this.header_.hasAttribute('data-config')) {
this.config_ = (0, _jsonUtils.jsonStringToObject)(this.header_.getAttribute('data-config'));
}
this.content_ = this.header_.parentNode.querySelector('.' + MDL_LAYOUT_CONTENT) || null;
if (this.content_) {
this.content_.style.paddingTop = this.header_.offsetHeight + 'px'; // Make room for sticky header
this.lastScrollTop_ = this.content_.scrollTop;
this.content_.addEventListener('scroll', this.scrollHandler_.bind(this));
window.addEventListener('resize', this.resizeHandler_.bind(this));
window.addEventListener('orientationchange', this.resizeHandler_.bind(this));
this.addMutationObserver_();
this.updatePosition_();
// Set upgraded flag
this.header_.classList.add(_constants.IS_UPGRADED);
}
}
};
/*
* Downgrade component
* E.g remove listeners and clean up resources
*
* Nothing to clean
*
MaterialExtStickyHeader.prototype.mdlDowngrade_ = function() {
'use strict';
console.log('***** MaterialExtStickyHeader.prototype.mdlDowngrade_');
};
*/
// The component registers itself. It can assume componentHandler is available
// in the global scope.
/* eslint no-undef: 0 */
componentHandler.register({
constructor: MaterialExtStickyHeader,
classAsString: 'MaterialExtStickyHeader',
cssClass: 'mdlext-js-sticky-header'
});
})(); /**
* @license
* Copyright 2016 Leif Olsen. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This code is built with Google Material Design Lite,
* which is Licensed under the Apache License, Version 2.0
*/
/**
* A sticky header makes site navigation easily accessible anywhere on the page and saves content space at the same.
* The header should auto-hide, i.e. hiding the header automatically when a user starts scrolling down the page and
* bringing the header back when a user might need it: they reach the bottom of the page or start scrolling up.
*/