mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
253 lines
7.0 KiB
TypeScript
253 lines
7.0 KiB
TypeScript
import { Component, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core';
|
|
|
|
import { Animation, AnimationBuilder, Color, ComponentProps, ComponentRef, Config, FrameworkDelegate, Mode, OverlayEventDetail, OverlayInterface } from '../../interface';
|
|
import { attachComponent, detachComponent } from '../../utils/framework-delegate';
|
|
import { BACKDROP, dismiss, eventMethod, present } from '../../utils/overlays';
|
|
import { createThemedClasses, getClassMap } from '../../utils/theme';
|
|
import { deepReady } from '../../utils/transition';
|
|
|
|
import { iosEnterAnimation } from './animations/ios.enter';
|
|
import { iosLeaveAnimation } from './animations/ios.leave';
|
|
import { mdEnterAnimation } from './animations/md.enter';
|
|
import { mdLeaveAnimation } from './animations/md.leave';
|
|
|
|
@Component({
|
|
tag: 'ion-popover',
|
|
styleUrls: {
|
|
ios: 'popover.ios.scss',
|
|
md: 'popover.md.scss'
|
|
}
|
|
})
|
|
export class Popover implements OverlayInterface {
|
|
|
|
private usersElement?: HTMLElement;
|
|
|
|
presented = false;
|
|
animation?: Animation;
|
|
|
|
@Element() el!: HTMLElement;
|
|
|
|
@Prop({ connect: 'ion-animation-controller' }) animationCtrl!: HTMLIonAnimationControllerElement;
|
|
@Prop({ context: 'config' }) config!: Config;
|
|
@Prop() delegate?: FrameworkDelegate;
|
|
@Prop() overlayId!: number;
|
|
@Prop() keyboardClose = true;
|
|
|
|
/**
|
|
* The color to use from your application's color palette.
|
|
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
|
|
* For more information on colors, see [theming](/docs/theming/basics).
|
|
*/
|
|
@Prop() color?: Color;
|
|
|
|
/**
|
|
* The mode determines which platform styles to use.
|
|
* Possible values are: `"ios"` or `"md"`.
|
|
*/
|
|
@Prop() mode!: Mode;
|
|
|
|
/**
|
|
* Animation to use when the popover is presented.
|
|
*/
|
|
@Prop() enterAnimation?: AnimationBuilder;
|
|
|
|
/**
|
|
* Animation to use when the popover is dismissed.
|
|
*/
|
|
@Prop() leaveAnimation?: AnimationBuilder;
|
|
|
|
/**
|
|
* The component to display inside of the popover.
|
|
*/
|
|
@Prop() component!: ComponentRef;
|
|
|
|
/**
|
|
* The data to pass to the popover component.
|
|
*/
|
|
@Prop() componentProps?: ComponentProps;
|
|
|
|
/**
|
|
* Additional classes to apply for custom CSS. If multiple classes are
|
|
* provided they should be separated by spaces.
|
|
*/
|
|
@Prop() cssClass?: string | string[];
|
|
|
|
/**
|
|
* If true, the popover will be dismissed when the backdrop is clicked. Defaults to `true`.
|
|
*/
|
|
@Prop() backdropDismiss = true;
|
|
|
|
/**
|
|
* The event to pass to the popover animation.
|
|
*/
|
|
@Prop() event: any;
|
|
|
|
/**
|
|
* If true, a backdrop will be displayed behind the popover. Defaults to `true`.
|
|
*/
|
|
@Prop() showBackdrop = true;
|
|
|
|
/**
|
|
* If true, the popover will be translucent. Defaults to `false`.
|
|
*/
|
|
@Prop() translucent = false;
|
|
|
|
/**
|
|
* If true, the popover will animate. Defaults to `true`.
|
|
*/
|
|
@Prop() willAnimate = true;
|
|
|
|
/**
|
|
* Emitted after the popover has loaded.
|
|
*/
|
|
@Event() ionPopoverDidLoad!: EventEmitter<void>;
|
|
|
|
/**
|
|
* Emitted after the popover has unloaded.
|
|
*/
|
|
@Event() ionPopoverDidUnload!: EventEmitter<void>;
|
|
|
|
/**
|
|
* Emitted after the popover has presented.
|
|
*/
|
|
@Event({ eventName: 'ionPopoverDidPresent' }) didPresent!: EventEmitter<void>;
|
|
|
|
/**
|
|
* Emitted before the popover has presented.
|
|
*/
|
|
@Event({ eventName: 'ionPopoverWillPresent' }) willPresent!: EventEmitter<void>;
|
|
|
|
/**
|
|
* Emitted before the popover has dismissed.
|
|
*/
|
|
@Event({ eventName: 'ionPopoverWillDismiss' }) willDismiss!: EventEmitter<OverlayEventDetail>;
|
|
|
|
/**
|
|
* Emitted after the popover has dismissed.
|
|
*/
|
|
@Event({ eventName: 'ionPopoverDidDismiss' }) didDismiss!: EventEmitter<OverlayEventDetail>;
|
|
|
|
componentDidLoad() {
|
|
this.ionPopoverDidLoad.emit();
|
|
}
|
|
|
|
componentDidUnload() {
|
|
this.ionPopoverDidUnload.emit();
|
|
}
|
|
|
|
@Listen('ionDismiss')
|
|
protected onDismiss(ev: UIEvent) {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
|
|
this.dismiss();
|
|
}
|
|
|
|
@Listen('ionBackdropTap')
|
|
protected onBackdropTap() {
|
|
this.dismiss(null, BACKDROP);
|
|
}
|
|
|
|
@Listen('ionPopoverDidPresent')
|
|
@Listen('ionPopoverWillPresent')
|
|
@Listen('ionPopoverWillDismiss')
|
|
@Listen('ionPopoverDidDismiss')
|
|
protected lifecycle(modalEvent: CustomEvent) {
|
|
const el = this.usersElement;
|
|
const name = LIFECYCLE_MAP[modalEvent.type];
|
|
if (el && name) {
|
|
const event = new CustomEvent(name, {
|
|
bubbles: false,
|
|
cancelable: false,
|
|
detail: modalEvent.detail
|
|
});
|
|
el.dispatchEvent(event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Present the popover overlay after it has been created.
|
|
*/
|
|
@Method()
|
|
async present(): Promise<void> {
|
|
if (this.presented) {
|
|
return;
|
|
}
|
|
const container = this.el.querySelector('.popover-content');
|
|
if (!container) {
|
|
throw new Error('container is undefined');
|
|
}
|
|
const data = {
|
|
...this.componentProps,
|
|
popover: this.el
|
|
};
|
|
this.usersElement = await attachComponent(this.delegate, container, this.component, ['popover-viewport'], data);
|
|
await deepReady(this.usersElement);
|
|
return present(this, 'popoverEnter', iosEnterAnimation, mdEnterAnimation, this.event);
|
|
}
|
|
|
|
/**
|
|
* Dismiss the popover overlay after it has been presented.
|
|
*/
|
|
@Method()
|
|
async dismiss(data?: any, role?: string): Promise<void> {
|
|
await dismiss(this, data, role, 'popoverLeave', iosLeaveAnimation, mdLeaveAnimation, this.event);
|
|
await detachComponent(this.delegate, this.usersElement);
|
|
}
|
|
|
|
/**
|
|
* Returns a promise that resolves when the popover did dismiss. It also accepts a callback
|
|
* that is called in the same circustances.
|
|
*
|
|
*/
|
|
@Method()
|
|
onDidDismiss(callback?: (detail: OverlayEventDetail) => void): Promise<OverlayEventDetail> {
|
|
return eventMethod(this.el, 'ionPopoverDidDismiss', callback);
|
|
}
|
|
|
|
/**
|
|
* Returns a promise that resolves when the popover will dismiss. It also accepts a callback
|
|
* that is called in the same circustances.
|
|
*
|
|
*/
|
|
@Method()
|
|
onWillDismiss(callback?: (detail: OverlayEventDetail) => void): Promise<OverlayEventDetail> {
|
|
return eventMethod(this.el, 'ionPopoverWillDismiss', callback);
|
|
}
|
|
|
|
hostData() {
|
|
const themedClasses = this.translucent ? createThemedClasses(this.mode, 'popover-translucent') : {};
|
|
|
|
return {
|
|
style: {
|
|
zIndex: 20000 + this.overlayId,
|
|
},
|
|
'no-router': true,
|
|
class: {
|
|
...createThemedClasses(this.mode, 'popover'),
|
|
...getClassMap(this.cssClass),
|
|
...themedClasses,
|
|
}
|
|
};
|
|
}
|
|
|
|
render() {
|
|
const wrapperClasses = createThemedClasses(this.mode, 'popover-wrapper');
|
|
|
|
return [
|
|
<ion-backdrop tappable={this.backdropDismiss}/>,
|
|
<div class={wrapperClasses}>
|
|
<div class="popover-arrow"></div>
|
|
<div class="popover-content"></div>
|
|
</div>
|
|
];
|
|
}
|
|
}
|
|
|
|
const LIFECYCLE_MAP: any = {
|
|
'ionPopoverDidPresent': 'ionViewDidEnter',
|
|
'ionPopoverWillPresent': 'ionViewWillEnter',
|
|
'ionPopoverWillDismiss': 'ionViewWillDismiss',
|
|
'ionPopoverDidDismiss': 'ionViewDidDismiss',
|
|
};
|