@rdlabo/ionic-theme-ios27 1.2.0-beta.pr231.sha22a35d481cf3 → 1.2.0-beta.pr233.sha647eef34274b
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/native/components/searchable-tabs.d.ts.map +1 -1
- package/dist/native/components/searchable-tabs.js +6 -7
- package/dist/native/definitions.d.ts +4 -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 +57 -27
- package/dist/native/runtime.d.ts.map +1 -1
- package/dist/native/runtime.js +18 -10
- package/dist/native/vertical-bars-web.d.ts.map +1 -1
- package/dist/native/vertical-bars-web.js +2 -0
- 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 +16 -8
- package/docs/iphone-duo.md +57 -44
- package/docs/native-ui-shell.md +5 -3
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSearchController.swift +1 -1
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +27 -2
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalSearch.swift +118 -0
- package/ios/Sources/IonicNativeUIShellPlugin/IonicNativeUIShellPlugin.swift +27 -179
- package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +37 -0
- package/package.json +1 -1
- package/src/native/components/searchable-tabs.ts +8 -11
- package/src/native/definitions.ts +6 -19
- package/src/native/index.ts +60 -29
- package/src/native/runtime.ts +16 -10
- package/src/native/vertical-bars-web.ts +2 -0
- package/src/tab-bar-searchable/index.ts +40 -9
- package/src/vertical-bars.ts +1 -2
package/README.md
CHANGED
|
@@ -125,6 +125,15 @@ Use this markup to preview the inset grouped list look. For the list structure t
|
|
|
125
125
|
|
|
126
126
|
Keep your existing Ionic theme and move tabs and supported toolbar actions into a vertical side area. **Start in Chrome** with one stylesheet, an app class, and `enableVerticalControlArea()`; then connect the layout to iPhone Duo device events for the system rail and hinge posture.
|
|
127
127
|
|
|
128
|
+
Device state is supplied by [`@erkamyaman/capacitor-foldable`](https://github.com/erkamyaman/capacitor-foldable), installed in your app:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npm install @erkamyaman/capacitor-foldable
|
|
132
|
+
npx cap sync
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Use its `getBarPlacement()` / `barPlacementChange` and `getFoldState()` / `foldStateChange` APIs to drive the theme's layout. Device monitoring is not bundled with the theme.
|
|
136
|
+
|
|
128
137
|
Follow [iPhone Duo with your existing theme](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/iphone-duo-with-original-theme) for the browser preview and iOS setup. For shared layout rules and APIs, see [iPhone Duo support](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/iphone-duo). Available in `1.2.0-0` as an experimental feature; APIs and supported behavior may change.
|
|
129
138
|
|
|
130
139
|
### Use only the iOS 27 theme
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchable-tabs.d.ts","sourceRoot":"","sources":["../../../src/native/components/searchable-tabs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAA0B,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAmB/E,eAAO,MAAM,mBAAmB,GAAI,KAAK,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,EAAE,UAAU,MAAM,IAAI;sBA8InF,OAAO;;
|
|
1
|
+
{"version":3,"file":"searchable-tabs.d.ts","sourceRoot":"","sources":["../../../src/native/components/searchable-tabs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAA0B,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAmB/E,eAAO,MAAM,mBAAmB,GAAI,KAAK,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,EAAE,UAAU,MAAM,IAAI;sBA8InF,OAAO;;yBAwBJ,SAAS,EAAE,WAAW,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,GAAG,SAAS,EAAE;yBAvJhE,mBAAmB;;mBA+U9B,mBAAmB,YAAY,MAAM;uBAKjC,mBAAmB,UAAU,OAAO;qBAUtC,WAAW;gBAGhB,MAAM,EAAE;iBASP,gBAAgB;oBAqCb,mBAAmB;;CAoBtC,CAAC"}
|
|
@@ -151,7 +151,11 @@ export const createSearchSupport = (doc, id, schedule) => {
|
|
|
151
151
|
keyboard(visible) {
|
|
152
152
|
keyboardVisible = visible;
|
|
153
153
|
if (!visible) {
|
|
154
|
-
|
|
154
|
+
for (const [binding, state] of states) {
|
|
155
|
+
if (state.keyboardHidden)
|
|
156
|
+
binding.tabBar.classList.remove('tab-bar-hidden');
|
|
157
|
+
state.keyboardHidden = false;
|
|
158
|
+
}
|
|
155
159
|
releaseKeyboardResize();
|
|
156
160
|
}
|
|
157
161
|
schedule();
|
|
@@ -184,10 +188,7 @@ export const createSearchSupport = (doc, id, schedule) => {
|
|
|
184
188
|
};
|
|
185
189
|
for (const binding of bindings) {
|
|
186
190
|
const candidate = candidates.find((c) => c.element === binding.tabBar);
|
|
187
|
-
if (!candidate ||
|
|
188
|
-
candidate.control.search ||
|
|
189
|
-
candidate.control.placement === 'vertical-bars' ||
|
|
190
|
-
binding.tabBar.getAttribute('slot') !== 'bottom')
|
|
191
|
+
if (!candidate || candidate.control.search || binding.tabBar.getAttribute('slot') !== 'bottom')
|
|
191
192
|
continue;
|
|
192
193
|
const fab = binding.trigger.parentElement;
|
|
193
194
|
if (!fab?.matches('ion-fab[slot="fixed"]') ||
|
|
@@ -415,8 +416,6 @@ export const createSearchSupport = (doc, id, schedule) => {
|
|
|
415
416
|
const state = states.get(binding);
|
|
416
417
|
if (state?.keyboardHidden && keyboardVisible)
|
|
417
418
|
binding.tabBar.classList.add('tab-bar-hidden');
|
|
418
|
-
if (state)
|
|
419
|
-
state.keyboardHidden = false;
|
|
420
419
|
if (state?.focused) {
|
|
421
420
|
state.focused = false;
|
|
422
421
|
state.input.dispatchEvent(new FocusEvent('blur'));
|
|
@@ -26,12 +26,8 @@ export interface NativeUIShellHandle {
|
|
|
26
26
|
export type VerticalBarEdge = 'leading' | 'trailing' | null;
|
|
27
27
|
export interface VerticalBarPlacement {
|
|
28
28
|
edge: VerticalBarEdge;
|
|
29
|
-
inset
|
|
30
|
-
|
|
31
|
-
export declare enum HingeStatus {
|
|
32
|
-
Closed = "closed",
|
|
33
|
-
PartiallyOpen = "partiallyOpen",
|
|
34
|
-
FullyOpen = "fullyOpen"
|
|
29
|
+
inset?: number;
|
|
30
|
+
nativeEdge?: VerticalBarEdge;
|
|
35
31
|
}
|
|
36
32
|
export interface VerticalControlAreaHandle extends NativeUIShellHandle {
|
|
37
33
|
setPlacement(placement: VerticalBarEdge | VerticalBarPlacement, rtl?: boolean): void;
|
|
@@ -127,20 +123,13 @@ export interface ShellActivation {
|
|
|
127
123
|
export interface WebViewMetrics {
|
|
128
124
|
radius: number;
|
|
129
125
|
}
|
|
130
|
-
export interface DeviceLayout {
|
|
131
|
-
placement: VerticalBarPlacement;
|
|
132
|
-
hingeStatus: HingeStatus | null;
|
|
133
|
-
webViewMetrics: WebViewMetrics;
|
|
134
|
-
}
|
|
135
126
|
export interface NativeUIShellPlugin {
|
|
136
127
|
configure(options?: {
|
|
137
128
|
verticalBarsOnly?: boolean;
|
|
138
129
|
}): Promise<{
|
|
139
130
|
supported: boolean;
|
|
140
131
|
}>;
|
|
141
|
-
|
|
142
|
-
startDeviceLayoutMonitoring(): Promise<void>;
|
|
143
|
-
stopDeviceLayoutMonitoring(): Promise<void>;
|
|
132
|
+
getWebViewMetrics(): Promise<WebViewMetrics>;
|
|
144
133
|
update(snapshot: ShellSnapshot): Promise<{
|
|
145
134
|
revision: number;
|
|
146
135
|
rejectedSearches?: string[];
|
|
@@ -151,6 +140,6 @@ export interface NativeUIShellPlugin {
|
|
|
151
140
|
}): Promise<void>;
|
|
152
141
|
addListener(name: 'activate', listener: (event: ShellActivation) => void): Promise<PluginListenerHandle>;
|
|
153
142
|
addListener(name: 'search', listener: (event: ShellSearchEvent) => void): Promise<PluginListenerHandle>;
|
|
154
|
-
addListener(name: '
|
|
143
|
+
addListener(name: 'webViewMetricsChange', listener: (event: WebViewMetrics) => void): Promise<PluginListenerHandle>;
|
|
155
144
|
}
|
|
156
145
|
//# sourceMappingURL=definitions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/native/definitions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IAEnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IAEjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IAEpC,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAElC,SAAS,IAAI,mBAAmB,CAAC;IAEjC,OAAO,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAE5C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAGD,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,UAAU,GAAG,IAAI,CAAC;AAE5D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,eAAe,CAAC;IAEtB,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/native/definitions.ts"],"names":[],"mappings":"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"}
|
package/dist/native/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { NativeUIShellHandle, NativeUIShellOptions, NativeUIShellPlugin, VerticalBarEdge, VerticalBarPlacement, VerticalControlAreaHandle, WebViewMetrics } from './definitions';
|
|
2
|
-
export type { NativeUIShellComponent, NativeUIShellControls, NativeUIShellHandle, NativeUIShellOptions, NativeUIShellStatus, NativeUIShellSuspension,
|
|
3
|
-
export { HingeStatus } from './definitions';
|
|
2
|
+
export type { NativeUIShellComponent, NativeUIShellControls, NativeUIShellHandle, NativeUIShellOptions, NativeUIShellStatus, NativeUIShellSuspension, VerticalBarEdge, VerticalBarPlacement, VerticalControlAreaHandle, WebViewMetrics, } from './definitions';
|
|
4
3
|
export declare const IonicNativeUIShell: NativeUIShellPlugin;
|
|
5
4
|
export declare const configureNativeTransition: () => Promise<WebViewMetrics>;
|
|
6
5
|
export declare const setVerticalControlAreaPlacement: (placement: VerticalBarEdge | VerticalBarPlacement, rtl?: boolean) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/native/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,yBAAyB,EACzB,cAAc,EACf,MAAM,eAAe,CAAC;AAKvB,YAAY,EACV,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,uBAAuB,EACvB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/native/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,yBAAyB,EACzB,cAAc,EACf,MAAM,eAAe,CAAC;AAKvB,YAAY,EACV,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACpB,yBAAyB,EACzB,cAAc,GACf,MAAM,eAAe,CAAC;AAGvB,eAAO,MAAM,kBAAkB,qBAAS,CAAC;AAoEzC,eAAO,MAAM,yBAAyB,QAAa,OAAO,CAAC,cAAc,CAIxE,CAAC;AA6CF,eAAO,MAAM,+BAA+B,GAAI,WAAW,eAAe,GAAG,oBAAoB,EAAE,MAAM,OAAO,KAAG,IAclH,CAAC;AAGF,eAAO,MAAM,yBAAyB,QAAa,OAAO,CAAC,yBAAyB,CAQnF,CAAC;AAGF,eAAO,MAAM,mBAAmB,GAAI,UAAS,oBAAyB,KAAG,OAAO,CAAC,mBAAmB,CA4FnG,CAAC"}
|
package/dist/native/index.js
CHANGED
|
@@ -4,7 +4,6 @@ import { createRuntime } from './runtime';
|
|
|
4
4
|
import { createVerticalBarsWebProjection } from './vertical-bars-web';
|
|
5
5
|
import { prehideVerticalBarsToolbarSources } from './prehide';
|
|
6
6
|
import { observeVerticalBarsModals } from './shared/modal';
|
|
7
|
-
export { HingeStatus } from './definitions';
|
|
8
7
|
const plugin = registerPlugin('IonicNativeUIShell');
|
|
9
8
|
export const IonicNativeUIShell = plugin;
|
|
10
9
|
let active;
|
|
@@ -62,25 +61,59 @@ const manage = (handle, lifecycle = {}) => {
|
|
|
62
61
|
};
|
|
63
62
|
};
|
|
64
63
|
export const configureNativeTransition = async () => {
|
|
65
|
-
const metrics = typeof document !== 'undefined' && Capacitor.getPlatform() === 'ios' ?
|
|
64
|
+
const metrics = typeof document !== 'undefined' && Capacitor.getPlatform() === 'ios' ? await plugin.getWebViewMetrics() : { radius: 0 };
|
|
66
65
|
setConfig({ radius: metrics.radius });
|
|
67
66
|
return metrics;
|
|
68
67
|
};
|
|
69
68
|
const physicalVerticalBarEdge = (edge, rtl) => (edge === 'leading') !== rtl ? 'left' : 'right';
|
|
70
69
|
const elementRtl = (element) => element.closest('[dir]')?.getAttribute('dir') === 'rtl';
|
|
70
|
+
const nativePlacements = new WeakMap();
|
|
71
|
+
const horizontalFallbackAttribute = 'data-native-ui-shell-vertical-bars-suspended';
|
|
72
|
+
const observeNativeVerticalBarsLayout = (doc) => {
|
|
73
|
+
const reconcile = () => {
|
|
74
|
+
const app = doc.querySelector('ion-app');
|
|
75
|
+
if (!app)
|
|
76
|
+
return;
|
|
77
|
+
const requested = app.classList.contains('ios-theme-vertical-bars') || app.hasAttribute(horizontalFallbackAttribute);
|
|
78
|
+
const suspended = requested && nativePlacements.get(app)?.edge == null;
|
|
79
|
+
if (app.hasAttribute(horizontalFallbackAttribute) !== suspended)
|
|
80
|
+
app.toggleAttribute(horizontalFallbackAttribute, suspended);
|
|
81
|
+
if (app.classList.contains('ios-theme-vertical-bars') !== (requested && !suspended)) {
|
|
82
|
+
app.classList.toggle('ios-theme-vertical-bars', requested && !suspended);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const observer = new MutationObserver(reconcile);
|
|
86
|
+
observer.observe(doc.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['class'] });
|
|
87
|
+
doc.defaultView?.addEventListener('nativeUIShellRefresh', reconcile);
|
|
88
|
+
reconcile();
|
|
89
|
+
return () => {
|
|
90
|
+
observer.disconnect();
|
|
91
|
+
doc.defaultView?.removeEventListener('nativeUIShellRefresh', reconcile);
|
|
92
|
+
const app = doc.querySelector(`ion-app[${horizontalFallbackAttribute}]`);
|
|
93
|
+
if (app) {
|
|
94
|
+
app.removeAttribute(horizontalFallbackAttribute);
|
|
95
|
+
app.classList.add('ios-theme-vertical-bars');
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
};
|
|
71
99
|
export const setVerticalControlAreaPlacement = (placement, rtl) => {
|
|
72
100
|
if (typeof document === 'undefined')
|
|
73
101
|
return;
|
|
74
102
|
const app = document.querySelector('ion-app');
|
|
75
103
|
if (!app)
|
|
76
104
|
throw new Error('Vertical Control Area requires ion-app');
|
|
77
|
-
const { edge, inset } = placement && typeof placement === 'object' ? placement : { edge: placement, inset: 0 };
|
|
105
|
+
const { edge, inset = 0 } = placement && typeof placement === 'object' ? placement : { edge: placement, inset: 0 };
|
|
106
|
+
if (placement && typeof placement === 'object' && placement.nativeEdge !== undefined) {
|
|
107
|
+
nativePlacements.set(app, { edge: placement.nativeEdge, rtl });
|
|
108
|
+
}
|
|
109
|
+
app.removeAttribute(horizontalFallbackAttribute);
|
|
78
110
|
app.classList.toggle('ios-theme-vertical-bars', edge !== null);
|
|
79
111
|
app.classList.toggle('ios-theme-vertical-bars-left', edge !== null && physicalVerticalBarEdge(edge, rtl ?? elementRtl(app)) === 'left');
|
|
80
112
|
if (edge && Number.isFinite(inset) && inset > 0)
|
|
81
113
|
app.style.setProperty('--ios-theme-vertical-bars-native-inset', `${inset}px`);
|
|
82
114
|
else
|
|
83
115
|
app.style.removeProperty('--ios-theme-vertical-bars-native-inset');
|
|
116
|
+
document.defaultView?.dispatchEvent(new Event('nativeUIShellRefresh'));
|
|
84
117
|
};
|
|
85
118
|
export const enableVerticalControlArea = async () => {
|
|
86
119
|
const handle = await enableNativeUIShell({ controls: { tabs: true, toolbar: true }, verticalBarsOnly: true });
|
|
@@ -138,40 +171,38 @@ export const enableNativeUIShell = (options = {}) => {
|
|
|
138
171
|
if (Capacitor.getPlatform() !== 'ios')
|
|
139
172
|
return fallback('Requires Capacitor iOS');
|
|
140
173
|
let runtime;
|
|
141
|
-
let
|
|
142
|
-
let
|
|
174
|
+
let metricsListener;
|
|
175
|
+
let stopVerticalBarsLayout;
|
|
143
176
|
try {
|
|
144
|
-
if (!options.verticalBarsOnly)
|
|
145
|
-
await configureNativeTransition().catch(() => undefined);
|
|
146
177
|
const capabilities = await plugin.configure({ verticalBarsOnly: options.verticalBarsOnly === true });
|
|
147
178
|
if (!capabilities.supported)
|
|
148
179
|
return fallback('Requires iOS 26 or later');
|
|
149
|
-
|
|
180
|
+
if (!options.verticalBarsOnly)
|
|
181
|
+
stopVerticalBarsLayout = observeNativeVerticalBarsLayout(document);
|
|
150
182
|
const nativeVerticalBars = () => {
|
|
151
|
-
const
|
|
152
|
-
if (!
|
|
183
|
+
const app = document.querySelector('ion-app.ios-theme-vertical-bars');
|
|
184
|
+
if (!app)
|
|
153
185
|
return false;
|
|
154
|
-
const
|
|
155
|
-
|
|
186
|
+
const reported = nativePlacements.get(app);
|
|
187
|
+
if (!reported?.edge)
|
|
188
|
+
return false;
|
|
189
|
+
const physicalEdge = app.classList.contains('ios-theme-vertical-bars-left') ? 'left' : 'right';
|
|
190
|
+
return physicalEdge === physicalVerticalBarEdge(reported.edge, reported.rtl ?? elementRtl(app));
|
|
156
191
|
};
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
document.defaultView?.dispatchEvent(new Event('nativeUIShellRefresh'));
|
|
164
|
-
});
|
|
165
|
-
nativeEdge = (await plugin.getDeviceLayout()).placement.edge;
|
|
192
|
+
if (!options.verticalBarsOnly) {
|
|
193
|
+
metricsListener = await plugin.addListener('webViewMetricsChange', (metrics) => {
|
|
194
|
+
setConfig({ radius: metrics.radius });
|
|
195
|
+
});
|
|
196
|
+
await configureNativeTransition().catch(() => undefined);
|
|
197
|
+
}
|
|
166
198
|
const native = await createRuntime(document, plugin, options, nativeVerticalBars, options.verticalBarsOnly === true);
|
|
167
199
|
runtime = combine(native, createVerticalBarsWebProjection(document, options, () => !nativeVerticalBars() || native.getStatus().state === 'stopped'));
|
|
168
200
|
return manage(runtime, {
|
|
169
201
|
suspend: () => prehide?.suspend(),
|
|
170
202
|
release,
|
|
171
203
|
destroy: async () => {
|
|
172
|
-
await
|
|
173
|
-
|
|
174
|
-
await plugin.stopDeviceLayoutMonitoring().catch(() => { });
|
|
204
|
+
await metricsListener?.remove().catch(() => { });
|
|
205
|
+
stopVerticalBarsLayout?.();
|
|
175
206
|
prehide?.stop();
|
|
176
207
|
stopModals();
|
|
177
208
|
},
|
|
@@ -179,9 +210,8 @@ export const enableNativeUIShell = (options = {}) => {
|
|
|
179
210
|
}
|
|
180
211
|
catch (error) {
|
|
181
212
|
await runtime?.destroy();
|
|
182
|
-
await
|
|
183
|
-
|
|
184
|
-
await plugin.stopDeviceLayoutMonitoring().catch(() => { });
|
|
213
|
+
await metricsListener?.remove().catch(() => { });
|
|
214
|
+
stopVerticalBarsLayout?.();
|
|
185
215
|
return fallback(error instanceof Error ? error.message : String(error));
|
|
186
216
|
}
|
|
187
217
|
})());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/native/runtime.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EAEpB,MAAM,eAAe,CAAC;AA6BvB,eAAO,MAAM,aAAa,GACxB,KAAK,QAAQ,EACb,QAAQ,mBAAmB,EAC3B,UAAS,oBAAyB,EAClC,qBAAoB,MAAM,OAAoB,EAC9C,0BAAwB,KACvB,OAAO,CAAC,mBAAmB,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/native/runtime.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EAEpB,MAAM,eAAe,CAAC;AA6BvB,eAAO,MAAM,aAAa,GACxB,KAAK,QAAQ,EACb,QAAQ,mBAAmB,EAC3B,UAAS,oBAAyB,EAClC,qBAAoB,MAAM,OAAoB,EAC9C,0BAAwB,KACvB,OAAO,CAAC,mBAAmB,CAytB7B,CAAC"}
|
package/dist/native/runtime.js
CHANGED
|
@@ -378,13 +378,15 @@ export const createRuntime = async (doc, plugin, options = {}, nativeVerticalBar
|
|
|
378
378
|
verticalBarsMembers.add(element);
|
|
379
379
|
}
|
|
380
380
|
for (const element of accepted.flatMap(candidateSources)) {
|
|
381
|
+
const newlyProjected = !sources.has(element) || !element.hasAttribute(marker);
|
|
381
382
|
if (!sources.has(element)) {
|
|
382
383
|
sources.set(element, element.getAttribute('aria-hidden'));
|
|
383
384
|
crossfade.play(element, true, handoffInstant || isVerticalBarsSource(element));
|
|
384
|
-
element.setAttribute(marker, '');
|
|
385
|
-
element.setAttribute('aria-hidden', 'true');
|
|
386
|
-
element.dispatchEvent(new CustomEvent('nativeUIShellChange'));
|
|
387
385
|
}
|
|
386
|
+
element.setAttribute(marker, '');
|
|
387
|
+
element.setAttribute('aria-hidden', 'true');
|
|
388
|
+
if (newlyProjected)
|
|
389
|
+
element.dispatchEvent(new CustomEvent('nativeUIShellChange'));
|
|
388
390
|
}
|
|
389
391
|
const received = pendingActivations;
|
|
390
392
|
pendingActivations = [];
|
|
@@ -567,25 +569,31 @@ export const createRuntime = async (doc, plugin, options = {}, nativeVerticalBar
|
|
|
567
569
|
'focusout',
|
|
568
570
|
])
|
|
569
571
|
on(doc, name);
|
|
572
|
+
const refreshLayout = () => {
|
|
573
|
+
rejected = new WeakMap();
|
|
574
|
+
forceRefresh = true;
|
|
575
|
+
schedule();
|
|
576
|
+
};
|
|
570
577
|
on(doc, 'visibilitychange', () => {
|
|
571
|
-
if (doc.hidden)
|
|
578
|
+
if (doc.hidden) {
|
|
572
579
|
getNativeSearchBindings(doc).forEach((binding) => search.retire(binding));
|
|
573
|
-
|
|
580
|
+
schedule();
|
|
581
|
+
}
|
|
582
|
+
else
|
|
583
|
+
refreshLayout();
|
|
574
584
|
});
|
|
575
|
-
on(win, 'resize');
|
|
585
|
+
on(win, 'resize', refreshLayout);
|
|
576
586
|
on(win, 'keyboardWillShow', () => search.keyboard(true));
|
|
577
587
|
on(win, 'keyboardWillHide', () => search.keyboard(false));
|
|
578
588
|
on(win, 'scroll');
|
|
579
589
|
on(win.matchMedia('(prefers-color-scheme: dark)'), 'change');
|
|
580
590
|
on(win, 'nativeUIShellRefresh', (event) => {
|
|
581
|
-
rejected = new WeakMap();
|
|
582
591
|
if (event.retireSearch)
|
|
583
592
|
getNativeSearchBindings(doc).forEach((binding) => search.retire(binding));
|
|
584
|
-
|
|
585
|
-
schedule();
|
|
593
|
+
refreshLayout();
|
|
586
594
|
});
|
|
587
595
|
if (win.visualViewport) {
|
|
588
|
-
on(win.visualViewport, 'resize');
|
|
596
|
+
on(win.visualViewport, 'resize', refreshLayout);
|
|
589
597
|
on(win.visualViewport, 'scroll');
|
|
590
598
|
}
|
|
591
599
|
const handle = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vertical-bars-web.d.ts","sourceRoot":"","sources":["../../src/native/vertical-bars-web.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAuB,MAAM,eAAe,CAAC;AAqCpG,eAAO,MAAM,+BAA+B,GAC1C,KAAK,QAAQ,EACb,SAAS,oBAAoB,EAC7B,UAAS,MAAM,OAAoB,KAClC,
|
|
1
|
+
{"version":3,"file":"vertical-bars-web.d.ts","sourceRoot":"","sources":["../../src/native/vertical-bars-web.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAuB,MAAM,eAAe,CAAC;AAqCpG,eAAO,MAAM,+BAA+B,GAC1C,KAAK,QAAQ,EACb,SAAS,oBAAoB,EAC7B,UAAS,MAAM,OAAoB,KAClC,mBAoVF,CAAC"}
|
|
@@ -260,6 +260,8 @@ export const createVerticalBarsWebProjection = (doc, options, enabled = () => tr
|
|
|
260
260
|
const schedule = () => {
|
|
261
261
|
if (stopped)
|
|
262
262
|
return Promise.resolve();
|
|
263
|
+
if (!enabled())
|
|
264
|
+
restore();
|
|
263
265
|
const done = new Promise((resolve) => waiters.push(resolve));
|
|
264
266
|
if (!frame)
|
|
265
267
|
frame = win.requestAnimationFrame(update);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tab-bar-searchable/index.ts"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,wBAAwB,EAE9B,MAAM,2BAA2B,CAAC;AAiBnC,OAAO,EACL,oBAAoB,EACpB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,2BAA2B,CAAC;AAqBnC,eAAO,MAAM,sBAAsB,GACjC,WAAW,WAAW,EACtB,cAAc,WAAW,EACzB,WAAW,WAAW,KACrB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tab-bar-searchable/index.ts"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,wBAAwB,EAE9B,MAAM,2BAA2B,CAAC;AAiBnC,OAAO,EACL,oBAAoB,EACpB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,2BAA2B,CAAC;AAqBnC,eAAO,MAAM,sBAAsB,GACjC,WAAW,WAAW,EACtB,cAAc,WAAW,EACzB,WAAW,WAAW,KACrB,wBA+EF,CAAC"}
|
|
@@ -10,11 +10,14 @@ export const attachTabBarSearchable = (ionTabBar, ionFabButton, ionFooter) => {
|
|
|
10
10
|
}
|
|
11
11
|
ionFooter.style.pointerEvents = 'none';
|
|
12
12
|
ionFooter.style.opacity = '0';
|
|
13
|
+
ionFooter.querySelector('ion-buttons[slot=start]')?.classList.add('ios-theme-horizontal-only');
|
|
13
14
|
const nativeSearch = registerNativeSearch(ionTabBar, ionFabButton, ionFooter);
|
|
14
15
|
let searchableEventCache;
|
|
15
16
|
let resumeNative;
|
|
16
17
|
let nativeEntry = false;
|
|
17
18
|
let nativeRequest = 0;
|
|
19
|
+
let verticalSearch = false;
|
|
20
|
+
let tabVisibilityObserver;
|
|
18
21
|
return async (event, type) => {
|
|
19
22
|
const selector = type === TabBarSearchableType.Enter ? 'ion-fab-button' : 'ion-buttons[slot=start] ion-button';
|
|
20
23
|
if (!event.target?.closest(selector))
|
|
@@ -37,11 +40,28 @@ export const attachTabBarSearchable = (ionTabBar, ionFabButton, ionFooter) => {
|
|
|
37
40
|
return;
|
|
38
41
|
}
|
|
39
42
|
if (type === TabBarSearchableType.Enter) {
|
|
40
|
-
|
|
43
|
+
verticalSearch = !!ionTabBar.closest('ion-app.ios-theme-vertical-bars');
|
|
44
|
+
if (verticalSearch) {
|
|
45
|
+
const keepTabsVisible = () => {
|
|
46
|
+
if (ionTabBar.classList.contains('tab-bar-hidden') && ionTabBar.closest('ion-app.ios-theme-vertical-bars')) {
|
|
47
|
+
ionTabBar.classList.remove('tab-bar-hidden');
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
tabVisibilityObserver?.disconnect();
|
|
51
|
+
tabVisibilityObserver = new MutationObserver(keepTabsVisible);
|
|
52
|
+
tabVisibilityObserver.observe(ionTabBar, { attributes: true, attributeFilter: ['class'] });
|
|
53
|
+
keepTabsVisible();
|
|
54
|
+
}
|
|
55
|
+
resumeNative ?? (resumeNative = await suspendNativeUIShell([
|
|
56
|
+
...(verticalSearch ? [] : [ionTabBar]),
|
|
57
|
+
ionFooter,
|
|
58
|
+
ionFabButton.closest('ion-fab') ?? ionFabButton,
|
|
59
|
+
]));
|
|
41
60
|
try {
|
|
42
|
-
searchableEventCache = await enterEvent(event, ionTabBar, ionFabButton, ionFooter);
|
|
61
|
+
searchableEventCache = await enterEvent(event, ionTabBar, ionFabButton, ionFooter, verticalSearch);
|
|
43
62
|
}
|
|
44
63
|
catch (error) {
|
|
64
|
+
tabVisibilityObserver?.disconnect();
|
|
45
65
|
resumeNative();
|
|
46
66
|
resumeNative = undefined;
|
|
47
67
|
throw error;
|
|
@@ -49,10 +69,11 @@ export const attachTabBarSearchable = (ionTabBar, ionFabButton, ionFooter) => {
|
|
|
49
69
|
}
|
|
50
70
|
else if (searchableEventCache !== undefined) {
|
|
51
71
|
try {
|
|
52
|
-
await leaveEvent(event, searchableEventCache, ionTabBar, ionFabButton, ionFooter);
|
|
72
|
+
await leaveEvent(event, searchableEventCache, ionTabBar, ionFabButton, ionFooter, verticalSearch);
|
|
53
73
|
searchableEventCache = undefined;
|
|
54
74
|
}
|
|
55
75
|
finally {
|
|
76
|
+
tabVisibilityObserver?.disconnect();
|
|
56
77
|
resumeNative?.();
|
|
57
78
|
resumeNative = undefined;
|
|
58
79
|
}
|
|
@@ -62,7 +83,7 @@ export const attachTabBarSearchable = (ionTabBar, ionFabButton, ionFooter) => {
|
|
|
62
83
|
}
|
|
63
84
|
};
|
|
64
85
|
};
|
|
65
|
-
const enterEvent = async (event, ionTabBar, ionFabButton, ionFooter) => {
|
|
86
|
+
const enterEvent = async (event, ionTabBar, ionFabButton, ionFooter, verticalSearch) => {
|
|
66
87
|
if (!event.target?.closest('ion-fab-button')) {
|
|
67
88
|
throw throwErrorByFailedClickElement('ion-fab-button');
|
|
68
89
|
}
|
|
@@ -71,10 +92,13 @@ const enterEvent = async (event, ionTabBar, ionFabButton, ionFooter) => {
|
|
|
71
92
|
const colorSelected = references.selectedTabButton
|
|
72
93
|
? getComputedStyle(references.selectedTabButton).getPropertyValue('--color-selected').trim()
|
|
73
94
|
: '';
|
|
74
|
-
|
|
95
|
+
if (verticalSearch)
|
|
96
|
+
references.closeButtonIcon.setAttribute('name', 'close');
|
|
97
|
+
const tabAnimations = verticalSearch
|
|
98
|
+
? []
|
|
99
|
+
: [createTabBarAnimation(ionTabBar, references, sizes), createEffectAnimation(references, sizes)];
|
|
75
100
|
const searchContainerAnimation = createSearchContainerAnimation(references, sizes);
|
|
76
101
|
const closeButtonsAnimation = createCloseButtonsAnimation(references);
|
|
77
|
-
const tabBarAnimation = createTabBarAnimation(ionTabBar, references, sizes);
|
|
78
102
|
const fabButtonAnimation = createFabButtonAnimation(ionFabButton);
|
|
79
103
|
await createAnimation()
|
|
80
104
|
.delay(ANIMATION_DELAY_BASE)
|
|
@@ -83,22 +107,26 @@ const enterEvent = async (event, ionTabBar, ionFabButton, ionFooter) => {
|
|
|
83
107
|
.addElement(ionFooter)
|
|
84
108
|
.afterAddWrite(() => (ionFooter.style.pointerEvents = 'auto'))
|
|
85
109
|
.fromTo('opacity', '0.8', '1')
|
|
86
|
-
.addAnimation([
|
|
110
|
+
.addAnimation([...tabAnimations, fabButtonAnimation, searchContainerAnimation, closeButtonsAnimation])
|
|
87
111
|
.play();
|
|
88
112
|
return {
|
|
89
113
|
elementSizes: sizes,
|
|
90
114
|
colorSelected,
|
|
91
115
|
};
|
|
92
116
|
};
|
|
93
|
-
const leaveEvent = async (event, searchableEventCache, ionTabBar, ionFabButton, ionFooter) => {
|
|
117
|
+
const leaveEvent = async (event, searchableEventCache, ionTabBar, ionFabButton, ionFooter, verticalSearch) => {
|
|
94
118
|
if (!event.target?.closest('ion-buttons[slot=start] ion-button')) {
|
|
95
119
|
throw throwErrorByFailedClickElement('ion-buttons[slot=start] ion-button');
|
|
96
120
|
}
|
|
97
121
|
const references = getElementReferences(ionTabBar, ionFooter);
|
|
98
|
-
const
|
|
122
|
+
const tabAnimations = verticalSearch
|
|
123
|
+
? []
|
|
124
|
+
: [
|
|
125
|
+
createReverseTabBarAnimation(ionTabBar, references, searchableEventCache.elementSizes),
|
|
126
|
+
createReverseEffectAnimation(references, searchableEventCache.elementSizes, searchableEventCache.colorSelected),
|
|
127
|
+
];
|
|
99
128
|
const searchContainerAnimation = createReverseSearchContainerAnimation(references, searchableEventCache.elementSizes);
|
|
100
129
|
const closeButtonsAnimation = createReverseCloseButtonsAnimation(references);
|
|
101
|
-
const tabBarAnimation = createReverseTabBarAnimation(ionTabBar, references, searchableEventCache.elementSizes);
|
|
102
130
|
const fabButtonAnimation = createReverseFabButtonAnimation(ionFabButton, searchableEventCache.elementSizes);
|
|
103
131
|
await createAnimation()
|
|
104
132
|
.delay(ANIMATION_DELAY_BASE)
|
|
@@ -107,6 +135,6 @@ const leaveEvent = async (event, searchableEventCache, ionTabBar, ionFabButton,
|
|
|
107
135
|
.addElement(ionFooter)
|
|
108
136
|
.afterAddWrite(() => (ionFooter.style.pointerEvents = 'none'))
|
|
109
137
|
.fromTo('opacity', '1', '0')
|
|
110
|
-
.addAnimation([
|
|
138
|
+
.addAnimation([...tabAnimations, fabButtonAnimation, searchContainerAnimation, closeButtonsAnimation])
|
|
111
139
|
.play();
|
|
112
140
|
};
|
package/dist/vertical-bars.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
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';
|
|
5
4
|
//# sourceMappingURL=vertical-bars.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vertical-bars.d.ts","sourceRoot":"","sources":["../src/vertical-bars.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC1G,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"vertical-bars.d.ts","sourceRoot":"","sources":["../src/vertical-bars.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC1G,OAAO,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AAC9E,YAAY,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/vertical-bars.js
CHANGED
|
@@ -122,23 +122,31 @@ When the application owner is disposed, call `await rail.destroy()` to restore t
|
|
|
122
122
|
|
|
123
123
|
## Connect an iPhone Duo
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
Install [`@erkamyaman/capacitor-foldable`](https://github.com/erkamyaman/capacitor-foldable) for device state:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm install @erkamyaman/capacitor-foldable
|
|
129
|
+
npx cap sync ios
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Use Capacitor 8.5 or later and build with Xcode 27.1 or newer for actual rail placement and hinge posture on iOS 27.1. Native UI Shell uses Swift Package Manager; existing CocoaPods apps can follow [Native UI Shell setup](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/native-ui-shell#enable-the-shell). Keep this package's `vertical-bars.css`; the device plugin's `ionic-tabs.css` is not needed with our rail projection.
|
|
126
133
|
|
|
127
134
|
Replace the browser-only startup above with this after `ion-app` is mounted:
|
|
128
135
|
|
|
129
136
|
```ts
|
|
130
137
|
import { Capacitor, type PluginListenerHandle } from '@capacitor/core';
|
|
131
|
-
import { enableVerticalControlArea
|
|
138
|
+
import { enableVerticalControlArea } from '@rdlabo/ionic-theme-ios27/vertical-bars';
|
|
139
|
+
import { Foldable } from '@erkamyaman/capacitor-foldable';
|
|
132
140
|
|
|
133
141
|
const rail = await enableVerticalControlArea();
|
|
134
142
|
let layoutListener: PluginListenerHandle | undefined;
|
|
135
143
|
|
|
136
144
|
if (Capacitor.getPlatform() === 'ios') {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
rail.setPlacement(placement),
|
|
145
|
+
layoutListener = await Foldable.addListener('barPlacementChange', ({ verticalBarEdge, inset }) =>
|
|
146
|
+
rail.setPlacement({ edge: verticalBarEdge, nativeEdge: verticalBarEdge, inset }),
|
|
140
147
|
);
|
|
141
|
-
|
|
148
|
+
const { verticalBarEdge, inset } = await Foldable.getBarPlacement();
|
|
149
|
+
rail.setPlacement({ edge: verticalBarEdge, nativeEdge: verticalBarEdge, inset });
|
|
142
150
|
}
|
|
143
151
|
|
|
144
152
|
// Call when the application owner is disposed.
|
|
@@ -148,13 +156,13 @@ const stopVerticalArea = async () => {
|
|
|
148
156
|
};
|
|
149
157
|
```
|
|
150
158
|
|
|
151
|
-
`setPlacement()`
|
|
159
|
+
`setPlacement()` resolves the logical edge through the document direction. Pass Foldable’s measured `inset` with the reported edge so the theme reserves the actual bar width and follows width changes. An inset of `0` clears the explicit width; manually requested rails then use the theme’s CSS safe-area rules. A `null` edge restores the ordinary layout. Devices without a reported rail return `null`, so this example restores the ordinary layout there. On iOS 27.1 or later, Foldable can infer Duo bar placement from safe-area insets when the app is built with an older SDK. To deliberately request a rail when the plugin reports no edge, have your application choose a fixed edge with `rail.setPlacement('trailing')` instead of applying that null placement. This simulates the layout; it does not provide a real system rail or hinge measurements.
|
|
152
160
|
|
|
153
161
|
On supported iOS, controls in the rail use the system SwiftUI appearance; your custom Web styling still applies to ordinary content and horizontal controls. Web and Android use Web clones.
|
|
154
162
|
|
|
155
163
|
## Use hinge posture without projecting controls
|
|
156
164
|
|
|
157
|
-
If your existing theme needs only a posture-driven split pane or a layout switch, do not start a projection runtime or add `.ios-theme-vertical-bars`. Use `
|
|
165
|
+
If your existing theme needs only a posture-driven split pane or a layout switch, do not start a projection runtime or add `.ios-theme-vertical-bars`. Use `Foldable.getFoldState()` and `foldStateChange` directly, removing the listener when finished. There is no separate start/stop monitoring call.
|
|
158
166
|
|
|
159
167
|
See [Read the device layout](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/iphone-duo#read-the-device-layout) for the subscription example, null values, and monitoring lifetime. See [Adapt the split pane](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/iphone-duo#adapt-the-split-pane) for the opt-in width rules and half-open state.
|
|
160
168
|
|