@rdlabo/ionic-theme-ios27 1.2.0-3 → 1.2.0-4

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.
Files changed (175) hide show
  1. package/README.md +15 -2
  2. package/dist/_virtual/_rolldown/runtime.js +13 -0
  3. package/dist/css/ionic-theme-ios27.css +1 -1
  4. package/dist/css/vertical-bars.css +1 -1
  5. package/dist/focus-controller/index.d.ts +4 -2
  6. package/dist/focus-controller/index.d.ts.map +1 -1
  7. package/dist/focus-controller/index.js +100 -57
  8. package/dist/focus-controller/index.js.map +1 -0
  9. package/dist/index.d.ts +10 -8
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +54 -60
  12. package/dist/index.js.map +1 -0
  13. package/dist/native/components/index.d.ts +13 -10
  14. package/dist/native/components/index.d.ts.map +1 -1
  15. package/dist/native/components/index.js +39 -41
  16. package/dist/native/components/index.js.map +1 -0
  17. package/dist/native/components/ion-back-button.d.ts +6 -1
  18. package/dist/native/components/ion-back-button.d.ts.map +1 -1
  19. package/dist/native/components/ion-back-button.js +19 -13
  20. package/dist/native/components/ion-back-button.js.map +1 -0
  21. package/dist/native/components/ion-button.d.ts +8 -2
  22. package/dist/native/components/ion-button.d.ts.map +1 -1
  23. package/dist/native/components/ion-button.js +22 -17
  24. package/dist/native/components/ion-button.js.map +1 -0
  25. package/dist/native/components/ion-buttons.d.ts +8 -2
  26. package/dist/native/components/ion-buttons.d.ts.map +1 -1
  27. package/dist/native/components/ion-buttons.js +29 -33
  28. package/dist/native/components/ion-buttons.js.map +1 -0
  29. package/dist/native/components/ion-fab.d.ts +6 -1
  30. package/dist/native/components/ion-fab.d.ts.map +1 -1
  31. package/dist/native/components/ion-fab.js +131 -151
  32. package/dist/native/components/ion-fab.js.map +1 -0
  33. package/dist/native/components/ion-menu-button.d.ts +8 -2
  34. package/dist/native/components/ion-menu-button.d.ts.map +1 -1
  35. package/dist/native/components/ion-menu-button.js +29 -19
  36. package/dist/native/components/ion-menu-button.js.map +1 -0
  37. package/dist/native/components/ion-segment.d.ts +6 -1
  38. package/dist/native/components/ion-segment.d.ts.map +1 -1
  39. package/dist/native/components/ion-segment.js +29 -23
  40. package/dist/native/components/ion-segment.js.map +1 -0
  41. package/dist/native/components/ion-tab-bar.d.ts +6 -1
  42. package/dist/native/components/ion-tab-bar.d.ts.map +1 -1
  43. package/dist/native/components/ion-tab-bar.js +49 -40
  44. package/dist/native/components/ion-tab-bar.js.map +1 -0
  45. package/dist/native/components/searchable-tabs.d.ts +17 -15
  46. package/dist/native/components/searchable-tabs.d.ts.map +1 -1
  47. package/dist/native/components/searchable-tabs.js +382 -434
  48. package/dist/native/components/searchable-tabs.js.map +1 -0
  49. package/dist/native/definitions.d.ts +118 -111
  50. package/dist/native/definitions.d.ts.map +1 -1
  51. package/dist/native/definitions.js +0 -1
  52. package/dist/native/foldable/fold-state.d.ts +5 -0
  53. package/dist/native/foldable/fold-state.d.ts.map +1 -0
  54. package/dist/native/foldable/fold-state.js +14 -0
  55. package/dist/native/foldable/fold-state.js.map +1 -0
  56. package/dist/native/foldable/types.d.ts +12 -0
  57. package/dist/native/foldable/types.d.ts.map +1 -0
  58. package/dist/native/foldable/types.js +0 -0
  59. package/dist/native/index.d.ts +6 -3
  60. package/dist/native/index.d.ts.map +1 -1
  61. package/dist/native/index.js +228 -204
  62. package/dist/native/index.js.map +1 -0
  63. package/dist/native/prehide.d.ts +4 -2
  64. package/dist/native/prehide.d.ts.map +1 -1
  65. package/dist/native/prehide.js +220 -242
  66. package/dist/native/prehide.js.map +1 -0
  67. package/dist/native/runtime.d.ts +3 -1
  68. package/dist/native/runtime.d.ts.map +1 -1
  69. package/dist/native/runtime.js +626 -730
  70. package/dist/native/runtime.js.map +1 -0
  71. package/dist/native/shared/candidate.d.ts +16 -11
  72. package/dist/native/shared/candidate.d.ts.map +1 -1
  73. package/dist/native/shared/candidate.js +96 -95
  74. package/dist/native/shared/candidate.js.map +1 -0
  75. package/dist/native/shared/crossfade.d.ts +6 -4
  76. package/dist/native/shared/crossfade.d.ts.map +1 -1
  77. package/dist/native/shared/crossfade.js +49 -45
  78. package/dist/native/shared/crossfade.js.map +1 -0
  79. package/dist/native/shared/dom.d.ts +7 -5
  80. package/dist/native/shared/dom.d.ts.map +1 -1
  81. package/dist/native/shared/dom.js +163 -211
  82. package/dist/native/shared/dom.js.map +1 -0
  83. package/dist/native/shared/events.d.ts +8 -6
  84. package/dist/native/shared/events.d.ts.map +1 -1
  85. package/dist/native/shared/events.js +12 -7
  86. package/dist/native/shared/events.js.map +1 -0
  87. package/dist/native/shared/icons.d.ts +4 -2
  88. package/dist/native/shared/icons.d.ts.map +1 -1
  89. package/dist/native/shared/icons.js +59 -53
  90. package/dist/native/shared/icons.js.map +1 -0
  91. package/dist/native/shared/modal.d.ts +3 -1
  92. package/dist/native/shared/modal.d.ts.map +1 -1
  93. package/dist/native/shared/modal.js +156 -139
  94. package/dist/native/shared/modal.js.map +1 -0
  95. package/dist/native/vertical-bars-web.d.ts +3 -1
  96. package/dist/native/vertical-bars-web.d.ts.map +1 -1
  97. package/dist/native/vertical-bars-web.js +332 -346
  98. package/dist/native/vertical-bars-web.js.map +1 -0
  99. package/dist/native-integration/index.d.ts +10 -8
  100. package/dist/native-integration/index.d.ts.map +1 -1
  101. package/dist/native-integration/index.js +102 -100
  102. package/dist/native-integration/index.js.map +1 -0
  103. package/dist/native-integration/transition.d.ts +3 -1
  104. package/dist/native-integration/transition.d.ts.map +1 -1
  105. package/dist/native-integration/transition.js +15 -6
  106. package/dist/native-integration/transition.js.map +1 -0
  107. package/dist/searchbar/index.d.ts +4 -2
  108. package/dist/searchbar/index.d.ts.map +1 -1
  109. package/dist/searchbar/index.js +64 -56
  110. package/dist/searchbar/index.js.map +1 -0
  111. package/dist/segment/index.d.ts +3 -1
  112. package/dist/segment/index.d.ts.map +1 -1
  113. package/dist/segment/index.js +465 -324
  114. package/dist/segment/index.js.map +1 -0
  115. package/dist/sheets-of-glass/animations.d.ts +13 -12
  116. package/dist/sheets-of-glass/animations.d.ts.map +1 -1
  117. package/dist/sheets-of-glass/animations.js +496 -306
  118. package/dist/sheets-of-glass/animations.js.map +1 -0
  119. package/dist/sheets-of-glass/index.d.ts +3 -1
  120. package/dist/sheets-of-glass/index.d.ts.map +1 -1
  121. package/dist/sheets-of-glass/index.js +294 -357
  122. package/dist/sheets-of-glass/index.js.map +1 -0
  123. package/dist/sheets-of-glass/interfaces.d.ts +11 -9
  124. package/dist/sheets-of-glass/interfaces.d.ts.map +1 -1
  125. package/dist/sheets-of-glass/interfaces.js +0 -1
  126. package/dist/tab-bar-searchable/animations/enter.d.ts +4 -2
  127. package/dist/tab-bar-searchable/animations/enter.d.ts.map +1 -1
  128. package/dist/tab-bar-searchable/animations/enter.js +50 -69
  129. package/dist/tab-bar-searchable/animations/enter.js.map +1 -0
  130. package/dist/tab-bar-searchable/animations/leave.d.ts +4 -2
  131. package/dist/tab-bar-searchable/animations/leave.d.ts.map +1 -1
  132. package/dist/tab-bar-searchable/animations/leave.js +48 -62
  133. package/dist/tab-bar-searchable/animations/leave.js.map +1 -0
  134. package/dist/tab-bar-searchable/index.d.ts +5 -3
  135. package/dist/tab-bar-searchable/index.d.ts.map +1 -1
  136. package/dist/tab-bar-searchable/index.js +123 -135
  137. package/dist/tab-bar-searchable/index.js.map +1 -0
  138. package/dist/transition/index.d.ts +9 -8
  139. package/dist/transition/index.d.ts.map +1 -1
  140. package/dist/transition/index.js +203 -192
  141. package/dist/transition/index.js.map +1 -0
  142. package/dist/transition/ios.transition.d.ts +7 -5
  143. package/dist/transition/ios.transition.d.ts.map +1 -1
  144. package/dist/transition/ios.transition.js +15 -11
  145. package/dist/transition/ios.transition.js.map +1 -0
  146. package/dist/utils.d.ts +10 -8
  147. package/dist/utils.d.ts.map +1 -1
  148. package/dist/utils.js +58 -66
  149. package/dist/utils.js.map +1 -0
  150. package/dist/vertical-bars.d.ts +5 -4
  151. package/dist/vertical-bars.js +4 -2
  152. package/docs/iphone-duo-with-original-theme.md +18 -29
  153. package/docs/iphone-duo.md +44 -204
  154. package/docs/native-ui-shell.md +10 -8
  155. package/docs/special-markup.md +2 -0
  156. package/docs/vertical-bars.md +283 -0
  157. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +34 -14
  158. package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellSnapshot.swift +2 -0
  159. package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +3 -0
  160. package/package.json +9 -6
  161. package/src/native/components/index.ts +3 -2
  162. package/src/native/components/ion-button.ts +3 -2
  163. package/src/native/components/ion-buttons.ts +4 -3
  164. package/src/native/components/ion-menu-button.ts +3 -2
  165. package/src/native/definitions.ts +22 -1
  166. package/src/native/foldable/fold-state.ts +9 -0
  167. package/src/native/foldable/types.ts +4 -0
  168. package/src/native/index.ts +17 -3
  169. package/src/native/prehide.ts +3 -1
  170. package/src/native/runtime.ts +2 -2
  171. package/src/native/shared/candidate.ts +39 -10
  172. package/src/native/shared/dom.ts +4 -4
  173. package/src/styles/vertical-bars.scss +4 -2
  174. package/src/vertical-bars.ts +9 -1
  175. package/dist/vertical-bars.d.ts.map +0 -1
