@rdlabo/ionic-theme-ios27 1.2.0-1 → 1.2.0-3
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 +9 -0
- package/dist/css/components/ion-tabs.css +1 -1
- package/dist/css/ionic-theme-ios27.css +1 -1
- package/dist/css/vertical-bars.css +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -12
- package/dist/native/components/index.d.ts.map +1 -1
- package/dist/native/components/index.js +4 -1
- package/dist/native/components/ion-button.d.ts.map +1 -1
- package/dist/native/components/ion-button.js +3 -3
- package/dist/native/components/ion-buttons.d.ts.map +1 -1
- package/dist/native/components/ion-buttons.js +3 -2
- package/dist/native/components/searchable-tabs.d.ts.map +1 -1
- package/dist/native/components/searchable-tabs.js +6 -7
- package/dist/native/definitions.d.ts +8 -15
- package/dist/native/definitions.d.ts.map +1 -1
- package/dist/native/definitions.js +1 -6
- package/dist/native/index.d.ts +1 -2
- package/dist/native/index.d.ts.map +1 -1
- package/dist/native/index.js +64 -28
- package/dist/native/prehide.d.ts.map +1 -1
- package/dist/native/prehide.js +6 -4
- package/dist/native/runtime.d.ts.map +1 -1
- package/dist/native/runtime.js +40 -19
- package/dist/native/shared/candidate.d.ts.map +1 -1
- package/dist/native/shared/candidate.js +8 -0
- package/dist/native/shared/dom.d.ts.map +1 -1
- package/dist/native/shared/dom.js +11 -10
- package/dist/native/shared/modal.d.ts +8 -0
- package/dist/native/shared/modal.d.ts.map +1 -0
- package/dist/native/shared/modal.js +143 -0
- package/dist/native/vertical-bars-web.d.ts.map +1 -1
- package/dist/native/vertical-bars-web.js +11 -3
- package/dist/sheets-of-glass/animations.d.ts +13 -4
- package/dist/sheets-of-glass/animations.d.ts.map +1 -1
- package/dist/sheets-of-glass/animations.js +56 -40
- package/dist/sheets-of-glass/index.d.ts +1 -1
- package/dist/sheets-of-glass/index.d.ts.map +1 -1
- package/dist/sheets-of-glass/index.js +64 -20
- package/dist/tab-bar-searchable/index.d.ts.map +1 -1
- package/dist/tab-bar-searchable/index.js +39 -11
- package/dist/vertical-bars.d.ts +1 -2
- package/dist/vertical-bars.d.ts.map +1 -1
- package/dist/vertical-bars.js +0 -1
- package/docs/iphone-duo-with-original-theme.md +37 -15
- package/docs/iphone-duo.md +90 -47
- package/docs/migration.md +3 -3
- package/docs/native-ui-shell.md +12 -8
- package/docs/special-markup.md +0 -13
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSearchController.swift +1 -1
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +50 -6
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalSearch.swift +118 -0
- package/ios/Sources/IonicNativeUIShellPlugin/IonicNativeUIShellPlugin.swift +32 -179
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellSnapshot.swift +5 -0
- package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +67 -0
- package/package.json +1 -1
- package/src/index.ts +40 -9
- package/src/native/components/index.ts +6 -1
- package/src/native/components/ion-button.ts +4 -3
- package/src/native/components/ion-buttons.ts +4 -2
- package/src/native/components/searchable-tabs.ts +8 -11
- package/src/native/definitions.ts +11 -19
- package/src/native/index.ts +67 -30
- package/src/native/prehide.ts +7 -4
- package/src/native/runtime.ts +40 -18
- package/src/native/shared/candidate.ts +10 -0
- package/src/native/shared/dom.ts +11 -10
- package/src/native/shared/modal.ts +157 -0
- package/src/native/vertical-bars-web.ts +12 -3
- package/src/sheets-of-glass/animations.ts +66 -41
- package/src/sheets-of-glass/index.ts +82 -16
- package/src/styles/components/ion-tabs.scss +7 -4
- package/src/styles/vertical-bars.scss +110 -35
- package/src/tab-bar-searchable/index.ts +40 -9
- package/src/vertical-bars.ts +1 -2
|
@@ -31,8 +31,21 @@ ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-left {
|
|
|
31
31
|
--ios-theme-vertical-bars-safe-area-right-resolved: var(--ios-theme-vertical-bars-safe-area-right, 0px);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// Centered dialogs do not inherit the page's rail reservation.
|
|
35
|
+
ion-app.ios-theme-vertical-bars ion-modal:not(.ios-theme-vertical-bars-modal) {
|
|
36
|
+
--ios-theme-vertical-bars-safe-area-left-resolved: 0px;
|
|
37
|
+
--ios-theme-vertical-bars-safe-area-right-resolved: 0px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
ion-app.ios-theme-vertical-bars ion-modal.ios-theme-vertical-bars-modal {
|
|
41
|
+
--ios-theme-vertical-bars-modal-toolbar-top: max(
|
|
42
|
+
24px,
|
|
43
|
+
calc(var(--ios-theme-vertical-bars-toolbar-top, 220px) - var(--ios-theme-vertical-bars-modal-offset-top, 0px))
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
34
47
|
// Supply material defaults only to Web projections, never to ordinary controls.
|
|
35
|
-
ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-toolbar-ready
|
|
48
|
+
:is(ion-app.ios-theme-vertical-bars, ion-modal.ios-theme-vertical-bars-modal).ios-theme-vertical-bars-toolbar-ready
|
|
36
49
|
> :is(ion-menu-button, ion-button).ios-theme-vertical-bars-toolbar-projection {
|
|
37
50
|
--ios26-glass-background-rgb: var(--ion-background-color-rgb, 255, 255, 255);
|
|
38
51
|
--ios26-glass-border-color-rgb: var(--ion-background-color-rgb, 255, 255, 255);
|
|
@@ -44,9 +57,19 @@ ion-app.ios-theme-vertical-bars .ios-transition-shade {
|
|
|
44
57
|
clip-path: inset(0 var(--ios-theme-vertical-bars-safe-area-right-resolved) 0 var(--ios-theme-vertical-bars-safe-area-left-resolved));
|
|
45
58
|
}
|
|
46
59
|
|
|
60
|
+
// A vertical system rail can leave no top inset for the page header.
|
|
61
|
+
ion-app.ios-theme-vertical-bars
|
|
62
|
+
ion-header.ios:not(.header-collapse-condense, .ios-theme-disabled, .ios26-disabled):not(
|
|
63
|
+
:where(ion-menu *, ion-modal:not(.ios-theme-vertical-bars-modal) *, ion-popover *)
|
|
64
|
+
) {
|
|
65
|
+
--ion-safe-area-top: max(24px, env(safe-area-inset-top, 0px));
|
|
66
|
+
}
|
|
67
|
+
|
|
47
68
|
// Keep page backgrounds and the router full width; move only their foregrounds.
|
|
48
69
|
ion-app.ios-theme-vertical-bars
|
|
49
|
-
ion-content:is(.ios, .md):not(.ios-theme-disabled, .ios26-disabled):not(
|
|
70
|
+
ion-content:is(.ios, .md):not(.ios-theme-disabled, .ios26-disabled):not(
|
|
71
|
+
:where(ion-menu *, ion-modal:not(.ios-theme-vertical-bars-modal) *, ion-popover *)
|
|
72
|
+
) {
|
|
50
73
|
--ion-safe-area-left: 0px;
|
|
51
74
|
--ion-safe-area-right: 0px;
|
|
52
75
|
|
|
@@ -62,7 +85,9 @@ ion-app.ios-theme-vertical-bars
|
|
|
62
85
|
}
|
|
63
86
|
|
|
64
87
|
ion-app.ios-theme-vertical-bars
|
|
65
|
-
ion-toolbar:is(.ios, .md):not(.ios-theme-disabled, .ios26-disabled):not(
|
|
88
|
+
ion-toolbar:is(.ios, .md):not(.ios-theme-disabled, .ios26-disabled):not(
|
|
89
|
+
:where(ion-menu *, ion-modal:not(.ios-theme-vertical-bars-modal) *, ion-popover *)
|
|
90
|
+
) {
|
|
66
91
|
--ion-safe-area-left: 0px;
|
|
67
92
|
--ion-safe-area-right: 0px;
|
|
68
93
|
|
|
@@ -131,37 +156,44 @@ ion-app.ios-theme-vertical-bars {
|
|
|
131
156
|
ion-app.ios-theme-vertical-bars
|
|
132
157
|
ion-tabs:not(:where(ion-menu *, ion-modal *, ion-popover *))
|
|
133
158
|
> ion-tab-bar:is(.ios, .md):not(.ios-theme-disabled, .ios26-disabled) {
|
|
134
|
-
|
|
135
|
-
--ios-theme-side-tab-bar-
|
|
136
|
-
--ios-theme-side-
|
|
159
|
+
// iOS 27.1 Duo: 48pt platter, 44 × 58pt items with 8pt overlap, 24pt edge gaps.
|
|
160
|
+
--ios-theme-side-tab-bar-width: 48px;
|
|
161
|
+
--ios-theme-side-tab-content-width: 44px;
|
|
162
|
+
--ios-theme-side-tab-bar-gap: 24px;
|
|
137
163
|
contain: layout style;
|
|
138
164
|
position: absolute;
|
|
139
165
|
z-index: 2;
|
|
140
166
|
display: flex;
|
|
141
167
|
flex-direction: column;
|
|
142
|
-
width:
|
|
143
|
-
min-width:
|
|
144
|
-
max-width:
|
|
168
|
+
width: 44px;
|
|
169
|
+
min-width: 44px;
|
|
170
|
+
max-width: 44px;
|
|
145
171
|
height: fit-content;
|
|
146
172
|
min-height: 0;
|
|
147
173
|
padding: 2px;
|
|
148
|
-
border-radius:
|
|
174
|
+
border-radius: 24px;
|
|
149
175
|
left: auto;
|
|
150
|
-
right:
|
|
176
|
+
right: calc(24px + (48px - var(--ios-theme-side-tab-bar-width)) / 2);
|
|
151
177
|
margin: 0;
|
|
152
178
|
top: auto;
|
|
153
|
-
bottom:
|
|
179
|
+
bottom: var(--ios-theme-side-tab-bar-gap);
|
|
180
|
+
|
|
181
|
+
&.ios27-enable-gesture {
|
|
182
|
+
touch-action: pan-x pinch-zoom;
|
|
183
|
+
}
|
|
154
184
|
|
|
155
185
|
> ion-tab-button:not(.ion-cloned-element) {
|
|
156
186
|
display: block;
|
|
157
187
|
flex: none;
|
|
158
|
-
width:
|
|
159
|
-
min-width:
|
|
160
|
-
height:
|
|
161
|
-
min-height:
|
|
162
|
-
border-radius:
|
|
188
|
+
width: 44px;
|
|
189
|
+
min-width: 44px;
|
|
190
|
+
height: 58px;
|
|
191
|
+
min-height: 58px;
|
|
192
|
+
border-radius: 22px;
|
|
163
193
|
&::part(native) {
|
|
164
194
|
justify-content: center;
|
|
195
|
+
min-height: 58px;
|
|
196
|
+
padding: 0;
|
|
165
197
|
}
|
|
166
198
|
ion-label {
|
|
167
199
|
position: absolute;
|
|
@@ -179,32 +211,54 @@ ion-app.ios-theme-vertical-bars
|
|
|
179
211
|
font-size: 28px;
|
|
180
212
|
}
|
|
181
213
|
}
|
|
214
|
+
> ion-tab-button.ion-activated {
|
|
215
|
+
transform: scale(1.16);
|
|
216
|
+
}
|
|
182
217
|
> ion-tab-button:not(.ion-cloned-element) ~ ion-tab-button:not(.ion-cloned-element) {
|
|
183
|
-
margin-block-start:
|
|
218
|
+
margin-block-start: -8px;
|
|
184
219
|
margin-inline-start: 0;
|
|
185
220
|
}
|
|
186
|
-
|
|
221
|
+
// Duo's expanded platter keeps its center and reveals labels without shrinking icons.
|
|
222
|
+
&:is(.ios27-animated, :has(> ion-tab-button:is(.ion-activated, :active))) {
|
|
223
|
+
--ios-theme-side-tab-bar-width: 72px;
|
|
224
|
+
--ios-theme-side-tab-content-width: 64px;
|
|
225
|
+
--ios-theme-side-tab-bar-gap: 20px;
|
|
226
|
+
width: 64px;
|
|
227
|
+
min-width: 64px;
|
|
228
|
+
max-width: 64px;
|
|
229
|
+
padding: 4px;
|
|
230
|
+
border-radius: 36px;
|
|
231
|
+
transform: none;
|
|
232
|
+
-webkit-transform: none;
|
|
233
|
+
}
|
|
234
|
+
&:is(.ios27-animated, :has(> ion-tab-button:is(.ion-activated, :active))) > ion-tab-button:not(.ion-cloned-element) {
|
|
235
|
+
width: 64px;
|
|
236
|
+
min-width: 64px;
|
|
237
|
+
height: 64px;
|
|
238
|
+
min-height: 64px;
|
|
239
|
+
border-radius: 32px;
|
|
187
240
|
&::part(native) {
|
|
188
241
|
flex-direction: column;
|
|
189
|
-
|
|
242
|
+
min-height: 64px;
|
|
243
|
+
padding-block: 11px;
|
|
190
244
|
}
|
|
191
245
|
ion-label {
|
|
192
246
|
position: static;
|
|
193
247
|
width: auto;
|
|
194
248
|
height: auto;
|
|
195
|
-
max-width:
|
|
249
|
+
max-width: 64px;
|
|
196
250
|
padding: 0;
|
|
197
251
|
margin: 1px 0 0;
|
|
198
252
|
overflow: hidden;
|
|
199
253
|
clip: auto;
|
|
200
|
-
font-size:
|
|
201
|
-
line-height:
|
|
254
|
+
font-size: 10px;
|
|
255
|
+
line-height: 12px;
|
|
202
256
|
text-overflow: ellipsis;
|
|
203
257
|
white-space: nowrap;
|
|
204
258
|
}
|
|
205
259
|
ion-icon {
|
|
206
260
|
margin: 0;
|
|
207
|
-
font-size:
|
|
261
|
+
font-size: 28px;
|
|
208
262
|
}
|
|
209
263
|
}
|
|
210
264
|
}
|
|
@@ -212,9 +266,8 @@ ion-app.ios-theme-vertical-bars
|
|
|
212
266
|
ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-left
|
|
213
267
|
ion-tabs:not(:where(ion-menu *, ion-modal *, ion-popover *))
|
|
214
268
|
> ion-tab-bar:is(.ios, .md):not(.ios-theme-disabled, .ios26-disabled) {
|
|
215
|
-
--ios-theme-side-safe-area: var(--ios-theme-vertical-bars-safe-area-left-resolved);
|
|
216
269
|
right: auto;
|
|
217
|
-
left:
|
|
270
|
+
left: calc(24px + (48px - var(--ios-theme-side-tab-bar-width)) / 2);
|
|
218
271
|
}
|
|
219
272
|
|
|
220
273
|
.ios-theme-native-ui-shell-prehidden {
|
|
@@ -251,8 +304,11 @@ html.ios-theme-native-ui-shell-prehide
|
|
|
251
304
|
visibility: hidden !important;
|
|
252
305
|
}
|
|
253
306
|
|
|
254
|
-
ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-toolbar-ready
|
|
255
|
-
|
|
307
|
+
ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-toolbar-ready,
|
|
308
|
+
ion-app.ios-theme-vertical-bars ion-modal.ios-theme-vertical-bars-modal.ios-theme-vertical-bars-toolbar-ready {
|
|
309
|
+
ion-toolbar:is(.ios, .md):not(.ios-theme-disabled, .ios26-disabled):not(
|
|
310
|
+
:where(ion-menu *, ion-modal:not(.ios-theme-vertical-bars-modal) *, ion-popover *)
|
|
311
|
+
)
|
|
256
312
|
ion-back-button:is(.ios, .md)[data-native-ui-shell]:not(
|
|
257
313
|
.ios-theme-disabled,
|
|
258
314
|
.ios26-disabled,
|
|
@@ -260,7 +316,9 @@ ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-toolbar-ready {
|
|
|
260
316
|
) {
|
|
261
317
|
display: none;
|
|
262
318
|
}
|
|
263
|
-
ion-toolbar:is(.ios, .md):not(.ios-theme-disabled, .ios26-disabled):not(
|
|
319
|
+
ion-toolbar:is(.ios, .md):not(.ios-theme-disabled, .ios26-disabled):not(
|
|
320
|
+
:where(ion-menu *, ion-modal:not(.ios-theme-vertical-bars-modal) *, ion-popover *)
|
|
321
|
+
)
|
|
264
322
|
ion-buttons:is(.ios, .md)
|
|
265
323
|
> :is(ion-button, ion-menu-button):is(.ios, .md)[data-native-ui-shell]:not(
|
|
266
324
|
.ios-theme-disabled,
|
|
@@ -269,7 +327,9 @@ ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-toolbar-ready {
|
|
|
269
327
|
) {
|
|
270
328
|
display: none;
|
|
271
329
|
}
|
|
272
|
-
ion-toolbar:is(.ios, .md):not(.ios-theme-disabled, .ios26-disabled):not(
|
|
330
|
+
ion-toolbar:is(.ios, .md):not(.ios-theme-disabled, .ios26-disabled):not(
|
|
331
|
+
:where(ion-menu *, ion-modal:not(.ios-theme-vertical-bars-modal) *, ion-popover *)
|
|
332
|
+
)
|
|
273
333
|
ion-buttons:is(.ios, .md):has(> :is(ion-back-button, ion-button, ion-menu-button)[data-native-ui-shell]):not(
|
|
274
334
|
:has(> :is(ion-back-button, ion-button, ion-menu-button):not([data-native-ui-shell]))
|
|
275
335
|
) {
|
|
@@ -278,7 +338,7 @@ ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-toolbar-ready {
|
|
|
278
338
|
> ion-back-button.ios-theme-vertical-bars-back-button-projection {
|
|
279
339
|
position: fixed;
|
|
280
340
|
z-index: 1001;
|
|
281
|
-
top: var(--ios-theme-vertical-bars-toolbar-top, 220px);
|
|
341
|
+
top: var(--ios-theme-vertical-bars-modal-toolbar-top, var(--ios-theme-vertical-bars-toolbar-top, 220px));
|
|
282
342
|
right: max(4px, calc((var(--ios-theme-vertical-bars-safe-area-right-resolved) - 46px) / 2));
|
|
283
343
|
display: block;
|
|
284
344
|
width: 46px;
|
|
@@ -288,7 +348,10 @@ ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-toolbar-ready {
|
|
|
288
348
|
> :is(.ios, .md).ios-theme-vertical-bars-toolbar-projection {
|
|
289
349
|
position: fixed;
|
|
290
350
|
z-index: 1001;
|
|
291
|
-
top: calc(
|
|
351
|
+
top: calc(
|
|
352
|
+
var(--ios-theme-vertical-bars-modal-toolbar-top, var(--ios-theme-vertical-bars-toolbar-top, 220px)) +
|
|
353
|
+
var(--ios-theme-vertical-bars-toolbar-offset, 0px)
|
|
354
|
+
);
|
|
292
355
|
right: max(4px, calc((var(--ios-theme-vertical-bars-safe-area-right-resolved) - 46px) / 2));
|
|
293
356
|
width: 46px;
|
|
294
357
|
min-width: 46px;
|
|
@@ -307,10 +370,12 @@ ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-toolbar-ready {
|
|
|
307
370
|
border-radius: 50%;
|
|
308
371
|
}
|
|
309
372
|
> ion-button:is(.ios, .md).ios-theme-vertical-bars-toolbar-projection {
|
|
310
|
-
|
|
373
|
+
&:not(.button-solid):not(.button-outline) {
|
|
374
|
+
--background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.72);
|
|
375
|
+
}
|
|
311
376
|
--border-radius: 50%;
|
|
312
377
|
@include glass.light-shadow('--box-shadow');
|
|
313
|
-
|
|
378
|
+
&:not(.button-outline)::part(native) {
|
|
314
379
|
@include api.glass-control-background($include-background: false, $shadow: var(--box-shadow));
|
|
315
380
|
}
|
|
316
381
|
}
|
|
@@ -333,10 +398,20 @@ ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-toolbar-ready {
|
|
|
333
398
|
}
|
|
334
399
|
}
|
|
335
400
|
|
|
336
|
-
ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-left
|
|
401
|
+
ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-left,
|
|
402
|
+
ion-app.ios-theme-vertical-bars.ios-theme-vertical-bars-left ion-modal.ios-theme-vertical-bars-modal {
|
|
337
403
|
> ion-back-button.ios-theme-vertical-bars-back-button-projection,
|
|
338
404
|
> :is(.ios, .md).ios-theme-vertical-bars-toolbar-projection {
|
|
339
405
|
right: auto;
|
|
340
406
|
left: max(4px, calc((var(--ios-theme-vertical-bars-safe-area-left-resolved) - 46px) / 2));
|
|
341
407
|
}
|
|
342
408
|
}
|
|
409
|
+
|
|
410
|
+
// The lens is attached to body, outside the rail selectors.
|
|
411
|
+
ion-tab-button.ios.ion-cloned-element.ios27-vertical-tab-effect {
|
|
412
|
+
border-radius: 32px;
|
|
413
|
+
&::part(native) {
|
|
414
|
+
min-height: 0;
|
|
415
|
+
height: 100%;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
@@ -64,6 +64,8 @@ export const attachTabBarSearchable = (
|
|
|
64
64
|
// Initialize
|
|
65
65
|
ionFooter.style.pointerEvents = 'none';
|
|
66
66
|
ionFooter.style.opacity = '0';
|
|
67
|
+
// Search dismissal belongs beside the Web search field, never in the vertical rail.
|
|
68
|
+
ionFooter.querySelector('ion-buttons[slot=start]')?.classList.add('ios-theme-horizontal-only');
|
|
67
69
|
const nativeSearch = registerNativeSearch(ionTabBar, ionFabButton, ionFooter);
|
|
68
70
|
|
|
69
71
|
// Saved Params
|
|
@@ -72,6 +74,8 @@ export const attachTabBarSearchable = (
|
|
|
72
74
|
// Leave is valid both while native Enter awaits its ack and after it completes.
|
|
73
75
|
let nativeEntry = false;
|
|
74
76
|
let nativeRequest = 0;
|
|
77
|
+
let verticalSearch = false;
|
|
78
|
+
let tabVisibilityObserver: MutationObserver | undefined;
|
|
75
79
|
|
|
76
80
|
return async (event: Event, type: TabBarSearchableType) => {
|
|
77
81
|
const selector = type === TabBarSearchableType.Enter ? 'ion-fab-button' : 'ion-buttons[slot=start] ion-button';
|
|
@@ -93,19 +97,38 @@ export const attachTabBarSearchable = (
|
|
|
93
97
|
return;
|
|
94
98
|
}
|
|
95
99
|
if (type === TabBarSearchableType.Enter) {
|
|
96
|
-
|
|
100
|
+
verticalSearch = !!ionTabBar.closest('ion-app.ios-theme-vertical-bars');
|
|
101
|
+
if (verticalSearch) {
|
|
102
|
+
// Ionic hides horizontal tabs when the keyboard opens; keep this rail available.
|
|
103
|
+
const keepTabsVisible = () => {
|
|
104
|
+
if (ionTabBar.classList.contains('tab-bar-hidden') && ionTabBar.closest('ion-app.ios-theme-vertical-bars')) {
|
|
105
|
+
ionTabBar.classList.remove('tab-bar-hidden');
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
tabVisibilityObserver?.disconnect();
|
|
109
|
+
tabVisibilityObserver = new MutationObserver(keepTabsVisible);
|
|
110
|
+
tabVisibilityObserver.observe(ionTabBar, { attributes: true, attributeFilter: ['class'] });
|
|
111
|
+
keepTabsVisible();
|
|
112
|
+
}
|
|
113
|
+
resumeNative ??= await suspendNativeUIShell([
|
|
114
|
+
...(verticalSearch ? [] : [ionTabBar]),
|
|
115
|
+
ionFooter,
|
|
116
|
+
ionFabButton.closest('ion-fab') ?? ionFabButton,
|
|
117
|
+
]);
|
|
97
118
|
try {
|
|
98
|
-
searchableEventCache = await enterEvent(event, ionTabBar, ionFabButton, ionFooter);
|
|
119
|
+
searchableEventCache = await enterEvent(event, ionTabBar, ionFabButton, ionFooter, verticalSearch);
|
|
99
120
|
} catch (error) {
|
|
121
|
+
tabVisibilityObserver?.disconnect();
|
|
100
122
|
resumeNative();
|
|
101
123
|
resumeNative = undefined;
|
|
102
124
|
throw error;
|
|
103
125
|
}
|
|
104
126
|
} else if (searchableEventCache !== undefined) {
|
|
105
127
|
try {
|
|
106
|
-
await leaveEvent(event, searchableEventCache, ionTabBar, ionFabButton, ionFooter);
|
|
128
|
+
await leaveEvent(event, searchableEventCache, ionTabBar, ionFabButton, ionFooter, verticalSearch);
|
|
107
129
|
searchableEventCache = undefined;
|
|
108
130
|
} finally {
|
|
131
|
+
tabVisibilityObserver?.disconnect();
|
|
109
132
|
resumeNative?.();
|
|
110
133
|
resumeNative = undefined;
|
|
111
134
|
}
|
|
@@ -120,6 +143,7 @@ const enterEvent = async (
|
|
|
120
143
|
ionTabBar: HTMLElement,
|
|
121
144
|
ionFabButton: HTMLElement,
|
|
122
145
|
ionFooter: HTMLElement,
|
|
146
|
+
verticalSearch: boolean,
|
|
123
147
|
): Promise<SearchableEventCache> => {
|
|
124
148
|
if (!(event.target as HTMLElement)?.closest('ion-fab-button')) {
|
|
125
149
|
throw throwErrorByFailedClickElement('ion-fab-button');
|
|
@@ -131,10 +155,12 @@ const enterEvent = async (
|
|
|
131
155
|
? getComputedStyle(references.selectedTabButton).getPropertyValue('--color-selected').trim()
|
|
132
156
|
: '';
|
|
133
157
|
|
|
134
|
-
|
|
158
|
+
if (verticalSearch) references.closeButtonIcon.setAttribute('name', 'close');
|
|
159
|
+
const tabAnimations = verticalSearch
|
|
160
|
+
? []
|
|
161
|
+
: [createTabBarAnimation(ionTabBar, references, sizes), createEffectAnimation(references, sizes)];
|
|
135
162
|
const searchContainerAnimation = createSearchContainerAnimation(references, sizes);
|
|
136
163
|
const closeButtonsAnimation = createCloseButtonsAnimation(references);
|
|
137
|
-
const tabBarAnimation = createTabBarAnimation(ionTabBar, references, sizes);
|
|
138
164
|
const fabButtonAnimation = createFabButtonAnimation(ionFabButton);
|
|
139
165
|
|
|
140
166
|
await createAnimation()
|
|
@@ -144,7 +170,7 @@ const enterEvent = async (
|
|
|
144
170
|
.addElement(ionFooter)
|
|
145
171
|
.afterAddWrite(() => (ionFooter.style.pointerEvents = 'auto'))
|
|
146
172
|
.fromTo('opacity', '0.8', '1')
|
|
147
|
-
.addAnimation([
|
|
173
|
+
.addAnimation([...tabAnimations, fabButtonAnimation, searchContainerAnimation, closeButtonsAnimation])
|
|
148
174
|
.play();
|
|
149
175
|
|
|
150
176
|
return {
|
|
@@ -159,6 +185,7 @@ const leaveEvent = async (
|
|
|
159
185
|
ionTabBar: HTMLElement,
|
|
160
186
|
ionFabButton: HTMLElement,
|
|
161
187
|
ionFooter: HTMLElement,
|
|
188
|
+
verticalSearch: boolean,
|
|
162
189
|
): Promise<void> => {
|
|
163
190
|
if (!(event.target as HTMLElement)?.closest('ion-buttons[slot=start] ion-button')) {
|
|
164
191
|
throw throwErrorByFailedClickElement('ion-buttons[slot=start] ion-button');
|
|
@@ -166,10 +193,14 @@ const leaveEvent = async (
|
|
|
166
193
|
|
|
167
194
|
const references = getElementReferences(ionTabBar, ionFooter);
|
|
168
195
|
|
|
169
|
-
const
|
|
196
|
+
const tabAnimations = verticalSearch
|
|
197
|
+
? []
|
|
198
|
+
: [
|
|
199
|
+
createReverseTabBarAnimation(ionTabBar, references, searchableEventCache.elementSizes),
|
|
200
|
+
createReverseEffectAnimation(references, searchableEventCache.elementSizes, searchableEventCache.colorSelected),
|
|
201
|
+
];
|
|
170
202
|
const searchContainerAnimation = createReverseSearchContainerAnimation(references, searchableEventCache.elementSizes);
|
|
171
203
|
const closeButtonsAnimation = createReverseCloseButtonsAnimation(references);
|
|
172
|
-
const tabBarAnimation = createReverseTabBarAnimation(ionTabBar, references, searchableEventCache.elementSizes);
|
|
173
204
|
const fabButtonAnimation = createReverseFabButtonAnimation(ionFabButton, searchableEventCache.elementSizes);
|
|
174
205
|
|
|
175
206
|
await createAnimation()
|
|
@@ -179,6 +210,6 @@ const leaveEvent = async (
|
|
|
179
210
|
.addElement(ionFooter)
|
|
180
211
|
.afterAddWrite(() => (ionFooter.style.pointerEvents = 'none'))
|
|
181
212
|
.fromTo('opacity', '1', '0')
|
|
182
|
-
.addAnimation([
|
|
213
|
+
.addAnimation([...tabAnimations, fabButtonAnimation, searchContainerAnimation, closeButtonsAnimation])
|
|
183
214
|
.play();
|
|
184
215
|
};
|
package/src/vertical-bars.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export { enableVerticalControlArea, setVerticalControlAreaPlacement, IonicNativeUIShell } from './native';
|
|
2
|
-
export { HingeStatus } from './native';
|
|
3
2
|
export { withNativeUIShellTransition } from './native-integration/transition';
|
|
4
|
-
export type {
|
|
3
|
+
export type { NativeUIShellStatus, NativeUIShellSuspension, VerticalBarEdge, VerticalControlAreaHandle } from './native';
|