@rdlabo/ionic-theme-ios27 1.2.1 → 2.0.0-1
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 +24 -280
- package/dist/css/components/ion-toolbar.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 +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/layout/index.d.ts +18 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +26 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/segment/index.js +3 -3
- package/dist/segment/index.js.map +1 -1
- package/dist/sheets-of-glass/index.js +7 -7
- package/dist/sheets-of-glass/index.js.map +1 -1
- package/dist/tab-bar-searchable/dismiss-button.js +53 -3
- package/dist/tab-bar-searchable/dismiss-button.js.map +1 -1
- package/dist/tab-bar-searchable/index.js +18 -3
- package/dist/tab-bar-searchable/index.js.map +1 -1
- package/dist/transition/ios.transition.d.ts.map +1 -1
- package/dist/transition/ios.transition.js +7 -4
- package/dist/transition/ios.transition.js.map +1 -1
- package/dist/transition/native-ui.d.ts +6 -0
- package/dist/transition/native-ui.d.ts.map +1 -0
- package/dist/{native-integration/index.js → transition/native-ui.js} +16 -30
- package/dist/transition/native-ui.js.map +1 -0
- package/docs/adaptive-themes.md +161 -0
- package/docs/add-native-ui.md +53 -0
- package/docs/animation.md +19 -3
- package/docs/eslint.md +3 -1
- package/docs/features.md +9 -5
- package/docs/iphone-duo.md +62 -35
- package/docs/migration.md +17 -26
- package/docs/setup.md +92 -0
- package/docs/special-markup.md +41 -10
- package/docs/using-ion-item-group.md +2 -2
- package/docs/vertical-bars-layout.md +67 -0
- package/package.json +12 -42
- package/src/index.ts +3 -1
- package/src/layout/index.ts +29 -0
- package/src/segment/index.ts +3 -3
- package/src/sheets-of-glass/index.ts +7 -7
- package/src/styles/components/ion-toolbar.scss +1 -1
- package/src/styles/vertical-bars.scss +15 -18
- package/src/tab-bar-searchable/dismiss-button.ts +73 -4
- package/src/tab-bar-searchable/index.ts +20 -3
- package/src/transition/ios.transition.ts +7 -4
- package/src/{native-integration/index.ts → transition/native-ui.ts} +17 -46
- package/Package.swift +0 -21
- package/dist/_virtual/_rolldown/runtime.js +0 -13
- package/dist/native/components/index.d.ts +0 -19
- package/dist/native/components/index.d.ts.map +0 -1
- package/dist/native/components/index.js +0 -40
- package/dist/native/components/index.js.map +0 -1
- package/dist/native/components/ion-back-button.d.ts +0 -9
- package/dist/native/components/ion-back-button.d.ts.map +0 -1
- package/dist/native/components/ion-back-button.js +0 -20
- package/dist/native/components/ion-back-button.js.map +0 -1
- package/dist/native/components/ion-button.d.ts +0 -10
- package/dist/native/components/ion-button.d.ts.map +0 -1
- package/dist/native/components/ion-button.js +0 -23
- package/dist/native/components/ion-button.js.map +0 -1
- package/dist/native/components/ion-buttons.d.ts +0 -11
- package/dist/native/components/ion-buttons.d.ts.map +0 -1
- package/dist/native/components/ion-buttons.js +0 -30
- package/dist/native/components/ion-buttons.js.map +0 -1
- package/dist/native/components/ion-fab.d.ts +0 -10
- package/dist/native/components/ion-fab.d.ts.map +0 -1
- package/dist/native/components/ion-fab.js +0 -132
- package/dist/native/components/ion-fab.js.map +0 -1
- package/dist/native/components/ion-menu-button.d.ts +0 -13
- package/dist/native/components/ion-menu-button.d.ts.map +0 -1
- package/dist/native/components/ion-menu-button.js +0 -31
- package/dist/native/components/ion-menu-button.js.map +0 -1
- package/dist/native/components/ion-segment.d.ts +0 -11
- package/dist/native/components/ion-segment.d.ts.map +0 -1
- package/dist/native/components/ion-segment.js +0 -30
- package/dist/native/components/ion-segment.js.map +0 -1
- package/dist/native/components/ion-tab-bar.d.ts +0 -11
- package/dist/native/components/ion-tab-bar.d.ts.map +0 -1
- package/dist/native/components/ion-tab-bar.js +0 -50
- package/dist/native/components/ion-tab-bar.js.map +0 -1
- package/dist/native/components/searchable-tabs.d.ts +0 -20
- package/dist/native/components/searchable-tabs.d.ts.map +0 -1
- package/dist/native/components/searchable-tabs.js +0 -383
- package/dist/native/components/searchable-tabs.js.map +0 -1
- package/dist/native/definitions.d.ts +0 -152
- package/dist/native/definitions.d.ts.map +0 -1
- package/dist/native/definitions.js +0 -0
- package/dist/native/foldable/fold-state.d.ts +0 -5
- package/dist/native/foldable/fold-state.d.ts.map +0 -1
- package/dist/native/foldable/fold-state.js +0 -14
- package/dist/native/foldable/fold-state.js.map +0 -1
- package/dist/native/foldable/types.d.ts +0 -12
- package/dist/native/foldable/types.d.ts.map +0 -1
- package/dist/native/foldable/types.js +0 -0
- package/dist/native/index.d.ts +0 -11
- package/dist/native/index.d.ts.map +0 -1
- package/dist/native/index.js +0 -242
- package/dist/native/index.js.map +0 -1
- package/dist/native/prehide.d.ts +0 -7
- package/dist/native/prehide.d.ts.map +0 -1
- package/dist/native/prehide.js +0 -230
- package/dist/native/prehide.js.map +0 -1
- package/dist/native/runtime.d.ts +0 -5
- package/dist/native/runtime.d.ts.map +0 -1
- package/dist/native/runtime.js +0 -640
- package/dist/native/runtime.js.map +0 -1
- package/dist/native/shared/candidate.d.ts +0 -21
- package/dist/native/shared/candidate.d.ts.map +0 -1
- package/dist/native/shared/candidate.js +0 -98
- package/dist/native/shared/candidate.js.map +0 -1
- package/dist/native/shared/crossfade.d.ts +0 -10
- package/dist/native/shared/crossfade.d.ts.map +0 -1
- package/dist/native/shared/crossfade.js +0 -50
- package/dist/native/shared/crossfade.js.map +0 -1
- package/dist/native/shared/dom.d.ts +0 -46
- package/dist/native/shared/dom.d.ts.map +0 -1
- package/dist/native/shared/dom.js +0 -172
- package/dist/native/shared/dom.js.map +0 -1
- package/dist/native/shared/events.d.ts +0 -11
- package/dist/native/shared/events.d.ts.map +0 -1
- package/dist/native/shared/events.js +0 -13
- package/dist/native/shared/events.js.map +0 -1
- package/dist/native/shared/icons.d.ts +0 -8
- package/dist/native/shared/icons.d.ts.map +0 -1
- package/dist/native/shared/icons.js +0 -61
- package/dist/native/shared/icons.js.map +0 -1
- package/dist/native/shared/modal.d.ts +0 -10
- package/dist/native/shared/modal.d.ts.map +0 -1
- package/dist/native/shared/modal.js +0 -160
- package/dist/native/shared/modal.js.map +0 -1
- package/dist/native/vertical-bars-web-search.d.ts +0 -9
- package/dist/native/vertical-bars-web-search.d.ts.map +0 -1
- package/dist/native/vertical-bars-web-search.js +0 -84
- package/dist/native/vertical-bars-web-search.js.map +0 -1
- package/dist/native/vertical-bars-web.d.ts +0 -5
- package/dist/native/vertical-bars-web.d.ts.map +0 -1
- package/dist/native/vertical-bars-web.js +0 -341
- package/dist/native/vertical-bars-web.js.map +0 -1
- package/dist/native-integration/index.d.ts +0 -23
- package/dist/native-integration/index.d.ts.map +0 -1
- package/dist/native-integration/index.js.map +0 -1
- package/dist/native-integration/transition.d.ts +0 -5
- package/dist/native-integration/transition.d.ts.map +0 -1
- package/dist/native-integration/transition.js +0 -16
- package/dist/native-integration/transition.js.map +0 -1
- package/dist/vertical-bars.d.ts +0 -5
- package/dist/vertical-bars.js +0 -4
- package/docs/e2e-testing.md +0 -51
- package/docs/feedback.md +0 -65
- package/docs/iphone-duo-with-original-theme.md +0 -158
- package/docs/native-ui-shell.md +0 -285
- package/docs/vertical-bars.md +0 -285
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellButton.swift +0 -71
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellButtons.swift +0 -24
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellComponents.swift +0 -18
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellFab.swift +0 -83
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSearchController.swift +0 -432
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSegment.swift +0 -91
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellTabBar.swift +0 -221
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +0 -437
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalSearch.swift +0 -118
- package/ios/Sources/IonicNativeUIShellPlugin/IonicNativeUIShellPlugin.swift +0 -396
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellCrossfade.swift +0 -35
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellHost.swift +0 -8
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellRendering.swift +0 -27
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellSnapshot.swift +0 -135
- package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +0 -650
- package/src/native/components/index.ts +0 -53
- package/src/native/components/ion-back-button.ts +0 -19
- package/src/native/components/ion-button.ts +0 -24
- package/src/native/components/ion-buttons.ts +0 -38
- package/src/native/components/ion-fab.ts +0 -151
- package/src/native/components/ion-menu-button.ts +0 -24
- package/src/native/components/ion-segment.ts +0 -27
- package/src/native/components/ion-tab-bar.ts +0 -50
- package/src/native/components/searchable-tabs.ts +0 -459
- package/src/native/definitions.ts +0 -191
- package/src/native/foldable/fold-state.ts +0 -9
- package/src/native/foldable/types.ts +0 -4
- package/src/native/index.ts +0 -281
- package/src/native/prehide.ts +0 -265
- package/src/native/runtime.ts +0 -781
- package/src/native/shared/candidate.ts +0 -150
- package/src/native/shared/crossfade.ts +0 -50
- package/src/native/shared/dom.ts +0 -286
- package/src/native/shared/events.ts +0 -8
- package/src/native/shared/icons.ts +0 -57
- package/src/native/shared/modal.ts +0 -157
- package/src/native/vertical-bars-web-search.ts +0 -80
- package/src/native/vertical-bars-web.ts +0 -411
- package/src/native-integration/transition.ts +0 -15
- package/src/vertical-bars.ts +0 -11
|
@@ -1,459 +0,0 @@
|
|
|
1
|
-
import { getNativeSearchBindings, requestNativeSearch } from '../../native-integration';
|
|
2
|
-
import type { NativeSearchBinding } from '../../native-integration';
|
|
3
|
-
import { excluded, inFixedToolbar, isShellDisabled, marker, text, visible } from '../shared/dom';
|
|
4
|
-
import { iconSource } from '../shared/icons';
|
|
5
|
-
import type { Candidate } from '../shared/candidate';
|
|
6
|
-
import type { ShellItem, ShellSearch, ShellSearchEvent } from '../definitions';
|
|
7
|
-
|
|
8
|
-
interface SearchState {
|
|
9
|
-
binding: NativeSearchBinding;
|
|
10
|
-
bar: HTMLIonSearchbarElement;
|
|
11
|
-
input: HTMLInputElement;
|
|
12
|
-
editSequence: number;
|
|
13
|
-
composing: boolean;
|
|
14
|
-
focused: boolean;
|
|
15
|
-
minimumRevision: number;
|
|
16
|
-
valueVersion: number;
|
|
17
|
-
applyingInput: boolean;
|
|
18
|
-
keyboardHidden?: boolean;
|
|
19
|
-
last?: ShellSearch;
|
|
20
|
-
layout?: string;
|
|
21
|
-
rejectedLayout?: string;
|
|
22
|
-
restore: () => void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const createSearchSupport = (doc: Document, id: (element: Element) => string, schedule: () => void) => {
|
|
26
|
-
const states = new Map<NativeSearchBinding, SearchState>();
|
|
27
|
-
let keyboardVisible = false;
|
|
28
|
-
let heldKeyboardResize: string | null = null;
|
|
29
|
-
let keyboardResizeGeneration = 0;
|
|
30
|
-
type KeyboardPlugin = {
|
|
31
|
-
setResizeMode?: (options: { mode: string }) => Promise<void>;
|
|
32
|
-
getResizeMode?: () => Promise<{ mode?: string }>;
|
|
33
|
-
};
|
|
34
|
-
const keyboardPlugin = (): KeyboardPlugin | undefined =>
|
|
35
|
-
(
|
|
36
|
-
doc.defaultView as Window & {
|
|
37
|
-
Capacitor?: { Plugins?: { Keyboard?: KeyboardPlugin } };
|
|
38
|
-
}
|
|
39
|
-
)?.Capacitor?.Plugins?.Keyboard;
|
|
40
|
-
const projected = (binding: NativeSearchBinding) => binding.footer.hasAttribute(marker);
|
|
41
|
-
// Hold Cap resize at none while search is active so WebView shrink does not fight UISearchTab.
|
|
42
|
-
const holdKeyboardResize = () => {
|
|
43
|
-
const keyboard = keyboardPlugin();
|
|
44
|
-
if (!keyboard?.setResizeMode || heldKeyboardResize !== null) return;
|
|
45
|
-
const setResizeMode = keyboard.setResizeMode.bind(keyboard);
|
|
46
|
-
const generation = ++keyboardResizeGeneration;
|
|
47
|
-
heldKeyboardResize = 'native';
|
|
48
|
-
void (async () => {
|
|
49
|
-
let restoreMode = 'native';
|
|
50
|
-
try {
|
|
51
|
-
const current = await keyboard.getResizeMode?.();
|
|
52
|
-
if (typeof current?.mode === 'string') restoreMode = current.mode;
|
|
53
|
-
} catch {
|
|
54
|
-
/* keep default restore target */
|
|
55
|
-
}
|
|
56
|
-
if (generation !== keyboardResizeGeneration) return;
|
|
57
|
-
heldKeyboardResize = restoreMode;
|
|
58
|
-
try {
|
|
59
|
-
await setResizeMode({ mode: 'none' });
|
|
60
|
-
} catch {
|
|
61
|
-
if (generation === keyboardResizeGeneration) heldKeyboardResize = null;
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
// Leave raced ahead of setResizeMode(none) — undo the stale none apply.
|
|
65
|
-
if (generation !== keyboardResizeGeneration) void setResizeMode({ mode: restoreMode }).catch(() => undefined);
|
|
66
|
-
})();
|
|
67
|
-
};
|
|
68
|
-
const hasActiveProjection = () => [...states.keys()].some((binding) => binding.active && projected(binding));
|
|
69
|
-
const releaseKeyboardResize = () => {
|
|
70
|
-
if (hasActiveProjection()) return;
|
|
71
|
-
keyboardResizeGeneration++;
|
|
72
|
-
const mode = heldKeyboardResize;
|
|
73
|
-
heldKeyboardResize = null;
|
|
74
|
-
if (!mode) return;
|
|
75
|
-
void keyboardPlugin()?.setResizeMode?.({ mode });
|
|
76
|
-
};
|
|
77
|
-
const isCurrent = (state: SearchState) =>
|
|
78
|
-
state.bar.isConnected &&
|
|
79
|
-
state.binding.footer.querySelector('ion-searchbar') === state.bar &&
|
|
80
|
-
state.bar.querySelector('input.searchbar-input') === state.input;
|
|
81
|
-
const install = (binding: NativeSearchBinding, bar: HTMLIonSearchbarElement, input: HTMLInputElement): SearchState => {
|
|
82
|
-
const originalFocus = bar.setFocus;
|
|
83
|
-
const ownValue = Object.getOwnPropertyDescriptor(bar, 'value');
|
|
84
|
-
let owner: object | null = bar;
|
|
85
|
-
let value = ownValue;
|
|
86
|
-
while (!value && (owner = Object.getPrototypeOf(owner))) value = Object.getOwnPropertyDescriptor(owner, 'value');
|
|
87
|
-
const focus = async () => {
|
|
88
|
-
if (projected(binding) && (await requestNativeSearch(binding, true, true))) return;
|
|
89
|
-
await originalFocus.call(bar);
|
|
90
|
-
};
|
|
91
|
-
bar.setFocus = focus;
|
|
92
|
-
let wrapped: PropertyDescriptor | undefined;
|
|
93
|
-
if (value?.get && value.set && ownValue?.configurable !== false) {
|
|
94
|
-
const descriptor = value;
|
|
95
|
-
wrapped = {
|
|
96
|
-
configurable: true,
|
|
97
|
-
enumerable: value.enumerable,
|
|
98
|
-
get: () => descriptor.get!.call(bar),
|
|
99
|
-
set: (next: string | null | undefined) => {
|
|
100
|
-
if (state.applyingInput) state.applyingInput = false;
|
|
101
|
-
else state.valueVersion++;
|
|
102
|
-
descriptor.set!.call(bar, next);
|
|
103
|
-
schedule();
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
Object.defineProperty(bar, 'value', wrapped);
|
|
107
|
-
}
|
|
108
|
-
const state: SearchState = {
|
|
109
|
-
binding,
|
|
110
|
-
bar,
|
|
111
|
-
input,
|
|
112
|
-
editSequence: 0,
|
|
113
|
-
composing: false,
|
|
114
|
-
focused: false,
|
|
115
|
-
minimumRevision: 0,
|
|
116
|
-
valueVersion: 0,
|
|
117
|
-
applyingInput: false,
|
|
118
|
-
restore: () => {
|
|
119
|
-
if (bar.setFocus === focus) bar.setFocus = originalFocus;
|
|
120
|
-
if (wrapped && Object.getOwnPropertyDescriptor(bar, 'value')?.set === wrapped.set) {
|
|
121
|
-
if (ownValue) Object.defineProperty(bar, 'value', ownValue);
|
|
122
|
-
else Reflect.deleteProperty(bar, 'value');
|
|
123
|
-
}
|
|
124
|
-
binding.active = binding.focused = false;
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
states.set(binding, state);
|
|
128
|
-
return state;
|
|
129
|
-
};
|
|
130
|
-
const readIcon = (element: HTMLElement, owner: Element, candidate: Candidate): ShellItem | undefined => {
|
|
131
|
-
const style = getComputedStyle(element);
|
|
132
|
-
const rect = element.getBoundingClientRect();
|
|
133
|
-
const item: ShellItem = {
|
|
134
|
-
id: id(owner),
|
|
135
|
-
x: rect.x,
|
|
136
|
-
y: rect.y,
|
|
137
|
-
width: rect.width,
|
|
138
|
-
height: rect.height,
|
|
139
|
-
label: text(element),
|
|
140
|
-
accessibilityLabel: owner.getAttribute('aria-label') ?? text(element),
|
|
141
|
-
disabled: false,
|
|
142
|
-
selected: false,
|
|
143
|
-
fontSize: parseFloat(style.fontSize),
|
|
144
|
-
fontWeight: parseInt(style.fontWeight, 10) || 400,
|
|
145
|
-
color: style.color,
|
|
146
|
-
};
|
|
147
|
-
const icon = element.matches('ion-icon') ? element : element.querySelector('ion-icon');
|
|
148
|
-
const svg = icon?.shadowRoot?.querySelector<SVGElement>('svg') ?? element.querySelector<SVGElement>('svg');
|
|
149
|
-
if (svg) {
|
|
150
|
-
const source = iconSource(svg);
|
|
151
|
-
if (!source) return;
|
|
152
|
-
const iconStyle = getComputedStyle(icon ?? svg);
|
|
153
|
-
item.iconWidth = parseFloat(iconStyle.width);
|
|
154
|
-
item.iconHeight = parseFloat(iconStyle.height);
|
|
155
|
-
// ion-searchbar gives its icon a full-height box; SVG meet sizing keeps the glyph square.
|
|
156
|
-
const box = (svg as SVGSVGElement).viewBox?.baseVal;
|
|
157
|
-
if (box?.width && box.height && svg.getAttribute('preserveAspectRatio') !== 'none') {
|
|
158
|
-
const scale = Math.min(item.iconWidth! / box.width, item.iconHeight / box.height);
|
|
159
|
-
item.iconWidth = box.width * scale;
|
|
160
|
-
item.iconHeight = box.height * scale;
|
|
161
|
-
}
|
|
162
|
-
candidate.icons.push({ item, source });
|
|
163
|
-
} else if (!item.label) return;
|
|
164
|
-
return item;
|
|
165
|
-
};
|
|
166
|
-
return {
|
|
167
|
-
keyboard(visible: boolean) {
|
|
168
|
-
keyboardVisible = visible;
|
|
169
|
-
if (!visible) {
|
|
170
|
-
for (const [binding, state] of states) {
|
|
171
|
-
// Ionic can wait for the pre-keyboard height indefinitely after rotation.
|
|
172
|
-
// Release the visibility override we own when native search's keyboard closes.
|
|
173
|
-
if (state.keyboardHidden) binding.tabBar.classList.remove('tab-bar-hidden');
|
|
174
|
-
state.keyboardHidden = false;
|
|
175
|
-
}
|
|
176
|
-
releaseKeyboardResize();
|
|
177
|
-
}
|
|
178
|
-
schedule();
|
|
179
|
-
},
|
|
180
|
-
keepSearchTabsVisible() {
|
|
181
|
-
if (!keyboardVisible) return;
|
|
182
|
-
for (const [binding, state] of states) {
|
|
183
|
-
// Ionic hides bottom tabs for every keyboard, including native search.
|
|
184
|
-
// Correct only that framework class; application styles still apply.
|
|
185
|
-
if (binding.active && projected(binding) && binding.tabBar.classList.contains('tab-bar-hidden')) {
|
|
186
|
-
state.keyboardHidden = true;
|
|
187
|
-
binding.tabBar.classList.remove('tab-bar-hidden');
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
decorate(candidates: Candidate[], blocked: (element: HTMLElement) => boolean): Candidate[] {
|
|
192
|
-
const bindings = getNativeSearchBindings(doc);
|
|
193
|
-
for (const [binding, state] of states) {
|
|
194
|
-
if (!bindings.includes(binding) || !isCurrent(state)) {
|
|
195
|
-
this.retire(binding);
|
|
196
|
-
state.restore();
|
|
197
|
-
states.delete(binding);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
const inactive: [Candidate, ShellSearch][] = [];
|
|
201
|
-
const pageHidden = (element: HTMLElement) => !!element.closest('.ion-page-hidden, .ion-page-invisible');
|
|
202
|
-
const retainInactive = (candidate: Candidate, state?: SearchState, keepAvailable = false) => {
|
|
203
|
-
// Keep ShellSearchController across page transitions instead of demoting to UITabBar.
|
|
204
|
-
// While the searchable page is only blocked mid-transition (not yet hidden), keep
|
|
205
|
-
// available:true so the system search button stays available over album content.
|
|
206
|
-
if (state?.last) inactive.push([candidate, { ...state.last, active: false, available: keepAvailable, focused: false }]);
|
|
207
|
-
};
|
|
208
|
-
for (const binding of bindings) {
|
|
209
|
-
const candidate = candidates.find((c) => c.element === binding.tabBar);
|
|
210
|
-
if (!candidate || candidate.control.search || binding.tabBar.getAttribute('slot') !== 'bottom') continue;
|
|
211
|
-
const fab = binding.trigger.parentElement;
|
|
212
|
-
// Search also supports the existing page-level fixed FAB, outside scrolling content.
|
|
213
|
-
if (
|
|
214
|
-
!fab?.matches('ion-fab[slot="fixed"]') ||
|
|
215
|
-
!(fab.parentElement?.matches('ion-content') || (fab.parentElement?.matches('.ion-page') && !fab.closest('ion-content')))
|
|
216
|
-
)
|
|
217
|
-
continue;
|
|
218
|
-
if (isShellDisabled(fab) || isShellDisabled(binding.footer)) continue;
|
|
219
|
-
const pageUnavailable = blocked(binding.footer) || !!binding.footer.closest(excluded);
|
|
220
|
-
const existing = states.get(binding);
|
|
221
|
-
if (pageUnavailable) {
|
|
222
|
-
retainInactive(candidate, existing, !!existing?.last?.available && !pageHidden(binding.footer));
|
|
223
|
-
if (existing?.last) continue;
|
|
224
|
-
// First registration during a transition: fall through and measure available:false.
|
|
225
|
-
}
|
|
226
|
-
if (binding.active && existing && isCurrent(existing) && existing.last && !pageUnavailable) {
|
|
227
|
-
const back = binding.footer.querySelector<HTMLIonButtonElement>('ion-buttons[slot=start] ion-button');
|
|
228
|
-
// Close must stay eligible; otherwise fall through and demote instead of caching active.
|
|
229
|
-
if (back && !back.disabled && !back.closest(excluded) && !existing.bar.closest(excluded)) {
|
|
230
|
-
candidate.control.search = {
|
|
231
|
-
...existing.last,
|
|
232
|
-
active: true,
|
|
233
|
-
available: true,
|
|
234
|
-
focused: binding.focused,
|
|
235
|
-
value: existing.bar.value ?? '',
|
|
236
|
-
placeholder: existing.bar.placeholder ?? '',
|
|
237
|
-
disabled: existing.bar.disabled,
|
|
238
|
-
editSequence: existing.editSequence,
|
|
239
|
-
valueVersion: existing.valueVersion,
|
|
240
|
-
};
|
|
241
|
-
existing.last = candidate.control.search;
|
|
242
|
-
candidate.sources = [binding.tabBar, binding.trigger.closest<HTMLElement>('ion-fab') ?? binding.trigger, binding.footer];
|
|
243
|
-
candidate.actions.set(existing.last.trigger.id, binding.trigger);
|
|
244
|
-
candidate.actions.set(existing.last.closeId, back);
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
if (!visible(binding.trigger) && !pageUnavailable) {
|
|
249
|
-
retainInactive(candidate, existing);
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
const bar = binding.footer.querySelector<HTMLIonSearchbarElement>('ion-searchbar');
|
|
253
|
-
const input = bar?.querySelector<HTMLInputElement>('input.searchbar-input');
|
|
254
|
-
const back = binding.footer.querySelector<HTMLIonButtonElement>('ion-buttons[slot=start] ion-button');
|
|
255
|
-
if (
|
|
256
|
-
!bar ||
|
|
257
|
-
!input ||
|
|
258
|
-
!back ||
|
|
259
|
-
!binding.footer.matches('ion-footer') ||
|
|
260
|
-
!inFixedToolbar(bar) ||
|
|
261
|
-
!inFixedToolbar(back) ||
|
|
262
|
-
(!pageUnavailable && bar.closest(excluded)) ||
|
|
263
|
-
!bar.classList.contains('ios') ||
|
|
264
|
-
bar.classList.contains('searchbar-classic')
|
|
265
|
-
) {
|
|
266
|
-
retainInactive(candidate, existing);
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
if (
|
|
270
|
-
bar.color ||
|
|
271
|
-
back.fill !== 'default' ||
|
|
272
|
-
back.color ||
|
|
273
|
-
bar.maxlength != null ||
|
|
274
|
-
bar.minlength != null ||
|
|
275
|
-
bar.inputmode ||
|
|
276
|
-
bar.enterkeyhint ||
|
|
277
|
-
!['off', 'none'].includes(bar.autocapitalize) ||
|
|
278
|
-
bar.autocomplete !== 'off' ||
|
|
279
|
-
![false, 'off', undefined].includes(bar.autocorrect) ||
|
|
280
|
-
bar.spellcheck ||
|
|
281
|
-
bar.showClearButton !== 'always' ||
|
|
282
|
-
bar.clearIcon
|
|
283
|
-
) {
|
|
284
|
-
retainInactive(candidate, existing);
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
if (bar.showCancelButton !== 'never' || bar.type !== 'search' || !getComputedStyle(input).backdropFilter.includes('blur')) {
|
|
288
|
-
retainInactive(candidate, existing);
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
const fabButton = binding.trigger as HTMLIonFabButtonElement;
|
|
292
|
-
const nativeTrigger = fabButton.shadowRoot?.querySelector<HTMLElement>('[part=native]');
|
|
293
|
-
if (
|
|
294
|
-
(!pageUnavailable && binding.trigger.closest(excluded)) ||
|
|
295
|
-
(!pageUnavailable && back.closest(excluded)) ||
|
|
296
|
-
back.disabled ||
|
|
297
|
-
fabButton.disabled ||
|
|
298
|
-
fabButton.color ||
|
|
299
|
-
fabButton.type !== 'button' ||
|
|
300
|
-
fabButton.href ||
|
|
301
|
-
!nativeTrigger ||
|
|
302
|
-
getComputedStyle(fabButton).backgroundColor !== 'rgba(0, 0, 0, 0)' ||
|
|
303
|
-
!getComputedStyle(nativeTrigger).backdropFilter.includes('blur')
|
|
304
|
-
) {
|
|
305
|
-
retainInactive(candidate, existing);
|
|
306
|
-
continue;
|
|
307
|
-
}
|
|
308
|
-
const state = existing ?? install(binding, bar, input);
|
|
309
|
-
const trigger = readIcon(binding.trigger, binding.trigger, candidate);
|
|
310
|
-
const searchIcon = bar.querySelector<HTMLElement>('.searchbar-search-icon');
|
|
311
|
-
const field = searchIcon && readIcon(searchIcon, bar, candidate);
|
|
312
|
-
if (!trigger || !field) {
|
|
313
|
-
retainInactive(candidate, state);
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
field.label = '';
|
|
317
|
-
field.accessibilityLabel = bar.getAttribute('aria-label') ?? input.getAttribute('aria-label') ?? 'Search';
|
|
318
|
-
// Icon-only search FABs often rely on the searchbar for their accessible
|
|
319
|
-
// name. Carry that semantic name into the independent resting trigger.
|
|
320
|
-
if (!trigger.accessibilityLabel) trigger.accessibilityLabel = field.accessibilityLabel;
|
|
321
|
-
state.layout = JSON.stringify([
|
|
322
|
-
innerWidth,
|
|
323
|
-
innerHeight,
|
|
324
|
-
candidate.control.x,
|
|
325
|
-
candidate.control.y,
|
|
326
|
-
candidate.control.width,
|
|
327
|
-
candidate.control.height,
|
|
328
|
-
candidate.control.rtl,
|
|
329
|
-
trigger.x,
|
|
330
|
-
trigger.y,
|
|
331
|
-
trigger.width,
|
|
332
|
-
trigger.height,
|
|
333
|
-
candidate.control.items,
|
|
334
|
-
candidate.icons.map(({ source }) => source),
|
|
335
|
-
field,
|
|
336
|
-
trigger,
|
|
337
|
-
bar.placeholder,
|
|
338
|
-
bar.disabled,
|
|
339
|
-
]);
|
|
340
|
-
if (state.rejectedLayout === state.layout) {
|
|
341
|
-
retainInactive(candidate, state);
|
|
342
|
-
continue;
|
|
343
|
-
}
|
|
344
|
-
const available = !pageUnavailable && visible(binding.trigger);
|
|
345
|
-
candidate.control.search = {
|
|
346
|
-
id: id(input),
|
|
347
|
-
field,
|
|
348
|
-
trigger,
|
|
349
|
-
closeId: id(back),
|
|
350
|
-
active: available && binding.active,
|
|
351
|
-
available,
|
|
352
|
-
focused: available && binding.focused,
|
|
353
|
-
value: bar.value ?? '',
|
|
354
|
-
placeholder: bar.placeholder ?? '',
|
|
355
|
-
disabled: bar.disabled,
|
|
356
|
-
editSequence: state.editSequence,
|
|
357
|
-
valueVersion: state.valueVersion,
|
|
358
|
-
};
|
|
359
|
-
state.last = candidate.control.search;
|
|
360
|
-
if (available) {
|
|
361
|
-
candidate.sources = [binding.tabBar, binding.trigger.closest<HTMLElement>('ion-fab') ?? binding.trigger, binding.footer];
|
|
362
|
-
candidate.actions.set(id(binding.trigger), binding.trigger);
|
|
363
|
-
candidate.actions.set(id(back), back);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
for (const [candidate, configuration] of inactive) candidate.control.search ??= configuration;
|
|
367
|
-
const groups = candidates.filter((c) => c.control.search);
|
|
368
|
-
return candidates.filter((c) => !groups.some((group) => group !== c && group.sources?.some((source) => source.contains(c.element))));
|
|
369
|
-
},
|
|
370
|
-
projected,
|
|
371
|
-
hasActive() {
|
|
372
|
-
for (const [binding] of states) if (binding.active && projected(binding)) return true;
|
|
373
|
-
return false;
|
|
374
|
-
},
|
|
375
|
-
begin(binding: NativeSearchBinding, revision: number) {
|
|
376
|
-
const state = states.get(binding);
|
|
377
|
-
if (state) state.minimumRevision = revision;
|
|
378
|
-
},
|
|
379
|
-
/** Enter / focus / leave. `focused` is ignored when leaving. */
|
|
380
|
-
setStatus(binding: NativeSearchBinding, active: boolean, focused = false) {
|
|
381
|
-
if (!active) {
|
|
382
|
-
this.retire(binding);
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
binding.active = true;
|
|
386
|
-
// Projection intent only. Ionic focus/blur events still wait for the native phase.
|
|
387
|
-
binding.focused = focused;
|
|
388
|
-
holdKeyboardResize();
|
|
389
|
-
},
|
|
390
|
-
release(element: HTMLElement) {
|
|
391
|
-
for (const binding of states.keys()) if (binding.footer === element) this.retire(binding);
|
|
392
|
-
},
|
|
393
|
-
reject(ids: string[]) {
|
|
394
|
-
states.forEach((state) => {
|
|
395
|
-
if (ids.includes(id(state.binding.tabBar))) {
|
|
396
|
-
state.rejectedLayout = state.layout;
|
|
397
|
-
// UIKit removed the search surface — do not reuse its cache; project ordinary tabs.
|
|
398
|
-
state.last = undefined;
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
},
|
|
402
|
-
event(event: ShellSearchEvent) {
|
|
403
|
-
const state = Array.from(states.values()).find((s) => id(s.input) === event.id && projected(s.binding) && isCurrent(s));
|
|
404
|
-
if (
|
|
405
|
-
!state ||
|
|
406
|
-
!state.binding.active ||
|
|
407
|
-
((event.phase === 'input' || event.phase === 'clear') && event.valueVersion !== state.valueVersion) ||
|
|
408
|
-
event.revision < state.minimumRevision ||
|
|
409
|
-
event.sequence <= state.editSequence ||
|
|
410
|
-
state.bar.disabled
|
|
411
|
-
)
|
|
412
|
-
return;
|
|
413
|
-
state.editSequence = event.sequence;
|
|
414
|
-
const { input, binding } = state;
|
|
415
|
-
if (event.phase === 'input') {
|
|
416
|
-
if (state.composing !== event.composing)
|
|
417
|
-
input.dispatchEvent(
|
|
418
|
-
new CompositionEvent(event.composing ? 'compositionstart' : 'compositionend', { bubbles: true, data: event.value }),
|
|
419
|
-
);
|
|
420
|
-
state.composing = event.composing;
|
|
421
|
-
input.value = event.value;
|
|
422
|
-
state.applyingInput = true;
|
|
423
|
-
try {
|
|
424
|
-
input.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true, isComposing: event.composing }));
|
|
425
|
-
} finally {
|
|
426
|
-
state.applyingInput = false;
|
|
427
|
-
}
|
|
428
|
-
} else if (event.phase === 'clear') {
|
|
429
|
-
state.bar.querySelector<HTMLButtonElement>('.searchbar-clear-button')?.click();
|
|
430
|
-
} else if (event.phase === 'commit') {
|
|
431
|
-
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
432
|
-
} else {
|
|
433
|
-
if (state.focused === (event.phase === 'focus')) return;
|
|
434
|
-
state.focused = binding.focused = event.phase === 'focus';
|
|
435
|
-
input.dispatchEvent(new FocusEvent(event.phase, { composed: true }));
|
|
436
|
-
}
|
|
437
|
-
schedule();
|
|
438
|
-
},
|
|
439
|
-
retire(binding: NativeSearchBinding) {
|
|
440
|
-
const state = states.get(binding);
|
|
441
|
-
// Keep ownership until keyboardWillHide can release this class.
|
|
442
|
-
if (state?.keyboardHidden && keyboardVisible) binding.tabBar.classList.add('tab-bar-hidden');
|
|
443
|
-
if (state?.focused) {
|
|
444
|
-
state.focused = false;
|
|
445
|
-
state.input.dispatchEvent(new FocusEvent('blur'));
|
|
446
|
-
}
|
|
447
|
-
binding.active = binding.focused = false;
|
|
448
|
-
releaseKeyboardResize();
|
|
449
|
-
},
|
|
450
|
-
destroy() {
|
|
451
|
-
states.forEach((state) => {
|
|
452
|
-
this.retire(state.binding);
|
|
453
|
-
state.restore();
|
|
454
|
-
});
|
|
455
|
-
states.clear();
|
|
456
|
-
releaseKeyboardResize();
|
|
457
|
-
},
|
|
458
|
-
};
|
|
459
|
-
};
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import type { PluginListenerHandle } from '@capacitor/core';
|
|
2
|
-
|
|
3
|
-
import type { NativeUIShellComponent } from './components';
|
|
4
|
-
export type { NativeUIShellComponent } from './components';
|
|
5
|
-
|
|
6
|
-
export interface NativeUIShellStatus {
|
|
7
|
-
state: 'web' | 'native' | 'stopped';
|
|
8
|
-
projected: number;
|
|
9
|
-
updates: number;
|
|
10
|
-
reason?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface VerticalControlAreaOptions {
|
|
14
|
-
/** Appearance of native vertical ion-button and ion-menu-button actions.
|
|
15
|
-
* `system` (default) uses SwiftUI styling and template icons; `source` projects supported Ionic fill and colors.
|
|
16
|
-
* Local data-projection="source|system" or ios-theme-projection-source / ios-theme-projection-system take precedence:
|
|
17
|
-
* the button itself, then its nearest ion-buttons, then this option. On the same element, a valid attribute wins;
|
|
18
|
-
* otherwise system wins if both classes are present. Invalid attribute values are ignored.
|
|
19
|
-
* Local settings update live; removing them restores inheritance. Actions, disabled state and grouping are preserved.
|
|
20
|
-
* Does not affect back buttons, tabs, FABs, horizontal controls, source elements or Web clones.
|
|
21
|
-
*/
|
|
22
|
-
buttonProjection?: 'source' | 'system';
|
|
23
|
-
/** Default fill for native vertical ion-button actions resolved to `source`, including local overrides.
|
|
24
|
-
* Use `solid` for Ionic's default design, or `null` (also the omitted default) for the iOS theme's glass design.
|
|
25
|
-
* Applies when fill is omitted or `default`, outside ion-buttons. Inside ion-buttons the default is clear.
|
|
26
|
-
* Explicit clear, solid and outline take precedence; clear and outline cannot be configured as defaults.
|
|
27
|
-
* Source elements and Web clones are unchanged.
|
|
28
|
-
*/
|
|
29
|
-
buttonDefaultFill?: 'solid' | null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface NativeUIShellOptions extends VerticalControlAreaOptions {
|
|
33
|
-
/** Enables Native UI Shell globally. Defaults to `true`. */
|
|
34
|
-
enabled?: boolean;
|
|
35
|
-
/** Controls eligible for native projection. Omit to enable every control; when present, only `true` controls are enabled. */
|
|
36
|
-
controls?: NativeUIShellControls;
|
|
37
|
-
/** Internal: limit native projection to the Vertical Control Area. */
|
|
38
|
-
verticalBarsOnly?: boolean;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface NativeUIShellControls {
|
|
42
|
-
/** Projects tab bars and their native search presentation. */
|
|
43
|
-
tabs?: boolean;
|
|
44
|
-
/** Projects toolbar buttons, including back and menu buttons. */
|
|
45
|
-
toolbar?: boolean;
|
|
46
|
-
/** Projects segments. */
|
|
47
|
-
segment?: boolean;
|
|
48
|
-
/** Projects floating action buttons. */
|
|
49
|
-
fab?: boolean;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface NativeUIShellHandle {
|
|
53
|
-
/** Returns the current Web/native projection state. */
|
|
54
|
-
getStatus(): NativeUIShellStatus;
|
|
55
|
-
/** Restores projected controls to the Web until the returned lease is resumed. */
|
|
56
|
-
suspend(): Promise<NativeUIShellSuspension>;
|
|
57
|
-
/** Stops synchronization, restores Web controls and releases native resources. */
|
|
58
|
-
destroy(): Promise<void>;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Logical edge in the reading direction, matching UIVerticalBarEdge and capacitor-foldable. */
|
|
62
|
-
export type VerticalBarEdge = 'leading' | 'trailing' | null;
|
|
63
|
-
|
|
64
|
-
export interface VerticalBarPlacement {
|
|
65
|
-
edge: VerticalBarEdge;
|
|
66
|
-
/** Explicit rail width in CSS pixels; omitted to use the stylesheet's safe-area rules. */
|
|
67
|
-
inset?: number;
|
|
68
|
-
/** Native logical edge reported by the application's device plugin. Null or an unregistered edge uses a Web rail in verticalBarsOnly mode, or the ordinary Native UI Shell layout otherwise. Omission keeps the last supplied value. */
|
|
69
|
-
nativeEdge?: VerticalBarEdge;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface VerticalControlAreaHandle extends NativeUIShellHandle {
|
|
73
|
-
/** Applies the application's chosen placement to both Web and native controls. */
|
|
74
|
-
setPlacement(placement: VerticalBarEdge | VerticalBarPlacement, rtl?: boolean): void;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface NativeUIShellSuspension {
|
|
78
|
-
/** Releases this suspension. Native projection resumes after all active suspensions are released. */
|
|
79
|
-
resume(): Promise<void>;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface Frame {
|
|
83
|
-
x: number;
|
|
84
|
-
y: number;
|
|
85
|
-
width: number;
|
|
86
|
-
height: number;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export interface ShellBadge {
|
|
90
|
-
value: string;
|
|
91
|
-
color: string;
|
|
92
|
-
textColor: string;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface ShellItem extends Frame {
|
|
96
|
-
id: string;
|
|
97
|
-
label: string;
|
|
98
|
-
accessibilityLabel: string;
|
|
99
|
-
disabled: boolean;
|
|
100
|
-
selected: boolean;
|
|
101
|
-
fontSize: number;
|
|
102
|
-
fontWeight: number;
|
|
103
|
-
color: string;
|
|
104
|
-
/** Resolved ion-button fill for vertical projection; clear omits the native glass background. */
|
|
105
|
-
buttonFill?: 'clear' | 'solid' | 'outline';
|
|
106
|
-
backgroundColor?: string;
|
|
107
|
-
borderColor?: string;
|
|
108
|
-
borderWidth?: number;
|
|
109
|
-
badge?: ShellBadge;
|
|
110
|
-
icon?: string;
|
|
111
|
-
iconWidth?: number;
|
|
112
|
-
iconHeight?: number;
|
|
113
|
-
iconPosition?: 'leading' | 'trailing' | 'top';
|
|
114
|
-
imagePadding?: number;
|
|
115
|
-
contentInsetLeading?: number;
|
|
116
|
-
contentInsetTrailing?: number;
|
|
117
|
-
iconTemplate?: boolean;
|
|
118
|
-
visible?: boolean;
|
|
119
|
-
closeIcon?: string;
|
|
120
|
-
closeIconWidth?: number;
|
|
121
|
-
closeIconHeight?: number;
|
|
122
|
-
iconTransition?: number;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export interface ShellControl extends Frame {
|
|
126
|
-
id: string;
|
|
127
|
-
kind: NativeUIShellComponent;
|
|
128
|
-
/** Lets the native host own adaptive placement instead of mirroring the DOM frame. */
|
|
129
|
-
placement?: 'vertical-bars';
|
|
130
|
-
/** Logical Ionic toolbar slot, preserved when projecting to the vertical rail. */
|
|
131
|
-
toolbarSlot?: 'start' | 'end';
|
|
132
|
-
items: ShellItem[];
|
|
133
|
-
dark: boolean;
|
|
134
|
-
rtl: boolean;
|
|
135
|
-
tabBarAnchor?: { x: 0 | 0.5 | 1; y: 0 | 1 };
|
|
136
|
-
search?: ShellSearch;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export interface ShellSearch {
|
|
140
|
-
id: string;
|
|
141
|
-
field: ShellItem;
|
|
142
|
-
trigger: ShellItem;
|
|
143
|
-
closeId: string;
|
|
144
|
-
active: boolean;
|
|
145
|
-
available: boolean;
|
|
146
|
-
focused: boolean;
|
|
147
|
-
value: string;
|
|
148
|
-
placeholder: string;
|
|
149
|
-
disabled: boolean;
|
|
150
|
-
editSequence: number;
|
|
151
|
-
valueVersion: number;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export interface ShellSearchEvent extends ShellActivation {
|
|
155
|
-
valueVersion: number;
|
|
156
|
-
phase: 'input' | 'focus' | 'blur' | 'clear' | 'commit';
|
|
157
|
-
value: string;
|
|
158
|
-
composing: boolean;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export interface ShellSnapshot {
|
|
162
|
-
revision: number;
|
|
163
|
-
transitionDuration?: number;
|
|
164
|
-
viewportWidth: number;
|
|
165
|
-
/** Physical side the native rail is drawn on; the runtime resolves the logical edge through the document direction. */
|
|
166
|
-
verticalBarEdge?: 'left' | 'right';
|
|
167
|
-
/** Visible foreground modal bounds; omitted for the full page. */
|
|
168
|
-
verticalBarFrame?: Frame;
|
|
169
|
-
controls: ShellControl[];
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export interface ShellActivation {
|
|
173
|
-
revision: number;
|
|
174
|
-
id: string;
|
|
175
|
-
sequence: number;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export interface WebViewMetrics {
|
|
179
|
-
/** The WebView's effective top-left corner radius in points, or `0` when unavailable. */
|
|
180
|
-
radius: number;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export interface NativeUIShellPlugin {
|
|
184
|
-
configure(options?: { verticalBarsOnly?: boolean }): Promise<{ supported: boolean }>;
|
|
185
|
-
getWebViewMetrics(): Promise<WebViewMetrics>;
|
|
186
|
-
update(snapshot: ShellSnapshot): Promise<{ revision: number; rejectedSearches?: string[]; rejectedControls?: string[] }>;
|
|
187
|
-
clear(options: { revision: number }): Promise<void>;
|
|
188
|
-
addListener(name: 'activate', listener: (event: ShellActivation) => void): Promise<PluginListenerHandle>;
|
|
189
|
-
addListener(name: 'search', listener: (event: ShellSearchEvent) => void): Promise<PluginListenerHandle>;
|
|
190
|
-
addListener(name: 'webViewMetricsChange', listener: (event: WebViewMetrics) => void): Promise<PluginListenerHandle>;
|
|
191
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { FoldState } from './types';
|
|
2
|
-
|
|
3
|
-
/** Keep posture on ion-app so descendants can follow it through CSS. */
|
|
4
|
-
export function applyFoldStateClasses(root: HTMLElement, fold: FoldState): void {
|
|
5
|
-
for (const state of ['flat', 'half-opened', 'closed']) {
|
|
6
|
-
root.classList.toggle(`ios-theme-fold-${state}`, fold.state === state);
|
|
7
|
-
}
|
|
8
|
-
root.classList.toggle('ios-theme-fold-expanded', fold.state === 'half-opened' || (fold.state === 'flat' && !!fold.hingeBounds));
|
|
9
|
-
}
|