UNPKG

framework7

Version:

Full featured mobile HTML framework for building iOS & Android apps

518 lines (443 loc) 14.3 kB
import $ from 'dom7'; import Utils from '../../utils/utils'; import Framework7Class from '../../utils/class'; import SwipePanel from './swipe-panel'; import ResizablePanel from './resizable-panel'; class Panel extends Framework7Class { constructor(app, params = {}) { const extendedParams = Utils.extend( { on: {} }, app.params.panel, params ); super(extendedParams, [app]); const panel = this; panel.params = extendedParams; let $el; if (panel.params.el) { $el = $(panel.params.el).eq(0); } else if (panel.params.content) { $el = $(panel.params.content).filter((elIndex, 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') effect = $el.hasClass('panel-cover') ? 'cover' : '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 = app.root.children('.panel-backdrop'); if ($backdropEl.length === 0) { $backdropEl = $('<div class="panel-backdrop"></div>'); app.root.prepend($backdropEl); } } Utils.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; const app = panel.app; let viewEl; if (app.root.children('.views').length > 0) { viewEl = app.root.children('.views')[0]; } else { viewEl = app.root.children('.view')[0]; } return viewEl; } 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 = 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, params, visibleBreakpointDisabled } = panel; const breakpoint = params.visibleBreakpoint; const $viewEl = $(panel.getViewEl()); const wasVisible = $el.hasClass('panel-in-breakpoint'); if (app.width >= breakpoint && typeof breakpoint !== 'undefined' && breakpoint !== null && !visibleBreakpointDisabled) { if (!wasVisible) { $('html').removeClass(`with-panel-${side}-reveal with-panel-${side}-cover with-panel`); $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.$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.$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) { const panel = this; const app = panel.app; if (!panel.collapsedBreakpointResizeHandler) { panel.collapsedBreakpointResizeHandler = function collapsedBreakpointResizeHandler() { panel.setCollapsedBreakpoint(); }; app.on('resize', panel.collapsedBreakpointResizeHandler); } const { side, $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) { $('html').removeClass(`with-panel-${side}-reveal with-panel-${side}-cover with-panel`); $el.addClass('panel-in-collapsed').removeClass('panel-in'); panel.collapsed = true; app.allowPanelOpen = true; if (emitEvents) { panel.emit('local::collapsedBreakpoint panelCollapsedBreakpoint'); 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.$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 = true) { const panel = this; const app = panel.app; panel.opened = true; app.panel.allowOpen = false; panel.$el.trigger('panel:beforeopen'); panel.emit('local::beforeOpen panelBeforeOpen', panel); if (modifyHtmlClasses) { $('html').addClass(`with-panel with-panel-${panel.side}-${panel.effect}`); } 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); $('html').addClass('with-panel-closing'); $('html').removeClass(`with-panel with-panel-${panel.side}-${panel.effect}`); panel.$el.trigger('panel:close'); panel.emit('local::close panelClose', panel); } onClosed() { const panel = this; const app = panel.app; app.panel.allowOpen = true; $('html').removeClass('with-panel-closing'); panel.$el.removeClass('panel-out'); panel.$el.trigger('panel:closed'); panel.emit('local::closed panelClosed', panel); } toggle(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 { $el, app, $backdropEl } = panel; const $panelParentEl = $el.parent(); const wasInDom = $el.parents(document).length > 0; if (!$panelParentEl.is(app.root) || $el.prevAll('.views, .view').length) { const $insertBeforeEl = app.root.children('.panel, .views, .view').eq(0); const $insertAfterEl = app.root.children('.panel-backdrop').eq(0); if ($insertBeforeEl.length) { $el.insertBefore($insertBeforeEl); } else if ($insertAfterEl) { $el.insertBefore($insertAfterEl); } else { app.root.prepend($el); } if ($backdropEl && $backdropEl.length && ( ( !$backdropEl.parent().is(app.root) && $backdropEl.nextAll('.panel').length === 0 ) || ( $backdropEl.parent().is(app.root) && $backdropEl.nextAll('.panel').length === 0 ) ) ) { $backdropEl.insertBefore($el); } panel.once('panelClosed', () => { if (wasInDom) { $panelParentEl.append($el); } else { $el.remove(); } }); } } open(animate = true) { const panel = this; const app = panel.app; if (!app.panel.allowOpen) return false; const { effect, $el, $backdropEl, opened } = 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'); $backdropEl[animate ? 'removeClass' : 'addClass']('not-animated'); if (panel.effect === 'cover') { /* eslint no-underscore-dangle: ["error", { "allow": ["_clientLeft"] }] */ panel._clientLeft = $el[0].clientLeft; } // Transitionend const transitionEndTarget = effect === 'reveal' ? $el.nextAll('.view, .views').eq(0) : $el; 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'); } 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(); } return true; } close(animate = true) { const panel = this; const { effect, $el, $backdropEl, opened } = panel; if (!opened || $el.hasClass('panel-in-breakpoint') || !$el.hasClass('panel-in')) return panel; $el[animate ? 'removeClass' : 'addClass']('not-animated'); $backdropEl[animate ? 'removeClass' : 'addClass']('not-animated'); const transitionEndTarget = effect === 'reveal' ? $el.nextAll('.view, .views').eq(0) : $el; function transitionEnd() { if ($el.hasClass('panel-out')) { panel.onClosed(); } else if ($el.hasClass('panel-in')) { panel.onOpened(); } $('html').removeClass('with-panel-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; 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()); panel.$el.removeClass('panel-in-breakpoint panel-in-collapsed panel-in'); $viewEl.css({ [`margin-${panel.side}`]: '', }); panel.emit('local::breakpoint panelBreakpoint'); panel.$el.trigger('panel:breakpoint'); } panel.$el.trigger('panel:destroy'); panel.emit('local::destroy panelDestroy'); if (panel.el) { panel.el.f7Panel = null; delete panel.el.f7Panel; } Utils.deleteProps(panel); panel = null; } } export default Panel;