@@ -1,358 +1,295 @@
1
- import { createGesture } from '@ionic/core';
2
- import { changeSelectedElement, cloneElement, getStep } from '../utils';
3
- import { isNativeUIShell } from '../native-integration';
4
- import { animationRect, createMoveAnimation, createPreMoveAnimation, createTabBarDragAnimation, createTabBarPressAnimation, createTabBarReleaseAnimation, getMoveAnimationKeyframe, getScaleAnimation, } from './animations';
5
- const GESTURE_NAME = 'ios27-enable-gesture';
6
- const ANIMATED_NAME = 'ios27-animated';
7
- export const registerEffect = (targetElement, effectTagName, selectedClassName, scales, vertical = false) => {
8
- if (!targetElement.classList.contains('ios')) {
9
- return undefined;
10
- }
11
- let gesture;
12
- let moveAnimation;
13
- let currentTouchedElement;
14
- let selectedElementBeforeGesture;
15
- let clearActivatedTimer;
16
- let animationPosition = undefined;
17
- let scaleAnimationPromise;
18
- let startAnimationPromise;
19
- let maxVelocity = 0;
20
- let releaseAnimation;
21
- let tabPressAnimation;
22
- let tabPressStartedAt = 0;
23
- let tabDragging = false;
24
- let destroyed = false;
25
- const originalTabBarScale = targetElement.style.getPropertyValue('--ios27-tab-bar-scale');
26
- const effectElement = cloneElement(effectTagName, false);
27
- if (vertical)
28
- effectElement.classList.add('ios27-vertical-tab-effect');
29
- const stopTabPress = () => {
30
- if (!tabPressAnimation)
31
- return;
32
- const native = effectElement.shadowRoot.querySelector('[part="native"]');
33
- const transform = getComputedStyle(effectElement).transform;
34
- const scale = getComputedStyle(native).transform;
35
- tabPressAnimation.destroy();
36
- tabPressAnimation = undefined;
37
- effectElement.style.transform = transform;
38
- native.style.transform = scale;
39
- };
40
- let onPointerUp;
41
- let onPointerCancel;
42
- const removePointerEndListeners = () => {
43
- if (onPointerUp) {
44
- document.removeEventListener('pointerup', onPointerUp);
45
- onPointerUp = undefined;
46
- }
47
- if (onPointerCancel) {
48
- document.removeEventListener('pointercancel', onPointerCancel);
49
- onPointerCancel = undefined;
50
- }
51
- };
52
- const cancelActiveGesture = () => {
53
- stopTabPress();
54
- releaseAnimation?.destroy();
55
- releaseAnimation = undefined;
56
- removePointerEndListeners();
57
- if (clearActivatedTimer !== undefined) {
58
- clearTimeout(clearActivatedTimer);
59
- clearActivatedTimer = undefined;
60
- }
61
- currentTouchedElement?.classList.remove('ion-activated');
62
- if (!currentTouchedElement?.classList.contains(selectedClassName)) {
63
- selectedElementBeforeGesture?.classList.add(selectedClassName);
64
- }
65
- currentTouchedElement = undefined;
66
- selectedElementBeforeGesture = undefined;
67
- moveAnimation?.destroy();
68
- moveAnimation = undefined;
69
- effectElement.style.display = 'none';
70
- maxVelocity = 0;
71
- targetElement.classList.remove(ANIMATED_NAME);
72
- };
73
- const onPointerDown = (event) => {
74
- if (isNativeUIShell(targetElement))
75
- return;
76
- stopTabPress();
77
- tabDragging = false;
78
- releaseAnimation?.destroy();
79
- releaseAnimation = undefined;
80
- clearActivated();
81
- gesture.destroy();
82
- createAnimationGesture();
83
- removePointerEndListeners();
84
- onPointerUp = () => {
85
- clearActivatedTimer = setTimeout(async () => {
86
- await onEndGesture();
87
- if (destroyed)
88
- return;
89
- gesture.destroy();
90
- createAnimationGesture();
91
- });
92
- removePointerEndListeners();
93
- };
94
- onPointerCancel = () => {
95
- cancelActiveGesture();
96
- gesture.destroy();
97
- createAnimationGesture();
98
- };
99
- document.addEventListener('pointerup', onPointerUp);
100
- document.addEventListener('pointercancel', onPointerCancel);
101
- };
102
- targetElement.addEventListener('pointerdown', onPointerDown);
103
- const onClick = (event) => {
104
- if (vertical && tabDragging && event.isTrusted && event.detail > 0) {
105
- event.preventDefault();
106
- event.stopImmediatePropagation();
107
- }
108
- };
109
- targetElement.addEventListener('click', onClick, true);
110
- const axisDetail = (detail) => vertical
111
- ? {
112
- ...detail,
113
- currentX: detail.currentY,
114
- currentY: detail.currentX,
115
- startX: detail.startY,
116
- startY: detail.startX,
117
- velocityX: detail.velocityY,
118
- velocityY: detail.velocityX,
119
- }
120
- : detail;
121
- const createAnimationGesture = () => {
122
- targetElement.classList.add(GESTURE_NAME);
123
- gesture = createGesture({
124
- el: targetElement,
125
- threshold: 0,
126
- direction: vertical ? 'y' : 'x',
127
- gestureName: `${GESTURE_NAME}_${effectTagName}_${crypto.randomUUID()}`,
128
- onStart: (event) => onStartGesture(axisDetail(event)),
129
- onMove: (event) => onMoveGesture(axisDetail(event)),
130
- onEnd: (detail) => {
131
- if (effectTagName === 'ion-tab-button' && tabDragging)
132
- onMoveGesture(axisDetail(detail));
133
- onEndGesture().then();
134
- },
135
- });
136
- gesture.enable(true);
137
- };
138
- createAnimationGesture();
139
- const clearActivated = (isEndGesture = false) => {
140
- if (!currentTouchedElement) {
141
- return;
142
- }
143
- if (!isEndGesture && !currentTouchedElement.classList.contains(selectedClassName)) {
144
- currentTouchedElement.click();
145
- currentTouchedElement.classList.add(selectedClassName);
146
- }
147
- currentTouchedElement.classList.remove('ion-activated');
148
- currentTouchedElement = undefined;
149
- selectedElementBeforeGesture = undefined;
150
- effectElement.style.display = 'none';
151
- maxVelocity = 0;
152
- targetElement.classList.remove(ANIMATED_NAME);
153
- };
154
- const onStartGesture = (detail) => {
155
- if (isNativeUIShell(targetElement))
156
- return false;
157
- currentTouchedElement = detail.event.target.closest(effectTagName) || undefined;
158
- const tabSelectedElement = targetElement.querySelector(`${effectTagName}.${selectedClassName}`);
159
- if (currentTouchedElement === undefined ||
160
- currentTouchedElement.parentElement !== targetElement ||
161
- currentTouchedElement.disabled ||
162
- tabSelectedElement === null) {
163
- currentTouchedElement = undefined;
164
- return false;
165
- }
166
- selectedElementBeforeGesture = tabSelectedElement;
167
- if (effectTagName === 'ion-tab-button') {
168
- targetElement.style.setProperty('--ios27-tab-bar-scale', String(1 + 14.14 / (vertical ? targetElement.offsetHeight : targetElement.offsetWidth)));
169
- }
170
- if (vertical)
171
- targetElement.classList.add(ANIMATED_NAME);
172
- animationPosition = {
173
- minPositionX: animationRect(targetElement, vertical).left,
174
- maxPositionX: animationRect(targetElement, vertical).right - (vertical ? tabSelectedElement.clientHeight : tabSelectedElement.clientWidth),
175
- width: vertical ? tabSelectedElement.clientHeight : tabSelectedElement.clientWidth,
176
- positionY: animationRect(tabSelectedElement, vertical).top,
177
- };
178
- targetElement.classList.add(ANIMATED_NAME);
179
- changeSelectedElement(targetElement, currentTouchedElement, effectTagName, selectedClassName);
180
- if (effectTagName === 'ion-tab-button') {
181
- moveAnimation = createMoveAnimation(effectElement, detail, tabSelectedElement, animationPosition, vertical);
182
- moveAnimation.progressStart(true, getStep(animationRect(tabSelectedElement, vertical).left + animationRect(tabSelectedElement, vertical).width / 2, animationPosition));
183
- tabPressAnimation = createTabBarPressAnimation(effectElement, tabSelectedElement, currentTouchedElement, targetElement, vertical);
184
- tabPressStartedAt = performance.now();
185
- void tabPressAnimation.play();
186
- return true;
187
- }
188
- const startAnimation = (() => {
189
- if (tabSelectedElement === currentTouchedElement) {
190
- return new Promise((resolve) => resolve());
191
- }
192
- else {
193
- const preMoveAnimation = createPreMoveAnimation(effectElement, tabSelectedElement, currentTouchedElement, animationPosition);
194
- return preMoveAnimation.play().finally(() => preMoveAnimation.destroy());
195
- }
196
- })();
197
- startAnimationPromise = startAnimation;
198
- startAnimation.then(() => {
199
- if (!currentTouchedElement) {
200
- return;
201
- }
202
- moveAnimation = createMoveAnimation(effectElement, detail, tabSelectedElement, animationPosition);
203
- moveAnimation.progressStart(true, getStep(currentTouchedElement.getBoundingClientRect().left + currentTouchedElement.clientWidth / 2, animationPosition));
204
- });
205
- getScaleAnimation(effectElement).duration(200).to('opacity', 1).to('transform', scales.large).play();
206
- return true;
207
- };
208
- const onMoveGesture = (detail) => {
209
- if (currentTouchedElement === undefined || !moveAnimation || releaseAnimation) {
210
- return false;
211
- }
212
- if (effectTagName === 'ion-tab-button') {
213
- if (Math.abs(detail.currentX - detail.startX) < 3 && !tabDragging)
214
- return;
215
- tabDragging = true;
216
- stopTabPress();
217
- tabPressAnimation = createTabBarDragAnimation(effectElement, targetElement, detail.velocityX, vertical);
218
- void tabPressAnimation.play();
219
- }
220
- else if (scaleAnimationPromise === undefined) {
221
- if (Math.abs(detail.velocityX) > maxVelocity) {
222
- maxVelocity = Math.abs(detail.velocityX);
223
- }
224
- if (Math.abs(detail.velocityX) > 0.2) {
225
- scaleAnimationPromise = getScaleAnimation(effectElement)
226
- .duration(720)
227
- .keyframes(getMoveAnimationKeyframe('slowly', scales))
228
- .play()
229
- .finally(() => (scaleAnimationPromise = undefined));
230
- }
231
- if (maxVelocity > 0.2 && Math.abs(detail.velocityX) < 0.15 && Math.abs(detail.startX - detail.currentX) > 100) {
232
- scaleAnimationPromise = getScaleAnimation(effectElement)
233
- .duration(720)
234
- .keyframes(getMoveAnimationKeyframe(detail.velocityX > 0 ? 'moveRight' : 'moveLeft', scales))
235
- .play()
236
- .finally(() => (scaleAnimationPromise = undefined));
237
- maxVelocity = 0;
238
- }
239
- }
240
- const currentX = detail.currentX;
241
- const previousY = animationRect(targetElement, vertical).top + animationRect(targetElement, vertical).height / 2;
242
- const nextEl = targetElement.getRootNode().elementFromPoint(vertical ? previousY : currentX, vertical ? currentX : previousY);
243
- let latestTouchedElement = nextEl?.closest(effectTagName) || undefined;
244
- if (vertical && effectTagName === 'ion-tab-button') {
245
- const bar = targetElement.getBoundingClientRect();
246
- const tabs = Array.from(targetElement.querySelectorAll(effectTagName)).filter((tab) => tab.offsetWidth > 0);
247
- const distance = (tab) => Math.abs(currentX - (bar.top + tab.offsetTop + tab.offsetHeight / 2));
248
- latestTouchedElement =
249
- currentX >= bar.top && currentX <= bar.bottom
250
- ? tabs.reduce((closest, tab) => (!closest || distance(tab) < distance(closest) ? tab : closest), undefined)
251
- : undefined;
252
- }
253
- if (latestTouchedElement &&
254
- latestTouchedElement.parentElement === targetElement &&
255
- !latestTouchedElement.disabled &&
256
- currentTouchedElement !== latestTouchedElement) {
257
- currentTouchedElement = latestTouchedElement;
258
- changeSelectedElement(targetElement, currentTouchedElement, effectTagName, selectedClassName);
259
- }
260
- moveAnimation.progressStep(getStep(detail.currentX, animationPosition));
261
- return true;
262
- };
263
- const onEndGesture = async () => {
264
- if (releaseAnimation)
265
- return;
266
- if (clearActivatedTimer !== undefined) {
267
- clearTimeout(clearActivatedTimer);
268
- clearActivatedTimer = undefined;
269
- }
270
- if (currentTouchedElement && !currentTouchedElement.classList.contains(selectedClassName)) {
271
- currentTouchedElement.click();
272
- currentTouchedElement.classList.add(selectedClassName);
273
- }
274
- if (startAnimationPromise) {
275
- await startAnimationPromise;
276
- }
277
- const tapElapsed = tabPressAnimation && !tabDragging && selectedElementBeforeGesture !== currentTouchedElement
278
- ? performance.now() - tabPressStartedAt
279
- : undefined;
280
- if (tabPressAnimation && !tabDragging && tapElapsed === undefined) {
281
- const pressing = tabPressAnimation;
282
- const remaining = 200 - (performance.now() - tabPressStartedAt);
283
- if (remaining > 0)
284
- await new Promise((resolve) => setTimeout(resolve, remaining));
285
- if (tabPressAnimation !== pressing)
286
- return;
287
- }
288
- if (currentTouchedElement === undefined || !moveAnimation || releaseAnimation) {
289
- return false;
290
- }
291
- if (effectTagName !== 'ion-tab-button')
292
- setTimeout(() => {
293
- if (!currentTouchedElement) {
294
- return;
295
- }
296
- const targetX = currentTouchedElement.getBoundingClientRect().left + currentTouchedElement.clientWidth / 2;
297
- const step = getStep(targetX, animationPosition);
298
- moveAnimation.progressStep(step);
299
- });
300
- if (effectTagName === 'ion-tab-button') {
301
- stopTabPress();
302
- moveAnimation.destroy();
303
- moveAnimation = undefined;
304
- const tabs = Array.from(targetElement.querySelectorAll(effectTagName)).filter((tab) => tab.offsetWidth > 0);
305
- if (vertical) {
306
- currentTouchedElement.classList.remove('ion-activated');
307
- targetElement.classList.remove(ANIMATED_NAME);
308
- }
309
- const releasing = createTabBarReleaseAnimation(effectElement, currentTouchedElement, tapElapsed !== undefined && tapElapsed < 200
310
- ? {
311
- elapsed: tapElapsed,
312
- distance: Math.abs(tabs.indexOf(currentTouchedElement) - tabs.indexOf(selectedElementBeforeGesture)),
313
- }
314
- : undefined, vertical);
315
- currentTouchedElement.classList.remove('ion-activated');
316
- releaseAnimation = releasing;
317
- await releasing.play();
318
- if (releaseAnimation !== releasing)
319
- return;
320
- clearActivated(true);
321
- releasing.destroy();
322
- releaseAnimation = undefined;
323
- }
324
- else {
325
- await getScaleAnimation(effectElement).duration(120).to('transform', `scale(1, 0.92)`).play();
326
- }
327
- moveAnimation?.destroy();
328
- clearActivated(true);
329
- return true;
330
- };
331
- const nativeChanged = () => {
332
- gesture.enable(!isNativeUIShell(targetElement));
333
- cancelActiveGesture();
334
- };
335
- targetElement.addEventListener('nativeUIShellChange', nativeChanged);
336
- if (isNativeUIShell(targetElement))
337
- nativeChanged();
338
- return {
339
- destroy: () => {
340
- destroyed = true;
341
- cancelActiveGesture();
342
- targetElement.removeEventListener('pointerdown', onPointerDown);
343
- targetElement.removeEventListener('click', onClick, true);
344
- targetElement.removeEventListener('nativeUIShellChange', nativeChanged);
345
- if (gesture) {
346
- gesture.destroy();
347
- }
348
- targetElement.classList.remove(GESTURE_NAME);
349
- effectElement.remove();
350
- if (effectTagName === 'ion-tab-button') {
351
- if (originalTabBarScale)
352
- targetElement.style.setProperty('--ios27-tab-bar-scale', originalTabBarScale);
353
- else
354
- targetElement.style.removeProperty('--ios27-tab-bar-scale');
355
- }
356
- },
357
- };
1
+ import { changeSelectedElement, cloneElement, getStep } from "../utils.js";
2
+ import { isNativeUIShell } from "../native-integration/index.js";
3
+ import { animationRect, createMoveAnimation, createPreMoveAnimation, createTabBarDragAnimation, createTabBarPressAnimation, createTabBarReleaseAnimation, getMoveAnimationKeyframe, getScaleAnimation } from "./animations.js";
4
+ import { createGesture } from "@ionic/core";
5
+ //#region src/sheets-of-glass/index.ts
6
+ const GESTURE_NAME = "ios27-enable-gesture";
7
+ const ANIMATED_NAME = "ios27-animated";
8
+ const registerEffect = (targetElement, effectTagName, selectedClassName, scales, vertical = false) => {
9
+ if (!targetElement.classList.contains("ios")) return;
10
+ let gesture;
11
+ let moveAnimation;
12
+ let currentTouchedElement;
13
+ let selectedElementBeforeGesture;
14
+ let clearActivatedTimer;
15
+ let animationPosition = void 0;
16
+ let scaleAnimationPromise;
17
+ let startAnimationPromise;
18
+ let maxVelocity = 0;
19
+ let releaseAnimation;
20
+ let tabPressAnimation;
21
+ let tabPressStartedAt = 0;
22
+ let tabDragging = false;
23
+ let destroyed = false;
24
+ const originalTabBarScale = targetElement.style.getPropertyValue("--ios27-tab-bar-scale");
25
+ const effectElement = cloneElement(effectTagName, false);
26
+ if (vertical) effectElement.classList.add("ios27-vertical-tab-effect");
27
+ const stopTabPress = () => {
28
+ if (!tabPressAnimation) return;
29
+ const native = effectElement.shadowRoot.querySelector("[part=\"native\"]");
30
+ const transform = getComputedStyle(effectElement).transform;
31
+ const scale = getComputedStyle(native).transform;
32
+ tabPressAnimation.destroy();
33
+ tabPressAnimation = void 0;
34
+ effectElement.style.transform = transform;
35
+ native.style.transform = scale;
36
+ };
37
+ let onPointerUp;
38
+ let onPointerCancel;
39
+ const removePointerEndListeners = () => {
40
+ if (onPointerUp) {
41
+ document.removeEventListener("pointerup", onPointerUp);
42
+ onPointerUp = void 0;
43
+ }
44
+ if (onPointerCancel) {
45
+ document.removeEventListener("pointercancel", onPointerCancel);
46
+ onPointerCancel = void 0;
47
+ }
48
+ };
49
+ const cancelActiveGesture = () => {
50
+ stopTabPress();
51
+ releaseAnimation?.destroy();
52
+ releaseAnimation = void 0;
53
+ removePointerEndListeners();
54
+ if (clearActivatedTimer !== void 0) {
55
+ clearTimeout(clearActivatedTimer);
56
+ clearActivatedTimer = void 0;
57
+ }
58
+ currentTouchedElement?.classList.remove("ion-activated");
59
+ if (!currentTouchedElement?.classList.contains(selectedClassName)) selectedElementBeforeGesture?.classList.add(selectedClassName);
60
+ currentTouchedElement = void 0;
61
+ selectedElementBeforeGesture = void 0;
62
+ moveAnimation?.destroy();
63
+ moveAnimation = void 0;
64
+ effectElement.style.display = "none";
65
+ maxVelocity = 0;
66
+ targetElement.classList.remove(ANIMATED_NAME);
67
+ };
68
+ /**
69
+ * These event listeners fix a bug where gestures don't complete properly.
70
+ * They terminate the gesture using native events as a fallback.
71
+ */
72
+ const onPointerDown = (event) => {
73
+ if (isNativeUIShell(targetElement)) return;
74
+ stopTabPress();
75
+ tabDragging = false;
76
+ releaseAnimation?.destroy();
77
+ releaseAnimation = void 0;
78
+ clearActivated();
79
+ gesture.destroy();
80
+ createAnimationGesture();
81
+ removePointerEndListeners();
82
+ onPointerUp = () => {
83
+ clearActivatedTimer = setTimeout(async () => {
84
+ await onEndGesture();
85
+ if (destroyed) return;
86
+ gesture.destroy();
87
+ createAnimationGesture();
88
+ });
89
+ removePointerEndListeners();
90
+ };
91
+ onPointerCancel = () => {
92
+ cancelActiveGesture();
93
+ gesture.destroy();
94
+ createAnimationGesture();
95
+ };
96
+ document.addEventListener("pointerup", onPointerUp);
97
+ document.addEventListener("pointercancel", onPointerCancel);
98
+ };
99
+ targetElement.addEventListener("pointerdown", onPointerDown);
100
+ const onClick = (event) => {
101
+ if (vertical && tabDragging && event.isTrusted && event.detail > 0) {
102
+ event.preventDefault();
103
+ event.stopImmediatePropagation();
104
+ }
105
+ };
106
+ targetElement.addEventListener("click", onClick, true);
107
+ const axisDetail = (detail) => vertical ? {
108
+ ...detail,
109
+ currentX: detail.currentY,
110
+ currentY: detail.currentX,
111
+ startX: detail.startY,
112
+ startY: detail.startX,
113
+ velocityX: detail.velocityY,
114
+ velocityY: detail.velocityX
115
+ } : detail;
116
+ const createAnimationGesture = () => {
117
+ targetElement.classList.add(GESTURE_NAME);
118
+ gesture = createGesture({
119
+ el: targetElement,
120
+ threshold: 0,
121
+ direction: vertical ? "y" : "x",
122
+ gestureName: `${GESTURE_NAME}_${effectTagName}_${crypto.randomUUID()}`,
123
+ onStart: (event) => onStartGesture(axisDetail(event)),
124
+ onMove: (event) => onMoveGesture(axisDetail(event)),
125
+ onEnd: (detail) => {
126
+ if (effectTagName === "ion-tab-button" && tabDragging) onMoveGesture(axisDetail(detail));
127
+ onEndGesture().then();
128
+ }
129
+ });
130
+ gesture.enable(true);
131
+ };
132
+ createAnimationGesture();
133
+ const clearActivated = (isEndGesture = false) => {
134
+ if (!currentTouchedElement) return;
135
+ if (!isEndGesture && !currentTouchedElement.classList.contains(selectedClassName)) {
136
+ currentTouchedElement.click();
137
+ currentTouchedElement.classList.add(selectedClassName);
138
+ }
139
+ currentTouchedElement.classList.remove("ion-activated");
140
+ currentTouchedElement = void 0;
141
+ selectedElementBeforeGesture = void 0;
142
+ effectElement.style.display = "none";
143
+ maxVelocity = 0;
144
+ targetElement.classList.remove(ANIMATED_NAME);
145
+ };
146
+ const onStartGesture = (detail) => {
147
+ if (isNativeUIShell(targetElement)) return false;
148
+ currentTouchedElement = detail.event.target.closest(effectTagName) || void 0;
149
+ const tabSelectedElement = targetElement.querySelector(`${effectTagName}.${selectedClassName}`);
150
+ if (currentTouchedElement === void 0 || currentTouchedElement.parentElement !== targetElement || currentTouchedElement.disabled || tabSelectedElement === null) {
151
+ currentTouchedElement = void 0;
152
+ return false;
153
+ }
154
+ selectedElementBeforeGesture = tabSelectedElement;
155
+ if (effectTagName === "ion-tab-button") targetElement.style.setProperty("--ios27-tab-bar-scale", String(1 + 14.14 / (vertical ? targetElement.offsetHeight : targetElement.offsetWidth)));
156
+ if (vertical) targetElement.classList.add(ANIMATED_NAME);
157
+ animationPosition = {
158
+ minPositionX: animationRect(targetElement, vertical).left,
159
+ maxPositionX: animationRect(targetElement, vertical).right - (vertical ? tabSelectedElement.clientHeight : tabSelectedElement.clientWidth),
160
+ width: vertical ? tabSelectedElement.clientHeight : tabSelectedElement.clientWidth,
161
+ positionY: animationRect(tabSelectedElement, vertical).top
162
+ };
163
+ targetElement.classList.add(ANIMATED_NAME);
164
+ changeSelectedElement(targetElement, currentTouchedElement, effectTagName, selectedClassName);
165
+ if (effectTagName === "ion-tab-button") {
166
+ moveAnimation = createMoveAnimation(effectElement, detail, tabSelectedElement, animationPosition, vertical);
167
+ moveAnimation.progressStart(true, getStep(animationRect(tabSelectedElement, vertical).left + animationRect(tabSelectedElement, vertical).width / 2, animationPosition));
168
+ tabPressAnimation = createTabBarPressAnimation(effectElement, tabSelectedElement, currentTouchedElement, targetElement, vertical);
169
+ tabPressStartedAt = performance.now();
170
+ tabPressAnimation.play();
171
+ return true;
172
+ }
173
+ const startAnimation = (() => {
174
+ if (tabSelectedElement === currentTouchedElement) return new Promise((resolve) => resolve());
175
+ else {
176
+ const preMoveAnimation = createPreMoveAnimation(effectElement, tabSelectedElement, currentTouchedElement, animationPosition);
177
+ return preMoveAnimation.play().finally(() => preMoveAnimation.destroy());
178
+ }
179
+ })();
180
+ startAnimationPromise = startAnimation;
181
+ startAnimation.then(() => {
182
+ if (!currentTouchedElement) return;
183
+ moveAnimation = createMoveAnimation(effectElement, detail, tabSelectedElement, animationPosition);
184
+ moveAnimation.progressStart(true, getStep(currentTouchedElement.getBoundingClientRect().left + currentTouchedElement.clientWidth / 2, animationPosition));
185
+ });
186
+ getScaleAnimation(effectElement).duration(200).to("opacity", 1).to("transform", scales.large).play();
187
+ return true;
188
+ };
189
+ const onMoveGesture = (detail) => {
190
+ if (currentTouchedElement === void 0 || !moveAnimation || releaseAnimation) return false;
191
+ if (effectTagName === "ion-tab-button") {
192
+ if (Math.abs(detail.currentX - detail.startX) < 3 && !tabDragging) return;
193
+ tabDragging = true;
194
+ stopTabPress();
195
+ tabPressAnimation = createTabBarDragAnimation(effectElement, targetElement, detail.velocityX, vertical);
196
+ tabPressAnimation.play();
197
+ } else if (scaleAnimationPromise === void 0) {
198
+ if (Math.abs(detail.velocityX) > maxVelocity) maxVelocity = Math.abs(detail.velocityX);
199
+ if (Math.abs(detail.velocityX) > .2) scaleAnimationPromise = getScaleAnimation(effectElement).duration(720).keyframes(getMoveAnimationKeyframe("slowly", scales)).play().finally(() => scaleAnimationPromise = void 0);
200
+ if (maxVelocity > .2 && Math.abs(detail.velocityX) < .15 && Math.abs(detail.startX - detail.currentX) > 100) {
201
+ scaleAnimationPromise = getScaleAnimation(effectElement).duration(720).keyframes(getMoveAnimationKeyframe(detail.velocityX > 0 ? "moveRight" : "moveLeft", scales)).play().finally(() => scaleAnimationPromise = void 0);
202
+ maxVelocity = 0;
203
+ }
204
+ }
205
+ const currentX = detail.currentX;
206
+ const previousY = animationRect(targetElement, vertical).top + animationRect(targetElement, vertical).height / 2;
207
+ let latestTouchedElement = targetElement.getRootNode().elementFromPoint(vertical ? previousY : currentX, vertical ? currentX : previousY)?.closest(effectTagName) || void 0;
208
+ if (vertical && effectTagName === "ion-tab-button") {
209
+ const bar = targetElement.getBoundingClientRect();
210
+ const tabs = Array.from(targetElement.querySelectorAll(effectTagName)).filter((tab) => tab.offsetWidth > 0);
211
+ const distance = (tab) => Math.abs(currentX - (bar.top + tab.offsetTop + tab.offsetHeight / 2));
212
+ latestTouchedElement = currentX >= bar.top && currentX <= bar.bottom ? tabs.reduce((closest, tab) => !closest || distance(tab) < distance(closest) ? tab : closest, void 0) : void 0;
213
+ }
214
+ if (latestTouchedElement && latestTouchedElement.parentElement === targetElement && !latestTouchedElement.disabled && currentTouchedElement !== latestTouchedElement) {
215
+ currentTouchedElement = latestTouchedElement;
216
+ changeSelectedElement(targetElement, currentTouchedElement, effectTagName, selectedClassName);
217
+ }
218
+ moveAnimation.progressStep(getStep(detail.currentX, animationPosition));
219
+ return true;
220
+ };
221
+ const onEndGesture = async () => {
222
+ if (releaseAnimation) return;
223
+ if (clearActivatedTimer !== void 0) {
224
+ clearTimeout(clearActivatedTimer);
225
+ clearActivatedTimer = void 0;
226
+ }
227
+ if (currentTouchedElement && !currentTouchedElement.classList.contains(selectedClassName)) {
228
+ currentTouchedElement.click();
229
+ currentTouchedElement.classList.add(selectedClassName);
230
+ }
231
+ if (startAnimationPromise) await startAnimationPromise;
232
+ const tapElapsed = tabPressAnimation && !tabDragging && selectedElementBeforeGesture !== currentTouchedElement ? performance.now() - tabPressStartedAt : void 0;
233
+ if (tabPressAnimation && !tabDragging && tapElapsed === void 0) {
234
+ const pressing = tabPressAnimation;
235
+ const remaining = 200 - (performance.now() - tabPressStartedAt);
236
+ if (remaining > 0) await new Promise((resolve) => setTimeout(resolve, remaining));
237
+ if (tabPressAnimation !== pressing) return;
238
+ }
239
+ if (currentTouchedElement === void 0 || !moveAnimation || releaseAnimation) return false;
240
+ if (effectTagName !== "ion-tab-button") setTimeout(() => {
241
+ if (!currentTouchedElement) return;
242
+ const targetX = currentTouchedElement.getBoundingClientRect().left + currentTouchedElement.clientWidth / 2;
243
+ const step = getStep(targetX, animationPosition);
244
+ moveAnimation.progressStep(step);
245
+ });
246
+ if (effectTagName === "ion-tab-button") {
247
+ stopTabPress();
248
+ moveAnimation.destroy();
249
+ moveAnimation = void 0;
250
+ const tabs = Array.from(targetElement.querySelectorAll(effectTagName)).filter((tab) => tab.offsetWidth > 0);
251
+ if (vertical) {
252
+ currentTouchedElement.classList.remove("ion-activated");
253
+ targetElement.classList.remove(ANIMATED_NAME);
254
+ }
255
+ const releasing = createTabBarReleaseAnimation(effectElement, currentTouchedElement, tapElapsed !== void 0 && tapElapsed < 200 ? {
256
+ elapsed: tapElapsed,
257
+ distance: Math.abs(tabs.indexOf(currentTouchedElement) - tabs.indexOf(selectedElementBeforeGesture))
258
+ } : void 0, vertical);
259
+ currentTouchedElement.classList.remove("ion-activated");
260
+ releaseAnimation = releasing;
261
+ await releasing.play();
262
+ if (releaseAnimation !== releasing) return;
263
+ clearActivated(true);
264
+ releasing.destroy();
265
+ releaseAnimation = void 0;
266
+ } else await getScaleAnimation(effectElement).duration(120).to("transform", `scale(1, 0.92)`).play();
267
+ moveAnimation?.destroy();
268
+ clearActivated(true);
269
+ return true;
270
+ };
271
+ const nativeChanged = () => {
272
+ gesture.enable(!isNativeUIShell(targetElement));
273
+ cancelActiveGesture();
274
+ };
275
+ targetElement.addEventListener("nativeUIShellChange", nativeChanged);
276
+ if (isNativeUIShell(targetElement)) nativeChanged();
277
+ return { destroy: () => {
278
+ destroyed = true;
279
+ cancelActiveGesture();
280
+ targetElement.removeEventListener("pointerdown", onPointerDown);
281
+ targetElement.removeEventListener("click", onClick, true);
282
+ targetElement.removeEventListener("nativeUIShellChange", nativeChanged);
283
+ if (gesture) gesture.destroy();
284
+ targetElement.classList.remove(GESTURE_NAME);
285
+ effectElement.remove();
286
+ if (effectTagName === "ion-tab-button") {
287
+ if (originalTabBarScale) targetElement.style.setProperty("--ios27-tab-bar-scale", originalTabBarScale);
288
+ else targetElement.style.removeProperty("--ios27-tab-bar-scale");
289
+ }
290
+ } };
358
291
  };
292
+ //#endregion
293
+ export { registerEffect };
294
+
295
+ //# sourceMappingURL=index.js.map