framework7
Version:
Full featured mobile HTML framework for building iOS & Android apps
556 lines (551 loc) • 17.3 kB
JavaScript
import { getDocument } from 'ssr-window';
import $ from '../../shared/dom7.js';
import { extend, deleteProps } from '../../shared/utils.js';
import Framework7Class from '../../shared/class.js';
import swipePanel from './swipe-panel.js';
import resizablePanel from './resizable-panel.js';
class Panel extends Framework7Class {
constructor(app, params) {
if (params === void 0) {
params = {};
}
const extendedParams = extend({
on: {}
}, app.params.panel, params);
super(extendedParams, [app]);
const panel = this;
panel.params = extendedParams;
panel.$containerEl = panel.params.containerEl ? $(panel.params.containerEl).eq(0) : app.$el;
panel.containerEl = panel.$containerEl[0];
if (!panel.containerEl) {
panel.$containerEl = app.$el;
panel.containerEl = app.$el[0];
}
let $el;
if (panel.params.el) {
$el = $(panel.params.el).eq(0);
} else if (panel.params.content) {
$el = $(panel.params.content).filter(node => node.nodeType === 1).eq(0);
}
if ($el.length === 0) return panel;
if ($el[0].f7Panel) return $el[0].f7Panel;
$el[0].f7Panel = panel;
let {
side,
effect,
resizable
} = panel.params;
if (typeof side === 'undefined') side = $el.hasClass('panel-left') ? 'left' : 'right';
if (typeof effect === 'undefined')
// eslint-disable-next-line
effect = $el.hasClass('panel-cover') ? 'cover' : $el.hasClass('panel-push') ? 'push' : $el.hasClass('panel-floating') ? 'floating' : 'reveal';
if (typeof resizable === 'undefined') resizable = $el.hasClass('panel-resizable');
let $backdropEl;
if (panel.params.backdrop && panel.params.backdropEl) {
$backdropEl = $(panel.params.backdropEl);
} else if (panel.params.backdrop) {
$backdropEl = panel.$containerEl.children('.panel-backdrop');
if ($backdropEl.length === 0) {
$backdropEl = $('<div class="panel-backdrop"></div>');
panel.$containerEl.prepend($backdropEl);
}
}
extend(panel, {
app,
side,
effect,
resizable,
$el,
el: $el[0],
opened: false,
$backdropEl,
backdropEl: $backdropEl && $backdropEl[0]
});
// Install Modules
panel.useModules();
// Init
panel.init();
return panel;
}
getViewEl() {
const panel = this;
let viewEl;
if (panel.$containerEl.children('.views').length > 0) {
viewEl = panel.$containerEl.children('.views')[0];
} else {
viewEl = panel.$containerEl.children('.view')[0];
}
return viewEl;
}
setStateClasses(state) {
const panel = this;
const {
side,
el,
effect
} = panel;
const viewEl = panel.getViewEl();
const panelInView = viewEl && viewEl.contains(el);
const $targetEl = !viewEl || panelInView ? panel.$containerEl : $('html');
if (state === 'open') {
$targetEl.addClass(`with-panel with-panel-${panel.side}-${panel.effect}`);
}
if (state === 'before-closing') {
$targetEl.addClass('with-panel-closing');
}
if (state === 'closing') {
$targetEl.addClass('with-panel-closing');
$targetEl.removeClass(`with-panel with-panel-${panel.side}-${panel.effect}`);
}
if (state === 'after-closing') {
$targetEl.removeClass('with-panel-closing');
}
if (state === 'closed') {
$targetEl.removeClass(`with-panel-${side}-${effect}`);
}
}
enableVisibleBreakpoint() {
const panel = this;
panel.visibleBreakpointDisabled = false;
panel.setVisibleBreakpoint();
return panel;
}
disableVisibleBreakpoint() {
const panel = this;
panel.visibleBreakpointDisabled = true;
panel.setVisibleBreakpoint();
return panel;
}
toggleVisibleBreakpoint() {
const panel = this;
panel.visibleBreakpointDisabled = !panel.visibleBreakpointDisabled;
panel.setVisibleBreakpoint();
return panel;
}
setVisibleBreakpoint(emitEvents) {
if (emitEvents === void 0) {
emitEvents = true;
}
const panel = this;
const app = panel.app;
if (!panel.visibleBreakpointResizeHandler) {
panel.visibleBreakpointResizeHandler = function visibleBreakpointResizeHandler() {
panel.setVisibleBreakpoint();
};
app.on('resize', panel.visibleBreakpointResizeHandler);
}
const {
side,
$el,
$containerEl,
params,
visibleBreakpointDisabled
} = panel;
const breakpoint = params.visibleBreakpoint;
const $viewEl = $(panel.getViewEl());
const wasVisible = $el.hasClass('panel-in-breakpoint');
if ($containerEl && $containerEl.hasClass('page')) {
$viewEl.add($containerEl.children('.page-content, .tabs, .fab'));
}
if (app.width >= breakpoint && typeof breakpoint !== 'undefined' && breakpoint !== null && !visibleBreakpointDisabled) {
if (!wasVisible) {
panel.setStateClasses('closed');
$el.addClass('panel-in-breakpoint').removeClass('panel-in panel-in-collapsed');
panel.onOpen(false);
panel.onOpened();
$viewEl.css({
[`margin-${side}`]: `${$el.width()}px`
});
app.allowPanelOpen = true;
if (emitEvents) {
panel.emit('local::breakpoint panelBreakpoint', panel);
panel.$el.trigger('panel:breakpoint');
}
} else {
$viewEl.css({
[`margin-${side}`]: `${$el.width()}px`
});
}
} else if (wasVisible) {
$el.removeClass('panel-in-breakpoint panel-in');
panel.onClose();
panel.onClosed();
$viewEl.css({
[`margin-${side}`]: ''
});
if (emitEvents) {
panel.emit('local::breakpoint panelBreakpoint', panel);
panel.$el.trigger('panel:breakpoint');
}
}
}
enableCollapsedBreakpoint() {
const panel = this;
panel.collapsedBreakpointDisabled = false;
panel.setCollapsedBreakpoint();
return panel;
}
disableCollapsedBreakpoint() {
const panel = this;
panel.collapsedBreakpointDisabled = true;
panel.setCollapsedBreakpoint();
return panel;
}
toggleCollapsedBreakpoint() {
const panel = this;
panel.collapsedBreakpointDisabled = !panel.collapsedBreakpointDisabled;
panel.setCollapsedBreakpoint();
return panel;
}
setCollapsedBreakpoint(emitEvents) {
if (emitEvents === void 0) {
emitEvents = true;
}
const panel = this;
const app = panel.app;
if (!panel.collapsedBreakpointResizeHandler) {
panel.collapsedBreakpointResizeHandler = function collapsedBreakpointResizeHandler() {
panel.setCollapsedBreakpoint();
};
app.on('resize', panel.collapsedBreakpointResizeHandler);
}
const {
$el,
params,
collapsedBreakpointDisabled
} = panel;
if ($el.hasClass('panel-in-breakpoint')) return;
const breakpoint = params.collapsedBreakpoint;
const wasVisible = $el.hasClass('panel-in-collapsed');
if (app.width >= breakpoint && typeof breakpoint !== 'undefined' && breakpoint !== null && !collapsedBreakpointDisabled) {
if (!wasVisible) {
panel.setStateClasses('closed');
$el.addClass('panel-in-collapsed').removeClass('panel-in');
panel.collapsed = true;
app.allowPanelOpen = true;
if (emitEvents) {
panel.emit('local::collapsedBreakpoint panelCollapsedBreakpoint', panel);
panel.$el.trigger('panel:collapsedbreakpoint');
}
}
} else if (wasVisible) {
$el.removeClass('panel-in-collapsed panel-in');
panel.collapsed = false;
if (emitEvents) {
panel.emit('local::collapsedBreakpoint panelCollapsedBreakpoint', panel);
panel.$el.trigger('panel:collapsedbreakpoint');
}
}
}
enableResizable() {
const panel = this;
if (panel.resizableInitialized) {
panel.resizable = true;
panel.$el.addClass('panel-resizable');
} else {
resizablePanel(panel);
}
return panel;
}
disableResizable() {
const panel = this;
panel.resizable = false;
panel.$el.removeClass('panel-resizable');
return panel;
}
enableSwipe() {
const panel = this;
if (panel.swipeInitialized) {
panel.swipeable = true;
} else {
swipePanel(panel);
}
return panel;
}
disableSwipe() {
const panel = this;
panel.swipeable = false;
return panel;
}
onOpen(modifyHtmlClasses) {
if (modifyHtmlClasses === void 0) {
modifyHtmlClasses = true;
}
const panel = this;
// eslint-disable-next-line
panel._openTransitionStarted = false;
const app = panel.app;
panel.opened = true;
app.panel.allowOpen = false;
panel.$el.trigger('panel:beforeopen');
panel.emit('local::beforeOpen panelBeforeOpen', panel);
if (modifyHtmlClasses) {
panel.setStateClasses('open');
}
panel.$el.trigger('panel:open');
panel.emit('local::open panelOpen', panel);
}
onOpened() {
const panel = this;
const app = panel.app;
app.panel.allowOpen = true;
panel.$el.trigger('panel:opened');
panel.emit('local::opened panelOpened', panel);
}
onClose() {
const panel = this;
const app = panel.app;
panel.opened = false;
app.panel.allowOpen = false;
panel.$el.trigger('panel:beforeclose');
panel.emit('local::beforeClose panelBeforeClose', panel);
panel.setStateClasses('closing');
panel.$el.trigger('panel:close');
panel.emit('local::close panelClose', panel);
}
onClosed() {
const panel = this;
const app = panel.app;
app.panel.allowOpen = true;
panel.setStateClasses('after-closing');
panel.$el.removeClass('panel-out');
if (panel.$backdropEl) {
const otherPanel = app.panel.get('.panel-in');
const shouldHideBackdrop = !otherPanel || otherPanel && !otherPanel.$backdropEl;
if (shouldHideBackdrop) {
panel.$backdropEl.removeClass('panel-backdrop-in');
}
}
panel.$el.trigger('panel:closed');
panel.emit('local::closed panelClosed', panel);
}
toggle(animate) {
if (animate === void 0) {
animate = true;
}
const panel = this;
const breakpoint = panel.params.visibleBreakpoint;
const app = panel.app;
if (app.width >= breakpoint && typeof breakpoint !== 'undefined' && breakpoint !== null) {
return panel.toggleVisibleBreakpoint();
}
if (panel.opened) panel.close(animate);else panel.open(animate);
return panel;
}
insertToRoot() {
const panel = this;
const document = getDocument();
const {
$el,
$backdropEl,
$containerEl
} = panel;
const $panelParentEl = $el.parent();
const wasInDom = $el.parents(document).length > 0;
if (!$panelParentEl.is($containerEl) || $el.prevAll('.views, .view').length) {
const $insertBeforeEl = $containerEl.children('.panel, .views, .view').eq(0);
const $insertAfterEl = $containerEl.children('.panel-backdrop').eq(0);
if ($insertBeforeEl.length) {
$el.insertBefore($insertBeforeEl);
} else if ($insertAfterEl) {
$el.insertBefore($insertAfterEl);
} else {
$containerEl.prepend($el);
}
if ($backdropEl && $backdropEl.length && (!$backdropEl.parent().is($containerEl) && $backdropEl.nextAll('.panel').length === 0 || $backdropEl.parent().is($containerEl) && $backdropEl.nextAll('.panel').length === 0)) {
$backdropEl.insertBefore($el);
}
panel.once('panelClosed', () => {
if (wasInDom) {
$panelParentEl.append($el);
} else {
$el.remove();
}
});
}
}
open(animate) {
if (animate === void 0) {
animate = true;
}
const panel = this;
const app = panel.app;
if (!app.panel.allowOpen) return false;
const {
effect,
$el,
$backdropEl,
opened,
$containerEl
} = panel;
if (!$el || $el.hasClass('panel-in')) {
return panel;
}
panel.insertToRoot();
// Ignore if opened
if (opened || $el.hasClass('panel-in-breakpoint') || $el.hasClass('panel-in')) return false;
// Close if some panel is opened
const otherOpenedPanel = app.panel.get('.panel-in');
if (otherOpenedPanel && otherOpenedPanel !== panel) {
otherOpenedPanel.close(animate);
}
$el[animate ? 'removeClass' : 'addClass']('not-animated');
$el.addClass('panel-in');
if ($backdropEl) {
$backdropEl.addClass('panel-backdrop-in');
$backdropEl[animate ? 'removeClass' : 'addClass']('not-animated');
}
if (['cover', 'push', 'floating'].includes(panel.effect)) {
/* eslint no-underscore-dangle: ["error", { "allow": ["_clientLeft"] }] */
panel._clientLeft = $el[0].clientLeft;
}
// Transitionend
const $viewEl = $(panel.getViewEl());
if ($containerEl && $containerEl.hasClass('page')) {
$viewEl.add($containerEl.children('.page-content, .tabs'));
}
const transitionEndTarget = effect === 'reveal' ? $viewEl : $el;
function panelTransitionStart() {
transitionEndTarget.transitionStart(() => {
// eslint-disable-next-line
panel._openTransitionStarted = true;
});
}
function panelTransitionEnd() {
transitionEndTarget.transitionEnd(e => {
if ($(e.target).is(transitionEndTarget)) {
if ($el.hasClass('panel-out')) {
panel.onClosed();
} else {
panel.onOpened();
}
} else panelTransitionEnd();
});
}
if (animate) {
if ($backdropEl) {
$backdropEl.removeClass('not-animated');
}
panelTransitionStart();
panelTransitionEnd();
$el.removeClass('panel-out not-animated').addClass('panel-in');
panel.onOpen();
} else {
if ($backdropEl) {
$backdropEl.addClass('not-animated');
}
$el.removeClass('panel-out').addClass('panel-in not-animated');
panel.onOpen();
panel.onOpened();
// eslint-disable-next-line
panel._openTransitionStarted = true;
}
return true;
}
close(animate) {
if (animate === void 0) {
animate = true;
}
const panel = this;
const {
effect,
$el,
$backdropEl,
opened,
$containerEl
} = panel;
if (!opened || $el.hasClass('panel-in-breakpoint') || !$el.hasClass('panel-in')) return panel;
$el[animate ? 'removeClass' : 'addClass']('not-animated');
if ($backdropEl) {
$backdropEl[animate ? 'removeClass' : 'addClass']('not-animated');
}
const $viewEl = $(panel.getViewEl());
if ($containerEl && $containerEl.hasClass('page')) {
$viewEl.add($containerEl.children('.page-content, .tabs'));
}
const transitionEndTarget = effect === 'reveal' ? $viewEl : $el;
// eslint-disable-next-line
if (!panel._openTransitionStarted) {
// eslint-disable-next-line
animate = false;
}
function transitionEnd() {
if ($el.hasClass('panel-out')) {
panel.onClosed();
} else if ($el.hasClass('panel-in')) {
panel.onOpened();
}
panel.setStateClasses('after-closing');
}
if (animate) {
transitionEndTarget.transitionEnd(() => {
transitionEnd();
});
$el.removeClass('panel-in').addClass('panel-out');
// Emit close
panel.onClose();
} else {
$el.addClass('not-animated').removeClass('panel-in').addClass('panel-out');
// Emit close
panel.onClose();
panel.onClosed();
}
return panel;
}
init() {
const panel = this;
// const app = panel.app;
if (typeof panel.params.visibleBreakpoint !== 'undefined') {
panel.setVisibleBreakpoint();
}
if (typeof panel.params.collapsedBreakpoint !== 'undefined') {
panel.setCollapsedBreakpoint();
}
if (panel.params.swipe) {
panel.enableSwipe();
}
if (panel.resizable) {
panel.enableResizable();
}
}
destroy() {
let panel = this;
const app = panel.app;
const {
$containerEl
} = panel;
if (!panel.$el) {
// Panel already destroyed
return;
}
panel.emit('local::beforeDestroy panelBeforeDestroy', panel);
panel.$el.trigger('panel:beforedestroy');
if (panel.visibleBreakpointResizeHandler) {
app.off('resize', panel.visibleBreakpointResizeHandler);
}
if (panel.collapsedBreakpointResizeHandler) {
app.off('resize', panel.collapsedBreakpointResizeHandler);
}
if (panel.$el.hasClass('panel-in-breakpoint') || panel.$el.hasClass('panel-in-collapsed')) {
const $viewEl = $(panel.getViewEl());
if ($containerEl && $containerEl.hasClass('page')) {
$viewEl.add($containerEl.children('.page-content, .tabs'));
}
panel.$el.removeClass('panel-in-breakpoint panel-in-collapsed panel-in');
$viewEl.css({
[`margin-${panel.side}`]: ''
});
panel.emit('local::breakpoint panelBreakpoint', panel);
panel.$el.trigger('panel:breakpoint');
}
panel.$el.trigger('panel:destroy');
panel.emit('local::destroy panelDestroy', panel);
if (panel.el) {
panel.el.f7Panel = null;
delete panel.el.f7Panel;
}
deleteProps(panel);
panel = null;
}
}
export default Panel;