mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-09 16:16:41 +08:00
feat(popover): initial checkin of popover positioning
This commit is contained in:
@ -38,11 +38,11 @@ ion-popover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.popover-content ion-content,
|
.popover-content ion-content,
|
||||||
.popover-content .scroll-content {
|
.popover-content ion-scroll {
|
||||||
contain: none;
|
contain: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover-content .scroll-content {
|
.popover-content ion-scroll {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,96 @@ 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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Grab the default origin from the properties
|
||||||
|
let originY = 'top';
|
||||||
|
let originX = 'left';
|
||||||
|
|
||||||
|
// Popover content width and height
|
||||||
|
let popoverDim = popoverContentEle.getBoundingClientRect();
|
||||||
|
let popoverWidth = popoverDim.width;
|
||||||
|
let popoverHeight = popoverDim.height;
|
||||||
|
|
||||||
|
// Window body width and height
|
||||||
|
// TODO need to check if portrait/landscape?
|
||||||
|
let bodyWidth = window.screen.width;
|
||||||
|
let bodyHeight = 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 arrow that shows above the popover on iOS
|
||||||
|
let arrowDim = popoverArrowEle.getBoundingClientRect();
|
||||||
|
var arrowWidth = arrowDim.width;
|
||||||
|
var arrowHeight = arrowDim.height;
|
||||||
|
|
||||||
|
// If no ev was passed, hide the arrow
|
||||||
|
if (!targetDim) {
|
||||||
|
popoverArrowEle.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
let arrowCSS = {
|
||||||
|
top: targetTop + targetHeight,
|
||||||
|
left: targetLeft + (targetWidth / 2) - (arrowWidth / 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
let popoverCSS = {
|
||||||
|
top: targetTop + targetHeight + (arrowHeight - 1),
|
||||||
|
left: targetLeft + (targetWidth / 2) - (popoverWidth / 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 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 + '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
popoverArrowEle.style.top = arrowCSS.top + 'px';
|
||||||
|
popoverArrowEle.style.left = arrowCSS.left + 'px';
|
||||||
|
|
||||||
|
popoverContentEle.style.top = popoverCSS.top + 'px';
|
||||||
|
popoverContentEle.style.left = popoverCSS.left + 'px';
|
||||||
|
|
||||||
|
popoverContentEle.style.transformOrigin = originY + ' ' + originX;
|
||||||
|
|
||||||
|
// Since the transition starts before styling is done we
|
||||||
|
// want to wait for the styles to apply before showing the wrapper
|
||||||
|
popoverWrapperEle.style.opacity = '1';
|
||||||
|
}
|
||||||
|
|
||||||
private _present(resolve: Function) {
|
private _present(resolve: Function) {
|
||||||
if (this.animation) {
|
if (this.animation) {
|
||||||
this.animation.destroy();
|
this.animation.destroy();
|
||||||
@ -74,6 +164,7 @@ export class Popover {
|
|||||||
animation.onFinish((a: any) => {
|
animation.onFinish((a: any) => {
|
||||||
a.destroy();
|
a.destroy();
|
||||||
this.ionViewDidEnter();
|
this.ionViewDidEnter();
|
||||||
|
this.positionView(this.el, this.ev, this.mode);
|
||||||
resolve();
|
resolve();
|
||||||
}).play();
|
}).play();
|
||||||
});
|
});
|
||||||
@ -145,21 +236,11 @@ export class Popover {
|
|||||||
render() {
|
render() {
|
||||||
const ThisComponent = this.component;
|
const ThisComponent = this.component;
|
||||||
|
|
||||||
let userCssClasses = 'popover-content';
|
|
||||||
if (this.cssClass) {
|
|
||||||
userCssClasses += ` ${this.cssClass}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dialogClasses = createThemedClasses(
|
const dialogClasses = createThemedClasses(
|
||||||
this.mode,
|
this.mode,
|
||||||
this.color,
|
this.color,
|
||||||
'popover-wrapper'
|
'popover-wrapper'
|
||||||
);
|
);
|
||||||
const thisComponentClasses = createThemedClasses(
|
|
||||||
this.mode,
|
|
||||||
this.color,
|
|
||||||
userCssClasses
|
|
||||||
);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<ion-backdrop
|
<ion-backdrop
|
||||||
@ -172,7 +253,7 @@ export class Popover {
|
|||||||
<div class="popover-viewport">
|
<div class="popover-viewport">
|
||||||
<ThisComponent
|
<ThisComponent
|
||||||
props={this.componentProps}
|
props={this.componentProps}
|
||||||
class={thisComponentClasses}
|
class={this.cssClass}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -197,3 +278,17 @@ export interface PopoverEvent {
|
|||||||
popover: Popover;
|
popover: Popover;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const popoverViewProps: any = {
|
||||||
|
ios: {
|
||||||
|
bodyPadding: 2,
|
||||||
|
showArrow: true
|
||||||
|
},
|
||||||
|
md: {
|
||||||
|
bodyPadding: 12,
|
||||||
|
showArrow: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO FIX
|
||||||
|
const POPOVER_MD_BODY_PADDING = 12;
|
||||||
|
|||||||
@ -3,25 +3,35 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Ionic Slides Basic</title>
|
<title>Ionic Popover</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<script src="/dist/ionic.js"></script>
|
<script src="/dist/ionic.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<ion-app>
|
<ion-app>
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar color="primary">
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<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-header>
|
||||||
|
|
||||||
<ion-content>
|
<ion-content padding>
|
||||||
<ion-button onclick="presentPopover(event)">Show Popover</ion-button>
|
<ion-button block onclick="presentPopover('profile-page', event)">Show Popover</ion-button>
|
||||||
|
|
||||||
<ion-popover-controller></ion-popover-controller>
|
<ion-popover-controller></ion-popover-controller>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
</ion-app>
|
</ion-app>
|
||||||
<script>
|
<script>
|
||||||
function presentPopover(event) {
|
function presentPopover(componentName, event) {
|
||||||
var popoverController = document.querySelector('ion-popover-controller');
|
var popoverController = document.querySelector('ion-popover-controller');
|
||||||
popoverController.create({
|
popoverController.create({
|
||||||
component: 'profile-page',
|
component: componentName,
|
||||||
ev: event
|
ev: event
|
||||||
}).then(popover => {
|
}).then(popover => {
|
||||||
popover.present();
|
popover.present();
|
||||||
@ -37,27 +47,21 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
shadowRoot.innerHTML = `
|
shadowRoot.innerHTML = `
|
||||||
<ion-header>
|
<ion-content>
|
||||||
<ion-toolbar>
|
<ion-list>
|
||||||
My Profile
|
<ion-list-header>Ionic</ion-list-header>
|
||||||
</ion-toolbar>
|
<ion-item onclick="connectedCallback(event)">Dismiss</ion-item>
|
||||||
</ion-header>
|
<ion-item><ion-label>Item 0</ion-label></ion-item>
|
||||||
<ion-content padding>
|
</ion-list>
|
||||||
<p>
|
|
||||||
<ion-button>Dismiss</ion-button>
|
|
||||||
</p>
|
|
||||||
</ion-content>`;
|
</ion-content>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback(event) {
|
||||||
var btn = this.shadowRoot.querySelector('ion-button');
|
|
||||||
btn.addEventListener('click', (uiEvent) => {
|
|
||||||
var ev = new CustomEvent('ionDismiss', {
|
var ev = new CustomEvent('ionDismiss', {
|
||||||
composed: true,
|
composed: true,
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
uiEvent.target.dispatchEvent(ev);
|
event.target.dispatchEvent(ev);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user