comindware.ui
Version:
Comindware Core UI provides the basic components like editors, lists, dropdowns, popups that we so desperately need while creating Marionette-based single-page applications.
163 lines (140 loc) • 5.33 kB
JavaScript
/**
* Developer: Ksenia Kartvelishvili
* Date: 9/9/2016
* Copyright: 2009-2016 Comindware®
* All Rights Reserved
*
* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF Comindware
* The copyright notice above does not evidence any
* actual or intended publication of such source code.
*/
'use strict';
import { Handlebars, $ } from 'lib';
import template from '../templates/PopupStack.hbs';
const classes = {
POPUP_REGION: 'js-popup-region-',
POPUP_FADE: 'popup-fade'
};
const POPUP_ID_PREFIX = 'popup-region-';
export default Marionette.LayoutView.extend({
initialize() {
this.__stack = [];
this.__forceFadeBackground = false;
},
template: Handlebars.compile(template),
ui: {
fadingPanel: '.js-fading-panel'
},
showPopup(view, options) {
const { fadeBackground, transient, hostEl } = options;
if (!transient) {
this.__removeTransientPopups();
}
const popupId = _.uniqueId(POPUP_ID_PREFIX);
const regionEl = $(`<div data-popup-id="${popupId}" class="js-core-ui__global-popup-region">`);
let parentPopupId = null;
if (hostEl) {
parentPopupId = $(hostEl).closest('.js-core-ui__global-popup-region').data('popup-id') || null;
}
const config = {
view,
options,
regionEl,
popupId,
parentPopupId
};
if (parentPopupId) {
// If there is a child popup, it must be closed:
// 1. There might be nested dropdowns
// 2. There can't be dropdowns opened on the same level
const childPopupDef = this.__stack.find(x => x.parentPopupId === parentPopupId);
if (childPopupDef) {
this.closePopup(childPopupDef.popupId);
}
}
this.$el.append(regionEl);
this.addRegion(popupId, { el: regionEl });
this.getRegion(popupId).show(view);
if (fadeBackground) {
const lastFaded = _.last(this.__stack.filter(x => x.options.fadeBackground));
if (lastFaded) {
lastFaded.regionEl.removeClass(classes.POPUP_FADE);
} else {
this.__toggleFadedBackground(true);
}
regionEl.addClass(classes.POPUP_FADE);
}
this.__stack.push(config);
return popupId;
},
closePopup(popupId = null) {
if (this.__stack.length === 0) {
return;
}
let targets;
const popupDef = this.__stack.find(x => x.popupId === popupId);
if (popupDef) {
if (!popupDef.options.transient) {
this.__removeTransientPopups();
}
// All the children of the popup will also be closed
// Important: we collect only logical children because another popup might have been opened at the same level already.
// e.g.: focus-blur events (usually focus comes first) - one popup is opened on focus and the previous one is closed on blur.
if (this.__stack.includes(popupDef)) {
targets = [ popupDef ];
const handleChildren = pId => {
const children = this.__stack.filter(x => x.parentPopupId === pId);
targets.push(...children);
children.forEach(c => handleChildren(c.popupId));
};
handleChildren(popupId);
} else {
targets = [];
}
} else if (popupId) {
// If we don't find the popup, it must have been closed so the job is done
targets = [];
} else {
// Close all transient popups and the top-most non-transient
this.__removeTransientPopups();
const topMostNonTransient = _.last(this.__stack);
if (topMostNonTransient) {
targets = [ topMostNonTransient ];
}
}
targets.reverse().forEach(pd => {
this.__removePopup(pd);
});
const lastFaded = _.last(this.__stack.filter(x => x.options.fadeBackground));
if (lastFaded) {
lastFaded.regionEl.addClass(classes.POPUP_FADE);
} else {
this.__toggleFadedBackground(this.__forceFadeBackground);
}
},
__removeTransientPopups() {
this.__stack.filter(x => x.options.transient).reverse().forEach(popupDef => {
this.__removePopup(popupDef);
});
},
__removePopup(popupDef) {
this.removeRegion(popupDef.popupId);
popupDef.regionEl.remove();
this.__stack.splice(this.__stack.indexOf(popupDef), 1);
this.trigger('popup:close', popupDef.popupId);
},
get(popupId) {
const index = this.__stack.findIndex(x => x.popupId === popupId);
if (index === -1) {
return [];
}
return this.__stack.slice(index).map(x => x.view);
},
fadeBackground(fade) {
this.__forceFadeBackground = fade;
this.__toggleFadedBackground(this.__forceFadeBackground || this.__stack.find(x => x.options.fadeBackground));
},
__toggleFadedBackground(fade) {
this.ui.fadingPanel.toggleClass('fadingPanel_open', fade);
}
});