@rdlabo/ionic-theme-ios26 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -3
- package/dist/css/components/ion-fab.css +1 -1
- package/dist/css/components/ion-searchbar.css +1 -1
- package/dist/css/components/ion-toolbar.css +1 -1
- package/dist/css/ionic-theme-ios26.css +1 -1
- package/dist/css/utils/searchable.css +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/popover/animations/ios.enter.d.ts.map +1 -1
- package/dist/popover/animations/ios.enter.js +3 -17
- package/dist/popover/utils.d.ts +2 -13
- package/dist/popover/utils.d.ts.map +1 -1
- package/dist/popover/utils.js +11 -119
- package/dist/sheets-of-glass/index.d.ts.map +1 -1
- package/dist/sheets-of-glass/index.js +6 -1
- package/dist/tab-bar-searchable/animations/enter.d.ts +8 -0
- package/dist/tab-bar-searchable/animations/enter.d.ts.map +1 -0
- package/dist/tab-bar-searchable/animations/enter.js +73 -0
- package/dist/tab-bar-searchable/animations/leave.d.ts +8 -0
- package/dist/tab-bar-searchable/animations/leave.d.ts.map +1 -0
- package/dist/tab-bar-searchable/animations/leave.js +66 -0
- package/dist/tab-bar-searchable/index.d.ts +4 -0
- package/dist/tab-bar-searchable/index.d.ts.map +1 -0
- package/dist/tab-bar-searchable/index.js +75 -0
- package/dist/tab-bar-searchable/interfaces.d.ts +41 -0
- package/dist/tab-bar-searchable/interfaces.d.ts.map +1 -0
- package/dist/tab-bar-searchable/interfaces.js +5 -0
- package/dist/tab-bar-searchable/utils.d.ts +12 -0
- package/dist/tab-bar-searchable/utils.d.ts.map +1 -0
- package/dist/tab-bar-searchable/utils.js +60 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/popover/animations/ios.enter.ts +20 -52
- package/src/popover/utils.ts +8 -230
- package/src/sheets-of-glass/index.ts +6 -1
- package/src/styles/components/ion-fab.scss +31 -1
- package/src/styles/components/ion-searchbar.scss +10 -0
- package/src/styles/components/ion-toolbar.scss +20 -0
- package/src/styles/utils/searchable.scss +0 -0
- package/src/tab-bar-searchable/animations/enter.ts +92 -0
- package/src/tab-bar-searchable/animations/leave.ts +89 -0
- package/src/tab-bar-searchable/index.ts +141 -0
- package/src/tab-bar-searchable/interfaces.ts +28 -0
- package/src/tab-bar-searchable/utils.ts +75 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createAnimation
|
|
1
|
+
import { createAnimation } from '@ionic/core';
|
|
2
2
|
import type { Animation } from '@ionic/core/dist/types/utils/animation/animation-interface';
|
|
3
3
|
import { getElementRoot } from '../../utils';
|
|
4
|
-
import { calculateWindowAdjustment,
|
|
4
|
+
import { calculateWindowAdjustment, getPopoverDimensions, getPopoverPosition } from '../utils';
|
|
5
5
|
|
|
6
6
|
const POPOVER_IOS_BODY_PADDING = 5;
|
|
7
7
|
export const POPOVER_IOS_BODY_MARGIN = 8;
|
|
@@ -19,11 +19,9 @@ export const iosEnterAnimation = (baseEl: HTMLElement, opts?: any): Animation =>
|
|
|
19
19
|
|
|
20
20
|
const root = getElementRoot(baseEl);
|
|
21
21
|
const contentEl = root.querySelector('.popover-content') as HTMLElement;
|
|
22
|
-
const arrowEl = root.querySelector('.popover-arrow') as HTMLElement | null;
|
|
23
22
|
|
|
24
23
|
const referenceSizeEl = trigger || ev?.detail?.ionShadowTarget || ev?.target;
|
|
25
24
|
const { contentWidth, contentHeight } = getPopoverDimensions(size, contentEl, referenceSizeEl);
|
|
26
|
-
const { arrowWidth, arrowHeight } = getArrowDimensions(arrowEl);
|
|
27
25
|
|
|
28
26
|
const isReplace = ((): boolean => {
|
|
29
27
|
if (!['ion-button', 'ion-buttons'].includes(referenceSizeEl.localName)) {
|
|
@@ -42,43 +40,27 @@ export const iosEnterAnimation = (baseEl: HTMLElement, opts?: any): Animation =>
|
|
|
42
40
|
originY: 'top',
|
|
43
41
|
};
|
|
44
42
|
|
|
45
|
-
const results = getPopoverPosition(
|
|
46
|
-
isRTL,
|
|
47
|
-
contentWidth,
|
|
48
|
-
contentHeight,
|
|
49
|
-
arrowWidth,
|
|
50
|
-
arrowHeight,
|
|
51
|
-
reference,
|
|
52
|
-
side,
|
|
53
|
-
align,
|
|
54
|
-
defaultPosition,
|
|
55
|
-
trigger,
|
|
56
|
-
ev,
|
|
57
|
-
);
|
|
43
|
+
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, reference, side, align, defaultPosition, trigger, ev);
|
|
58
44
|
|
|
59
45
|
const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
|
|
60
46
|
const margin = size === 'cover' ? 0 : 25;
|
|
61
47
|
|
|
62
|
-
const { originX, originY, top, left, bottom, checkSafeAreaLeft, checkSafeAreaRight,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
arrowHeight,
|
|
79
|
-
referenceSizeEl.getBoundingClientRect(),
|
|
80
|
-
isReplace,
|
|
81
|
-
);
|
|
48
|
+
const { originX, originY, top, left, bottom, checkSafeAreaLeft, checkSafeAreaRight, addPopoverBottomClass } = calculateWindowAdjustment(
|
|
49
|
+
side,
|
|
50
|
+
results.top,
|
|
51
|
+
results.left,
|
|
52
|
+
padding,
|
|
53
|
+
bodyWidth,
|
|
54
|
+
bodyHeight,
|
|
55
|
+
contentWidth,
|
|
56
|
+
contentHeight,
|
|
57
|
+
margin,
|
|
58
|
+
results.originX,
|
|
59
|
+
results.originY,
|
|
60
|
+
results.referenceCoordinates,
|
|
61
|
+
referenceSizeEl.getBoundingClientRect(),
|
|
62
|
+
isReplace,
|
|
63
|
+
);
|
|
82
64
|
|
|
83
65
|
const baseAnimation = createAnimation();
|
|
84
66
|
const backdropAnimation = createAnimation();
|
|
@@ -97,13 +79,12 @@ export const iosEnterAnimation = (baseEl: HTMLElement, opts?: any): Animation =>
|
|
|
97
79
|
|
|
98
80
|
// In Chromium, if the wrapper animates, the backdrop filter doesn't work.
|
|
99
81
|
// The Chromium team stated that this behavior is expected and not a bug. The element animating opacity creates a backdrop root for the backdrop-filter.
|
|
100
|
-
// To get around this, instead of animating the wrapper, animate
|
|
82
|
+
// To get around this, instead of animating the wrapper, animate content.
|
|
101
83
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=1148826
|
|
102
84
|
contentAnimation
|
|
103
85
|
.easing('cubic-bezier(0, 1, 0.22, 1)')
|
|
104
86
|
.delay(100)
|
|
105
87
|
.duration(400)
|
|
106
|
-
.addElement(root.querySelector('.popover-arrow')!)
|
|
107
88
|
.addElement(root.querySelector('.popover-content')!)
|
|
108
89
|
.beforeStyles({ 'transform-origin': `${originY} ${originX}` })
|
|
109
90
|
.beforeAddWrite(() => {
|
|
@@ -114,7 +95,6 @@ export const iosEnterAnimation = (baseEl: HTMLElement, opts?: any): Animation =>
|
|
|
114
95
|
})
|
|
115
96
|
.fromTo('transform', 'scale(0)', 'scale(1)')
|
|
116
97
|
.fromTo('opacity', 0.01, 1);
|
|
117
|
-
// TODO(FW-4376) Ensure that arrow also blurs when translucent
|
|
118
98
|
|
|
119
99
|
if (isReplace) {
|
|
120
100
|
targetAnimation
|
|
@@ -159,18 +139,6 @@ export const iosEnterAnimation = (baseEl: HTMLElement, opts?: any): Animation =>
|
|
|
159
139
|
contentEl.style.setProperty('top', `calc(${top}px + var(--offset-y, 0))`);
|
|
160
140
|
contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
|
|
161
141
|
contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
|
|
162
|
-
|
|
163
|
-
if (arrowEl !== null) {
|
|
164
|
-
const didAdjustBounds = results.top !== top || results.left !== left;
|
|
165
|
-
const showArrow = shouldShowArrow(side, didAdjustBounds, ev, trigger);
|
|
166
|
-
|
|
167
|
-
if (showArrow) {
|
|
168
|
-
arrowEl.style.setProperty('top', `calc(${arrowTop}px + var(--offset-y, 0))`);
|
|
169
|
-
arrowEl.style.setProperty('left', `calc(${arrowLeft}px + var(--offset-x, 0))`);
|
|
170
|
-
} else {
|
|
171
|
-
arrowEl.style.setProperty('display', 'none');
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
142
|
})
|
|
175
143
|
.addAnimation([backdropAnimation, contentAnimation, targetAnimation]);
|
|
176
144
|
};
|
package/src/popover/utils.ts
CHANGED
|
@@ -19,8 +19,6 @@ interface PopoverPosition {
|
|
|
19
19
|
top: number;
|
|
20
20
|
left: number;
|
|
21
21
|
referenceCoordinates?: ReferenceCoordinates;
|
|
22
|
-
arrowTop?: number;
|
|
23
|
-
arrowLeft?: number;
|
|
24
22
|
originX: string;
|
|
25
23
|
originY: string;
|
|
26
24
|
}
|
|
@@ -33,25 +31,9 @@ export interface PopoverStyles {
|
|
|
33
31
|
originY: string;
|
|
34
32
|
checkSafeAreaLeft: boolean;
|
|
35
33
|
checkSafeAreaRight: boolean;
|
|
36
|
-
arrowTop: number;
|
|
37
|
-
arrowLeft: number;
|
|
38
34
|
addPopoverBottomClass: boolean;
|
|
39
35
|
}
|
|
40
36
|
|
|
41
|
-
/**
|
|
42
|
-
* Returns the dimensions of the popover
|
|
43
|
-
* arrow on `ios` mode. If arrow is disabled
|
|
44
|
-
* returns (0, 0).
|
|
45
|
-
*/
|
|
46
|
-
export const getArrowDimensions = (arrowEl: HTMLElement | null) => {
|
|
47
|
-
if (!arrowEl) {
|
|
48
|
-
return { arrowWidth: 0, arrowHeight: 0 };
|
|
49
|
-
}
|
|
50
|
-
const { width, height } = arrowEl.getBoundingClientRect();
|
|
51
|
-
|
|
52
|
-
return { arrowWidth: width, arrowHeight: height };
|
|
53
|
-
};
|
|
54
|
-
|
|
55
37
|
/**
|
|
56
38
|
* Returns the recommended dimensions of the popover
|
|
57
39
|
* that takes into account whether or not the width
|
|
@@ -324,119 +306,6 @@ const focusItem = (item: HTMLIonItemElement) => {
|
|
|
324
306
|
}
|
|
325
307
|
};
|
|
326
308
|
|
|
327
|
-
/**
|
|
328
|
-
* Returns `true` if `el` has been designated
|
|
329
|
-
* as a trigger element for an ion-popover.
|
|
330
|
-
*/
|
|
331
|
-
export const isTriggerElement = (el: HTMLElement) => el.hasAttribute('data-ion-popover-trigger');
|
|
332
|
-
|
|
333
|
-
export const configureKeyboardInteraction = (popoverEl: HTMLIonPopoverElement) => {
|
|
334
|
-
const callback = async (ev: KeyboardEvent) => {
|
|
335
|
-
const activeElement = document.activeElement as HTMLElement | null;
|
|
336
|
-
let items: HTMLIonItemElement[] = [];
|
|
337
|
-
|
|
338
|
-
const targetTagName = (ev.target as HTMLElement)?.tagName;
|
|
339
|
-
/**
|
|
340
|
-
* Only handle custom keyboard interactions for the host popover element
|
|
341
|
-
* and children ion-item elements.
|
|
342
|
-
*/
|
|
343
|
-
if (targetTagName !== 'ION-POPOVER' && targetTagName !== 'ION-ITEM') {
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
/**
|
|
347
|
-
* Complex selectors with :not() are :not supported
|
|
348
|
-
* in older versions of Chromium so we need to do a
|
|
349
|
-
* try/catch here so errors are not thrown.
|
|
350
|
-
*/
|
|
351
|
-
try {
|
|
352
|
-
/**
|
|
353
|
-
* Select all ion-items that are not children of child popovers.
|
|
354
|
-
* i.e. only select ion-item elements that are part of this popover
|
|
355
|
-
*/
|
|
356
|
-
items = Array.from(
|
|
357
|
-
popoverEl.querySelectorAll('ion-item:not(ion-popover ion-popover *):not([disabled])') as NodeListOf<HTMLIonItemElement>,
|
|
358
|
-
);
|
|
359
|
-
/* eslint-disable-next-line */
|
|
360
|
-
} catch {}
|
|
361
|
-
|
|
362
|
-
switch (ev.key) {
|
|
363
|
-
/**
|
|
364
|
-
* If we are in a child popover
|
|
365
|
-
* then pressing the left arrow key
|
|
366
|
-
* should close this popover and move
|
|
367
|
-
* focus to the popover that presented
|
|
368
|
-
* this one.
|
|
369
|
-
*/
|
|
370
|
-
case 'ArrowLeft':
|
|
371
|
-
const parentPopover = await popoverEl.getParentPopover();
|
|
372
|
-
if (parentPopover) {
|
|
373
|
-
popoverEl.dismiss(undefined, undefined, false);
|
|
374
|
-
}
|
|
375
|
-
break;
|
|
376
|
-
/**
|
|
377
|
-
* ArrowDown should move focus to the next focusable ion-item.
|
|
378
|
-
*/
|
|
379
|
-
case 'ArrowDown':
|
|
380
|
-
// Disable movement/scroll with keyboard
|
|
381
|
-
ev.preventDefault();
|
|
382
|
-
const nextItem = getNextItem(items, activeElement);
|
|
383
|
-
if (nextItem !== undefined) {
|
|
384
|
-
focusItem(nextItem);
|
|
385
|
-
}
|
|
386
|
-
break;
|
|
387
|
-
/**
|
|
388
|
-
* ArrowUp should move focus to the previous focusable ion-item.
|
|
389
|
-
*/
|
|
390
|
-
case 'ArrowUp':
|
|
391
|
-
// Disable movement/scroll with keyboard
|
|
392
|
-
ev.preventDefault();
|
|
393
|
-
const prevItem = getPrevItem(items, activeElement);
|
|
394
|
-
if (prevItem !== undefined) {
|
|
395
|
-
focusItem(prevItem);
|
|
396
|
-
}
|
|
397
|
-
break;
|
|
398
|
-
/**
|
|
399
|
-
* Home should move focus to the first focusable ion-item.
|
|
400
|
-
*/
|
|
401
|
-
case 'Home':
|
|
402
|
-
ev.preventDefault();
|
|
403
|
-
const firstItem = items[0];
|
|
404
|
-
if (firstItem !== undefined) {
|
|
405
|
-
focusItem(firstItem);
|
|
406
|
-
}
|
|
407
|
-
break;
|
|
408
|
-
/**
|
|
409
|
-
* End should move focus to the last focusable ion-item.
|
|
410
|
-
*/
|
|
411
|
-
case 'End':
|
|
412
|
-
ev.preventDefault();
|
|
413
|
-
const lastItem = items[items.length - 1];
|
|
414
|
-
if (lastItem !== undefined) {
|
|
415
|
-
focusItem(lastItem);
|
|
416
|
-
}
|
|
417
|
-
break;
|
|
418
|
-
/**
|
|
419
|
-
* ArrowRight, Spacebar, or Enter should activate
|
|
420
|
-
* the currently focused trigger item to open a
|
|
421
|
-
* popover if the element is a trigger item.
|
|
422
|
-
*/
|
|
423
|
-
case 'ArrowRight':
|
|
424
|
-
case ' ':
|
|
425
|
-
case 'Enter':
|
|
426
|
-
if (activeElement && isTriggerElement(activeElement)) {
|
|
427
|
-
const rightEvent = new CustomEvent('ionPopoverActivateTrigger');
|
|
428
|
-
activeElement.dispatchEvent(rightEvent);
|
|
429
|
-
}
|
|
430
|
-
break;
|
|
431
|
-
default:
|
|
432
|
-
break;
|
|
433
|
-
}
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
popoverEl.addEventListener('keydown', callback);
|
|
437
|
-
return () => popoverEl.removeEventListener('keydown', callback);
|
|
438
|
-
};
|
|
439
|
-
|
|
440
309
|
/**
|
|
441
310
|
* Positions a popover by taking into account
|
|
442
311
|
* the reference point, preferred side, alignment
|
|
@@ -446,8 +315,6 @@ export const getPopoverPosition = (
|
|
|
446
315
|
isRTL: boolean,
|
|
447
316
|
contentWidth: number,
|
|
448
317
|
contentHeight: number,
|
|
449
|
-
arrowWidth: number,
|
|
450
|
-
arrowHeight: number,
|
|
451
318
|
reference: PositionReference,
|
|
452
319
|
side: PositionSide,
|
|
453
320
|
align: PositionAlign,
|
|
@@ -524,7 +391,7 @@ export const getPopoverPosition = (
|
|
|
524
391
|
* popover to be positioned on the
|
|
525
392
|
* preferred side of the reference.
|
|
526
393
|
*/
|
|
527
|
-
const coordinates = calculatePopoverSide(side, referenceCoordinates, contentWidth, contentHeight,
|
|
394
|
+
const coordinates = calculatePopoverSide(side, referenceCoordinates, contentWidth, contentHeight, isRTL);
|
|
528
395
|
|
|
529
396
|
/**
|
|
530
397
|
* Get the top/left adjustments that
|
|
@@ -536,11 +403,9 @@ export const getPopoverPosition = (
|
|
|
536
403
|
const top = coordinates.top + alignedCoordinates.top;
|
|
537
404
|
const left = coordinates.left + alignedCoordinates.left;
|
|
538
405
|
|
|
539
|
-
const { arrowTop, arrowLeft } = calculateArrowPosition(side, arrowWidth, arrowHeight, top, left, contentWidth, contentHeight, isRTL);
|
|
540
|
-
|
|
541
406
|
const { originX, originY } = calculatePopoverOrigin(side, align, isRTL);
|
|
542
407
|
|
|
543
|
-
return { top, left, referenceCoordinates,
|
|
408
|
+
return { top, left, referenceCoordinates, originX, originY };
|
|
544
409
|
};
|
|
545
410
|
|
|
546
411
|
/**
|
|
@@ -589,55 +454,6 @@ const getOriginYAlignment = (align: PositionAlign) => {
|
|
|
589
454
|
}
|
|
590
455
|
};
|
|
591
456
|
|
|
592
|
-
/**
|
|
593
|
-
* Calculates where the arrow positioning
|
|
594
|
-
* should be relative to the popover content.
|
|
595
|
-
*/
|
|
596
|
-
const calculateArrowPosition = (
|
|
597
|
-
side: PositionSide,
|
|
598
|
-
arrowWidth: number,
|
|
599
|
-
arrowHeight: number,
|
|
600
|
-
top: number,
|
|
601
|
-
left: number,
|
|
602
|
-
contentWidth: number,
|
|
603
|
-
contentHeight: number,
|
|
604
|
-
isRTL: boolean,
|
|
605
|
-
) => {
|
|
606
|
-
/**
|
|
607
|
-
* Note: When side is left, right, start, or end, the arrow is
|
|
608
|
-
* been rotated using a `transform`, so to move the arrow up or down
|
|
609
|
-
* by its dimension, you need to use `arrowWidth`.
|
|
610
|
-
*/
|
|
611
|
-
const leftPosition = {
|
|
612
|
-
arrowTop: top + contentHeight / 2 - arrowWidth / 2,
|
|
613
|
-
arrowLeft: left + contentWidth - arrowWidth / 2,
|
|
614
|
-
};
|
|
615
|
-
|
|
616
|
-
/**
|
|
617
|
-
* Move the arrow to the left by arrowWidth and then
|
|
618
|
-
* again by half of its width because we have rotated
|
|
619
|
-
* the arrow using a transform.
|
|
620
|
-
*/
|
|
621
|
-
const rightPosition = { arrowTop: top + contentHeight / 2 - arrowWidth / 2, arrowLeft: left - arrowWidth * 1.5 };
|
|
622
|
-
|
|
623
|
-
switch (side) {
|
|
624
|
-
case 'top':
|
|
625
|
-
return { arrowTop: top + contentHeight, arrowLeft: left + contentWidth / 2 - arrowWidth / 2 };
|
|
626
|
-
case 'bottom':
|
|
627
|
-
return { arrowTop: top - arrowHeight, arrowLeft: left + contentWidth / 2 - arrowWidth / 2 };
|
|
628
|
-
case 'left':
|
|
629
|
-
return leftPosition;
|
|
630
|
-
case 'right':
|
|
631
|
-
return rightPosition;
|
|
632
|
-
case 'start':
|
|
633
|
-
return isRTL ? rightPosition : leftPosition;
|
|
634
|
-
case 'end':
|
|
635
|
-
return isRTL ? leftPosition : rightPosition;
|
|
636
|
-
default:
|
|
637
|
-
return { arrowTop: 0, arrowLeft: 0 };
|
|
638
|
-
}
|
|
639
|
-
};
|
|
640
|
-
|
|
641
457
|
/**
|
|
642
458
|
* Calculates the required top/left
|
|
643
459
|
* values needed to position the popover
|
|
@@ -649,30 +465,28 @@ const calculatePopoverSide = (
|
|
|
649
465
|
triggerBoundingBox: ReferenceCoordinates,
|
|
650
466
|
contentWidth: number,
|
|
651
467
|
contentHeight: number,
|
|
652
|
-
arrowWidth: number,
|
|
653
|
-
arrowHeight: number,
|
|
654
468
|
isRTL: boolean,
|
|
655
469
|
) => {
|
|
656
470
|
const sideLeft = {
|
|
657
471
|
top: triggerBoundingBox.top,
|
|
658
|
-
left: triggerBoundingBox.left - contentWidth
|
|
472
|
+
left: triggerBoundingBox.left - contentWidth,
|
|
659
473
|
};
|
|
660
474
|
const sideRight = {
|
|
661
475
|
top: triggerBoundingBox.top,
|
|
662
|
-
left: triggerBoundingBox.left + triggerBoundingBox.width
|
|
476
|
+
left: triggerBoundingBox.left + triggerBoundingBox.width,
|
|
663
477
|
};
|
|
664
478
|
|
|
665
479
|
switch (side) {
|
|
666
480
|
case 'top':
|
|
667
481
|
return {
|
|
668
|
-
top: triggerBoundingBox.top - contentHeight
|
|
482
|
+
top: triggerBoundingBox.top - contentHeight,
|
|
669
483
|
left: triggerBoundingBox.left,
|
|
670
484
|
};
|
|
671
485
|
case 'right':
|
|
672
486
|
return sideRight;
|
|
673
487
|
case 'bottom':
|
|
674
488
|
return {
|
|
675
|
-
top: triggerBoundingBox.top + triggerBoundingBox.height
|
|
489
|
+
top: triggerBoundingBox.top + triggerBoundingBox.height,
|
|
676
490
|
left: triggerBoundingBox.left,
|
|
677
491
|
};
|
|
678
492
|
case 'left':
|
|
@@ -794,14 +608,9 @@ export const calculateWindowAdjustment = (
|
|
|
794
608
|
contentOriginX: string,
|
|
795
609
|
contentOriginY: string,
|
|
796
610
|
triggerCoordinates?: ReferenceCoordinates,
|
|
797
|
-
coordArrowTop = 0,
|
|
798
|
-
coordArrowLeft = 0,
|
|
799
|
-
arrowHeight = 0,
|
|
800
611
|
eventElementRect?: DOMRect,
|
|
801
612
|
isReplace: boolean = false,
|
|
802
613
|
): PopoverStyles => {
|
|
803
|
-
let arrowTop = coordArrowTop;
|
|
804
|
-
const arrowLeft = coordArrowLeft;
|
|
805
614
|
const triggerTop = triggerCoordinates ? triggerCoordinates.top + triggerCoordinates.height : bodyHeight / 2 - contentHeight / 2;
|
|
806
615
|
const triggerHeight = triggerCoordinates ? triggerCoordinates.height : 0;
|
|
807
616
|
let left = coordLeft;
|
|
@@ -852,11 +661,10 @@ export const calculateWindowAdjustment = (
|
|
|
852
661
|
* it is not right up against the edge of the screen.
|
|
853
662
|
*/
|
|
854
663
|
if (!isReplace) {
|
|
855
|
-
top = Math.max(12, triggerTop - contentHeight - triggerHeight
|
|
664
|
+
top = Math.max(12, triggerTop - contentHeight - triggerHeight) - POPOVER_IOS_BODY_MARGIN;
|
|
856
665
|
} else {
|
|
857
|
-
top = Math.max(12, triggerTop - contentHeight
|
|
666
|
+
top = Math.max(12, triggerTop - contentHeight);
|
|
858
667
|
}
|
|
859
|
-
arrowTop = top + contentHeight;
|
|
860
668
|
originY = 'bottom';
|
|
861
669
|
addPopoverBottomClass = true;
|
|
862
670
|
|
|
@@ -877,36 +685,6 @@ export const calculateWindowAdjustment = (
|
|
|
877
685
|
originY,
|
|
878
686
|
checkSafeAreaLeft,
|
|
879
687
|
checkSafeAreaRight,
|
|
880
|
-
arrowTop,
|
|
881
|
-
arrowLeft,
|
|
882
688
|
addPopoverBottomClass,
|
|
883
689
|
};
|
|
884
690
|
};
|
|
885
|
-
|
|
886
|
-
export const shouldShowArrow = (side: PositionSide, didAdjustBounds = false, ev?: Event, trigger?: HTMLElement) => {
|
|
887
|
-
/**
|
|
888
|
-
* If no event provided and
|
|
889
|
-
* we do not have a trigger,
|
|
890
|
-
* then this popover was likely
|
|
891
|
-
* presented via the popoverController
|
|
892
|
-
* or users called `present` manually.
|
|
893
|
-
* In this case, the arrow should not be
|
|
894
|
-
* shown as we do not have a reference.
|
|
895
|
-
*/
|
|
896
|
-
if (!ev && !trigger) {
|
|
897
|
-
return false;
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
/**
|
|
901
|
-
* If popover is on the left or the right
|
|
902
|
-
* of a trigger, but we needed to adjust the
|
|
903
|
-
* popover due to screen bounds, then we should
|
|
904
|
-
* hide the arrow as it will never be pointing
|
|
905
|
-
* at the trigger.
|
|
906
|
-
*/
|
|
907
|
-
if (side !== 'top' && side !== 'bottom' && didAdjustBounds) {
|
|
908
|
-
return false;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
return true;
|
|
912
|
-
};
|
|
@@ -26,6 +26,7 @@ export const registerEffect = (
|
|
|
26
26
|
let scaleAnimationPromise: Promise<void> | undefined;
|
|
27
27
|
let startAnimationPromise: Promise<void> | undefined;
|
|
28
28
|
let maxVelocity = 0;
|
|
29
|
+
let wasRealUserClick = false;
|
|
29
30
|
const effectElement = cloneElement(effectTagName);
|
|
30
31
|
|
|
31
32
|
/**
|
|
@@ -38,6 +39,7 @@ export const registerEffect = (
|
|
|
38
39
|
createAnimationGesture();
|
|
39
40
|
};
|
|
40
41
|
const onPointerUp = (event: PointerEvent) => {
|
|
42
|
+
wasRealUserClick = true;
|
|
41
43
|
clearActivatedTimer = setTimeout(async () => {
|
|
42
44
|
await onEndGesture();
|
|
43
45
|
gesture.destroy();
|
|
@@ -68,12 +70,15 @@ export const registerEffect = (
|
|
|
68
70
|
if (!currentTouchedElement) {
|
|
69
71
|
return;
|
|
70
72
|
}
|
|
71
|
-
|
|
73
|
+
if (!wasRealUserClick) {
|
|
74
|
+
currentTouchedElement!.click();
|
|
75
|
+
}
|
|
72
76
|
currentTouchedElement?.classList.remove('ion-activated');
|
|
73
77
|
currentTouchedElement = undefined;
|
|
74
78
|
effectElement.style.display = 'none';
|
|
75
79
|
maxVelocity = 0;
|
|
76
80
|
targetElement.classList.remove(ANIMATED_NAME);
|
|
81
|
+
wasRealUserClick = false;
|
|
77
82
|
};
|
|
78
83
|
|
|
79
84
|
const onStartGesture = (detail: GestureDetail): boolean | undefined => {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
@use '../utils/api';
|
|
2
2
|
|
|
3
|
+
$scaleup-small-icon-only: 1.18;
|
|
4
|
+
$scaleup-default-icon-only: 1.2;
|
|
5
|
+
$scaleup-large-icon-only: 1.12;
|
|
6
|
+
|
|
3
7
|
ion-fab.ios:not(.ios26-disabled) {
|
|
4
8
|
ion-fab-button {
|
|
5
9
|
--transition:
|
|
@@ -25,6 +29,22 @@ ion-fab.ios:not(.ios26-disabled) {
|
|
|
25
29
|
|
|
26
30
|
&.ion-activated {
|
|
27
31
|
--color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.1);
|
|
32
|
+
&::part(native) {
|
|
33
|
+
transform: scale($scaleup-default-icon-only) translateZ(0);
|
|
34
|
+
-webkit-transform: scale($scaleup-default-icon-only) translateZ(0);
|
|
35
|
+
}
|
|
36
|
+
&.fab-button-large {
|
|
37
|
+
&::part(native) {
|
|
38
|
+
transform: scale($scaleup-large-icon-only) translateZ(0);
|
|
39
|
+
-webkit-transform: scale($scaleup-large-icon-only) translateZ(0);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
&.fab-button-small {
|
|
43
|
+
&::part(native) {
|
|
44
|
+
transform: scale($scaleup-small-icon-only) translateZ(0);
|
|
45
|
+
-webkit-transform: scale($scaleup-small-icon-only) translateZ(0);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
28
48
|
&.ion-color {
|
|
29
49
|
--color: rgba(var(--ion-color-base-rgb, var(--ion-text-color-rgb, 0, 0, 0)), 0.1);
|
|
30
50
|
}
|
|
@@ -44,10 +64,20 @@ ion-fab.ios:not(.ios26-disabled) {
|
|
|
44
64
|
}
|
|
45
65
|
|
|
46
66
|
&.fab-horizontal-start {
|
|
47
|
-
|
|
67
|
+
left: calc(16px + var(--ion-safe-area-left, 0px));
|
|
48
68
|
}
|
|
49
69
|
|
|
50
70
|
&.fab-horizontal-end {
|
|
51
71
|
right: calc(16px + var(--ion-safe-area-right, 0px));
|
|
52
72
|
}
|
|
73
|
+
|
|
74
|
+
&:has(.fab-button-small) {
|
|
75
|
+
&.fab-horizontal-start {
|
|
76
|
+
left: calc(10px + var(--ion-safe-area-left, 0px));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
&.fab-horizontal-end {
|
|
80
|
+
right: calc(10px + var(--ion-safe-area-right, 0px));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
53
83
|
}
|
|
@@ -34,3 +34,13 @@ ion-searchbar.ios:not(.ios26-disabled) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* This is an override for a class where cascading is specified too heavily.
|
|
40
|
+
*/
|
|
41
|
+
ion-toolbar.ios:not(.ios26-disabled).ion-color.sc-ion-searchbar-ios-h:not(.ion-color) .searchbar-input.sc-ion-searchbar-ios,
|
|
42
|
+
ion-toolbar:not(.ios26-disabled).ion-color
|
|
43
|
+
:not(.ios26-disabled).sc-ion-searchbar-ios-h:not(.ion-color)
|
|
44
|
+
.searchbar-input.sc-ion-searchbar-ios {
|
|
45
|
+
background: rgba(var(--ios26-glass-background-rgb), 0.72);
|
|
46
|
+
}
|
|
@@ -30,6 +30,26 @@ ion-toolbar.ios:not(.ios26-disabled).toolbar-searchbar {
|
|
|
30
30
|
*[slot='end'] {
|
|
31
31
|
align-self: center;
|
|
32
32
|
}
|
|
33
|
+
&:has(ion-buttons[slot='start']) {
|
|
34
|
+
ion-buttons[slot='start'] {
|
|
35
|
+
margin-right: 6px;
|
|
36
|
+
}
|
|
37
|
+
ion-searchbar.ios:not(.ios26-disabled) {
|
|
38
|
+
.searchbar-input-container {
|
|
39
|
+
margin: 0 12px 0 6px;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
&:has(ion-buttons[slot='end']) {
|
|
44
|
+
ion-buttons[slot='end'] {
|
|
45
|
+
margin-left: 6px;
|
|
46
|
+
}
|
|
47
|
+
ion-searchbar.ios:not(.ios26-disabled) {
|
|
48
|
+
.searchbar-input-container {
|
|
49
|
+
margin: 0 6px 0 12px;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
33
53
|
}
|
|
34
54
|
|
|
35
55
|
ion-header.ios:not(.ios26-disabled),
|
|
File without changes
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ElementReferences, ElementSizes } from '../interfaces';
|
|
2
|
+
import { Animation, createAnimation } from '@ionic/core';
|
|
3
|
+
import { cloneElement } from '../../utils';
|
|
4
|
+
import { ANIMATION_DELAY_CLOSE_BUTTONS, OPACITY_TRANSITION } from '../utils';
|
|
5
|
+
|
|
6
|
+
export const createEffectAnimation = (references: ElementReferences, sizes: ElementSizes): Animation => {
|
|
7
|
+
const effectElement = cloneElement('ion-icon');
|
|
8
|
+
const closeButtonRect = references.closeButtonIcon?.getBoundingClientRect();
|
|
9
|
+
const iconName = references.selectedTabButtonIcon?.getAttribute('name');
|
|
10
|
+
const selectedTabButtonIconRect = references.selectedTabButtonIcon?.getBoundingClientRect();
|
|
11
|
+
|
|
12
|
+
return createAnimation()
|
|
13
|
+
.addElement(effectElement)
|
|
14
|
+
.beforeAddWrite(() => {
|
|
15
|
+
effectElement.style.display = 'inline-block';
|
|
16
|
+
references.closeButtonIcon.style.opacity = '0';
|
|
17
|
+
if (iconName && closeButtonRect) {
|
|
18
|
+
effectElement.setAttribute('name', iconName);
|
|
19
|
+
effectElement.style.width = `${closeButtonRect.width}px`;
|
|
20
|
+
effectElement.style.height = `${closeButtonRect.height}px`;
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
.afterAddWrite(() => {
|
|
24
|
+
effectElement.style.display = 'none';
|
|
25
|
+
references.closeButtonIcon.style.opacity = '1';
|
|
26
|
+
})
|
|
27
|
+
.fromTo(
|
|
28
|
+
'transform',
|
|
29
|
+
`translate3d(${selectedTabButtonIconRect.left}px, ${selectedTabButtonIconRect.top}px, 0)`,
|
|
30
|
+
`translate3d(${closeButtonRect.left}px, ${closeButtonRect.top}px, 0)`,
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const createSearchContainerAnimation = (references: ElementReferences, sizes: ElementSizes): Animation => {
|
|
35
|
+
return createAnimation()
|
|
36
|
+
.addElement(references.searchContainer)
|
|
37
|
+
.beforeAddWrite(() => (references.searchContainer.style.transformOrigin = 'right center'))
|
|
38
|
+
.fromTo(
|
|
39
|
+
'transform',
|
|
40
|
+
`scale(${sizes.fabButton.width / sizes.searchContainer.width}, ${sizes.fabButton.height / sizes.searchContainer.height})`,
|
|
41
|
+
'scale(1)',
|
|
42
|
+
)
|
|
43
|
+
.fromTo('opacity', '0.2', '1');
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const createCloseButtonsAnimation = (references: ElementReferences): Animation => {
|
|
47
|
+
return createAnimation()
|
|
48
|
+
.delay(ANIMATION_DELAY_CLOSE_BUTTONS)
|
|
49
|
+
.addElement(references.closeButtons)
|
|
50
|
+
.beforeAddWrite(() => (references.closeButtons.style.transformOrigin = 'left center'))
|
|
51
|
+
.afterClearStyles(['transform', 'transform-origin'])
|
|
52
|
+
.fromTo('transform', 'scale(1.5, 1)', 'scale(1)');
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const createTabBarAnimation = (ionTabBar: HTMLElement, references: ElementReferences, sizes: ElementSizes): Animation => {
|
|
56
|
+
return createAnimation()
|
|
57
|
+
.addElement(ionTabBar)
|
|
58
|
+
.beforeAddWrite(() => {
|
|
59
|
+
ionTabBar.querySelectorAll<HTMLElement>('ion-tab-button').forEach((element: HTMLElement) => {
|
|
60
|
+
element.style.transition = OPACITY_TRANSITION;
|
|
61
|
+
element.style.opacity = '0';
|
|
62
|
+
});
|
|
63
|
+
references.selectedTabButton.classList.remove('tab-selected');
|
|
64
|
+
const iconName = references.selectedTabButtonIcon?.getAttribute('name');
|
|
65
|
+
if (iconName) {
|
|
66
|
+
references.closeButtonIcon?.setAttribute('name', iconName);
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
.afterAddWrite(() => {
|
|
70
|
+
references.selectedTabButton.classList.add('tab-selected');
|
|
71
|
+
ionTabBar.style.pointerEvents = 'none';
|
|
72
|
+
})
|
|
73
|
+
.fromTo(
|
|
74
|
+
'transform',
|
|
75
|
+
'scale(1)',
|
|
76
|
+
`scale(${sizes.closeButton.width / sizes.tabBar.width}, ${sizes.closeButton.height / sizes.tabBar.height})`,
|
|
77
|
+
)
|
|
78
|
+
.fromTo('opacity', '1', '0');
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const createFabButtonAnimation = (ionFabButton: HTMLElement): Animation => {
|
|
82
|
+
return createAnimation()
|
|
83
|
+
.addElement(ionFabButton)
|
|
84
|
+
.beforeAddWrite(() => {
|
|
85
|
+
ionFabButton.style.transformOrigin = 'center right';
|
|
86
|
+
ionFabButton.querySelector<HTMLElement>('ion-icon')?.style.setProperty('opacity', '0');
|
|
87
|
+
})
|
|
88
|
+
.afterAddWrite(() => {
|
|
89
|
+
ionFabButton.style.pointerEvents = 'none';
|
|
90
|
+
})
|
|
91
|
+
.fromTo('opacity', '1', '0');
|
|
92
|
+
};
|