nly-adminlte-vue
Version:
nly adminlte3 components
219 lines (214 loc) • 7.77 kB
JavaScript
import Vue from "../../../utils/vue";
import {
getAttr,
hasAttr,
removeAttr,
setAttr,
addClass,
removeClass,
getBCR,
getCS,
selectAll,
requestAF
} from "../../../utils/dom";
import { isBrowser } from "../../../utils/env";
import { isNull } from "../../../utils/inspect";
import { toFloat, toInteger } from "../../../utils/number";
const DEFAULT_ZINDEX = 1040;
const Selector = {
FIXED_CONTENT: ".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",
STICKY_CONTENT: ".sticky-top",
NAVBAR_TOGGLER: ".navbar-toggler"
};
const ModalManager = Vue.extend({
data() {
return {
modals: [],
baseZIndex: null,
scrollbarWidth: null,
isBodyOverflowing: false
};
},
computed: {
modalCount() {
return this.modals.length;
},
modalsAreOpen() {
return this.modalCount > 0;
}
},
watch: {
modalCount(newCount, oldCount) {
if (isBrowser) {
this.getScrollbarWidth();
if (newCount > 0 && oldCount === 0) {
// Transitioning to modal(s) open
this.checkScrollbar();
this.setScrollbar();
addClass(document.body, "modal-open");
} else if (newCount === 0 && oldCount > 0) {
// Transitioning to modal(s) closed
this.resetScrollbar();
removeClass(document.body, "modal-open");
}
setAttr(document.body, "data-modal-open-count", String(newCount));
}
},
modals(newVal) {
this.checkScrollbar();
requestAF(() => {
this.updateModals(newVal || []);
});
}
},
methods: {
// Public methods
registerModal(modal) {
// Register the modal if not already registered
if (modal && this.modals.indexOf(modal) === -1) {
// Add modal to modals array
this.modals.push(modal);
modal.$once("hook:beforeDestroy", () => {
this.unregisterModal(modal);
});
}
},
unregisterModal(modal) {
const index = this.modals.indexOf(modal);
if (index > -1) {
// Remove modal from modals array
this.modals.splice(index, 1);
// Reset the modal's data
if (!(modal._isBeingDestroyed || modal._isDestroyed)) {
this.resetModal(modal);
}
}
},
getBaseZIndex() {
if (isNull(this.baseZIndex) && isBrowser) {
// Create a temporary `div.modal-backdrop` to get computed z-index
const div = document.createElement("div");
div.className = "modal-backdrop d-none";
div.style.display = "none";
document.body.appendChild(div);
this.baseZIndex = toInteger(getCS(div).zIndex, DEFAULT_ZINDEX);
document.body.removeChild(div);
}
return this.baseZIndex || DEFAULT_ZINDEX;
},
getScrollbarWidth() {
if (isNull(this.scrollbarWidth) && isBrowser) {
// Create a temporary `div.measure-scrollbar` to get computed z-index
const div = document.createElement("div");
div.className = "modal-scrollbar-measure";
document.body.appendChild(div);
this.scrollbarWidth = getBCR(div).width - div.clientWidth;
document.body.removeChild(div);
}
return this.scrollbarWidth || 0;
},
// Private methods
updateModals(modals) {
const baseZIndex = this.getBaseZIndex();
const scrollbarWidth = this.getScrollbarWidth();
modals.forEach((modal, index) => {
// We update data values on each modal
modal.zIndex = baseZIndex + index;
modal.scrollbarWidth = scrollbarWidth;
modal.isTop = index === this.modals.length - 1;
modal.isBodyOverflowing = this.isBodyOverflowing;
});
},
resetModal(modal) {
if (modal) {
modal.zIndex = this.getBaseZIndex();
modal.isTop = true;
modal.isBodyOverflowing = false;
}
},
checkScrollbar() {
// Determine if the body element is overflowing
const { left, right } = getBCR(document.body);
this.isBodyOverflowing = left + right < window.innerWidth;
},
setScrollbar() {
const body = document.body;
// Storage place to cache changes to margins and padding
// Note: This assumes the following element types are not added to the
// document after the modal has opened.
body._paddingChangedForModal = body._paddingChangedForModal || [];
body._marginChangedForModal = body._marginChangedForModal || [];
if (this.isBodyOverflowing) {
const scrollbarWidth = this.scrollbarWidth;
// Adjust fixed content padding
/* istanbul ignore next: difficult to test in JSDOM */
selectAll(Selector.FIXED_CONTENT).forEach(el => {
const actualPadding = el.style.paddingRight;
setAttr(el, "data-padding-right", actualPadding);
el.style.paddingRight = `${toFloat(getCS(el).paddingRight, 0) +
scrollbarWidth}px`;
body._paddingChangedForModal.push(el);
});
// Adjust sticky content margin
/* istanbul ignore next: difficult to test in JSDOM */
selectAll(Selector.STICKY_CONTENT).forEach(
el => /* istanbul ignore next */ {
const actualMargin = el.style.marginRight;
setAttr(el, "data-margin-right", actualMargin);
el.style.marginRight = `${toFloat(getCS(el).marginRight, 0) -
scrollbarWidth}px`;
body._marginChangedForModal.push(el);
}
);
// Adjust <b-navbar-toggler> margin
/* istanbul ignore next: difficult to test in JSDOM */
selectAll(Selector.NAVBAR_TOGGLER).forEach(
el => /* istanbul ignore next */ {
const actualMargin = el.style.marginRight;
setAttr(el, "data-margin-right", actualMargin);
el.style.marginRight = `${toFloat(getCS(el).marginRight, 0) +
scrollbarWidth}px`;
body._marginChangedForModal.push(el);
}
);
// Adjust body padding
const actualPadding = body.style.paddingRight;
setAttr(body, "data-padding-right", actualPadding);
body.style.paddingRight = `${toFloat(getCS(body).paddingRight, 0) +
scrollbarWidth}px`;
}
},
resetScrollbar() {
const body = document.body;
if (body._paddingChangedForModal) {
// Restore fixed content padding
body._paddingChangedForModal.forEach(el => {
/* istanbul ignore next: difficult to test in JSDOM */
if (hasAttr(el, "data-padding-right")) {
el.style.paddingRight = getAttr(el, "data-padding-right") || "";
removeAttr(el, "data-padding-right");
}
});
}
if (body._marginChangedForModal) {
// Restore sticky content and navbar-toggler margin
body._marginChangedForModal.forEach(el => {
/* istanbul ignore next: difficult to test in JSDOM */
if (hasAttr(el, "data-margin-right")) {
el.style.marginRight = getAttr(el, "data-margin-right") || "";
removeAttr(el, "data-margin-right");
}
});
}
body._paddingChangedForModal = null;
body._marginChangedForModal = null;
// Restore body padding
if (hasAttr(body, "data-padding-right")) {
body.style.paddingRight = getAttr(body, "data-padding-right") || "";
removeAttr(body, "data-padding-right");
}
}
}
});
// Create and export our modal manager instance
export const modalManager = new ModalManager();