mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-26 16:21:55 +08:00
fix(popover): position for all modes in one function
This commit is contained in:
@ -10,16 +10,11 @@ export default function(Animation: Animation, baseElm: HTMLElement) {
|
||||
const backdropAnimation = new Animation();
|
||||
backdropAnimation.addElement(baseElm.querySelector('.popover-backdrop'));
|
||||
|
||||
const wrapperAnimation = new Animation();
|
||||
wrapperAnimation.addElement(baseElm.querySelector('.popover-wrapper'));
|
||||
|
||||
wrapperAnimation.fromTo('opacity', 0.01, 1);
|
||||
backdropAnimation.fromTo('opacity', 0.01, 0.08);
|
||||
|
||||
return baseAnimation
|
||||
.addElement(baseElm)
|
||||
.easing('ease')
|
||||
.duration(100)
|
||||
.add(backdropAnimation)
|
||||
.add(wrapperAnimation);
|
||||
.add(backdropAnimation);
|
||||
}
|
||||
|
@ -52,92 +52,127 @@ export class Popover {
|
||||
}
|
||||
|
||||
|
||||
// TODO currently only positions iOS
|
||||
private positionView(nativeEle: HTMLElement, ev: any, mode: string) {
|
||||
// Default to material design mode unless mode is ios
|
||||
const popoverMode = (mode === 'ios') ? mode : 'md';
|
||||
const popoverProps = popoverViewProps[popoverMode];
|
||||
const popoverPadding = popoverProps.bodyPadding;
|
||||
private positionPopover(nativeEl: HTMLElement, ev: any, props: any) {
|
||||
console.debug('Position popover', nativeEl, ev, props);
|
||||
|
||||
// Declare the popover elements
|
||||
let popoverWrapperEle = nativeEle.querySelector('.popover-wrapper') as HTMLElement;
|
||||
let popoverContentEle = nativeEle.querySelector('.popover-content') as HTMLElement;
|
||||
let popoverArrowEle = nativeEle.querySelector('.popover-arrow') as HTMLElement;
|
||||
let contentEl = nativeEl.querySelector('.popover-content') as HTMLElement;
|
||||
let arrowEl = nativeEl.querySelector('.popover-arrow') as HTMLElement;
|
||||
|
||||
// Grab the default origin from the properties
|
||||
let originY = 'top';
|
||||
let originX = 'left';
|
||||
// Set the default transform origin direction
|
||||
let origin = {
|
||||
y: 'top',
|
||||
x: 'left'
|
||||
}
|
||||
|
||||
// Popover content width and height
|
||||
let popoverDim = popoverContentEle.getBoundingClientRect();
|
||||
let popoverWidth = popoverDim.width;
|
||||
let popoverHeight = popoverDim.height;
|
||||
const popover = {
|
||||
width: contentEl.getBoundingClientRect().width,
|
||||
height: contentEl.getBoundingClientRect().height
|
||||
}
|
||||
|
||||
// Window body width and height
|
||||
// TODO need to check if portrait/landscape?
|
||||
let bodyWidth = window.screen.width;
|
||||
let bodyHeight = window.screen.height;
|
||||
const body = {
|
||||
width: window.screen.width,
|
||||
height: window.screen.height
|
||||
}
|
||||
|
||||
// If ev was passed, use that for target element
|
||||
let targetDim = ev && ev.target && ev.target.getBoundingClientRect();
|
||||
|
||||
let targetTop = (targetDim && 'top' in targetDim) ? targetDim.top : (bodyHeight / 2) - (popoverHeight / 2);
|
||||
let targetLeft = (targetDim && 'left' in targetDim) ? targetDim.left : (bodyWidth / 2);
|
||||
let targetWidth = targetDim && targetDim.width || 0;
|
||||
let targetHeight = targetDim && targetDim.height || 0;
|
||||
// The target is the object that dispatched the event that was passed
|
||||
let target = {
|
||||
top: (targetDim && 'top' in targetDim) ? targetDim.top : (body.height / 2) - (popover.height / 2),
|
||||
left: (targetDim && 'left' in targetDim) ? targetDim.left : (body.width / 2) - (popover.width / 2),
|
||||
width: targetDim && targetDim.width || 0,
|
||||
height: targetDim && targetDim.height || 0
|
||||
};
|
||||
|
||||
// If the popover should be centered to the target
|
||||
if (props.centerTarget) {
|
||||
target.left = (targetDim && 'left' in targetDim) ? targetDim.left : (body.width / 2);
|
||||
}
|
||||
|
||||
// The arrow that shows above the popover on iOS
|
||||
let arrowDim = popoverArrowEle.getBoundingClientRect();
|
||||
var arrowWidth = arrowDim.width;
|
||||
var arrowHeight = arrowDim.height;
|
||||
let arrowDim = arrowEl.getBoundingClientRect();
|
||||
|
||||
const arrow = {
|
||||
width: arrowDim.width,
|
||||
height: arrowDim.height
|
||||
}
|
||||
|
||||
// If no ev was passed, hide the arrow
|
||||
if (!targetDim) {
|
||||
popoverArrowEle.style.display = 'none';
|
||||
arrowEl.style.display = 'none';
|
||||
}
|
||||
|
||||
let arrowCSS = {
|
||||
top: targetTop + targetHeight,
|
||||
left: targetLeft + (targetWidth / 2) - (arrowWidth / 2)
|
||||
top: target.top + target.height,
|
||||
left: target.left + (target.width / 2) - (arrow.width / 2)
|
||||
};
|
||||
|
||||
let popoverCSS = {
|
||||
top: targetTop + targetHeight + (arrowHeight - 1),
|
||||
left: targetLeft + (targetWidth / 2) - (popoverWidth / 2)
|
||||
top: target.top + target.height + (arrow.height - 1),
|
||||
left: target.left
|
||||
};
|
||||
|
||||
// If the popover should be centered to the target
|
||||
if (props.centerTarget) {
|
||||
popoverCSS.left = target.left + (target.width / 2) - (popover.width / 2)
|
||||
}
|
||||
|
||||
// If the popover left is less than the padding it is off screen
|
||||
// to the left so adjust it, else if the width of the popover
|
||||
// exceeds the body width it is off screen to the right so adjust
|
||||
if (popoverCSS.left < popoverPadding) {
|
||||
popoverCSS.left = popoverPadding;
|
||||
} else if (popoverWidth + popoverPadding + popoverCSS.left > bodyWidth) {
|
||||
popoverCSS.left = bodyWidth - popoverWidth - popoverPadding;
|
||||
originX = 'right';
|
||||
if (popoverCSS.left < props.padding) {
|
||||
popoverCSS.left = props.padding;
|
||||
} else if (popover.width + props.padding + popoverCSS.left > body.width) {
|
||||
popoverCSS.left = body.width - popover.width - props.padding;
|
||||
origin.x = 'right';
|
||||
}
|
||||
|
||||
// If the popover when popped down stretches past bottom of screen,
|
||||
// make it pop up if there's room above
|
||||
if (targetTop + targetHeight + popoverHeight > bodyHeight && targetTop - popoverHeight > 0) {
|
||||
arrowCSS.top = targetTop - (arrowHeight + 1);
|
||||
popoverCSS.top = targetTop - popoverHeight - (arrowHeight - 1);
|
||||
nativeEle.className = nativeEle.className + ' popover-bottom';
|
||||
originY = 'bottom';
|
||||
// If there isn't room for it to pop up above the target cut it off
|
||||
} else if (targetTop + targetHeight + popoverHeight > bodyHeight) {
|
||||
popoverContentEle.style.bottom = popoverPadding + '%';
|
||||
if (this.showFromBottom(target, popover, body)) {
|
||||
nativeEl.className = nativeEl.className + ' popover-bottom';
|
||||
origin.y = 'bottom';
|
||||
|
||||
popoverCSS.top = target.top - popover.height;
|
||||
|
||||
if (props.showArrow) {
|
||||
arrowCSS.top = target.top - (arrow.height + 1);
|
||||
popoverCSS.top = target.top - popover.height - (arrow.height - 1);
|
||||
}
|
||||
|
||||
// If the popover exceeds the viewport then cut the bottom off
|
||||
} else if (this.exceedsViewport(target, popover, body)) {
|
||||
contentEl.style.bottom = props.padding + props.unit;
|
||||
}
|
||||
|
||||
popoverArrowEle.style.top = arrowCSS.top + 'px';
|
||||
popoverArrowEle.style.left = arrowCSS.left + 'px';
|
||||
arrowEl.style.top = arrowCSS.top + 'px';
|
||||
arrowEl.style.left = arrowCSS.left + 'px';
|
||||
|
||||
popoverContentEle.style.top = popoverCSS.top + 'px';
|
||||
popoverContentEle.style.left = popoverCSS.left + 'px';
|
||||
contentEl.style.top = popoverCSS.top + 'px';
|
||||
contentEl.style.left = popoverCSS.left + 'px';
|
||||
|
||||
popoverContentEle.style.transformOrigin = originY + ' ' + originX;
|
||||
contentEl.style.transformOrigin = origin.y + ' ' + origin.x;
|
||||
|
||||
// Since the transition starts before styling is done we
|
||||
// want to wait for the styles to apply before showing the wrapper
|
||||
this.displayWrapper();
|
||||
}
|
||||
|
||||
private showFromBottom(target: any, popover: any, body: any): boolean {
|
||||
return target.top + target.height + popover.height > body.height && target.top - popover.height > 0;
|
||||
}
|
||||
|
||||
private exceedsViewport(target: any, popover: any, body: any): boolean {
|
||||
return target.top + target.height + popover.height > body.height;
|
||||
}
|
||||
|
||||
private displayWrapper() {
|
||||
let popoverWrapperEle = this.el.querySelector('.popover-wrapper') as HTMLElement;
|
||||
popoverWrapperEle.style.opacity = '1';
|
||||
}
|
||||
|
||||
@ -164,7 +199,7 @@ export class Popover {
|
||||
animation.onFinish((a: any) => {
|
||||
a.destroy();
|
||||
this.ionViewDidEnter();
|
||||
this.positionView(this.el, this.ev, this.mode);
|
||||
this.positionPopover(this.el, this.ev, POPOVER_POSITION_PROPERTIES[this.mode]);
|
||||
resolve();
|
||||
}).play();
|
||||
});
|
||||
@ -279,16 +314,23 @@ export interface PopoverEvent {
|
||||
};
|
||||
}
|
||||
|
||||
export const popoverViewProps: any = {
|
||||
export const POPOVER_POSITION_PROPERTIES: any = {
|
||||
ios: {
|
||||
bodyPadding: 2,
|
||||
showArrow: true
|
||||
padding: 2,
|
||||
unit: '%',
|
||||
showArrow: true,
|
||||
centerTarget: true
|
||||
},
|
||||
md: {
|
||||
bodyPadding: 12,
|
||||
showArrow: false
|
||||
padding: 12,
|
||||
unit: 'px',
|
||||
showArrow: false,
|
||||
centerTarget: false
|
||||
},
|
||||
wp: {
|
||||
padding: 12,
|
||||
unit: 'px',
|
||||
showArrow: false,
|
||||
centerTarget: false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO FIX
|
||||
const POPOVER_MD_BODY_PADDING = 12;
|
||||
|
@ -26,6 +26,17 @@
|
||||
|
||||
<ion-popover-controller></ion-popover-controller>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
<ion-toolbar color="primary">
|
||||
<ion-buttons slot="end">
|
||||
<ion-button onclick="presentPopover('profile-page', event)">
|
||||
<ion-icon slot="icon-only" name="person"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Popover</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
</ion-app>
|
||||
<script>
|
||||
function presentPopover(componentName, event) {
|
||||
@ -61,7 +72,8 @@
|
||||
composed: true,
|
||||
bubbles: true
|
||||
});
|
||||
event.target.dispatchEvent(ev);
|
||||
console.log('event', event);
|
||||
// event.target.dispatchEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user