@rdlabo/ionic-theme-ios27 1.0.4 → 1.1.0-beta.pr224.shac63de393e27b
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/dist/css/components/ion-toggle.css +1 -1
- package/dist/css/ionic-theme-ios27.css +1 -1
- package/dist/native/components/searchable-tabs.d.ts.map +1 -1
- package/dist/native/components/searchable-tabs.js +2 -0
- package/dist/native/runtime.d.ts.map +1 -1
- package/dist/native/runtime.js +73 -7
- package/dist/native/shared/crossfade.d.ts +2 -2
- package/dist/native/shared/crossfade.d.ts.map +1 -1
- package/dist/native/shared/crossfade.js +3 -3
- package/dist/segment/index.d.ts.map +1 -1
- package/dist/segment/index.js +7 -0
- package/docs/animation.md +2 -2
- package/docs/native-ui-shell.md +2 -0
- package/package.json +2 -5
- package/src/native/components/searchable-tabs.ts +3 -0
- package/src/native/runtime.ts +81 -6
- package/src/native/shared/crossfade.ts +4 -3
- package/src/segment/index.ts +11 -0
- package/src/styles/components/ion-toggle.scss +8 -2
- package/src/tab-bar-searchable/index.ts +1 -1
- package/node_modules/@rdlabo/ionic-theme-utils/LICENSE +0 -21
- package/node_modules/@rdlabo/ionic-theme-utils/README.md +0 -28
- package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.d.ts +0 -3
- package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.js +0 -13
- package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.d.ts +0 -6
- package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.js +0 -37
- package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/index.d.ts +0 -13
- package/node_modules/@rdlabo/ionic-theme-utils/dist/index.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/index.js +0 -11
- package/node_modules/@rdlabo/ionic-theme-utils/dist/index.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.enter.d.ts +0 -3
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.enter.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.enter.js +0 -196
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.enter.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.leave.d.ts +0 -3
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.leave.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.leave.js +0 -80
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.leave.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/callout-surface.d.ts +0 -2
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/callout-surface.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/callout-surface.js +0 -45
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/callout-surface.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/popover-interface.d.ts +0 -38
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/popover-interface.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/popover-interface.js +0 -2
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/popover-interface.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/utils.d.ts +0 -38
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/utils.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/utils.js +0 -378
- package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/utils.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.d.ts +0 -16
- package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.js +0 -2
- package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/interfaces.d.ts +0 -41
- package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/interfaces.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/interfaces.js +0 -6
- package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/interfaces.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/utils.d.ts +0 -12
- package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/utils.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/utils.js +0 -61
- package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/utils.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.d.ts +0 -15
- package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.js +0 -49
- package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.d.ts +0 -18
- package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.d.ts.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.js +0 -412
- package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.js.map +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/package.json +0 -68
- package/node_modules/@rdlabo/ionic-theme-utils/scripts/copy-structured-list.mjs +0 -17
- package/node_modules/@rdlabo/ionic-theme-utils/src/dom.spec.ts +0 -19
- package/node_modules/@rdlabo/ionic-theme-utils/src/dom.ts +0 -16
- package/node_modules/@rdlabo/ionic-theme-utils/src/focus-controller.ts +0 -38
- package/node_modules/@rdlabo/ionic-theme-utils/src/index.ts +0 -59
- package/node_modules/@rdlabo/ionic-theme-utils/src/popover/animations/ios.enter.ts +0 -259
- package/node_modules/@rdlabo/ionic-theme-utils/src/popover/animations/ios.leave.ts +0 -99
- package/node_modules/@rdlabo/ionic-theme-utils/src/popover/callout-surface.ts +0 -54
- package/node_modules/@rdlabo/ionic-theme-utils/src/popover/popover-interface.ts +0 -44
- package/node_modules/@rdlabo/ionic-theme-utils/src/popover/utils.spec.ts +0 -58
- package/node_modules/@rdlabo/ionic-theme-utils/src/popover/utils.ts +0 -698
- package/node_modules/@rdlabo/ionic-theme-utils/src/sheets-of-glass.ts +0 -17
- package/node_modules/@rdlabo/ionic-theme-utils/src/styles/index.scss +0 -1
- package/node_modules/@rdlabo/ionic-theme-utils/src/styles/md-ion-list-inset.scss +0 -20
- package/node_modules/@rdlabo/ionic-theme-utils/src/styles/structured-list.scss +0 -310
- package/node_modules/@rdlabo/ionic-theme-utils/src/tab-bar-searchable/interfaces.ts +0 -28
- package/node_modules/@rdlabo/ionic-theme-utils/src/tab-bar-searchable/utils.spec.ts +0 -56
- package/node_modules/@rdlabo/ionic-theme-utils/src/tab-bar-searchable/utils.ts +0 -75
- package/node_modules/@rdlabo/ionic-theme-utils/src/theme.ts +0 -51
- package/node_modules/@rdlabo/ionic-theme-utils/src/transition/ios.transition.spec.ts +0 -52
- package/node_modules/@rdlabo/ionic-theme-utils/src/transition/ios.transition.ts +0 -542
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
createIosTransitionAnimation,
|
|
3
|
-
shadow,
|
|
4
|
-
type IosTransitionAnimationConfig,
|
|
5
|
-
type IosTransitionAnimationOptions,
|
|
6
|
-
} from './transition/ios.transition';
|
|
7
|
-
|
|
8
|
-
export type {
|
|
9
|
-
PopoverInterface,
|
|
10
|
-
PopoverOptions,
|
|
11
|
-
PopoverSize,
|
|
12
|
-
TriggerAction,
|
|
13
|
-
PositionReference,
|
|
14
|
-
PositionSide,
|
|
15
|
-
PositionAlign,
|
|
16
|
-
} from './popover/popover-interface';
|
|
17
|
-
|
|
18
|
-
export {
|
|
19
|
-
POPOVER_IOS_BODY_MARGIN,
|
|
20
|
-
getPopoverDimensions,
|
|
21
|
-
configureDismissInteraction,
|
|
22
|
-
configureTriggerInteraction,
|
|
23
|
-
getIndexOfItem,
|
|
24
|
-
getNextItem,
|
|
25
|
-
getPrevItem,
|
|
26
|
-
getPopoverPosition,
|
|
27
|
-
calculateWindowAdjustment,
|
|
28
|
-
type ReferenceCoordinates,
|
|
29
|
-
type PopoverStyles,
|
|
30
|
-
} from './popover/utils';
|
|
31
|
-
|
|
32
|
-
export { createCalloutSurface } from './popover/callout-surface';
|
|
33
|
-
export { iosEnterAnimation as iosPopoverEnterAnimation } from './popover/animations/ios.enter';
|
|
34
|
-
export { iosLeaveAnimation as iosPopoverLeaveAnimation } from './popover/animations/ios.leave';
|
|
35
|
-
export { cloneElement, getStep, changeSelectedElement, Config, config } from './theme';
|
|
36
|
-
export { getElementRoot, raf } from './dom';
|
|
37
|
-
export { createFocusController, type FocusController } from './focus-controller';
|
|
38
|
-
export type { EffectScales, registeredEffect, AnimationPosition } from './sheets-of-glass';
|
|
39
|
-
|
|
40
|
-
export {
|
|
41
|
-
TabBarSearchableType,
|
|
42
|
-
type TabBarSearchableFunction,
|
|
43
|
-
type SearchableEventCache,
|
|
44
|
-
type ElementSizes,
|
|
45
|
-
type ElementReferences,
|
|
46
|
-
} from './tab-bar-searchable/interfaces';
|
|
47
|
-
|
|
48
|
-
export {
|
|
49
|
-
ANIMATION_DURATION,
|
|
50
|
-
ANIMATION_DELAY_BASE,
|
|
51
|
-
ANIMATION_DELAY_CLOSE_BUTTONS,
|
|
52
|
-
ANIMATION_EASING,
|
|
53
|
-
OPACITY_TRANSITION,
|
|
54
|
-
throwErrorByFailedClickElement,
|
|
55
|
-
throwErrorByFailedExistElement,
|
|
56
|
-
getElement,
|
|
57
|
-
getElementReferences,
|
|
58
|
-
getElementSizes,
|
|
59
|
-
} from './tab-bar-searchable/utils';
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
import { createAnimation } from '@ionic/core';
|
|
2
|
-
import type { Animation } from '@ionic/core';
|
|
3
|
-
import { calculateWindowAdjustment, getPopoverDimensions, getPopoverPosition, POPOVER_IOS_BODY_MARGIN } from '../utils';
|
|
4
|
-
import { createCalloutSurface } from '../callout-surface';
|
|
5
|
-
import { getElementRoot } from '../../dom';
|
|
6
|
-
|
|
7
|
-
const POPOVER_IOS_BODY_PADDING = 5;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* iOS Popover Enter Animation
|
|
11
|
-
*/
|
|
12
|
-
// TODO(FW-2832): types
|
|
13
|
-
export const iosEnterAnimation = (baseEl: HTMLElement, opts: any = {}): Animation => {
|
|
14
|
-
const { event: ev, size, trigger, reference, side, align } = opts;
|
|
15
|
-
const doc = baseEl.ownerDocument as any;
|
|
16
|
-
const isRTL = doc.dir === 'rtl';
|
|
17
|
-
const bodyWidth = doc.defaultView.innerWidth;
|
|
18
|
-
const bodyHeight = doc.defaultView.innerHeight;
|
|
19
|
-
|
|
20
|
-
const root = getElementRoot(baseEl);
|
|
21
|
-
const contentEl = root.querySelector('.popover-content') as HTMLElement;
|
|
22
|
-
const arrowEl = root.querySelector<HTMLElement>('.popover-arrow');
|
|
23
|
-
|
|
24
|
-
const referenceSizeEl = trigger || ev?.detail?.ionShadowTarget || ev?.target;
|
|
25
|
-
const anchorBounds = referenceSizeEl?.getBoundingClientRect();
|
|
26
|
-
// Overlays live outside the page, but must stay in the pane that opened them.
|
|
27
|
-
let paneAnchor = referenceSizeEl as HTMLElement | undefined;
|
|
28
|
-
let pane: HTMLElement | null = null;
|
|
29
|
-
while (paneAnchor && !pane) {
|
|
30
|
-
pane = paneAnchor.closest('ion-content, .ion-page, ion-menu');
|
|
31
|
-
paneAnchor = (paneAnchor.getRootNode() as ShadowRoot).host as HTMLElement | undefined;
|
|
32
|
-
}
|
|
33
|
-
const paneBounds = pane?.getBoundingClientRect();
|
|
34
|
-
const scroll = pane?.shadowRoot?.querySelector<HTMLElement>('[part="scroll"]');
|
|
35
|
-
const scrollStyle = scroll ? getComputedStyle(scroll) : null;
|
|
36
|
-
let paneLeft = Math.max(0, (paneBounds?.left ?? 0) + (parseFloat(scrollStyle?.paddingLeft ?? '0') || 0));
|
|
37
|
-
let paneRight = Math.min(bodyWidth, (paneBounds?.right ?? bodyWidth) - (parseFloat(scrollStyle?.paddingRight ?? '0') || 0));
|
|
38
|
-
// Toolbars may be siblings of ion-content; exclude the visible menu there too.
|
|
39
|
-
const splitPane = pane?.closest('ion-split-pane.split-pane-visible');
|
|
40
|
-
if (splitPane && pane?.closest('.split-pane-main')) {
|
|
41
|
-
for (const menu of Array.from(splitPane.querySelectorAll<HTMLElement>(':scope > ion-menu.menu-pane-visible'))) {
|
|
42
|
-
const rect = menu.getBoundingClientRect();
|
|
43
|
-
if (rect.left <= paneLeft && rect.right > paneLeft) paneLeft = rect.right;
|
|
44
|
-
if (rect.right >= paneRight && rect.left < paneRight) paneRight = rect.left;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const paneMargin = size === 'cover' ? 0 : POPOVER_IOS_BODY_MARGIN;
|
|
48
|
-
const availableWidth = Math.max(0, paneRight - paneLeft - paneMargin * 2);
|
|
49
|
-
if (pane && availableWidth > 0 && contentEl.getBoundingClientRect().width > availableWidth) {
|
|
50
|
-
contentEl.dataset['previousMaxWidth'] = contentEl.style.maxWidth;
|
|
51
|
-
contentEl.dataset['previousMaxWidthPriority'] = contentEl.style.getPropertyPriority('max-width');
|
|
52
|
-
contentEl.style.maxWidth = `${availableWidth}px`;
|
|
53
|
-
}
|
|
54
|
-
const { contentWidth, contentHeight } = getPopoverDimensions(size, contentEl, referenceSizeEl);
|
|
55
|
-
|
|
56
|
-
const isReplace = ((): boolean => {
|
|
57
|
-
if (reference === 'event' || !referenceSizeEl || !['ion-button', 'ion-buttons'].includes(referenceSizeEl.localName)) {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
if (referenceSizeEl.matches('.ios-theme-disabled, .ios26-disabled')) {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
return true;
|
|
64
|
-
})();
|
|
65
|
-
|
|
66
|
-
const defaultPosition = {
|
|
67
|
-
top: bodyHeight / 2 - contentHeight / 2,
|
|
68
|
-
left: bodyWidth / 2 - contentWidth / 2,
|
|
69
|
-
originX: isRTL ? 'right' : 'left',
|
|
70
|
-
originY: 'top',
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, reference, side, align, defaultPosition, trigger, ev);
|
|
74
|
-
|
|
75
|
-
// Use the same reference for placement, the callout and the animation origin.
|
|
76
|
-
const anchor = reference === 'event' ? results.referenceCoordinates : anchorBounds;
|
|
77
|
-
|
|
78
|
-
const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
|
|
79
|
-
const margin = size === 'cover' ? 0 : POPOVER_IOS_BODY_MARGIN;
|
|
80
|
-
|
|
81
|
-
const {
|
|
82
|
-
originX,
|
|
83
|
-
originY,
|
|
84
|
-
top,
|
|
85
|
-
left: windowLeft,
|
|
86
|
-
bottom,
|
|
87
|
-
checkSafeAreaLeft,
|
|
88
|
-
checkSafeAreaRight,
|
|
89
|
-
addPopoverBottomClass,
|
|
90
|
-
} = calculateWindowAdjustment(
|
|
91
|
-
side,
|
|
92
|
-
results.top,
|
|
93
|
-
results.left,
|
|
94
|
-
padding,
|
|
95
|
-
bodyWidth,
|
|
96
|
-
bodyHeight,
|
|
97
|
-
contentWidth,
|
|
98
|
-
contentHeight,
|
|
99
|
-
margin,
|
|
100
|
-
results.originX,
|
|
101
|
-
results.originY,
|
|
102
|
-
results.referenceCoordinates,
|
|
103
|
-
referenceSizeEl?.getBoundingClientRect(),
|
|
104
|
-
isReplace,
|
|
105
|
-
);
|
|
106
|
-
// A replacing surface grows inward from the button's edge, not its center.
|
|
107
|
-
const preferredLeft =
|
|
108
|
-
isReplace && anchorBounds
|
|
109
|
-
? anchorBounds.left + anchorBounds.width / 2 <= (paneLeft + paneRight) / 2
|
|
110
|
-
? anchorBounds.left
|
|
111
|
-
: anchorBounds.right - contentWidth
|
|
112
|
-
: windowLeft;
|
|
113
|
-
const left = pane ? Math.max(paneLeft + paneMargin, Math.min(paneRight - paneMargin - contentWidth, preferredLeft)) : preferredLeft;
|
|
114
|
-
const physicalSide = side === 'start' ? (isRTL ? 'right' : 'left') : side === 'end' ? (isRTL ? 'left' : 'right') : side;
|
|
115
|
-
const horizontal = physicalSide === 'left' || physicalSide === 'right';
|
|
116
|
-
const above = addPopoverBottomClass || physicalSide === 'top';
|
|
117
|
-
const hasCallout = !!arrowEl && !isReplace && !!anchor && size !== 'cover' && bottom === undefined;
|
|
118
|
-
const surfaceTop = hasCallout && !horizontal ? top + (above ? -5 : 5) : top;
|
|
119
|
-
const contentOrigin = anchor
|
|
120
|
-
? `${anchor.left + anchor.width / 2 - left}px ${anchor.top + anchor.height / 2 - surfaceTop}px`
|
|
121
|
-
: `${originX} ${originY}`;
|
|
122
|
-
|
|
123
|
-
const baseAnimation = createAnimation();
|
|
124
|
-
const backdropAnimation = createAnimation();
|
|
125
|
-
const contentAnimation = createAnimation();
|
|
126
|
-
const targetAnimation = createAnimation();
|
|
127
|
-
const arrowAnimation = createAnimation();
|
|
128
|
-
const surfaceAnimation = createAnimation();
|
|
129
|
-
if (arrowEl) {
|
|
130
|
-
arrowAnimation.addElement(arrowEl).delay(300).duration(200).fromTo('opacity', 0, 1);
|
|
131
|
-
}
|
|
132
|
-
if (hasCallout && anchor) {
|
|
133
|
-
const length = horizontal ? contentHeight : contentWidth;
|
|
134
|
-
const inset = Math.min(48, length / 2);
|
|
135
|
-
const center = Math.max(
|
|
136
|
-
inset,
|
|
137
|
-
Math.min(length - inset, horizontal ? anchor.top + anchor.height / 2 - top : anchor.left + anchor.width / 2 - left),
|
|
138
|
-
);
|
|
139
|
-
const arrowSide = horizontal ? (physicalSide === 'left' ? 'right' : 'left') : above ? 'bottom' : 'top';
|
|
140
|
-
const layers = createCalloutSurface(root, contentWidth, contentHeight, arrowSide, center);
|
|
141
|
-
baseEl.classList.add('ios-theme-callout');
|
|
142
|
-
for (const layer of layers) {
|
|
143
|
-
layer.style.left = `calc(${left - 32}px + var(--offset-x, 0))`;
|
|
144
|
-
layer.style.top = `calc(${surfaceTop - 32}px + var(--offset-y, 0))`;
|
|
145
|
-
const [originLeft, originTop] = contentOrigin.split(' ').map(parseFloat);
|
|
146
|
-
layer.style.transformOrigin = `${originLeft + 32}px ${originTop + 32}px`;
|
|
147
|
-
}
|
|
148
|
-
surfaceAnimation
|
|
149
|
-
.addElement(layers)
|
|
150
|
-
.delay(100)
|
|
151
|
-
.duration(400)
|
|
152
|
-
.easing('cubic-bezier(0, 1, 0.22, 1)')
|
|
153
|
-
.fromTo('transform', 'scale(0)', 'scale(1)')
|
|
154
|
-
.fromTo('opacity', 0.01, 1);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
backdropAnimation
|
|
158
|
-
.delay(100)
|
|
159
|
-
.duration(300)
|
|
160
|
-
.addElement(root.querySelector('ion-backdrop')!)
|
|
161
|
-
.fromTo('opacity', 0.01, 'var(--backdrop-opacity)')
|
|
162
|
-
.beforeStyles({
|
|
163
|
-
'pointer-events': 'none',
|
|
164
|
-
})
|
|
165
|
-
.afterClearStyles(['pointer-events']);
|
|
166
|
-
|
|
167
|
-
// In Chromium, if the wrapper animates, the backdrop filter doesn't work.
|
|
168
|
-
// 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.
|
|
169
|
-
// To get around this, instead of animating the wrapper, animate content.
|
|
170
|
-
// https://bugs.chromium.org/p/chromium/issues/detail?id=1148826
|
|
171
|
-
contentAnimation
|
|
172
|
-
.easing('cubic-bezier(0, 1, 0.22, 1)')
|
|
173
|
-
.delay(100)
|
|
174
|
-
.duration(400)
|
|
175
|
-
.addElement(root.querySelector('.popover-content')!)
|
|
176
|
-
.beforeStyles({ 'transform-origin': contentOrigin })
|
|
177
|
-
.beforeAddWrite(() => {
|
|
178
|
-
/**
|
|
179
|
-
* 'transformOrigin' use for leave animation.
|
|
180
|
-
*/
|
|
181
|
-
root.querySelector<HTMLElement>('.popover-content')!.dataset['transformOrigin'] = contentOrigin;
|
|
182
|
-
})
|
|
183
|
-
.fromTo('transform', 'scale(0)', 'scale(1)')
|
|
184
|
-
.fromTo('opacity', 0.01, 1);
|
|
185
|
-
|
|
186
|
-
if (isReplace) {
|
|
187
|
-
targetAnimation
|
|
188
|
-
.delay(0)
|
|
189
|
-
.duration(200)
|
|
190
|
-
.addElement(referenceSizeEl)
|
|
191
|
-
.beforeStyles({ 'transform-origin': `${originY} ${originX}` })
|
|
192
|
-
.beforeAddClass('ios-theme-replace-element')
|
|
193
|
-
.fromTo('transform', 'scale(1)', 'scale(1.05)')
|
|
194
|
-
.fromTo('opacity', 1, 0);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return baseAnimation
|
|
198
|
-
.easing('ease')
|
|
199
|
-
.delay(100)
|
|
200
|
-
.duration(100)
|
|
201
|
-
.beforeAddWrite(() => {
|
|
202
|
-
if (size === 'cover') {
|
|
203
|
-
baseEl.dataset['iosThemePreviousWidth'] = baseEl.style.getPropertyValue('--width');
|
|
204
|
-
baseEl.dataset['iosThemePreviousWidthPriority'] = baseEl.style.getPropertyPriority('--width');
|
|
205
|
-
baseEl.style.setProperty('--width', `${contentWidth}px`);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (addPopoverBottomClass) {
|
|
209
|
-
baseEl.classList.add('popover-bottom');
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (bottom !== undefined) {
|
|
213
|
-
contentEl.style.setProperty('bottom', `${bottom}px`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
|
|
217
|
-
const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
|
|
218
|
-
|
|
219
|
-
let leftValue = `${left}px`;
|
|
220
|
-
|
|
221
|
-
if (checkSafeAreaLeft && !pane) {
|
|
222
|
-
leftValue = `${left}px${safeAreaLeft}`;
|
|
223
|
-
}
|
|
224
|
-
if (checkSafeAreaRight && !pane) {
|
|
225
|
-
leftValue = `${left}px${safeAreaRight}`;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
contentEl.style.setProperty('top', `calc(${surfaceTop}px + var(--offset-y, 0))`);
|
|
229
|
-
contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
|
|
230
|
-
contentEl.style.setProperty('transform-origin', contentOrigin);
|
|
231
|
-
|
|
232
|
-
// Morphing buttons replace their anchor; ordinary anchored popovers point to it.
|
|
233
|
-
if (arrowEl) {
|
|
234
|
-
arrowEl.style.display = 'none';
|
|
235
|
-
if (hasCallout && anchor) {
|
|
236
|
-
const inset = Math.min(48, (horizontal ? contentHeight : contentWidth) / 2);
|
|
237
|
-
const clamp = (value: number, length: number) => Math.max(inset, Math.min(length - inset, value));
|
|
238
|
-
let arrowLeft: number;
|
|
239
|
-
let arrowTop: number;
|
|
240
|
-
let rotation: number;
|
|
241
|
-
if (horizontal) {
|
|
242
|
-
arrowLeft = physicalSide === 'left' ? left + contentWidth - 10 : left - 24;
|
|
243
|
-
arrowTop = top + clamp(anchor.top + anchor.height / 2 - top, contentHeight) - 7;
|
|
244
|
-
rotation = physicalSide === 'left' ? 90 : -90;
|
|
245
|
-
} else {
|
|
246
|
-
arrowLeft = left + clamp(anchor.left + anchor.width / 2 - left, contentWidth) - 17;
|
|
247
|
-
arrowTop = above ? surfaceTop + contentHeight - 1 : surfaceTop - 13;
|
|
248
|
-
rotation = above ? 180 : 0;
|
|
249
|
-
}
|
|
250
|
-
arrowEl.style.setProperty('display', 'block');
|
|
251
|
-
arrowEl.style.setProperty('top', `calc(${arrowTop}px + var(--offset-y, 0))`);
|
|
252
|
-
arrowEl.style.setProperty('left', `calc(${arrowLeft}px + var(--offset-x, 0))`);
|
|
253
|
-
arrowEl.style.setProperty('bottom', 'auto');
|
|
254
|
-
arrowEl.style.setProperty('transform', `rotate(${rotation}deg)`);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
})
|
|
258
|
-
.addAnimation([backdropAnimation, contentAnimation, targetAnimation, arrowAnimation, surfaceAnimation]);
|
|
259
|
-
};
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { createAnimation } from '@ionic/core';
|
|
2
|
-
import { getElementRoot } from '../../dom';
|
|
3
|
-
|
|
4
|
-
import type { Animation } from '@ionic/core';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* iOS Popover Leave Animation
|
|
8
|
-
*/
|
|
9
|
-
export const iosLeaveAnimation = (baseEl: HTMLElement): Animation => {
|
|
10
|
-
const root = getElementRoot(baseEl);
|
|
11
|
-
const contentEl = root.querySelector('.popover-content') as HTMLElement;
|
|
12
|
-
const arrowEl = root.querySelector('.popover-arrow') as HTMLElement | null;
|
|
13
|
-
|
|
14
|
-
const baseAnimation = createAnimation();
|
|
15
|
-
const backdropAnimation = createAnimation();
|
|
16
|
-
const contentAnimation = createAnimation();
|
|
17
|
-
const targetAnimation = createAnimation();
|
|
18
|
-
const surfaceAnimation = createAnimation()
|
|
19
|
-
.addElement(Array.from(root.querySelectorAll<HTMLElement>('.ios-theme-callout-layer')))
|
|
20
|
-
.duration(400)
|
|
21
|
-
.easing('ease')
|
|
22
|
-
.fromTo('opacity', 0.99, 0)
|
|
23
|
-
.fromTo('transform', 'scale(1)', 'scale(0)');
|
|
24
|
-
|
|
25
|
-
const doc = baseEl.ownerDocument as any;
|
|
26
|
-
const replaceElement = doc.querySelector('.ios-theme-replace-element') as HTMLElement | null;
|
|
27
|
-
|
|
28
|
-
if (replaceElement) {
|
|
29
|
-
const ratio = contentEl.getBoundingClientRect().width / contentEl.getBoundingClientRect().height;
|
|
30
|
-
const scale = ratio > 1 ? `${Math.min(1.2, 1.05 * ratio)}, 1.05` : `1.05, ${Math.min(1.2, 1.05 * ratio)}`;
|
|
31
|
-
|
|
32
|
-
targetAnimation
|
|
33
|
-
.addElement(replaceElement)
|
|
34
|
-
.delay(100)
|
|
35
|
-
.duration(300)
|
|
36
|
-
.afterRemoveClass('ios-theme-replace-element')
|
|
37
|
-
.fromTo('transform', `scale(${scale})`, 'scale(1)')
|
|
38
|
-
.fromTo('opacity', 0, 0.9);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
backdropAnimation.addElement(root.querySelector('ion-backdrop')!).fromTo('opacity', 'var(--backdrop-opacity)', 0);
|
|
42
|
-
|
|
43
|
-
contentAnimation
|
|
44
|
-
.duration(400)
|
|
45
|
-
.easing('ease')
|
|
46
|
-
.addElement(root.querySelector('.popover-arrow')!)
|
|
47
|
-
.addElement(root.querySelector('.popover-content')!)
|
|
48
|
-
.fromTo('opacity', 0.99, 0);
|
|
49
|
-
|
|
50
|
-
const popoverContentDataset = root.querySelector<HTMLElement>('.popover-content')!.dataset['transformOrigin'];
|
|
51
|
-
if (popoverContentDataset) {
|
|
52
|
-
contentAnimation.beforeStyles({ 'transform-origin': popoverContentDataset }).afterAddRead(() => {
|
|
53
|
-
root.querySelector<HTMLElement>('.popover-content')!.dataset['transformOrigin'] = '';
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
if (replaceElement) {
|
|
57
|
-
contentAnimation.fromTo('transform', 'scale(1)', 'scale(0.55)');
|
|
58
|
-
} else {
|
|
59
|
-
contentAnimation.fromTo('transform', 'scale(1)', 'scale(0)');
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return baseAnimation
|
|
64
|
-
.easing('ease')
|
|
65
|
-
.afterAddWrite(() => {
|
|
66
|
-
if (baseEl.dataset['iosThemePreviousWidth'] !== undefined) {
|
|
67
|
-
baseEl.style.setProperty('--width', baseEl.dataset['iosThemePreviousWidth'], baseEl.dataset['iosThemePreviousWidthPriority'] ?? '');
|
|
68
|
-
delete baseEl.dataset['iosThemePreviousWidth'];
|
|
69
|
-
delete baseEl.dataset['iosThemePreviousWidthPriority'];
|
|
70
|
-
}
|
|
71
|
-
baseEl.classList.remove('popover-bottom');
|
|
72
|
-
baseEl.classList.remove('ios-theme-callout');
|
|
73
|
-
root.querySelectorAll('.ios-theme-callout-layer').forEach((layer) => layer.remove());
|
|
74
|
-
|
|
75
|
-
contentEl.style.removeProperty('top');
|
|
76
|
-
contentEl.style.removeProperty('left');
|
|
77
|
-
contentEl.style.removeProperty('bottom');
|
|
78
|
-
contentEl.style.removeProperty('transform-origin');
|
|
79
|
-
if (contentEl.dataset['previousMaxWidth'] !== undefined) {
|
|
80
|
-
contentEl.style.setProperty(
|
|
81
|
-
'max-width',
|
|
82
|
-
contentEl.dataset['previousMaxWidth'],
|
|
83
|
-
contentEl.dataset['previousMaxWidthPriority'] ?? '',
|
|
84
|
-
);
|
|
85
|
-
delete contentEl.dataset['previousMaxWidth'];
|
|
86
|
-
delete contentEl.dataset['previousMaxWidthPriority'];
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (arrowEl) {
|
|
90
|
-
arrowEl.style.removeProperty('top');
|
|
91
|
-
arrowEl.style.removeProperty('left');
|
|
92
|
-
arrowEl.style.removeProperty('display');
|
|
93
|
-
arrowEl.style.removeProperty('bottom');
|
|
94
|
-
arrowEl.style.removeProperty('transform');
|
|
95
|
-
}
|
|
96
|
-
})
|
|
97
|
-
.duration(300)
|
|
98
|
-
.addAnimation([backdropAnimation, contentAnimation, targetAnimation, surfaceAnimation]);
|
|
99
|
-
};
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/** Draw one glass silhouette so the arrow cannot sample the body's own border/shadow. */
|
|
2
|
-
export const createCalloutSurface = (
|
|
3
|
-
root: ShadowRoot | HTMLElement,
|
|
4
|
-
width: number,
|
|
5
|
-
height: number,
|
|
6
|
-
side: 'top' | 'right' | 'bottom' | 'left',
|
|
7
|
-
center: number,
|
|
8
|
-
): HTMLElement[] => {
|
|
9
|
-
const inset = 32;
|
|
10
|
-
const x = inset;
|
|
11
|
-
const y = inset;
|
|
12
|
-
const right = x + width;
|
|
13
|
-
const bottom = y + height;
|
|
14
|
-
const radius = Math.max(0, Math.min(34, width / 2, height / 2, (side === 'left' || side === 'right' ? height : width) / 2 - 17));
|
|
15
|
-
const point = (u: number, v: number): string => {
|
|
16
|
-
switch (side) {
|
|
17
|
-
case 'top':
|
|
18
|
-
return `${x + center - 17 + u} ${y - 13 + v}`;
|
|
19
|
-
case 'right':
|
|
20
|
-
return `${right + 13 - v} ${y + center - 17 + u}`;
|
|
21
|
-
case 'bottom':
|
|
22
|
-
return `${x + center + 17 - u} ${bottom + 13 - v}`;
|
|
23
|
-
case 'left':
|
|
24
|
-
return `${x - 13 + v} ${y + center + 17 - u}`;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
const arrow = `L ${point(0, 13)} C ${point(3, 13)} ${point(4.5, 12)} ${point(6.5, 10)} L ${point(14.5, 1.5)} C ${point(16, -0.5)} ${point(18, -0.5)} ${point(19.5, 1.5)} L ${point(27.5, 10)} C ${point(29.5, 12)} ${point(31, 13)} ${point(34, 13)}`;
|
|
28
|
-
const path = `M ${x + radius} ${y} ${side === 'top' ? arrow : ''}
|
|
29
|
-
L ${right - radius} ${y} A ${radius} ${radius} 0 0 1 ${right} ${y + radius} ${side === 'right' ? arrow : ''}
|
|
30
|
-
L ${right} ${bottom - radius} A ${radius} ${radius} 0 0 1 ${right - radius} ${bottom} ${side === 'bottom' ? arrow : ''}
|
|
31
|
-
L ${x + radius} ${bottom} A ${radius} ${radius} 0 0 1 ${x} ${bottom - radius} ${side === 'left' ? arrow : ''}
|
|
32
|
-
L ${x} ${y + radius} A ${radius} ${radius} 0 0 1 ${x + radius} ${y} Z`;
|
|
33
|
-
const svg = (body: string) =>
|
|
34
|
-
`url("data:image/svg+xml,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" width="${width + inset * 2}" height="${height + inset * 2}">${body}</svg>`)}")`;
|
|
35
|
-
const fill = svg(`<path d="${path}" fill="white"/>`);
|
|
36
|
-
const stroke = svg(`<path d="${path}" fill="none" stroke="white" stroke-width="0.5"/>`);
|
|
37
|
-
const shadow = svg(
|
|
38
|
-
`<defs><filter id="s" x="-50%" y="-100%" width="200%" height="300%"><feGaussianBlur stdDeviation="4"/><feOffset dy="2"/><feComposite in2="SourceGraphic" operator="out"/></filter></defs><path d="${path}" fill="white" filter="url(#s)"/>`,
|
|
39
|
-
);
|
|
40
|
-
const wrapper = root.querySelector('.popover-wrapper')!;
|
|
41
|
-
return [
|
|
42
|
-
['callout-shadow', shadow],
|
|
43
|
-
['callout-glass', fill],
|
|
44
|
-
['callout-border', stroke],
|
|
45
|
-
].map(([part, mask]) => {
|
|
46
|
-
const element = document.createElement('div');
|
|
47
|
-
element.setAttribute('part', part);
|
|
48
|
-
element.className = 'ios-theme-callout-layer';
|
|
49
|
-
element.setAttribute('aria-hidden', 'true');
|
|
50
|
-
element.style.cssText = `position:absolute;pointer-events:none;z-index:9;width:${width + inset * 2}px;height:${height + inset * 2}px;mask-image:${mask};-webkit-mask-image:${mask};mask-repeat:no-repeat;`;
|
|
51
|
-
wrapper.append(element);
|
|
52
|
-
return element;
|
|
53
|
-
});
|
|
54
|
-
};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { AnimationBuilder, ComponentProps, ComponentRef, FrameworkDelegate, Mode, OverlayInterface } from '@ionic/core';
|
|
2
|
-
|
|
3
|
-
export interface PopoverInterface extends OverlayInterface {
|
|
4
|
-
present: (event?: MouseEvent | TouchEvent | PointerEvent) => Promise<void>;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export interface PopoverOptions<T extends ComponentRef = ComponentRef> {
|
|
8
|
-
component: T;
|
|
9
|
-
componentProps?: ComponentProps<T>;
|
|
10
|
-
showBackdrop?: boolean;
|
|
11
|
-
backdropDismiss?: boolean;
|
|
12
|
-
translucent?: boolean;
|
|
13
|
-
cssClass?: string | string[];
|
|
14
|
-
event?: Event;
|
|
15
|
-
delegate?: FrameworkDelegate;
|
|
16
|
-
animated?: boolean;
|
|
17
|
-
focusTrap?: boolean;
|
|
18
|
-
|
|
19
|
-
mode?: Mode;
|
|
20
|
-
keyboardClose?: boolean;
|
|
21
|
-
id?: string;
|
|
22
|
-
htmlAttributes?: { [key: string]: any };
|
|
23
|
-
|
|
24
|
-
enterAnimation?: AnimationBuilder;
|
|
25
|
-
leaveAnimation?: AnimationBuilder;
|
|
26
|
-
|
|
27
|
-
size?: PopoverSize;
|
|
28
|
-
dismissOnSelect?: boolean;
|
|
29
|
-
reference?: PositionReference;
|
|
30
|
-
side?: PositionSide;
|
|
31
|
-
alignment?: PositionAlign;
|
|
32
|
-
arrow?: boolean;
|
|
33
|
-
|
|
34
|
-
trigger?: string;
|
|
35
|
-
triggerAction?: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export type PopoverSize = 'cover' | 'auto';
|
|
39
|
-
|
|
40
|
-
export type TriggerAction = 'click' | 'hover' | 'context-menu';
|
|
41
|
-
|
|
42
|
-
export type PositionReference = 'trigger' | 'event';
|
|
43
|
-
export type PositionSide = 'top' | 'right' | 'bottom' | 'left' | 'start' | 'end';
|
|
44
|
-
export type PositionAlign = 'start' | 'center' | 'end';
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { calculateWindowAdjustment, getIndexOfItem, getNextItem, getPopoverPosition, getPrevItem } from './utils';
|
|
4
|
-
|
|
5
|
-
describe('popover utilities', () => {
|
|
6
|
-
it('navigates only relative to ion-item elements', () => {
|
|
7
|
-
const items = [document.createElement('ion-item'), document.createElement('ion-item')] as HTMLIonItemElement[];
|
|
8
|
-
|
|
9
|
-
expect(getIndexOfItem(items, items[0])).toBe(0);
|
|
10
|
-
expect(getIndexOfItem(items, document.createElement('div'))).toBe(-1);
|
|
11
|
-
expect(getNextItem(items, items[0])).toBe(items[1]);
|
|
12
|
-
expect(getPrevItem(items, items[1])).toBe(items[0]);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('keeps a popover inside the left safe-area boundary', () => {
|
|
16
|
-
expect(calculateWindowAdjustment('bottom', 20, -10, 16, 390, 844, 280, 140, 8, 'center', 'top')).toMatchObject({
|
|
17
|
-
top: 28,
|
|
18
|
-
left: 16,
|
|
19
|
-
originX: 'left',
|
|
20
|
-
checkSafeAreaLeft: true,
|
|
21
|
-
checkSafeAreaRight: false,
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('keeps a popover inside the right safe-area boundary', () => {
|
|
26
|
-
expect(calculateWindowAdjustment('bottom', 20, 150, 16, 390, 844, 280, 140, 8, 'center', 'top')).toMatchObject({
|
|
27
|
-
top: 28,
|
|
28
|
-
left: 94,
|
|
29
|
-
originX: 'right',
|
|
30
|
-
checkSafeAreaLeft: false,
|
|
31
|
-
checkSafeAreaRight: true,
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it.each([
|
|
36
|
-
{ isRTL: false, side: 'start' as const, left: 60, originX: 'right' },
|
|
37
|
-
{ isRTL: false, side: 'end' as const, left: 140, originX: 'left' },
|
|
38
|
-
{ isRTL: true, side: 'start' as const, left: 140, originX: 'left' },
|
|
39
|
-
{ isRTL: true, side: 'end' as const, left: 60, originX: 'right' },
|
|
40
|
-
])('maps the logical $side side in RTL=$isRTL', ({ isRTL, side, left, originX }) => {
|
|
41
|
-
const trigger = document.createElement('button');
|
|
42
|
-
trigger.getBoundingClientRect = () => ({
|
|
43
|
-
top: 20,
|
|
44
|
-
left: 100,
|
|
45
|
-
width: 40,
|
|
46
|
-
height: 30,
|
|
47
|
-
right: 140,
|
|
48
|
-
bottom: 50,
|
|
49
|
-
x: 100,
|
|
50
|
-
y: 20,
|
|
51
|
-
toJSON: () => ({}),
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
expect(
|
|
55
|
-
getPopoverPosition(isRTL, 40, 20, 'trigger', side, 'start', { top: 0, left: 0, originX: 'left', originY: 'top' }, trigger),
|
|
56
|
-
).toMatchObject({ left, originX });
|
|
57
|
-
});
|
|
58
|
-
});
|