@rdlabo/ionic-theme-ios27 1.2.0-beta.pr246.sha237333ceac21 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/css/components/ion-menu.css +1 -1
  2. package/dist/css/ionic-theme-ios27.css +1 -1
  3. package/dist/css/utils/searchable.css +1 -1
  4. package/dist/css/vertical-bars.css +1 -1
  5. package/dist/native/components/searchable-tabs.js +1 -1
  6. package/dist/native/components/searchable-tabs.js.map +1 -1
  7. package/dist/native/vertical-bars-web-search.d.ts +9 -0
  8. package/dist/native/vertical-bars-web-search.d.ts.map +1 -0
  9. package/dist/native/vertical-bars-web-search.js +84 -0
  10. package/dist/native/vertical-bars-web-search.js.map +1 -0
  11. package/dist/native/vertical-bars-web.d.ts.map +1 -1
  12. package/dist/native/vertical-bars-web.js +20 -16
  13. package/dist/native/vertical-bars-web.js.map +1 -1
  14. package/dist/tab-bar-searchable/dismiss-button.d.ts +4 -0
  15. package/dist/tab-bar-searchable/dismiss-button.d.ts.map +1 -0
  16. package/dist/tab-bar-searchable/dismiss-button.js +34 -0
  17. package/dist/tab-bar-searchable/dismiss-button.js.map +1 -0
  18. package/dist/tab-bar-searchable/index.d.ts.map +1 -1
  19. package/dist/tab-bar-searchable/index.js +6 -0
  20. package/dist/tab-bar-searchable/index.js.map +1 -1
  21. package/docs/native-ui-shell.md +1 -1
  22. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +13 -0
  23. package/package.json +1 -1
  24. package/src/native/components/searchable-tabs.ts +2 -2
  25. package/src/native/vertical-bars-web-search.ts +80 -0
  26. package/src/native/vertical-bars-web.ts +30 -12
  27. package/src/styles/components/ion-menu.scss +2 -2
  28. package/src/styles/ionic-theme-ios27.scss +2 -0
  29. package/src/styles/utils/searchable.scss +80 -0
  30. package/src/styles/vertical-bars.scss +32 -0
  31. package/src/tab-bar-searchable/dismiss-button.ts +33 -0
  32. package/src/tab-bar-searchable/index.ts +6 -0
