Skip to content

Commit d797740

Browse files
committed
MOBILE-4857 theme: Fix popover position with CSS zoom
The previous attempt to fix the popover position using a CSS workaround did not work on iOS. Ionic uses getBoundingClientRect() and clientX/clientY to calculate positions, which do not account for CSS zoom. We need to patch Ionic to calculate the position correctly.
1 parent b849a09 commit d797740

File tree

2 files changed

+205
-22
lines changed

2 files changed

+205
-22
lines changed

patches/@ionic+core+8.7.5.patch

Lines changed: 205 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,48 @@ index ec9ab81..a384b35 100644
1212
// If the item has an href or button property it will render a native
1313
// anchor or button that is clickable
1414
diff --git a/node_modules/@ionic/core/components/popover.js b/node_modules/@ionic/core/components/popover.js
15-
index 56b1107..560b1d6 100644
15+
index 56b1107..03e5116 100644
1616
--- a/node_modules/@ionic/core/components/popover.js
1717
+++ b/node_modules/@ionic/core/components/popover.js
18-
@@ -763,8 +763,10 @@ const iosEnterAnimation = (baseEl, opts) => {
18+
@@ -23,7 +23,9 @@ const getArrowDimensions = (arrowEl) => {
19+
return { arrowWidth: 0, arrowHeight: 0 };
20+
}
21+
const { width, height } = arrowEl.getBoundingClientRect();
22+
- return { arrowWidth: width, arrowHeight: height };
23+
+ // Patched: getBoundingClientRect result does not account for CSS zoom.
24+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
25+
+ return { arrowWidth: width / zoom, arrowHeight: height / zoom };
26+
};
27+
/**
28+
* Returns the recommended dimensions of the popover
29+
@@ -38,9 +40,11 @@ const getPopoverDimensions = (size, contentEl, triggerEl) => {
30+
const triggerDimensions = triggerEl.getBoundingClientRect();
31+
contentWidth = triggerDimensions.width;
32+
}
33+
+ // Patched: getBoundingClientRect result does not account for CSS zoom.
34+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
35+
return {
36+
- contentWidth,
37+
- contentHeight,
38+
+ contentWidth: contentWidth / zoom,
39+
+ contentHeight: contentHeight / zoom,
40+
};
41+
};
42+
const configureDismissInteraction = (triggerEl, triggerAction, popoverEl, parentPopoverEl) => {
43+
@@ -437,6 +441,12 @@ const getPopoverPosition = (isRTL, contentWidth, contentHeight, arrowWidth, arro
44+
};
45+
break;
46+
}
47+
+ // Patched: getBoundingClientRect result clientX/clientY do not account for CSS zoom.
48+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
49+
+ referenceCoordinates.top = referenceCoordinates.top / zoom;
50+
+ referenceCoordinates.left = referenceCoordinates.left / zoom;
51+
+ referenceCoordinates.width = referenceCoordinates.width / zoom;
52+
+ referenceCoordinates.height = referenceCoordinates.height / zoom;
53+
/**
54+
* Get top/left offset that would allow
55+
* popover to be positioned on the
56+
@@ -763,8 +773,10 @@ const iosEnterAnimation = (baseEl, opts) => {
1957
const { event: ev, size, trigger, reference, side, align } = opts;
2058
const doc = baseEl.ownerDocument;
2159
const isRTL = doc.dir === 'rtl';
@@ -28,7 +66,7 @@ index 56b1107..560b1d6 100644
2866
const root = getElementRoot(baseEl);
2967
const contentEl = root.querySelector('.popover-content');
3068
const arrowEl = root.querySelector('.popover-arrow');
31-
@@ -884,8 +886,10 @@ const mdEnterAnimation = (baseEl, opts) => {
69+
@@ -884,8 +896,10 @@ const mdEnterAnimation = (baseEl, opts) => {
3270
const { event: ev, size, trigger, reference, side, align } = opts;
3371
const doc = baseEl.ownerDocument;
3472
const isRTL = doc.dir === 'rtl';
@@ -55,10 +93,48 @@ index 4ce05a4..cc5562b 100644
5593
// If the item has an href or button property it will render a native
5694
// anchor or button that is clickable
5795
diff --git a/node_modules/@ionic/core/dist/cjs/ion-popover.cjs.entry.js b/node_modules/@ionic/core/dist/cjs/ion-popover.cjs.entry.js
58-
index 687100e..47a7389 100644
96+
index 687100e..9abe028 100644
5997
--- a/node_modules/@ionic/core/dist/cjs/ion-popover.cjs.entry.js
6098
+++ b/node_modules/@ionic/core/dist/cjs/ion-popover.cjs.entry.js
61-
@@ -766,8 +766,10 @@ const iosEnterAnimation = (baseEl, opts) => {
99+
@@ -26,7 +26,9 @@ const getArrowDimensions = (arrowEl) => {
100+
return { arrowWidth: 0, arrowHeight: 0 };
101+
}
102+
const { width, height } = arrowEl.getBoundingClientRect();
103+
- return { arrowWidth: width, arrowHeight: height };
104+
+ // Patched: getBoundingClientRect result does not account for CSS zoom.
105+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
106+
+ return { arrowWidth: width / zoom, arrowHeight: height / zoom };
107+
};
108+
/**
109+
* Returns the recommended dimensions of the popover
110+
@@ -41,9 +43,11 @@ const getPopoverDimensions = (size, contentEl, triggerEl) => {
111+
const triggerDimensions = triggerEl.getBoundingClientRect();
112+
contentWidth = triggerDimensions.width;
113+
}
114+
+ // Patched: getBoundingClientRect result does not account for CSS zoom.
115+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
116+
return {
117+
- contentWidth,
118+
- contentHeight,
119+
+ contentWidth: contentWidth / zoom,
120+
+ contentHeight: contentHeight / zoom,
121+
};
122+
};
123+
const configureDismissInteraction = (triggerEl, triggerAction, popoverEl, parentPopoverEl) => {
124+
@@ -440,6 +444,12 @@ const getPopoverPosition = (isRTL, contentWidth, contentHeight, arrowWidth, arro
125+
};
126+
break;
127+
}
128+
+ // Patched: getBoundingClientRect result clientX/clientY do not account for CSS zoom.
129+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
130+
+ referenceCoordinates.top = referenceCoordinates.top / zoom;
131+
+ referenceCoordinates.left = referenceCoordinates.left / zoom;
132+
+ referenceCoordinates.width = referenceCoordinates.width / zoom;
133+
+ referenceCoordinates.height = referenceCoordinates.height / zoom;
134+
/**
135+
* Get top/left offset that would allow
136+
* popover to be positioned on the
137+
@@ -766,8 +776,10 @@ const iosEnterAnimation = (baseEl, opts) => {
62138
const { event: ev, size, trigger, reference, side, align } = opts;
63139
const doc = baseEl.ownerDocument;
64140
const isRTL = doc.dir === 'rtl';
@@ -71,7 +147,7 @@ index 687100e..47a7389 100644
71147
const root = helpers.getElementRoot(baseEl);
72148
const contentEl = root.querySelector('.popover-content');
73149
const arrowEl = root.querySelector('.popover-arrow');
74-
@@ -887,8 +889,10 @@ const mdEnterAnimation = (baseEl, opts) => {
150+
@@ -887,8 +899,10 @@ const mdEnterAnimation = (baseEl, opts) => {
75151
const { event: ev, size, trigger, reference, side, align } = opts;
76152
const doc = baseEl.ownerDocument;
77153
const isRTL = doc.dir === 'rtl';
@@ -145,10 +221,48 @@ index 215918e..b83a6b1 100644
145221
// If the item has an href or button property it will render a native
146222
// anchor or button that is clickable
147223
diff --git a/node_modules/@ionic/core/dist/esm/ion-popover.entry.js b/node_modules/@ionic/core/dist/esm/ion-popover.entry.js
148-
index 2e352cf..e9e6ff0 100644
224+
index 2e352cf..4231f48 100644
149225
--- a/node_modules/@ionic/core/dist/esm/ion-popover.entry.js
150226
+++ b/node_modules/@ionic/core/dist/esm/ion-popover.entry.js
151-
@@ -764,8 +764,10 @@ const iosEnterAnimation = (baseEl, opts) => {
227+
@@ -24,7 +24,9 @@ const getArrowDimensions = (arrowEl) => {
228+
return { arrowWidth: 0, arrowHeight: 0 };
229+
}
230+
const { width, height } = arrowEl.getBoundingClientRect();
231+
- return { arrowWidth: width, arrowHeight: height };
232+
+ // Patched: getBoundingClientRect result does not account for CSS zoom.
233+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
234+
+ return { arrowWidth: width / zoom, arrowHeight: height / zoom };
235+
};
236+
/**
237+
* Returns the recommended dimensions of the popover
238+
@@ -39,9 +41,11 @@ const getPopoverDimensions = (size, contentEl, triggerEl) => {
239+
const triggerDimensions = triggerEl.getBoundingClientRect();
240+
contentWidth = triggerDimensions.width;
241+
}
242+
+ // Patched: getBoundingClientRect result does not account for CSS zoom.
243+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
244+
return {
245+
- contentWidth,
246+
- contentHeight,
247+
+ contentWidth: contentWidth / zoom,
248+
+ contentHeight: contentHeight / zoom,
249+
};
250+
};
251+
const configureDismissInteraction = (triggerEl, triggerAction, popoverEl, parentPopoverEl) => {
252+
@@ -438,6 +442,12 @@ const getPopoverPosition = (isRTL, contentWidth, contentHeight, arrowWidth, arro
253+
};
254+
break;
255+
}
256+
+ // Patched: getBoundingClientRect result clientX/clientY do not account for CSS zoom.
257+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
258+
+ referenceCoordinates.top = referenceCoordinates.top / zoom;
259+
+ referenceCoordinates.left = referenceCoordinates.left / zoom;
260+
+ referenceCoordinates.width = referenceCoordinates.width / zoom;
261+
+ referenceCoordinates.height = referenceCoordinates.height / zoom;
262+
/**
263+
* Get top/left offset that would allow
264+
* popover to be positioned on the
265+
@@ -764,8 +774,10 @@ const iosEnterAnimation = (baseEl, opts) => {
152266
const { event: ev, size, trigger, reference, side, align } = opts;
153267
const doc = baseEl.ownerDocument;
154268
const isRTL = doc.dir === 'rtl';
@@ -161,7 +275,7 @@ index 2e352cf..e9e6ff0 100644
161275
const root = getElementRoot(baseEl);
162276
const contentEl = root.querySelector('.popover-content');
163277
const arrowEl = root.querySelector('.popover-arrow');
164-
@@ -885,8 +887,10 @@ const mdEnterAnimation = (baseEl, opts) => {
278+
@@ -885,8 +897,10 @@ const mdEnterAnimation = (baseEl, opts) => {
165279
const { event: ev, size, trigger, reference, side, align } = opts;
166280
const doc = baseEl.ownerDocument;
167281
const isRTL = doc.dir === 'rtl';
@@ -175,7 +289,7 @@ index 2e352cf..e9e6ff0 100644
175289
const contentEl = root.querySelector('.popover-content');
176290
const referenceSizeEl = trigger || ((_a = ev === null || ev === void 0 ? void 0 : ev.detail) === null || _a === void 0 ? void 0 : _a.ionShadowTarget) || (ev === null || ev === void 0 ? void 0 : ev.target);
177291
diff --git a/node_modules/@ionic/core/hydrate/index.js b/node_modules/@ionic/core/hydrate/index.js
178-
index 7ea2833..56bf3a5 100644
292+
index 7ea2833..71aac42 100644
179293
--- a/node_modules/@ionic/core/hydrate/index.js
180294
+++ b/node_modules/@ionic/core/hydrate/index.js
181295
@@ -17985,7 +17985,7 @@ class Item {
@@ -187,7 +301,45 @@ index 7ea2833..56bf3a5 100644
187301
}
188302
// If the item has an href or button property it will render a native
189303
// anchor or button that is clickable
190-
@@ -26876,8 +26876,10 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
304+
@@ -26136,7 +26136,9 @@ const getArrowDimensions = (arrowEl) => {
305+
return { arrowWidth: 0, arrowHeight: 0 };
306+
}
307+
const { width, height } = arrowEl.getBoundingClientRect();
308+
- return { arrowWidth: width, arrowHeight: height };
309+
+ // Patched: getBoundingClientRect result does not account for CSS zoom.
310+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
311+
+ return { arrowWidth: width / zoom, arrowHeight: height / zoom };
312+
};
313+
/**
314+
* Returns the recommended dimensions of the popover
315+
@@ -26151,9 +26153,11 @@ const getPopoverDimensions = (size, contentEl, triggerEl) => {
316+
const triggerDimensions = triggerEl.getBoundingClientRect();
317+
contentWidth = triggerDimensions.width;
318+
}
319+
+ // Patched: getBoundingClientRect result does not account for CSS zoom.
320+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
321+
return {
322+
- contentWidth,
323+
- contentHeight,
324+
+ contentWidth: contentWidth / zoom,
325+
+ contentHeight: contentHeight / zoom,
326+
};
327+
};
328+
const configureDismissInteraction = (triggerEl, triggerAction, popoverEl, parentPopoverEl) => {
329+
@@ -26550,6 +26554,12 @@ const getPopoverPosition = (isRTL, contentWidth, contentHeight, arrowWidth, arro
330+
};
331+
break;
332+
}
333+
+ // Patched: getBoundingClientRect result clientX/clientY do not account for CSS zoom.
334+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
335+
+ referenceCoordinates.top = referenceCoordinates.top / zoom;
336+
+ referenceCoordinates.left = referenceCoordinates.left / zoom;
337+
+ referenceCoordinates.width = referenceCoordinates.width / zoom;
338+
+ referenceCoordinates.height = referenceCoordinates.height / zoom;
339+
/**
340+
* Get top/left offset that would allow
341+
* popover to be positioned on the
342+
@@ -26876,8 +26886,10 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
191343
const { event: ev, size, trigger, reference, side, align } = opts;
192344
const doc = baseEl.ownerDocument;
193345
const isRTL = doc.dir === 'rtl';
@@ -200,7 +352,7 @@ index 7ea2833..56bf3a5 100644
200352
const root = getElementRoot(baseEl);
201353
const contentEl = root.querySelector('.popover-content');
202354
const arrowEl = root.querySelector('.popover-arrow');
203-
@@ -26997,8 +26999,10 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
355+
@@ -26997,8 +27009,10 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
204356
const { event: ev, size, trigger, reference, side, align } = opts;
205357
const doc = baseEl.ownerDocument;
206358
const isRTL = doc.dir === 'rtl';
@@ -214,7 +366,7 @@ index 7ea2833..56bf3a5 100644
214366
const contentEl = root.querySelector('.popover-content');
215367
const referenceSizeEl = trigger || ((_a = ev === null || ev === void 0 ? void 0 : ev.detail) === null || _a === void 0 ? void 0 : _a.ionShadowTarget) || (ev === null || ev === void 0 ? void 0 : ev.target);
216368
diff --git a/node_modules/@ionic/core/hydrate/index.mjs b/node_modules/@ionic/core/hydrate/index.mjs
217-
index bab3773..7ef8a51 100644
369+
index bab3773..5ccdb98 100644
218370
--- a/node_modules/@ionic/core/hydrate/index.mjs
219371
+++ b/node_modules/@ionic/core/hydrate/index.mjs
220372
@@ -17983,7 +17983,7 @@ class Item {
@@ -226,7 +378,45 @@ index bab3773..7ef8a51 100644
226378
}
227379
// If the item has an href or button property it will render a native
228380
// anchor or button that is clickable
229-
@@ -26874,8 +26874,10 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
381+
@@ -26134,7 +26134,9 @@ const getArrowDimensions = (arrowEl) => {
382+
return { arrowWidth: 0, arrowHeight: 0 };
383+
}
384+
const { width, height } = arrowEl.getBoundingClientRect();
385+
- return { arrowWidth: width, arrowHeight: height };
386+
+ // Patched: getBoundingClientRect result does not account for CSS zoom.
387+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
388+
+ return { arrowWidth: width / zoom, arrowHeight: height / zoom };
389+
};
390+
/**
391+
* Returns the recommended dimensions of the popover
392+
@@ -26149,9 +26151,11 @@ const getPopoverDimensions = (size, contentEl, triggerEl) => {
393+
const triggerDimensions = triggerEl.getBoundingClientRect();
394+
contentWidth = triggerDimensions.width;
395+
}
396+
+ // Patched: getBoundingClientRect result does not account for CSS zoom.
397+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
398+
return {
399+
- contentWidth,
400+
- contentHeight,
401+
+ contentWidth: contentWidth / zoom,
402+
+ contentHeight: contentHeight / zoom,
403+
};
404+
};
405+
const configureDismissInteraction = (triggerEl, triggerAction, popoverEl, parentPopoverEl) => {
406+
@@ -26548,6 +26552,12 @@ const getPopoverPosition = (isRTL, contentWidth, contentHeight, arrowWidth, arro
407+
};
408+
break;
409+
}
410+
+ // Patched: getBoundingClientRect result clientX/clientY do not account for CSS zoom.
411+
+ const zoom = parseFloat(document.documentElement.style.getPropertyValue('--zoom-ratio')) || 1;
412+
+ referenceCoordinates.top = referenceCoordinates.top / zoom;
413+
+ referenceCoordinates.left = referenceCoordinates.left / zoom;
414+
+ referenceCoordinates.width = referenceCoordinates.width / zoom;
415+
+ referenceCoordinates.height = referenceCoordinates.height / zoom;
416+
/**
417+
* Get top/left offset that would allow
418+
* popover to be positioned on the
419+
@@ -26874,8 +26884,10 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
230420
const { event: ev, size, trigger, reference, side, align } = opts;
231421
const doc = baseEl.ownerDocument;
232422
const isRTL = doc.dir === 'rtl';
@@ -239,7 +429,7 @@ index bab3773..7ef8a51 100644
239429
const root = getElementRoot(baseEl);
240430
const contentEl = root.querySelector('.popover-content');
241431
const arrowEl = root.querySelector('.popover-arrow');
242-
@@ -26995,8 +26997,10 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
432+
@@ -26995,8 +27007,10 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
243433
const { event: ev, size, trigger, reference, side, align } = opts;
244434
const doc = baseEl.ownerDocument;
245435
const isRTL = doc.dir === 'rtl';

src/theme/components/ion-popover.scss

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
ion-popover {
22
&::part(content) {
33
border-radius: var(--modal-radius);
4-
5-
// When CSS zoom is not 100%, the calculated position is incorrect.
6-
// Workaround: revert zoom on the element but keep it for children.
7-
zoom: calc(1 / var(--zoom-ratio));
8-
& > * {
9-
zoom: var(--zoom-ratio);
10-
}
114
}
125
&.md {
136
margin-top: 2px;

0 commit comments

Comments
 (0)