UNPKG

framework7

Version:

Full featured mobile HTML framework for building iOS & Android apps

275 lines (246 loc) 8.7 kB
import $ from 'dom7'; import Utils from '../../utils/utils'; import Modal from '../modal/modal-class'; class Popover extends Modal { constructor(app, params) { const extendedParams = Utils.extend( { on: {} }, app.params.popover, params ); // Extends with open/close Modal methods; super(app, extendedParams); const popover = this; popover.params = extendedParams; // Find Element let $el; if (!popover.params.el) { $el = $(popover.params.content).filter((elIndex, node) => node.nodeType === 1).eq(0); } else { $el = $(popover.params.el).eq(0); } if ($el && $el.length > 0 && $el[0].f7Modal) { return $el[0].f7Modal; } // Find Target const $targetEl = $(popover.params.targetEl).eq(0); if ($el.length === 0) { return popover.destroy(); } // Backdrop let $backdropEl; if (popover.params.backdrop && popover.params.backdropEl) { $backdropEl = $(popover.params.backdropEl); } else if (popover.params.backdrop) { $backdropEl = app.root.children('.popover-backdrop'); if ($backdropEl.length === 0) { $backdropEl = $('<div class="popover-backdrop"></div>'); app.root.append($backdropEl); } } // Find Angle let $angleEl; if ($el.find('.popover-angle').length === 0) { $angleEl = $('<div class="popover-angle"></div>'); $el.prepend($angleEl); } else { $angleEl = $el.find('.popover-angle'); } // Open const originalOpen = popover.open; Utils.extend(popover, { app, $el, el: $el[0], $targetEl, targetEl: $targetEl[0], $angleEl, angleEl: $angleEl[0], $backdropEl, backdropEl: $backdropEl && $backdropEl[0], type: 'popover', open(...args) { let [targetEl, animate] = args; if (typeof args[0] === 'boolean') [animate, targetEl] = args; if (targetEl) { popover.$targetEl = $(targetEl); popover.targetEl = popover.$targetEl[0]; } return originalOpen.call(popover, animate); }, }); function handleResize() { popover.resize(); } popover.on('popoverOpen', () => { popover.resize(); app.on('resize', handleResize); $(window).on('keyboardDidShow keyboardDidHide', handleResize); popover.on('popoverClose popoverBeforeDestroy', () => { app.off('resize', handleResize); $(window).off('keyboardDidShow keyboardDidHide', handleResize); }); }); function handleClick(e) { const target = e.target; const $target = $(target); const keyboardOpened = !app.device.desktop && app.device.cordova && ((window.Keyboard && window.Keyboard.isVisible) || (window.cordova.plugins && window.cordova.plugins.Keyboard && window.cordova.plugins.Keyboard.isVisible)); if (keyboardOpened) return; if ($target.closest(popover.el).length === 0) { if ( popover.params.closeByBackdropClick && popover.params.backdrop && popover.backdropEl && popover.backdropEl === target ) { popover.close(); } else if (popover.params.closeByOutsideClick) { popover.close(); } } } function onKeyDown(e) { const keyCode = e.keyCode; if (keyCode === 27 && popover.params.closeOnEscape) { popover.close(); } } if (popover.params.closeOnEscape) { popover.on('popoverOpen', () => { $(document).on('keydown', onKeyDown); }); popover.on('popoverClose', () => { $(document).off('keydown', onKeyDown); }); } popover.on('popoverOpened', () => { if (popover.params.closeByOutsideClick || popover.params.closeByBackdropClick) { app.on('click', handleClick); } }); popover.on('popoverClose', () => { if (popover.params.closeByOutsideClick || popover.params.closeByBackdropClick) { app.off('click', handleClick); } }); $el[0].f7Modal = popover; return popover; } resize() { const popover = this; const { app, $el, $targetEl, $angleEl } = popover; const { targetX, targetY } = popover.params; $el.css({ left: '', top: '' }); const [width, height] = [$el.width(), $el.height()]; let angleSize = 0; let angleLeft; let angleTop; if (app.theme === 'ios' || app.theme === 'aurora') { $angleEl.removeClass('on-left on-right on-top on-bottom').css({ left: '', top: '' }); angleSize = $angleEl.width() / 2; } else { $el.removeClass('popover-on-left popover-on-right popover-on-top popover-on-bottom popover-on-middle').css({ left: '', top: '' }); } let targetWidth; let targetHeight; let targetOffsetLeft; let targetOffsetTop; if ($targetEl && $targetEl.length > 0) { targetWidth = $targetEl.outerWidth(); targetHeight = $targetEl.outerHeight(); const targetOffset = $targetEl.offset(); targetOffsetLeft = targetOffset.left - app.left; targetOffsetTop = targetOffset.top - app.top; const targetParentPage = $targetEl.parents('.page'); if (targetParentPage.length > 0) { targetOffsetTop -= targetParentPage[0].scrollTop; } } else if (typeof targetX !== 'undefined' && targetY !== 'undefined') { targetOffsetLeft = targetX; targetOffsetTop = targetY; targetWidth = popover.params.targetWidth || 0; targetHeight = popover.params.targetHeight || 0; } let [left, top, diff] = [0, 0, 0]; // Top Position let position = app.theme === 'md' ? 'bottom' : 'top'; if (app.theme === 'md') { if (height < app.height - targetOffsetTop - targetHeight) { // On bottom position = 'bottom'; top = targetOffsetTop + targetHeight; } else if (height < targetOffsetTop) { // On top top = targetOffsetTop - height; position = 'top'; } else { // On middle position = 'middle'; top = ((targetHeight / 2) + targetOffsetTop) - (height / 2); } top = Math.max(8, Math.min(top, app.height - height - 8)); // Horizontal Position let hPosition; if (targetOffsetLeft < app.width / 2) { hPosition = 'right'; left = position === 'middle' ? targetOffsetLeft + targetWidth : targetOffsetLeft; } else { hPosition = 'left'; left = position === 'middle' ? targetOffsetLeft - width : (targetOffsetLeft + targetWidth) - width; } left = Math.max(8, Math.min(left, app.width - width - 8)); $el.addClass(`popover-on-${position} popover-on-${hPosition}`); } else { // ios and aurora if ((height + angleSize) < targetOffsetTop) { // On top top = targetOffsetTop - height - angleSize; } else if ((height + angleSize) < app.height - targetOffsetTop - targetHeight) { // On bottom position = 'bottom'; top = targetOffsetTop + targetHeight + angleSize; } else { // On middle position = 'middle'; top = ((targetHeight / 2) + targetOffsetTop) - (height / 2); diff = top; top = Math.max(5, Math.min(top, app.height - height - 5)); diff -= top; } // Horizontal Position if (position === 'top' || position === 'bottom') { left = ((targetWidth / 2) + targetOffsetLeft) - (width / 2); diff = left; left = Math.max(5, Math.min(left, app.width - width - 5)); if (position === 'top') { $angleEl.addClass('on-bottom'); } if (position === 'bottom') { $angleEl.addClass('on-top'); } diff -= left; angleLeft = ((width / 2) - angleSize) + diff; angleLeft = Math.max(Math.min(angleLeft, width - (angleSize * 2) - 13), 13); $angleEl.css({ left: `${angleLeft}px` }); } else if (position === 'middle') { left = targetOffsetLeft - width - angleSize; $angleEl.addClass('on-right'); if (left < 5 || (left + width > app.width)) { if (left < 5) left = targetOffsetLeft + targetWidth + angleSize; if (left + width > app.width) left = app.width - width - 5; $angleEl.removeClass('on-right').addClass('on-left'); } angleTop = ((height / 2) - angleSize) + diff; angleTop = Math.max(Math.min(angleTop, height - (angleSize * 2) - 13), 13); $angleEl.css({ top: `${angleTop}px` }); } } // Apply Styles $el.css({ top: `${top}px`, left: `${left}px` }); } } export default Popover;