initial commit
This commit is contained in:
commit
52aad94550
94 changed files with 43651 additions and 0 deletions
583
node_modules/@materializecss/materialize/js/sidenav.js
generated
vendored
Executable file
583
node_modules/@materializecss/materialize/js/sidenav.js
generated
vendored
Executable file
|
@ -0,0 +1,583 @@
|
|||
(function($, anim) {
|
||||
'use strict';
|
||||
|
||||
let _defaults = {
|
||||
edge: 'left',
|
||||
draggable: true,
|
||||
dragTargetWidth: '10px',
|
||||
inDuration: 250,
|
||||
outDuration: 200,
|
||||
onOpenStart: null,
|
||||
onOpenEnd: null,
|
||||
onCloseStart: null,
|
||||
onCloseEnd: null,
|
||||
preventScrolling: true
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
class Sidenav extends Component {
|
||||
/**
|
||||
* Construct Sidenav instance and set up overlay
|
||||
* @constructor
|
||||
* @param {Element} el
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(el, options) {
|
||||
super(Sidenav, el, options);
|
||||
|
||||
this.el.M_Sidenav = this;
|
||||
this.id = this.$el.attr('id');
|
||||
|
||||
/**
|
||||
* Options for the Sidenav
|
||||
* @member Sidenav#options
|
||||
* @prop {String} [edge='left'] - Side of screen on which Sidenav appears
|
||||
* @prop {Boolean} [draggable=true] - Allow swipe gestures to open/close Sidenav
|
||||
* @prop {String} [dragTargetWidth='10px'] - Width of the area where you can start dragging
|
||||
* @prop {Number} [inDuration=250] - Length in ms of enter transition
|
||||
* @prop {Number} [outDuration=200] - Length in ms of exit transition
|
||||
* @prop {Function} onOpenStart - Function called when sidenav starts entering
|
||||
* @prop {Function} onOpenEnd - Function called when sidenav finishes entering
|
||||
* @prop {Function} onCloseStart - Function called when sidenav starts exiting
|
||||
* @prop {Function} onCloseEnd - Function called when sidenav finishes exiting
|
||||
*/
|
||||
this.options = $.extend({}, Sidenav.defaults, options);
|
||||
|
||||
/**
|
||||
* Describes open/close state of Sidenav
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.isOpen = false;
|
||||
|
||||
/**
|
||||
* Describes if Sidenav is fixed
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.isFixed = this.el.classList.contains('sidenav-fixed');
|
||||
|
||||
/**
|
||||
* Describes if Sidenav is being draggeed
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.isDragged = false;
|
||||
|
||||
// Window size variables for window resize checks
|
||||
this.lastWindowWidth = window.innerWidth;
|
||||
this.lastWindowHeight = window.innerHeight;
|
||||
|
||||
this._createOverlay();
|
||||
this._createDragTarget();
|
||||
this._setupEventHandlers();
|
||||
this._setupClasses();
|
||||
this._setupFixed();
|
||||
|
||||
Sidenav._sidenavs.push(this);
|
||||
}
|
||||
|
||||
static get defaults() {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
static init(els, options) {
|
||||
return super.init(this, els, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*/
|
||||
static getInstance(el) {
|
||||
let domElem = !!el.jquery ? el[0] : el;
|
||||
return domElem.M_Sidenav;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown component
|
||||
*/
|
||||
destroy() {
|
||||
this._removeEventHandlers();
|
||||
this._enableBodyScrolling();
|
||||
this._overlay.parentNode.removeChild(this._overlay);
|
||||
this.dragTarget.parentNode.removeChild(this.dragTarget);
|
||||
this.el.M_Sidenav = undefined;
|
||||
this.el.style.transform = '';
|
||||
|
||||
let index = Sidenav._sidenavs.indexOf(this);
|
||||
if (index >= 0) {
|
||||
Sidenav._sidenavs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
_createOverlay() {
|
||||
let overlay = document.createElement('div');
|
||||
this._closeBound = this.close.bind(this);
|
||||
overlay.classList.add('sidenav-overlay');
|
||||
|
||||
overlay.addEventListener('click', this._closeBound);
|
||||
|
||||
document.body.appendChild(overlay);
|
||||
this._overlay = overlay;
|
||||
}
|
||||
|
||||
_setupEventHandlers() {
|
||||
if (Sidenav._sidenavs.length === 0) {
|
||||
document.body.addEventListener('click', this._handleTriggerClick);
|
||||
}
|
||||
|
||||
this._handleDragTargetDragBound = this._handleDragTargetDrag.bind(this);
|
||||
this._handleDragTargetReleaseBound = this._handleDragTargetRelease.bind(this);
|
||||
this._handleCloseDragBound = this._handleCloseDrag.bind(this);
|
||||
this._handleCloseReleaseBound = this._handleCloseRelease.bind(this);
|
||||
this._handleCloseTriggerClickBound = this._handleCloseTriggerClick.bind(this);
|
||||
|
||||
this.dragTarget.addEventListener('touchmove', this._handleDragTargetDragBound, passiveIfSupported);
|
||||
this.dragTarget.addEventListener('touchend', this._handleDragTargetReleaseBound);
|
||||
this._overlay.addEventListener('touchmove', this._handleCloseDragBound, passiveIfSupported);
|
||||
this._overlay.addEventListener('touchend', this._handleCloseReleaseBound);
|
||||
this.el.addEventListener('touchmove', this._handleCloseDragBound, passiveIfSupported);
|
||||
this.el.addEventListener('touchend', this._handleCloseReleaseBound);
|
||||
this.el.addEventListener('click', this._handleCloseTriggerClickBound);
|
||||
|
||||
// Add resize for side nav fixed
|
||||
if (this.isFixed) {
|
||||
this._handleWindowResizeBound = this._handleWindowResize.bind(this);
|
||||
window.addEventListener('resize', this._handleWindowResizeBound);
|
||||
}
|
||||
}
|
||||
|
||||
_removeEventHandlers() {
|
||||
if (Sidenav._sidenavs.length === 1) {
|
||||
document.body.removeEventListener('click', this._handleTriggerClick);
|
||||
}
|
||||
|
||||
this.dragTarget.removeEventListener('touchmove', this._handleDragTargetDragBound);
|
||||
this.dragTarget.removeEventListener('touchend', this._handleDragTargetReleaseBound);
|
||||
this._overlay.removeEventListener('touchmove', this._handleCloseDragBound);
|
||||
this._overlay.removeEventListener('touchend', this._handleCloseReleaseBound);
|
||||
this.el.removeEventListener('touchmove', this._handleCloseDragBound);
|
||||
this.el.removeEventListener('touchend', this._handleCloseReleaseBound);
|
||||
this.el.removeEventListener('click', this._handleCloseTriggerClickBound);
|
||||
|
||||
// Remove resize for side nav fixed
|
||||
if (this.isFixed) {
|
||||
window.removeEventListener('resize', this._handleWindowResizeBound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Trigger Click
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleTriggerClick(e) {
|
||||
let $trigger = $(e.target).closest('.sidenav-trigger');
|
||||
if (e.target && $trigger.length) {
|
||||
let sidenavId = M.getIdFromTrigger($trigger[0]);
|
||||
|
||||
let sidenavInstance = document.getElementById(sidenavId).M_Sidenav;
|
||||
if (sidenavInstance) {
|
||||
sidenavInstance.open($trigger);
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set variables needed at the beginning of drag
|
||||
* and stop any current transition.
|
||||
* @param {Event} e
|
||||
*/
|
||||
_startDrag(e) {
|
||||
let clientX = e.targetTouches[0].clientX;
|
||||
this.isDragged = true;
|
||||
this._startingXpos = clientX;
|
||||
this._xPos = this._startingXpos;
|
||||
this._time = Date.now();
|
||||
this._width = this.el.getBoundingClientRect().width;
|
||||
this._overlay.style.display = 'block';
|
||||
this._initialScrollTop = this.isOpen ? this.el.scrollTop : M.getDocumentScrollTop();
|
||||
this._verticallyScrolling = false;
|
||||
anim.remove(this.el);
|
||||
anim.remove(this._overlay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set variables needed at each drag move update tick
|
||||
* @param {Event} e
|
||||
*/
|
||||
_dragMoveUpdate(e) {
|
||||
let clientX = e.targetTouches[0].clientX;
|
||||
let currentScrollTop = this.isOpen ? this.el.scrollTop : M.getDocumentScrollTop();
|
||||
this.deltaX = Math.abs(this._xPos - clientX);
|
||||
this._xPos = clientX;
|
||||
this.velocityX = this.deltaX / (Date.now() - this._time);
|
||||
this._time = Date.now();
|
||||
if (this._initialScrollTop !== currentScrollTop) {
|
||||
this._verticallyScrolling = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Dragging of Sidenav
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleDragTargetDrag(e) {
|
||||
// Check if draggable
|
||||
if (!this.options.draggable || this._isCurrentlyFixed() || this._verticallyScrolling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If not being dragged, set initial drag start variables
|
||||
if (!this.isDragged) {
|
||||
this._startDrag(e);
|
||||
}
|
||||
|
||||
// Run touchmove updates
|
||||
this._dragMoveUpdate(e);
|
||||
|
||||
// Calculate raw deltaX
|
||||
let totalDeltaX = this._xPos - this._startingXpos;
|
||||
|
||||
// dragDirection is the attempted user drag direction
|
||||
let dragDirection = totalDeltaX > 0 ? 'right' : 'left';
|
||||
|
||||
// Don't allow totalDeltaX to exceed Sidenav width or be dragged in the opposite direction
|
||||
totalDeltaX = Math.min(this._width, Math.abs(totalDeltaX));
|
||||
if (this.options.edge === dragDirection) {
|
||||
totalDeltaX = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* transformX is the drag displacement
|
||||
* transformPrefix is the initial transform placement
|
||||
* Invert values if Sidenav is right edge
|
||||
*/
|
||||
let transformX = totalDeltaX;
|
||||
let transformPrefix = 'translateX(-100%)';
|
||||
if (this.options.edge === 'right') {
|
||||
transformPrefix = 'translateX(100%)';
|
||||
transformX = -transformX;
|
||||
}
|
||||
|
||||
// Calculate open/close percentage of sidenav, with open = 1 and close = 0
|
||||
this.percentOpen = Math.min(1, totalDeltaX / this._width);
|
||||
|
||||
// Set transform and opacity styles
|
||||
this.el.style.transform = `${transformPrefix} translateX(${transformX}px)`;
|
||||
this._overlay.style.opacity = this.percentOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Drag Target Release
|
||||
*/
|
||||
_handleDragTargetRelease() {
|
||||
if (this.isDragged) {
|
||||
if (this.percentOpen > 0.2) {
|
||||
this.open();
|
||||
} else {
|
||||
this._animateOut();
|
||||
}
|
||||
|
||||
this.isDragged = false;
|
||||
this._verticallyScrolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Close Drag
|
||||
* @param {Event} e
|
||||
*/
|
||||
_handleCloseDrag(e) {
|
||||
if (this.isOpen) {
|
||||
// Check if draggable
|
||||
if (!this.options.draggable || this._isCurrentlyFixed() || this._verticallyScrolling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If not being dragged, set initial drag start variables
|
||||
if (!this.isDragged) {
|
||||
this._startDrag(e);
|
||||
}
|
||||
|
||||
// Run touchmove updates
|
||||
this._dragMoveUpdate(e);
|
||||
|
||||
// Calculate raw deltaX
|
||||
let totalDeltaX = this._xPos - this._startingXpos;
|
||||
|
||||
// dragDirection is the attempted user drag direction
|
||||
let dragDirection = totalDeltaX > 0 ? 'right' : 'left';
|
||||
|
||||
// Don't allow totalDeltaX to exceed Sidenav width or be dragged in the opposite direction
|
||||
totalDeltaX = Math.min(this._width, Math.abs(totalDeltaX));
|
||||
if (this.options.edge !== dragDirection) {
|
||||
totalDeltaX = 0;
|
||||
}
|
||||
|
||||
let transformX = -totalDeltaX;
|
||||
if (this.options.edge === 'right') {
|
||||
transformX = -transformX;
|
||||
}
|
||||
|
||||
// Calculate open/close percentage of sidenav, with open = 1 and close = 0
|
||||
this.percentOpen = Math.min(1, 1 - totalDeltaX / this._width);
|
||||
|
||||
// Set transform and opacity styles
|
||||
this.el.style.transform = `translateX(${transformX}px)`;
|
||||
this._overlay.style.opacity = this.percentOpen;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Close Release
|
||||
*/
|
||||
_handleCloseRelease() {
|
||||
if (this.isOpen && this.isDragged) {
|
||||
if (this.percentOpen > 0.8) {
|
||||
this._animateIn();
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
|
||||
this.isDragged = false;
|
||||
this._verticallyScrolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles closing of Sidenav when element with class .sidenav-close
|
||||
*/
|
||||
_handleCloseTriggerClick(e) {
|
||||
let $closeTrigger = $(e.target).closest('.sidenav-close');
|
||||
if ($closeTrigger.length && !this._isCurrentlyFixed()) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Window Resize
|
||||
*/
|
||||
_handleWindowResize() {
|
||||
// Only handle horizontal resizes
|
||||
if (this.lastWindowWidth !== window.innerWidth) {
|
||||
if (window.innerWidth > 992) {
|
||||
this.open();
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
this.lastWindowWidth = window.innerWidth;
|
||||
this.lastWindowHeight = window.innerHeight;
|
||||
}
|
||||
|
||||
_setupClasses() {
|
||||
if (this.options.edge === 'right') {
|
||||
this.el.classList.add('right-aligned');
|
||||
this.dragTarget.classList.add('right-aligned');
|
||||
}
|
||||
}
|
||||
|
||||
_removeClasses() {
|
||||
this.el.classList.remove('right-aligned');
|
||||
this.dragTarget.classList.remove('right-aligned');
|
||||
}
|
||||
|
||||
_setupFixed() {
|
||||
if (this._isCurrentlyFixed()) {
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
|
||||
_isCurrentlyFixed() {
|
||||
return this.isFixed && window.innerWidth > 992;
|
||||
}
|
||||
|
||||
_createDragTarget() {
|
||||
let dragTarget = document.createElement('div');
|
||||
dragTarget.classList.add('drag-target');
|
||||
dragTarget.style.width = this.options.dragTargetWidth;
|
||||
document.body.appendChild(dragTarget);
|
||||
this.dragTarget = dragTarget;
|
||||
}
|
||||
|
||||
_preventBodyScrolling() {
|
||||
let body = document.body;
|
||||
body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
_enableBodyScrolling() {
|
||||
let body = document.body;
|
||||
body.style.overflow = '';
|
||||
}
|
||||
|
||||
open() {
|
||||
if (this.isOpen === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = true;
|
||||
|
||||
// Run onOpenStart callback
|
||||
if (typeof this.options.onOpenStart === 'function') {
|
||||
this.options.onOpenStart.call(this, this.el);
|
||||
}
|
||||
|
||||
// Handle fixed Sidenav
|
||||
if (this._isCurrentlyFixed()) {
|
||||
anim.remove(this.el);
|
||||
anim({
|
||||
targets: this.el,
|
||||
translateX: 0,
|
||||
duration: 0,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
this._enableBodyScrolling();
|
||||
this._overlay.style.display = 'none';
|
||||
|
||||
// Handle non-fixed Sidenav
|
||||
} else {
|
||||
if (this.options.preventScrolling) {
|
||||
this._preventBodyScrolling();
|
||||
}
|
||||
|
||||
if (!this.isDragged || this.percentOpen != 1) {
|
||||
this._animateIn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.isOpen === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = false;
|
||||
|
||||
// Run onCloseStart callback
|
||||
if (typeof this.options.onCloseStart === 'function') {
|
||||
this.options.onCloseStart.call(this, this.el);
|
||||
}
|
||||
|
||||
// Handle fixed Sidenav
|
||||
if (this._isCurrentlyFixed()) {
|
||||
let transformX = this.options.edge === 'left' ? '-105%' : '105%';
|
||||
this.el.style.transform = `translateX(${transformX})`;
|
||||
|
||||
// Handle non-fixed Sidenav
|
||||
} else {
|
||||
this._enableBodyScrolling();
|
||||
|
||||
if (!this.isDragged || this.percentOpen != 0) {
|
||||
this._animateOut();
|
||||
} else {
|
||||
this._overlay.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_animateIn() {
|
||||
this._animateSidenavIn();
|
||||
this._animateOverlayIn();
|
||||
}
|
||||
|
||||
_animateSidenavIn() {
|
||||
let slideOutPercent = this.options.edge === 'left' ? -1 : 1;
|
||||
if (this.isDragged) {
|
||||
slideOutPercent =
|
||||
this.options.edge === 'left'
|
||||
? slideOutPercent + this.percentOpen
|
||||
: slideOutPercent - this.percentOpen;
|
||||
}
|
||||
|
||||
anim.remove(this.el);
|
||||
anim({
|
||||
targets: this.el,
|
||||
translateX: [`${slideOutPercent * 100}%`, 0],
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
// Run onOpenEnd callback
|
||||
if (typeof this.options.onOpenEnd === 'function') {
|
||||
this.options.onOpenEnd.call(this, this.el);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_animateOverlayIn() {
|
||||
let start = 0;
|
||||
if (this.isDragged) {
|
||||
start = this.percentOpen;
|
||||
} else {
|
||||
$(this._overlay).css({
|
||||
display: 'block'
|
||||
});
|
||||
}
|
||||
|
||||
anim.remove(this._overlay);
|
||||
anim({
|
||||
targets: this._overlay,
|
||||
opacity: [start, 1],
|
||||
duration: this.options.inDuration,
|
||||
easing: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
|
||||
_animateOut() {
|
||||
this._animateSidenavOut();
|
||||
this._animateOverlayOut();
|
||||
}
|
||||
|
||||
_animateSidenavOut() {
|
||||
let endPercent = this.options.edge === 'left' ? -1 : 1;
|
||||
let slideOutPercent = 0;
|
||||
if (this.isDragged) {
|
||||
slideOutPercent =
|
||||
this.options.edge === 'left'
|
||||
? endPercent + this.percentOpen
|
||||
: endPercent - this.percentOpen;
|
||||
}
|
||||
|
||||
anim.remove(this.el);
|
||||
anim({
|
||||
targets: this.el,
|
||||
translateX: [`${slideOutPercent * 100}%`, `${endPercent * 105}%`],
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
// Run onOpenEnd callback
|
||||
if (typeof this.options.onCloseEnd === 'function') {
|
||||
this.options.onCloseEnd.call(this, this.el);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_animateOverlayOut() {
|
||||
anim.remove(this._overlay);
|
||||
anim({
|
||||
targets: this._overlay,
|
||||
opacity: 0,
|
||||
duration: this.options.outDuration,
|
||||
easing: 'easeOutQuad',
|
||||
complete: () => {
|
||||
$(this._overlay).css('display', 'none');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @memberof Sidenav
|
||||
* @type {Array.<Sidenav>}
|
||||
*/
|
||||
Sidenav._sidenavs = [];
|
||||
|
||||
M.Sidenav = Sidenav;
|
||||
|
||||
if (M.jQueryLoaded) {
|
||||
M.initializeJqueryWrapper(Sidenav, 'sidenav', 'M_Sidenav');
|
||||
}
|
||||
})(cash, M.anime);
|
Loading…
Add table
Add a link
Reference in a new issue