UNPKG

18.5 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5const index = require('./index-a0a08b2a.js');
6const ionicGlobal = require('./ionic-global-06f21c1a.js');
7const frameworkDelegate = require('./framework-delegate-45524d8c.js');
8const overlays = require('./overlays-59863ad4.js');
9const theme = require('./theme-30b7a575.js');
10const index$1 = require('./index-222357e4.js');
11const animation = require('./animation-13cbbb20.js');
12require('./helpers-d381ec4d.js');
13require('./hardware-back-button-148ce546.js');
14
15/**
16 * iOS Popover Enter Animation
17 */
18const iosEnterAnimation = (baseEl, ev) => {
19 let originY = 'top';
20 let originX = 'left';
21 const contentEl = baseEl.querySelector('.popover-content');
22 const contentDimentions = contentEl.getBoundingClientRect();
23 const contentWidth = contentDimentions.width;
24 const contentHeight = contentDimentions.height;
25 const bodyWidth = baseEl.ownerDocument.defaultView.innerWidth;
26 const bodyHeight = baseEl.ownerDocument.defaultView.innerHeight;
27 // If ev was passed, use that for target element
28 const targetDim = ev && ev.target && ev.target.getBoundingClientRect();
29 const targetTop = targetDim != null && 'top' in targetDim ? targetDim.top : bodyHeight / 2 - contentHeight / 2;
30 const targetLeft = targetDim != null && 'left' in targetDim ? targetDim.left : bodyWidth / 2;
31 const targetWidth = (targetDim && targetDim.width) || 0;
32 const targetHeight = (targetDim && targetDim.height) || 0;
33 const arrowEl = baseEl.querySelector('.popover-arrow');
34 const arrowDim = arrowEl.getBoundingClientRect();
35 const arrowWidth = arrowDim.width;
36 const arrowHeight = arrowDim.height;
37 if (targetDim == null) {
38 arrowEl.style.display = 'none';
39 }
40 const arrowCSS = {
41 top: targetTop + targetHeight,
42 left: targetLeft + targetWidth / 2 - arrowWidth / 2
43 };
44 const popoverCSS = {
45 top: targetTop + targetHeight + (arrowHeight - 1),
46 left: targetLeft + targetWidth / 2 - contentWidth / 2
47 };
48 // If the popover left is less than the padding it is off screen
49 // to the left so adjust it, else if the width of the popover
50 // exceeds the body width it is off screen to the right so adjust
51 //
52 let checkSafeAreaLeft = false;
53 let checkSafeAreaRight = false;
54 // If the popover left is less than the padding it is off screen
55 // to the left so adjust it, else if the width of the popover
56 // exceeds the body width it is off screen to the right so adjust
57 // 25 is a random/arbitrary number. It seems to work fine for ios11
58 // and iPhoneX. Is it perfect? No. Does it work? Yes.
59 if (popoverCSS.left < POPOVER_IOS_BODY_PADDING + 25) {
60 checkSafeAreaLeft = true;
61 popoverCSS.left = POPOVER_IOS_BODY_PADDING;
62 }
63 else if (contentWidth + POPOVER_IOS_BODY_PADDING + popoverCSS.left + 25 > bodyWidth) {
64 // Ok, so we're on the right side of the screen,
65 // but now we need to make sure we're still a bit further right
66 // cus....notchurally... Again, 25 is random. It works tho
67 checkSafeAreaRight = true;
68 popoverCSS.left = bodyWidth - contentWidth - POPOVER_IOS_BODY_PADDING;
69 originX = 'right';
70 }
71 // make it pop up if there's room above
72 if (targetTop + targetHeight + contentHeight > bodyHeight && targetTop - contentHeight > 0) {
73 arrowCSS.top = targetTop - (arrowHeight + 1);
74 popoverCSS.top = targetTop - contentHeight - (arrowHeight - 1);
75 baseEl.className = baseEl.className + ' popover-bottom';
76 originY = 'bottom';
77 // If there isn't room for it to pop up above the target cut it off
78 }
79 else if (targetTop + targetHeight + contentHeight > bodyHeight) {
80 contentEl.style.bottom = POPOVER_IOS_BODY_PADDING + '%';
81 }
82 arrowEl.style.top = arrowCSS.top + 'px';
83 arrowEl.style.left = arrowCSS.left + 'px';
84 contentEl.style.top = popoverCSS.top + 'px';
85 contentEl.style.left = popoverCSS.left + 'px';
86 if (checkSafeAreaLeft) {
87 contentEl.style.left = `calc(${popoverCSS.left}px + var(--ion-safe-area-left, 0px))`;
88 }
89 if (checkSafeAreaRight) {
90 contentEl.style.left = `calc(${popoverCSS.left}px - var(--ion-safe-area-right, 0px))`;
91 }
92 contentEl.style.transformOrigin = originY + ' ' + originX;
93 const baseAnimation = animation.createAnimation();
94 const backdropAnimation = animation.createAnimation();
95 const wrapperAnimation = animation.createAnimation();
96 backdropAnimation
97 .addElement(baseEl.querySelector('ion-backdrop'))
98 .fromTo('opacity', 0.01, 'var(--backdrop-opacity)')
99 .beforeStyles({
100 'pointer-events': 'none'
101 })
102 .afterClearStyles(['pointer-events']);
103 wrapperAnimation
104 .addElement(baseEl.querySelector('.popover-wrapper'))
105 .fromTo('opacity', 0.01, 1);
106 return baseAnimation
107 .addElement(baseEl)
108 .easing('ease')
109 .duration(100)
110 .addAnimation([backdropAnimation, wrapperAnimation]);
111};
112const POPOVER_IOS_BODY_PADDING = 5;
113
114/**
115 * iOS Popover Leave Animation
116 */
117const iosLeaveAnimation = (baseEl) => {
118 const baseAnimation = animation.createAnimation();
119 const backdropAnimation = animation.createAnimation();
120 const wrapperAnimation = animation.createAnimation();
121 backdropAnimation
122 .addElement(baseEl.querySelector('ion-backdrop'))
123 .fromTo('opacity', 'var(--backdrop-opacity)', 0);
124 wrapperAnimation
125 .addElement(baseEl.querySelector('.popover-wrapper'))
126 .fromTo('opacity', 0.99, 0);
127 return baseAnimation
128 .addElement(baseEl)
129 .easing('ease')
130 .duration(500)
131 .addAnimation([backdropAnimation, wrapperAnimation]);
132};
133
134/**
135 * Md Popover Enter Animation
136 */
137const mdEnterAnimation = (baseEl, ev) => {
138 const POPOVER_MD_BODY_PADDING = 12;
139 const doc = baseEl.ownerDocument;
140 const isRTL = doc.dir === 'rtl';
141 let originY = 'top';
142 let originX = isRTL ? 'right' : 'left';
143 const contentEl = baseEl.querySelector('.popover-content');
144 const contentDimentions = contentEl.getBoundingClientRect();
145 const contentWidth = contentDimentions.width;
146 const contentHeight = contentDimentions.height;
147 const bodyWidth = doc.defaultView.innerWidth;
148 const bodyHeight = doc.defaultView.innerHeight;
149 // If ev was passed, use that for target element
150 const targetDim = ev && ev.target && ev.target.getBoundingClientRect();
151 // As per MD spec, by default position the popover below the target (trigger) element
152 const targetTop = targetDim != null && 'bottom' in targetDim
153 ? targetDim.bottom
154 : bodyHeight / 2 - contentHeight / 2;
155 const targetLeft = targetDim != null && 'left' in targetDim
156 ? isRTL
157 ? targetDim.left - contentWidth + targetDim.width
158 : targetDim.left
159 : bodyWidth / 2 - contentWidth / 2;
160 const targetHeight = (targetDim && targetDim.height) || 0;
161 const popoverCSS = {
162 top: targetTop,
163 left: targetLeft
164 };
165 // If the popover left is less than the padding it is off screen
166 // to the left so adjust it, else if the width of the popover
167 // exceeds the body width it is off screen to the right so adjust
168 if (popoverCSS.left < POPOVER_MD_BODY_PADDING) {
169 popoverCSS.left = POPOVER_MD_BODY_PADDING;
170 // Same origin in this case for both LTR & RTL
171 // Note: in LTR, originX is already 'left'
172 originX = 'left';
173 }
174 else if (contentWidth + POPOVER_MD_BODY_PADDING + popoverCSS.left >
175 bodyWidth) {
176 popoverCSS.left = bodyWidth - contentWidth - POPOVER_MD_BODY_PADDING;
177 // Same origin in this case for both LTR & RTL
178 // Note: in RTL, originX is already 'right'
179 originX = 'right';
180 }
181 // If the popover when popped down stretches past bottom of screen,
182 // make it pop up if there's room above
183 if (targetTop + targetHeight + contentHeight > bodyHeight &&
184 targetTop - contentHeight > 0) {
185 popoverCSS.top = targetTop - contentHeight - targetHeight;
186 baseEl.className = baseEl.className + ' popover-bottom';
187 originY = 'bottom';
188 // If there isn't room for it to pop up above the target cut it off
189 }
190 else if (targetTop + targetHeight + contentHeight > bodyHeight) {
191 contentEl.style.bottom = POPOVER_MD_BODY_PADDING + 'px';
192 }
193 const baseAnimation = animation.createAnimation();
194 const backdropAnimation = animation.createAnimation();
195 const wrapperAnimation = animation.createAnimation();
196 const contentAnimation = animation.createAnimation();
197 const viewportAnimation = animation.createAnimation();
198 backdropAnimation
199 .addElement(baseEl.querySelector('ion-backdrop'))
200 .fromTo('opacity', 0.01, 'var(--backdrop-opacity)')
201 .beforeStyles({
202 'pointer-events': 'none'
203 })
204 .afterClearStyles(['pointer-events']);
205 wrapperAnimation
206 .addElement(baseEl.querySelector('.popover-wrapper'))
207 .fromTo('opacity', 0.01, 1);
208 contentAnimation
209 .addElement(contentEl)
210 .beforeStyles({
211 'top': `${popoverCSS.top}px`,
212 'left': `${popoverCSS.left}px`,
213 'transform-origin': `${originY} ${originX}`
214 })
215 .fromTo('transform', 'scale(0.001)', 'scale(1)');
216 viewportAnimation
217 .addElement(baseEl.querySelector('.popover-viewport'))
218 .fromTo('opacity', 0.01, 1);
219 return baseAnimation
220 .addElement(baseEl)
221 .easing('cubic-bezier(0.36,0.66,0.04,1)')
222 .duration(300)
223 .addAnimation([backdropAnimation, wrapperAnimation, contentAnimation, viewportAnimation]);
224};
225
226/**
227 * Md Popover Leave Animation
228 */
229const mdLeaveAnimation = (baseEl) => {
230 const baseAnimation = animation.createAnimation();
231 const backdropAnimation = animation.createAnimation();
232 const wrapperAnimation = animation.createAnimation();
233 backdropAnimation
234 .addElement(baseEl.querySelector('ion-backdrop'))
235 .fromTo('opacity', 'var(--backdrop-opacity)', 0);
236 wrapperAnimation
237 .addElement(baseEl.querySelector('.popover-wrapper'))
238 .fromTo('opacity', 0.99, 0);
239 return baseAnimation
240 .addElement(baseEl)
241 .easing('ease')
242 .duration(500)
243 .addAnimation([backdropAnimation, wrapperAnimation]);
244};
245
246const popoverIosCss = ".sc-ion-popover-ios-h{--background:var(--ion-background-color, #fff);--min-width:0;--min-height:0;--max-width:auto;--height:auto;left:0;right:0;top:0;bottom:0;display:-ms-flexbox;display:flex;position:fixed;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;outline:none;color:var(--ion-text-color, #000);z-index:1001}.overlay-hidden.sc-ion-popover-ios-h{display:none}.popover-wrapper.sc-ion-popover-ios{opacity:0;z-index:10}.popover-content.sc-ion-popover-ios{display:-ms-flexbox;display:flex;position:absolute;-ms-flex-direction:column;flex-direction:column;width:var(--width);min-width:var(--min-width);max-width:var(--max-width);height:var(--height);min-height:var(--min-height);max-height:var(--max-height);background:var(--background);-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow);overflow:auto;z-index:10}.popover-viewport.sc-ion-popover-ios{--ion-safe-area-top:0px;--ion-safe-area-right:0px;--ion-safe-area-bottom:0px;--ion-safe-area-left:0px}.sc-ion-popover-ios-h{--width:200px;--max-height:90%;--box-shadow:none;--backdrop-opacity:var(--ion-backdrop-opacity, 0.08)}.popover-content.sc-ion-popover-ios{border-radius:10px}.popover-arrow.sc-ion-popover-ios{display:block;position:absolute;width:20px;height:10px;overflow:hidden}.popover-arrow.sc-ion-popover-ios::after{left:3px;top:3px;border-radius:3px;position:absolute;width:14px;height:14px;-webkit-transform:rotate(45deg);transform:rotate(45deg);background:var(--background);content:\"\";z-index:10}[dir=rtl].sc-ion-popover-ios .popover-arrow.sc-ion-popover-ios::after,[dir=rtl].sc-ion-popover-ios-h .popover-arrow.sc-ion-popover-ios::after,[dir=rtl] .sc-ion-popover-ios-h .popover-arrow.sc-ion-popover-ios::after{left:unset;right:unset;right:3px}.popover-bottom.sc-ion-popover-ios-h .popover-arrow.sc-ion-popover-ios{top:auto;bottom:-10px}.popover-bottom.sc-ion-popover-ios-h .popover-arrow.sc-ion-popover-ios::after{top:-6px}@supports ((-webkit-backdrop-filter: blur(0)) or (backdrop-filter: blur(0))){.popover-translucent.sc-ion-popover-ios-h .popover-content.sc-ion-popover-ios,.popover-translucent.sc-ion-popover-ios-h .popover-arrow.sc-ion-popover-ios::after{background:rgba(var(--ion-background-color-rgb, 255, 255, 255), 0.8);-webkit-backdrop-filter:saturate(180%) blur(20px);backdrop-filter:saturate(180%) blur(20px)}}";
247
248const popoverMdCss = ".sc-ion-popover-md-h{--background:var(--ion-background-color, #fff);--min-width:0;--min-height:0;--max-width:auto;--height:auto;left:0;right:0;top:0;bottom:0;display:-ms-flexbox;display:flex;position:fixed;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;outline:none;color:var(--ion-text-color, #000);z-index:1001}.overlay-hidden.sc-ion-popover-md-h{display:none}.popover-wrapper.sc-ion-popover-md{opacity:0;z-index:10}.popover-content.sc-ion-popover-md{display:-ms-flexbox;display:flex;position:absolute;-ms-flex-direction:column;flex-direction:column;width:var(--width);min-width:var(--min-width);max-width:var(--max-width);height:var(--height);min-height:var(--min-height);max-height:var(--max-height);background:var(--background);-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow);overflow:auto;z-index:10}.popover-viewport.sc-ion-popover-md{--ion-safe-area-top:0px;--ion-safe-area-right:0px;--ion-safe-area-bottom:0px;--ion-safe-area-left:0px}.sc-ion-popover-md-h{--width:250px;--max-height:90%;--box-shadow:0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);--backdrop-opacity:var(--ion-backdrop-opacity, 0.32)}.popover-content.sc-ion-popover-md{border-radius:4px;-webkit-transform-origin:left top;transform-origin:left top}[dir=rtl].sc-ion-popover-md .popover-content.sc-ion-popover-md,[dir=rtl].sc-ion-popover-md-h .popover-content.sc-ion-popover-md,[dir=rtl] .sc-ion-popover-md-h .popover-content.sc-ion-popover-md{-webkit-transform-origin:right top;transform-origin:right top}.popover-viewport.sc-ion-popover-md{-webkit-transition-delay:100ms;transition-delay:100ms}";
249
250const Popover = class {
251 constructor(hostRef) {
252 index.registerInstance(this, hostRef);
253 this.didPresent = index.createEvent(this, "ionPopoverDidPresent", 7);
254 this.willPresent = index.createEvent(this, "ionPopoverWillPresent", 7);
255 this.willDismiss = index.createEvent(this, "ionPopoverWillDismiss", 7);
256 this.didDismiss = index.createEvent(this, "ionPopoverDidDismiss", 7);
257 this.presented = false;
258 /**
259 * If `true`, the keyboard will be automatically dismissed when the overlay is presented.
260 */
261 this.keyboardClose = true;
262 /**
263 * If `true`, the popover will be dismissed when the backdrop is clicked.
264 */
265 this.backdropDismiss = true;
266 /**
267 * If `true`, a backdrop will be displayed behind the popover.
268 */
269 this.showBackdrop = true;
270 /**
271 * If `true`, the popover will be translucent.
272 * Only applies when the mode is `"ios"` and the device supports
273 * [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility).
274 */
275 this.translucent = false;
276 /**
277 * If `true`, the popover will animate.
278 */
279 this.animated = true;
280 this.onDismiss = (ev) => {
281 ev.stopPropagation();
282 ev.preventDefault();
283 this.dismiss();
284 };
285 this.onBackdropTap = () => {
286 this.dismiss(undefined, overlays.BACKDROP);
287 };
288 this.onLifecycle = (modalEvent) => {
289 const el = this.usersElement;
290 const name = LIFECYCLE_MAP[modalEvent.type];
291 if (el && name) {
292 const event = new CustomEvent(name, {
293 bubbles: false,
294 cancelable: false,
295 detail: modalEvent.detail
296 });
297 el.dispatchEvent(event);
298 }
299 };
300 }
301 connectedCallback() {
302 overlays.prepareOverlay(this.el);
303 }
304 /**
305 * Present the popover overlay after it has been created.
306 */
307 async present() {
308 if (this.presented) {
309 return;
310 }
311 const container = this.el.querySelector('.popover-content');
312 if (!container) {
313 throw new Error('container is undefined');
314 }
315 const data = Object.assign(Object.assign({}, this.componentProps), { popover: this.el });
316 this.usersElement = await frameworkDelegate.attachComponent(this.delegate, container, this.component, ['popover-viewport', this.el['s-sc']], data);
317 await index$1.deepReady(this.usersElement);
318 return overlays.present(this, 'popoverEnter', iosEnterAnimation, mdEnterAnimation, this.event);
319 }
320 /**
321 * Dismiss the popover overlay after it has been presented.
322 *
323 * @param data Any data to emit in the dismiss events.
324 * @param role The role of the element that is dismissing the popover. For example, 'cancel' or 'backdrop'.
325 */
326 async dismiss(data, role) {
327 const shouldDismiss = await overlays.dismiss(this, data, role, 'popoverLeave', iosLeaveAnimation, mdLeaveAnimation, this.event);
328 if (shouldDismiss) {
329 await frameworkDelegate.detachComponent(this.delegate, this.usersElement);
330 }
331 return shouldDismiss;
332 }
333 /**
334 * Returns a promise that resolves when the popover did dismiss.
335 */
336 onDidDismiss() {
337 return overlays.eventMethod(this.el, 'ionPopoverDidDismiss');
338 }
339 /**
340 * Returns a promise that resolves when the popover will dismiss.
341 */
342 onWillDismiss() {
343 return overlays.eventMethod(this.el, 'ionPopoverWillDismiss');
344 }
345 render() {
346 const mode = ionicGlobal.getIonMode(this);
347 const { onLifecycle } = this;
348 return (index.h(index.Host, { "aria-modal": "true", "no-router": true, tabindex: "-1", style: {
349 zIndex: `${20000 + this.overlayIndex}`,
350 }, class: Object.assign(Object.assign({}, theme.getClassMap(this.cssClass)), { [mode]: true, 'popover-translucent': this.translucent }), onIonPopoverDidPresent: onLifecycle, onIonPopoverWillPresent: onLifecycle, onIonPopoverWillDismiss: onLifecycle, onIonPopoverDidDismiss: onLifecycle, onIonDismiss: this.onDismiss, onIonBackdropTap: this.onBackdropTap }, index.h("ion-backdrop", { tappable: this.backdropDismiss, visible: this.showBackdrop }), index.h("div", { tabindex: "0" }), index.h("div", { class: "popover-wrapper ion-overlay-wrapper" }, index.h("div", { class: "popover-arrow" }), index.h("div", { class: "popover-content" })), index.h("div", { tabindex: "0" })));
351 }
352 get el() { return index.getElement(this); }
353};
354const LIFECYCLE_MAP = {
355 'ionPopoverDidPresent': 'ionViewDidEnter',
356 'ionPopoverWillPresent': 'ionViewWillEnter',
357 'ionPopoverWillDismiss': 'ionViewWillLeave',
358 'ionPopoverDidDismiss': 'ionViewDidLeave',
359};
360Popover.style = {
361 ios: popoverIosCss,
362 md: popoverMdCss
363};
364
365exports.ion_popover = Popover;