@@ -0,0 +1,80 @@
1
+ // UISearchTab keeps a compact selected-tab button until editing starts.
2
+ ion-footer.ios.ios-theme-searchable:not(.ios-theme-disabled, .ios26-disabled):has(
3
+ ion-searchbar.ios:not(.ios-theme-disabled, .ios26-disabled, .searchbar-classic)
4
+ ) {
5
+ > ion-toolbar {
6
+ --ios-theme-searchable-padding-start: 16px;
7
+ --ios-theme-searchable-padding-end: 20px;
8
+ --padding-start: var(--ios-theme-searchable-padding-start);
9
+ --padding-end: var(--ios-theme-searchable-padding-end);
10
+ }
11
+
12
+ ion-buttons[slot='start'],
13
+ .ios-theme-searchable-dismiss {
14
+ margin-inline: 0;
15
+
16
+ ion-button {
17
+ width: 48px;
18
+ height: 48px;
19
+ --border-radius: 50%;
20
+ margin: 0;
21
+ }
22
+ }
23
+
24
+ &.ios-theme-searchable-editing {
25
+ > ion-toolbar {
26
+ // Searchbar padding (8px) and container margin (4px) complete the 16px tab-bar inset.
27
+ --ios-theme-searchable-padding-start: 4px;
28
+ --ios-theme-searchable-padding-end: 8px;
29
+ }
30
+
31
+ ion-searchbar.ios:not(.ios-theme-disabled, .ios26-disabled, .searchbar-classic) {
32
+ padding-inline: 8px 12px;
33
+ }
34
+
35
+ ion-buttons[slot='start'] {
36
+ display: none;
37
+ }
38
+
39
+ .ios-theme-searchable-dismiss {
40
+ display: flex !important;
41
+ }
42
+ }
43
+
44
+ ion-searchbar.ios:not(.ios-theme-disabled, .ios26-disabled, .searchbar-classic) {
45
+ --border-radius: 24px;
46
+ padding-inline: 12px;
47
+
48
+ input.searchbar-input {
49
+ min-height: 48px;
50
+ padding-inline-end: 40px;
51
+ }
52
+
53
+ // Clear edits the value; the separate close button dismisses without clearing.
54
+ .searchbar-clear-button {
55
+ position: absolute;
56
+ inset-inline-end: 12px;
57
+ width: 20px;
58
+ height: 20px;
59
+ min-width: 20px;
60
+ min-height: 20px;
61
+ background: none;
62
+ box-shadow: none;
63
+ backdrop-filter: none;
64
+
65
+ ion-icon {
66
+ width: 20px;
67
+ height: 20px;
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ // The hidden trigger's container must not cover the focused dismissal button.
74
+ ion-fab.ios-theme-searchable-trigger {
75
+ pointer-events: none;
76
+
77
+ > ion-fab-button {
78
+ pointer-events: auto;
79
+ }
80
+ }
@@ -417,3 +417,35 @@ ion-tab-button.ios.ion-cloned-element.ios27-vertical-tab-effect {
417
417
  height: 100%;
418
418
  }
419
419
  }
420
+
421
+ // Search shares the Web rail before Native UI Shell takes ownership.
422
+ :is(ion-app.ios-theme-vertical-bars, ion-modal.ios-theme-vertical-bars-modal) > ion-fab.ios-theme-vertical-bars-search-projection {
423
+ position: fixed;
424
+ left: auto;
425
+ right: 25px;
426
+ top: auto;
427
+ bottom: 24px;
428
+ z-index: 3;
429
+ width: 46px;
430
+ height: 46px;
431
+ margin: 0;
432
+ > ion-fab-button {
433
+ width: 46px;
434
+ height: 46px;
435
+ margin: 0;
436
+ }
437
+ }
438
+
439
+ ion-app.ios-theme-vertical-bars ion-fab > ion-fab-button[data-native-ui-shell] {
440
+ visibility: hidden !important;
441
+ }
442
+
443
+ .ios-theme-vertical-bars-search-projection[hidden] {
444
+ display: none !important;
445
+ }
446
+
447
+ ion-app.ios-theme-vertical-bars-left > ion-fab.ios-theme-vertical-bars-search-projection,
448
+ ion-app.ios-theme-vertical-bars-left ion-modal.ios-theme-vertical-bars-modal > ion-fab.ios-theme-vertical-bars-search-projection {
449
+ left: 25px;
450
+ right: auto;
451
+ }
@@ -0,0 +1,33 @@
1
+ import { close } from 'ionicons/icons';
2
+
3
+ /** Keep the application's original dismissal handler for the focused search layout. */
4
+ export const addSearchDismissButton = (footer: HTMLElement): void => {
5
+ if (footer.querySelector('.ios-theme-searchable-dismiss')) return;
6
+ const original = footer.querySelector<HTMLElement>('ion-buttons[slot=start] ion-button');
7
+ if (!original) return;
8
+ const buttons = footer.ownerDocument.createElement('ion-buttons');
9
+ buttons.slot = 'end';
10
+ // Remain hidden when the full theme is absent or opted out.
11
+ buttons.style.display = 'none';
12
+ buttons.classList.add('ios-theme-searchable-dismiss', 'ios-theme-horizontal-only');
13
+ const button = footer.ownerDocument.createElement('ion-button');
14
+ button.fill = 'default';
15
+ button.type = 'button';
16
+ button.setAttribute('aria-label', original.getAttribute('aria-label') ?? 'Close');
17
+ // Keep the field focused until click; blur would hide this button before dismissal.
18
+ button.addEventListener('pointerdown', (event) => event.preventDefault());
19
+ button.addEventListener('click', () => original.click());
20
+ const icon = footer.ownerDocument.createElement('ion-icon');
21
+ icon.slot = 'icon-only';
22
+ icon.icon = close;
23
+ button.append(icon);
24
+ buttons.append(button);
25
+ // Keep the editing layout while focus moves from clear to the slotted close button.
26
+ footer.addEventListener('focusin', (event) => {
27
+ if ((event.target as HTMLElement).closest('ion-searchbar')) footer.classList.add('ios-theme-searchable-editing');
28
+ });
29
+ footer.addEventListener('focusout', (event) => {
30
+ if (!footer.contains(event.relatedTarget as Node | null)) footer.classList.remove('ios-theme-searchable-editing');
31
+ });
32
+ original.closest('ion-toolbar')?.append(buttons);
33
+ };
@@ -1,4 +1,5 @@
1
1
  import { createAnimation } from '@ionic/core';
2
+ import { addSearchDismissButton } from './dismiss-button';
2
3
  import {
3
4
  ANIMATION_DELAY_BASE,
4
5
  ANIMATION_DURATION,
@@ -62,6 +63,9 @@ export const attachTabBarSearchable = (
62
63
  throw new Error('TabBarSearchable should be defined with props');
63
64
  }
64
65
  // Initialize
66
+ ionFooter.classList.add('ios-theme-searchable');
67
+ ionFabButton.closest('ion-fab')?.classList.add('ios-theme-searchable-trigger');
68
+ addSearchDismissButton(ionFooter);
65
69
  ionFooter.style.pointerEvents = 'none';
66
70
  ionFooter.style.opacity = '0';
67
71
  // Search dismissal belongs beside the Web search field, never in the vertical rail.
@@ -191,6 +195,8 @@ const leaveEvent = async (
191
195
  throw throwErrorByFailedClickElement('ion-buttons[slot=start] ion-button');
192
196
  }
193
197
 
198
+ const focused = ionFooter.ownerDocument.activeElement;
199
+ if (focused instanceof HTMLElement && ionFooter.contains(focused)) focused.blur();
194
200
  const references = getElementReferences(ionTabBar, ionFooter);
195
201
 
196
202
  const tabAnimations = verticalSearch