@pi0/framework7
Version:
Full featured mobile HTML framework for building iOS & Android apps
237 lines (212 loc) • 6.94 kB
JavaScript
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({
backdrop: true,
closeByOutsideClick: app.params.popover.closeByOutsideClick,
on: {},
}, 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);
} else {
$el = $(popover.params.el);
}
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) {
$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];
}
originalOpen.call(popover, animate);
},
});
function handleResize() {
popover.resize();
}
popover.on('popoverOpen', () => {
popover.resize();
app.on('resize', handleResize);
popover.on('popoverClose', () => {
app.off('resize', handleResize);
});
});
function handleClick(e) {
const target = e.target;
if ($(target).closest(popover.el).length === 0) {
popover.close();
}
}
popover.on('popoverOpened', () => {
if (popover.params.closeByOutsideClick && !popover.params.backdrop) {
app.on('click', handleClick);
}
});
popover.on('popoverClose', () => {
if (popover.params.closeByOutsideClick && !popover.params.backdrop) {
app.off('click', handleClick);
}
});
$el[0].f7Modal = popover;
return popover;
}
resize() {
const popover = this;
const { app, $el, $targetEl, $angleEl } = popover;
$el.css({ left: '', top: '' });
const [width, height] = [$el.width(), $el.height()];
let angleSize = 0;
let angleLeft;
let angleTop;
if (app.theme === 'ios') {
$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').css({ left: '', top: '' });
}
const targetWidth = $targetEl.outerWidth();
const targetHeight = $targetEl.outerHeight();
const targetOffset = $targetEl.offset();
const targetOffsetLeft = targetOffset.left - app.left;
let targetOffsetTop = targetOffset.top - app.top;
const targetParentPage = $targetEl.parents('.page');
if (targetParentPage.length > 0) {
targetOffsetTop -= targetParentPage[0].scrollTop;
}
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;
} else if (height < targetOffsetTop) {
// On top
top = (targetOffsetTop - height) + targetHeight;
position = 'top';
} else {
// On middle
position = 'bottom';
top = targetOffsetTop;
}
if (top <= 0) {
top = 8;
} else if (top + height >= app.height) {
top = app.height - height - 8;
}
// Horizontal Position
left = (targetOffsetLeft + targetWidth) - width - 8;
if (left + width >= app.width - 8) {
left = (targetOffsetLeft + targetWidth) - width - 8;
}
if (left < 8) {
left = 8;
}
if (position === 'top') {
$el.addClass('popover-on-top');
}
if (position === 'bottom') {
$el.addClass('popover-on-bottom');
}
} else {
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;
if (top <= 0) {
top = 5;
} else if (top + height >= app.height) {
top = app.height - height - 5;
}
diff -= top;
}
// Horizontal Position
if (position === 'top' || position === 'bottom') {
left = ((targetWidth / 2) + targetOffsetLeft) - (width / 2);
diff = left;
if (left < 5) left = 5;
if (left + width > app.width) left = app.width - width - 5;
if (left < 0) left = 0;
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;