@webview-native-ui/engine 1.0.1 → 1.0.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.
- package/README.md +14 -0
- package/WebViewNativeUIControls.podspec +17 -0
- package/dist/controls/activation.d.ts +15 -0
- package/dist/controls/activation.js +370 -0
- package/dist/controls/components/index.d.ts +16 -0
- package/dist/controls/components/index.js +38 -0
- package/dist/controls/components/ion-back-button.d.ts +4 -0
- package/dist/controls/components/ion-back-button.js +16 -0
- package/dist/controls/components/ion-button.d.ts +4 -0
- package/dist/controls/components/ion-button.js +26 -0
- package/dist/controls/components/ion-buttons.d.ts +5 -0
- package/dist/controls/components/ion-buttons.js +34 -0
- package/dist/controls/components/ion-fab.d.ts +4 -0
- package/dist/controls/components/ion-fab.js +164 -0
- package/dist/controls/components/ion-menu-button.d.ts +7 -0
- package/dist/controls/components/ion-menu-button.js +41 -0
- package/dist/controls/components/ion-segment.d.ts +5 -0
- package/dist/controls/components/ion-segment.js +24 -0
- package/dist/controls/components/ion-tab-bar.d.ts +5 -0
- package/dist/controls/components/ion-tab-bar.js +49 -0
- package/dist/controls/components/searchable-tabs.d.ts +20 -0
- package/dist/controls/components/searchable-tabs.js +525 -0
- package/dist/controls/definitions.d.ts +360 -0
- package/dist/controls/definitions.js +1 -0
- package/dist/controls/extensions.d.ts +44 -0
- package/dist/controls/extensions.js +164 -0
- package/dist/controls/foldable/fold-state.d.ts +4 -0
- package/dist/controls/foldable/fold-state.js +8 -0
- package/dist/controls/foldable/types.d.ts +9 -0
- package/dist/controls/foldable/types.js +1 -0
- package/dist/controls/html.d.ts +8 -0
- package/dist/controls/html.js +260 -0
- package/dist/controls/index.d.ts +7 -0
- package/dist/controls/index.js +4 -0
- package/dist/controls/ionic.d.ts +1 -0
- package/dist/controls/ionic.js +1 -0
- package/dist/controls/legacy-layout.d.ts +7 -0
- package/dist/controls/legacy-layout.js +23 -0
- package/dist/controls/overlays/content.d.ts +2 -0
- package/dist/controls/overlays/content.js +55 -0
- package/dist/controls/overlays/controller.d.ts +7 -0
- package/dist/controls/overlays/controller.js +369 -0
- package/dist/controls/overlays/covered-overlay.d.ts +4 -0
- package/dist/controls/overlays/covered-overlay.js +9 -0
- package/dist/controls/overlays/dialog-animation.d.ts +7 -0
- package/dist/controls/overlays/dialog-animation.js +49 -0
- package/dist/controls/overlays/dismiss-animation.d.ts +2 -0
- package/dist/controls/overlays/dismiss-animation.js +14 -0
- package/dist/controls/overlays/focus.d.ts +2 -0
- package/dist/controls/overlays/focus.js +67 -0
- package/dist/controls/overlays/popover.d.ts +10 -0
- package/dist/controls/overlays/popover.js +117 -0
- package/dist/controls/overlays/styles.d.ts +9 -0
- package/dist/controls/overlays/styles.js +194 -0
- package/dist/controls/prehide.d.ts +6 -0
- package/dist/controls/prehide.js +280 -0
- package/dist/controls/runtime.d.ts +5 -0
- package/dist/controls/runtime.js +1685 -0
- package/dist/controls/shared/bridge.d.ts +4 -0
- package/dist/controls/shared/bridge.js +15 -0
- package/dist/controls/shared/candidate.d.ts +23 -0
- package/dist/controls/shared/candidate.js +128 -0
- package/dist/controls/shared/clipping.d.ts +1 -0
- package/dist/controls/shared/clipping.js +1 -0
- package/dist/controls/shared/connection.d.ts +21 -0
- package/dist/controls/shared/connection.js +112 -0
- package/dist/controls/shared/control-options.d.ts +4 -0
- package/dist/controls/shared/control-options.js +14 -0
- package/dist/controls/shared/covered-snapshot.d.ts +35 -0
- package/dist/controls/shared/covered-snapshot.js +41 -0
- package/dist/controls/shared/covered-source.d.ts +22 -0
- package/dist/controls/shared/covered-source.js +72 -0
- package/dist/controls/shared/crossfade.d.ts +11 -0
- package/dist/controls/shared/crossfade.js +71 -0
- package/dist/controls/shared/diagnostics.d.ts +2 -0
- package/dist/controls/shared/diagnostics.js +16 -0
- package/dist/controls/shared/dom.d.ts +45 -0
- package/dist/controls/shared/dom.js +258 -0
- package/dist/controls/shared/events.d.ts +12 -0
- package/dist/controls/shared/events.js +13 -0
- package/dist/controls/shared/horizontal-navigation.d.ts +36 -0
- package/dist/controls/shared/horizontal-navigation.js +90 -0
- package/dist/controls/shared/icons.d.ts +5 -0
- package/dist/controls/shared/icons.js +66 -0
- package/dist/controls/shared/modal-animation.d.ts +21 -0
- package/dist/controls/shared/modal-animation.js +97 -0
- package/dist/controls/shared/modal.d.ts +26 -0
- package/dist/controls/shared/modal.js +261 -0
- package/dist/controls/shared/projection-id.d.ts +2 -0
- package/dist/controls/shared/projection-id.js +2 -0
- package/dist/controls/shared/scroll-placement.d.ts +1 -0
- package/dist/controls/shared/scroll-placement.js +1 -0
- package/dist/controls/shared/tab-bar-width.d.ts +7 -0
- package/dist/controls/shared/tab-bar-width.js +72 -0
- package/dist/controls/shared/toolbar-options.d.ts +10 -0
- package/dist/controls/shared/toolbar-options.js +38 -0
- package/dist/controls/shared/transition-identity.d.ts +3 -0
- package/dist/controls/shared/transition-identity.js +64 -0
- package/dist/controls/shared/vertical-bars-geometry.d.ts +8 -0
- package/dist/controls/shared/vertical-bars-geometry.js +82 -0
- package/dist/controls/shared/vertical-navigation.d.ts +5 -0
- package/dist/controls/shared/vertical-navigation.js +39 -0
- package/dist/controls/startup.d.ts +7 -0
- package/dist/controls/startup.js +72 -0
- package/dist/controls/transport.d.ts +10 -0
- package/dist/controls/transport.js +1 -0
- package/dist/controls/vertical-bars-web-search.d.ts +8 -0
- package/dist/controls/vertical-bars-web-search.js +83 -0
- package/dist/controls/vertical-bars-web.d.ts +2 -0
- package/dist/controls/vertical-bars-web.js +609 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/integration/index.d.ts +36 -0
- package/dist/integration/index.js +25 -0
- package/dist/native/session.js +14 -8
- package/dist/projection/pause.d.ts +10 -0
- package/dist/projection/pause.js +58 -0
- package/dist/projection/styles.d.ts +1 -1
- package/dist/projection/styles.js +0 -1
- package/dist/projection/surfaces.js +2 -1
- package/dist/projection/web.js +39 -5
- package/dist/types.d.ts +5 -1
- package/docs/README.md +1 -0
- package/docs/configuration/README.md +3 -1
- package/docs/configuration/ionic-overlays.md +36 -2
- package/docs/configuration/pause-resume.md +51 -0
- package/docs/controls.md +39 -0
- package/ios/Sources/WebViewNativeUIControls/Components/NativeUIButton.swift +98 -0
- package/ios/Sources/WebViewNativeUIControls/Components/NativeUIButtons.swift +24 -0
- package/ios/Sources/WebViewNativeUIControls/Components/NativeUIComponents.swift +19 -0
- package/ios/Sources/WebViewNativeUIControls/Components/NativeUIFab.swift +80 -0
- package/ios/Sources/WebViewNativeUIControls/Components/NativeUISearchController.swift +449 -0
- package/ios/Sources/WebViewNativeUIControls/Components/NativeUISegment.swift +91 -0
- package/ios/Sources/WebViewNativeUIControls/Components/NativeUITabBar.swift +213 -0
- package/ios/Sources/WebViewNativeUIControls/Components/NativeUIToolbarTransition.swift +186 -0
- package/ios/Sources/WebViewNativeUIControls/Components/NativeUIVerticalBars.swift +696 -0
- package/ios/Sources/WebViewNativeUIControls/Components/NativeUIVerticalSearch.swift +117 -0
- package/ios/Sources/WebViewNativeUIControls/Extensions/NativeUI.swift +180 -0
- package/ios/Sources/WebViewNativeUIControls/Extensions/NativeUIRendering.swift +38 -0
- package/ios/Sources/WebViewNativeUIControls/NativeUIController.swift +801 -0
- package/ios/Sources/WebViewNativeUIControls/Overlays/NativeUIOverlayController.swift +131 -0
- package/ios/Sources/WebViewNativeUIControls/Overlays/NativeUIOverlayHost.swift +85 -0
- package/ios/Sources/WebViewNativeUIControls/Shared/NativeUIControlRendering.swift +21 -0
- package/ios/Sources/WebViewNativeUIControls/Shared/NativeUICoveredSnapshots.swift +14 -0
- package/ios/Sources/WebViewNativeUIControls/Shared/NativeUICrossfade.swift +35 -0
- package/ios/Sources/WebViewNativeUIControls/Shared/NativeUIHost.swift +8 -0
- package/ios/Sources/WebViewNativeUIControls/Shared/NativeUIScrollTracker.swift +2 -0
- package/ios/Sources/WebViewNativeUIControls/Shared/NativeUISnapshot.swift +206 -0
- package/ios/Sources/WebViewNativeUIControls/Shared/NativeUIVerticalBarsGeometry.swift +93 -0
- package/ios/Sources/WebViewNativeUIEngine/CoveredSnapshots.swift +2 -2
- package/ios/Sources/WebViewNativeUIEngine/NativeUIProjection.swift +1 -1
- package/ios/Sources/WebViewNativeUIEngine/Resources/native-ui-engine.js +3 -4
- package/package.json +30 -5
- package/src/controls/activation.ts +427 -0
- package/src/controls/components/index.ts +45 -0
- package/src/controls/components/ion-back-button.ts +22 -0
- package/src/controls/components/ion-button.ts +28 -0
- package/src/controls/components/ion-buttons.ts +37 -0
- package/src/controls/components/ion-fab.ts +151 -0
- package/src/controls/components/ion-menu-button.ts +37 -0
- package/src/controls/components/ion-segment.ts +27 -0
- package/src/controls/components/ion-tab-bar.ts +55 -0
- package/src/controls/components/searchable-tabs.ts +534 -0
- package/src/controls/definitions.ts +338 -0
- package/src/controls/extensions.ts +198 -0
- package/src/controls/foldable/fold-state.ts +10 -0
- package/src/controls/foldable/types.ts +4 -0
- package/src/controls/html.ts +239 -0
- package/src/controls/index.ts +7 -0
- package/src/controls/ionic.ts +1 -0
- package/src/controls/legacy-layout.ts +21 -0
- package/src/controls/overlays/content.ts +51 -0
- package/src/controls/overlays/controller.ts +394 -0
- package/src/controls/overlays/covered-overlay.ts +11 -0
- package/src/controls/overlays/dialog-animation.ts +53 -0
- package/src/controls/overlays/dismiss-animation.ts +15 -0
- package/src/controls/overlays/focus.ts +64 -0
- package/src/controls/overlays/popover.ts +116 -0
- package/src/controls/overlays/styles.ts +195 -0
- package/src/controls/prehide.ts +285 -0
- package/src/controls/runtime.ts +1731 -0
- package/src/controls/shared/bridge.ts +18 -0
- package/src/controls/shared/candidate.ts +163 -0
- package/src/controls/shared/clipping.ts +1 -0
- package/src/controls/shared/connection.ts +105 -0
- package/src/controls/shared/control-options.ts +20 -0
- package/src/controls/shared/covered-snapshot.ts +52 -0
- package/src/controls/shared/covered-source.ts +78 -0
- package/src/controls/shared/crossfade.ts +70 -0
- package/src/controls/shared/diagnostics.ts +13 -0
- package/src/controls/shared/dom.ts +310 -0
- package/src/controls/shared/events.ts +14 -0
- package/src/controls/shared/horizontal-navigation.ts +106 -0
- package/src/controls/shared/icons.ts +64 -0
- package/src/controls/shared/modal-animation.ts +109 -0
- package/src/controls/shared/modal.ts +286 -0
- package/src/controls/shared/projection-id.ts +2 -0
- package/src/controls/shared/scroll-placement.ts +1 -0
- package/src/controls/shared/tab-bar-width.ts +66 -0
- package/src/controls/shared/toolbar-options.ts +51 -0
- package/src/controls/shared/transition-identity.ts +67 -0
- package/src/controls/shared/vertical-bars-geometry.ts +92 -0
- package/src/controls/shared/vertical-navigation.ts +34 -0
- package/src/controls/startup.ts +74 -0
- package/src/controls/transport.ts +11 -0
- package/src/controls/vertical-bars-web-search.ts +86 -0
- package/src/controls/vertical-bars-web.ts +674 -0
- package/src/dom/geometry.ts +24 -0
- package/src/dom/overlays.ts +196 -0
- package/src/dom/scroll-bindings.ts +66 -0
- package/src/dom/scroll-placement.ts +92 -0
- package/src/dom/visibility.ts +29 -0
- package/src/index.ts +19 -0
- package/src/integration/index.ts +64 -0
- package/src/ionic.ts +34 -0
- package/src/native/bootstrap.ts +132 -0
- package/src/native/bridge.ts +18 -0
- package/src/native/resource-url.ts +11 -0
- package/src/native/session.ts +708 -0
- package/src/native/updates.ts +103 -0
- package/src/native/values.ts +29 -0
- package/src/projection/events.ts +58 -0
- package/src/projection/pause.ts +58 -0
- package/src/projection/placement.ts +34 -0
- package/src/projection/scope.ts +13 -0
- package/src/projection/styles.ts +26 -0
- package/src/projection/surface.ts +88 -0
- package/src/projection/surfaces.ts +119 -0
- package/src/projection/web.ts +1013 -0
- package/src/types.ts +58 -0
package/README.md
CHANGED
|
@@ -50,6 +50,18 @@ The Engine supports **iOS and Android**. Ionic and Angular are optional. Start w
|
|
|
50
50
|
|
|
51
51
|
Use matching Engine versions across npm and native dependencies, and retain dependency lockfiles for reproducible builds. Android placement requires API 23+, with composited snapshots available from API 26+.
|
|
52
52
|
|
|
53
|
+
## Optional Ionic integration
|
|
54
|
+
|
|
55
|
+
For Ionic applications using an Engine-enabled plugin, the `@webview-native-ui/engine/ionic` entry point connects Ionic overlay lifecycle events to native projection:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { enableSupportIonicOverlays } from '@webview-native-ui/engine/ionic';
|
|
59
|
+
|
|
60
|
+
enableSupportIonicOverlays();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Call it once during application startup unless your plugin already enables it. Applications importing it directly need `@webview-native-ui/engine` as a direct dependency. This shared framework integration coordinates coverage across plugins; each plugin retains control of its own presentation API. See [Ionic overlay support](docs/configuration/ionic-overlays.md) for setup, supported overlays and renderer helpers.
|
|
64
|
+
|
|
53
65
|
## Contributing
|
|
54
66
|
|
|
55
67
|
For repository development, verification and publishing, see [development and releases](docs/maintenance.md).
|
|
@@ -57,3 +69,5 @@ For repository development, verification and publishing, see [development and re
|
|
|
57
69
|
## License
|
|
58
70
|
|
|
59
71
|
[MIT](LICENSE). Commercial use, modification and redistribution are permitted with the license's notice and attribution requirements. The Engine's license does not change the licenses of applications or plugins that depend on it.
|
|
72
|
+
|
|
73
|
+
For the optional shared control runtime and adapter boundaries, see [shared controls](docs/controls.md).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
3
|
+
Pod::Spec.new do |s|
|
|
4
|
+
s.name = 'WebViewNativeUIControls'
|
|
5
|
+
s.version = package['version']
|
|
6
|
+
s.summary = 'Native UI infrastructure for Capacitor and Cordova plugin maintainers.'
|
|
7
|
+
s.homepage = 'https://github.com/webview-native-ui/engine'
|
|
8
|
+
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
|
9
|
+
s.preserve_paths = 'LICENSE'
|
|
10
|
+
s.author = { 'rdlabo' => 'sakakibara@rdlabo.jp' }
|
|
11
|
+
s.source = { :git => 'https://github.com/webview-native-ui/engine.git', :tag => "v#{s.version}" }
|
|
12
|
+
s.source_files = 'ios/Sources/WebViewNativeUIControls/**/*.swift'
|
|
13
|
+
s.ios.deployment_target = '15.0'
|
|
14
|
+
s.swift_version = '5.9'
|
|
15
|
+
s.frameworks = 'UIKit', 'WebKit', 'SwiftUI'
|
|
16
|
+
s.dependency 'WebViewNativeUIEngine', s.version.to_s
|
|
17
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { NativeUITransport } from './transport.js';
|
|
2
|
+
import type { NativeUIRuntimeHandle, NativeUIRuntimeOptions, NativeUIPlugin, WebViewMetrics } from './definitions.js';
|
|
3
|
+
import { type NativeUIConnection } from './shared/connection.js';
|
|
4
|
+
export interface OverlayController {
|
|
5
|
+
suspend(): Promise<() => void>;
|
|
6
|
+
destroy(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
type OverlayFactory = (doc: Document, plugin: NativeUIPlugin, options: NativeUIRuntimeOptions, retain: (deferActivation?: (id: string) => boolean) => () => void, nativeRail: () => boolean, connection?: NativeUIConnection) => Promise<OverlayController>;
|
|
9
|
+
export { defineNativeUI } from './extensions.js';
|
|
10
|
+
export type { NativeUIExtension, NativeUIValue } from './extensions.js';
|
|
11
|
+
export type { NativeUIComponent, NativeUIControls, NativeUIRuntimeHandle, NativeUIRuntimeOptions, NativeUIStatus, NativeUISuspension, VerticalBarEdge, WebViewMetrics, } from './definitions.js';
|
|
12
|
+
export declare const createNativeUIActivation: (transport: NativeUITransport) => {
|
|
13
|
+
enableNativeUI: (options?: NativeUIRuntimeOptions, overlayFactory?: OverlayFactory) => Promise<NativeUIRuntimeHandle>;
|
|
14
|
+
configureNativeTransition: () => Promise<WebViewMetrics>;
|
|
15
|
+
};
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { controlDefault } from './shared/control-options.js';
|
|
2
|
+
import { toolbarOptions } from './shared/toolbar-options.js';
|
|
3
|
+
import { createDiagnostics } from './shared/diagnostics.js';
|
|
4
|
+
import { createHTMLWebProjection } from './html.js';
|
|
5
|
+
import { validateExtensions } from './extensions.js';
|
|
6
|
+
import { nativeWebViewMetrics } from '../integration/index.js';
|
|
7
|
+
import { createRuntime } from './runtime.js';
|
|
8
|
+
import { bounded, boundedListener } from './shared/bridge.js';
|
|
9
|
+
import { createNativeConnection } from './shared/connection.js';
|
|
10
|
+
import { createVerticalBarsWebProjection } from './vertical-bars-web.js';
|
|
11
|
+
import { prehideVerticalBarsToolbarSources } from './prehide.js';
|
|
12
|
+
import { observeVerticalBarsModals, topModal, modalPreparing, modalPresentation } from './shared/modal.js';
|
|
13
|
+
export { defineNativeUI } from './extensions.js';
|
|
14
|
+
export const createNativeUIActivation = (transport) => {
|
|
15
|
+
const plugin = transport.plugin;
|
|
16
|
+
let active;
|
|
17
|
+
let activeFailed = false;
|
|
18
|
+
let activeRoot;
|
|
19
|
+
let activeRailHost;
|
|
20
|
+
let activeConfiguration;
|
|
21
|
+
let activeExtensions = [];
|
|
22
|
+
let stopping;
|
|
23
|
+
// Every stop entry point releases the activation immediately but reserves the
|
|
24
|
+
// shared native/DOM resources until its teardown has settled.
|
|
25
|
+
const stopActivation = (owner, destruction) => {
|
|
26
|
+
if (active !== owner)
|
|
27
|
+
return;
|
|
28
|
+
active = undefined;
|
|
29
|
+
activeFailed = false;
|
|
30
|
+
activeConfiguration = undefined;
|
|
31
|
+
activeRoot = undefined;
|
|
32
|
+
activeRailHost = undefined;
|
|
33
|
+
activeExtensions = [];
|
|
34
|
+
stopping = destruction;
|
|
35
|
+
const release = () => {
|
|
36
|
+
if (stopping === destruction)
|
|
37
|
+
stopping = undefined;
|
|
38
|
+
};
|
|
39
|
+
void destruction.then(release, release);
|
|
40
|
+
};
|
|
41
|
+
const web = (reason) => ({
|
|
42
|
+
markForCheck: () => { },
|
|
43
|
+
getStatus: () => ({ state: 'web', projected: 0, updates: 0, reason }),
|
|
44
|
+
suspend: async () => ({ resume: async () => { } }),
|
|
45
|
+
destroy: async () => { },
|
|
46
|
+
});
|
|
47
|
+
const combine = (native, fallback) => ({
|
|
48
|
+
markForCheck() {
|
|
49
|
+
native.markForCheck();
|
|
50
|
+
fallback.markForCheck();
|
|
51
|
+
},
|
|
52
|
+
getStatus() {
|
|
53
|
+
const nativeStatus = native.getStatus();
|
|
54
|
+
const fallbackStatus = fallback.getStatus();
|
|
55
|
+
return {
|
|
56
|
+
...nativeStatus,
|
|
57
|
+
state: nativeStatus.state === 'stopped' && fallbackStatus.state === 'stopped'
|
|
58
|
+
? 'stopped'
|
|
59
|
+
: nativeStatus.projected > 0
|
|
60
|
+
? 'native'
|
|
61
|
+
: 'web',
|
|
62
|
+
projected: nativeStatus.projected + fallbackStatus.projected,
|
|
63
|
+
updates: nativeStatus.updates + fallbackStatus.updates,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
async suspend(options) {
|
|
67
|
+
const [nativeLease, fallbackLease] = await Promise.all([native.suspend(options), fallback.suspend(options)]);
|
|
68
|
+
return { resume: async () => void (await Promise.all([nativeLease.resume(), fallbackLease.resume()])) };
|
|
69
|
+
},
|
|
70
|
+
async destroy() {
|
|
71
|
+
await Promise.all([native.destroy(), fallback.destroy()]);
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
/** Wraps a runtime handle with the shared enable-lifecycle: reason, suspend hooks and idempotent destroy. */
|
|
75
|
+
const manage = (handle, lifecycle = {}) => {
|
|
76
|
+
let destruction;
|
|
77
|
+
return {
|
|
78
|
+
markForCheck: () => {
|
|
79
|
+
if (!destruction)
|
|
80
|
+
handle.markForCheck();
|
|
81
|
+
},
|
|
82
|
+
getStatus: () => (lifecycle.reason ? { ...handle.getStatus(), reason: lifecycle.reason } : handle.getStatus()),
|
|
83
|
+
async suspend(options) {
|
|
84
|
+
const lease = await handle.suspend(options);
|
|
85
|
+
const resume = await lifecycle.suspend?.();
|
|
86
|
+
return {
|
|
87
|
+
async resume() {
|
|
88
|
+
await lease.resume();
|
|
89
|
+
resume?.();
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
destroy() {
|
|
94
|
+
if (!destruction) {
|
|
95
|
+
destruction = Promise.resolve().then(async () => {
|
|
96
|
+
try {
|
|
97
|
+
await handle.destroy();
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
await lifecycle.destroy?.();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
lifecycle.stopping?.(destruction);
|
|
104
|
+
}
|
|
105
|
+
return destruction;
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
/** Reads the current native WebView geometry and applies it to page transitions. */
|
|
110
|
+
const configureNativeTransition = async () => {
|
|
111
|
+
const metrics = typeof document !== 'undefined' && transport.getPlatform() === 'ios' && transport.isAvailable()
|
|
112
|
+
? await bounded(plugin.getWebViewMetrics())
|
|
113
|
+
: { radius: 0 };
|
|
114
|
+
nativeWebViewMetrics.radius = metrics.radius;
|
|
115
|
+
return metrics;
|
|
116
|
+
};
|
|
117
|
+
/** Resolves a logical vertical-bar edge to the physical side for the given direction. */
|
|
118
|
+
const physicalVerticalBarEdge = (edge, rtl) => (edge === 'leading') !== rtl ? 'left' : 'right';
|
|
119
|
+
const elementRtl = (element) => element.closest('[dir]')?.getAttribute('dir') === 'rtl';
|
|
120
|
+
const webProjection = (doc, options, enabled = () => true) => {
|
|
121
|
+
const ionic = () => createVerticalBarsWebProjection(doc, options, () => options.verticalBars?.enabled !== false && enabled());
|
|
122
|
+
if (options.source === 'html')
|
|
123
|
+
return createHTMLWebProjection(doc, options);
|
|
124
|
+
if (options.source === 'auto')
|
|
125
|
+
return combine(ionic(), createHTMLWebProjection(doc, options));
|
|
126
|
+
return ionic();
|
|
127
|
+
};
|
|
128
|
+
/** Call once at application startup. Ionic markup remains the source of truth. */
|
|
129
|
+
const enableNativeUI = (options = {}, overlayFactory) => {
|
|
130
|
+
if (typeof document === 'undefined')
|
|
131
|
+
return Promise.resolve(web('Requires a document'));
|
|
132
|
+
const extensions = [...(options.extensions ?? [])];
|
|
133
|
+
const nativeState = (options.nativeState = {});
|
|
134
|
+
try {
|
|
135
|
+
validateExtensions(extensions, document);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
return Promise.reject(error);
|
|
139
|
+
}
|
|
140
|
+
const configuration = JSON.stringify([
|
|
141
|
+
options.source ?? 'ionic',
|
|
142
|
+
options.debug === true,
|
|
143
|
+
options.ionic?.auto !== false,
|
|
144
|
+
options.verticalBars?.enabled !== false,
|
|
145
|
+
options.verticalBars?.placement?.edge,
|
|
146
|
+
options.rail?.edge,
|
|
147
|
+
toolbarOptions(options, false),
|
|
148
|
+
toolbarOptions(options, true),
|
|
149
|
+
options.ionic?.verticalBars?.buttonProjection ?? 'system',
|
|
150
|
+
// Local source overrides can use this even when the startup projection is system.
|
|
151
|
+
options.ionic?.verticalBars?.buttonDefaultFill ?? 'glass',
|
|
152
|
+
...['tabs', 'segment', 'fab'].flatMap((kind) => [
|
|
153
|
+
controlDefault(options, kind, false),
|
|
154
|
+
controlDefault(options, kind, true),
|
|
155
|
+
]),
|
|
156
|
+
options.ionic?.verticalBars?.popover === true,
|
|
157
|
+
]);
|
|
158
|
+
if (active && activeFailed) {
|
|
159
|
+
const previous = active;
|
|
160
|
+
stopActivation(previous, previous.then((handle) => handle.destroy()));
|
|
161
|
+
}
|
|
162
|
+
if (active &&
|
|
163
|
+
(activeConfiguration !== configuration ||
|
|
164
|
+
activeRoot !== options.root ||
|
|
165
|
+
activeRailHost !== options.rail?.host ||
|
|
166
|
+
extensions.length !== activeExtensions.length ||
|
|
167
|
+
extensions.some((extension, index) => extension !== activeExtensions[index])))
|
|
168
|
+
return Promise.reject(new Error('Native UI is already running with different controls or extensions; destroy it before changing configuration.'));
|
|
169
|
+
if (active)
|
|
170
|
+
return active;
|
|
171
|
+
activeConfiguration = configuration;
|
|
172
|
+
activeRoot = options.root;
|
|
173
|
+
activeRailHost = options.rail?.host;
|
|
174
|
+
activeExtensions = extensions;
|
|
175
|
+
const previousStop = stopping;
|
|
176
|
+
let start;
|
|
177
|
+
const stop = (destruction) => stopActivation(start, destruction);
|
|
178
|
+
let releaseNative = async () => { };
|
|
179
|
+
let retired = false;
|
|
180
|
+
const pagehide = () => runtimeStopped('pagehide');
|
|
181
|
+
const runtimeStopped = (cause) => {
|
|
182
|
+
if (cause === 'failure') {
|
|
183
|
+
// Keep the Web rail usable, but replace this degraded activation on the next enable.
|
|
184
|
+
if (active === start)
|
|
185
|
+
activeFailed = true;
|
|
186
|
+
void start
|
|
187
|
+
.then(async () => {
|
|
188
|
+
if (!retired)
|
|
189
|
+
document.defaultView?.addEventListener('pagehide', pagehide, { once: true });
|
|
190
|
+
await releaseNative();
|
|
191
|
+
})
|
|
192
|
+
.catch(() => { });
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const destruction = start.then((handle) => handle.destroy());
|
|
196
|
+
stop(destruction);
|
|
197
|
+
void destruction.catch(() => { });
|
|
198
|
+
};
|
|
199
|
+
return (start = active =
|
|
200
|
+
(async () => {
|
|
201
|
+
// Native controls and DOM ownership are shared. A replacement activation
|
|
202
|
+
// must wait until the old handle has finished clearing both surfaces.
|
|
203
|
+
if (previousStop)
|
|
204
|
+
await previousStop.catch(() => { });
|
|
205
|
+
const platform = transport.getPlatform();
|
|
206
|
+
const diagnose = createDiagnostics(options.debug);
|
|
207
|
+
let nativeModalAnimations = () => false;
|
|
208
|
+
const stopModals = options.source === 'html' ? () => { } : observeVerticalBarsModals(document, () => nativeModalAnimations());
|
|
209
|
+
const prehide = platform !== 'android' && options.source !== 'html' && options.ionic?.auto !== false
|
|
210
|
+
? prehideVerticalBarsToolbarSources(document, () => options.verticalBars?.enabled !== false, options.root, options)
|
|
211
|
+
: undefined;
|
|
212
|
+
const fallback = (reason) => {
|
|
213
|
+
diagnose(`web-fallback: ${reason}`);
|
|
214
|
+
return manage(webProjection(document, options), {
|
|
215
|
+
reason,
|
|
216
|
+
suspend: () => prehide?.suspend(),
|
|
217
|
+
destroy: () => {
|
|
218
|
+
retired = true;
|
|
219
|
+
document.defaultView?.removeEventListener('pagehide', pagehide);
|
|
220
|
+
prehide?.stop();
|
|
221
|
+
stopModals();
|
|
222
|
+
},
|
|
223
|
+
stopping: stop,
|
|
224
|
+
});
|
|
225
|
+
};
|
|
226
|
+
if (platform !== 'ios' && platform !== 'android')
|
|
227
|
+
return fallback('Requires an iOS or Android native host');
|
|
228
|
+
if (!transport.isAvailable())
|
|
229
|
+
return fallback('Native UI plugin is not registered');
|
|
230
|
+
let runtime;
|
|
231
|
+
let overlays;
|
|
232
|
+
let connection;
|
|
233
|
+
let metricsListener;
|
|
234
|
+
let nativeRelease;
|
|
235
|
+
releaseNative = () => (nativeRelease ??= (async () => {
|
|
236
|
+
try {
|
|
237
|
+
connection?.stop();
|
|
238
|
+
await overlays?.destroy();
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
if (metricsListener)
|
|
242
|
+
await bounded(metricsListener.remove()).catch(() => { });
|
|
243
|
+
}
|
|
244
|
+
})());
|
|
245
|
+
const cleanup = async () => {
|
|
246
|
+
retired = true;
|
|
247
|
+
try {
|
|
248
|
+
await releaseNative();
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
document.defaultView?.removeEventListener('pagehide', pagehide);
|
|
252
|
+
prehide?.stop();
|
|
253
|
+
stopModals();
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
try {
|
|
257
|
+
const ordinaryControls = ['button', 'backButton', 'tabs', 'segment', 'fab'].some((kind) => controlDefault(options, kind, false));
|
|
258
|
+
const capabilities = await bounded(plugin.configure({ suppressScrollEdgeEffect: ordinaryControls }));
|
|
259
|
+
if (!capabilities.supported)
|
|
260
|
+
return fallback('Native UI is unavailable on this device');
|
|
261
|
+
if (capabilities.connectionState)
|
|
262
|
+
connection = createNativeConnection(document, plugin);
|
|
263
|
+
const listen = connection?.listen ?? plugin.addListener.bind(plugin);
|
|
264
|
+
const unavailableExtensions = new Map();
|
|
265
|
+
for (const extension of extensions) {
|
|
266
|
+
if (platform === 'android' && extension.placement === 'tab-accessory')
|
|
267
|
+
unavailableExtensions.set(extension.name, 'unsupported-placement: tab-accessory requires a supported iOS tab host.');
|
|
268
|
+
else if (!capabilities.extensions?.includes(extension.name))
|
|
269
|
+
unavailableExtensions.set(extension.name, 'unregistered-extension: register this name in the native app before enabling the shell, then rebuild.');
|
|
270
|
+
}
|
|
271
|
+
if (platform === 'android') {
|
|
272
|
+
runtime = await createRuntime(document, plugin, {
|
|
273
|
+
...options,
|
|
274
|
+
source: 'extensions',
|
|
275
|
+
coveredSnapshots: capabilities.coveredSnapshots === true,
|
|
276
|
+
extensions,
|
|
277
|
+
unavailableExtensions,
|
|
278
|
+
}, () => false, runtimeStopped, connection);
|
|
279
|
+
runtime = combine(runtime, webProjection(document, options));
|
|
280
|
+
metricsListener = await boundedListener(listen('webViewMetricsChange', () => {
|
|
281
|
+
document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));
|
|
282
|
+
}));
|
|
283
|
+
connection?.start();
|
|
284
|
+
return manage(runtime, {
|
|
285
|
+
stopping: stop,
|
|
286
|
+
destroy: cleanup,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
let deviceEdge = null;
|
|
290
|
+
const ionicRoot = () => {
|
|
291
|
+
if (options.source === 'html')
|
|
292
|
+
return null;
|
|
293
|
+
const root = options.root ?? document.body;
|
|
294
|
+
return root.closest('ion-app') ?? root.querySelector('ion-app');
|
|
295
|
+
};
|
|
296
|
+
const updateNativeEdge = () => {
|
|
297
|
+
const requested = options.verticalBars?.placement?.edge;
|
|
298
|
+
const root = ionicRoot() ?? options.root ?? document.body;
|
|
299
|
+
nativeState.edge =
|
|
300
|
+
deviceEdge && (!requested || requested === deviceEdge) ? physicalVerticalBarEdge(deviceEdge, elementRtl(root)) : undefined;
|
|
301
|
+
};
|
|
302
|
+
// Ionic layout is read-only input; device metrics determine native availability.
|
|
303
|
+
const nativeVerticalBars = () => {
|
|
304
|
+
updateNativeEdge();
|
|
305
|
+
if (options.verticalBars?.enabled === false)
|
|
306
|
+
return false;
|
|
307
|
+
const app = ionicRoot();
|
|
308
|
+
if (!app?.classList.contains('ios-theme-vertical-bars'))
|
|
309
|
+
return !!nativeState.edge;
|
|
310
|
+
const physicalEdge = app.classList.contains('ios-theme-vertical-bars-left') ? 'left' : 'right';
|
|
311
|
+
return physicalEdge === nativeState.edge;
|
|
312
|
+
};
|
|
313
|
+
nativeModalAnimations = () => capabilities.modalAnimation === true && nativeVerticalBars();
|
|
314
|
+
const applyMetrics = (metrics) => {
|
|
315
|
+
if (ordinaryControls)
|
|
316
|
+
nativeWebViewMetrics.radius = metrics.radius;
|
|
317
|
+
deviceEdge = metrics.verticalBarEdge ?? null;
|
|
318
|
+
updateNativeEdge();
|
|
319
|
+
document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));
|
|
320
|
+
};
|
|
321
|
+
metricsListener = await boundedListener(listen('webViewMetricsChange', applyMetrics));
|
|
322
|
+
await bounded(Promise.resolve().then(() => plugin.getWebViewMetrics()))
|
|
323
|
+
.then(applyMetrics)
|
|
324
|
+
.catch(() => undefined);
|
|
325
|
+
connection?.observe(async (_, restored) => {
|
|
326
|
+
if (restored)
|
|
327
|
+
await bounded(plugin.getWebViewMetrics())
|
|
328
|
+
.then(applyMetrics)
|
|
329
|
+
.catch(() => undefined);
|
|
330
|
+
});
|
|
331
|
+
const native = await createRuntime(document, plugin, {
|
|
332
|
+
...options,
|
|
333
|
+
extensions,
|
|
334
|
+
unavailableExtensions,
|
|
335
|
+
coveredSnapshots: capabilities.coveredSnapshots === true,
|
|
336
|
+
nativeScroll: capabilities.nativeScroll === true,
|
|
337
|
+
}, nativeVerticalBars, runtimeStopped, connection);
|
|
338
|
+
runtime = combine(native, webProjection(document, options, () => {
|
|
339
|
+
const modal = topModal(document);
|
|
340
|
+
return (!(modal && (modalPreparing(modal) || modalPresentation(modal))) &&
|
|
341
|
+
(!nativeVerticalBars() || native.getStatus().projected === 0));
|
|
342
|
+
}));
|
|
343
|
+
if (options.ionic?.verticalBars?.popover === true && overlayFactory)
|
|
344
|
+
overlays = await overlayFactory(document, plugin, options, native.retain, nativeVerticalBars, connection);
|
|
345
|
+
connection?.start();
|
|
346
|
+
return manage(runtime, {
|
|
347
|
+
suspend: async () => {
|
|
348
|
+
const restorePrehide = prehide?.suspend();
|
|
349
|
+
const resumeOverlays = await overlays?.suspend();
|
|
350
|
+
return () => {
|
|
351
|
+
restorePrehide?.();
|
|
352
|
+
resumeOverlays?.();
|
|
353
|
+
};
|
|
354
|
+
},
|
|
355
|
+
stopping: stop,
|
|
356
|
+
destroy: cleanup,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
connection?.stop();
|
|
361
|
+
await overlays?.destroy().catch(() => { });
|
|
362
|
+
await runtime?.destroy().catch(() => { });
|
|
363
|
+
if (metricsListener)
|
|
364
|
+
await bounded(metricsListener.remove()).catch(() => { });
|
|
365
|
+
return fallback(error instanceof Error ? error.message : String(error));
|
|
366
|
+
}
|
|
367
|
+
})());
|
|
368
|
+
};
|
|
369
|
+
return { enableNativeUI, configureNativeTransition };
|
|
370
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { VerticalControlAreaOptions } from '../definitions.js';
|
|
2
|
+
import * as button from './ion-button.js';
|
|
3
|
+
import * as buttons from './ion-buttons.js';
|
|
4
|
+
import * as backButton from './ion-back-button.js';
|
|
5
|
+
import * as menuButton from './ion-menu-button.js';
|
|
6
|
+
import * as tabBar from './ion-tab-bar.js';
|
|
7
|
+
import * as segment from './ion-segment.js';
|
|
8
|
+
import * as fab from './ion-fab.js';
|
|
9
|
+
import type { Candidate, Identify } from '../shared/candidate.js';
|
|
10
|
+
export declare const components: readonly [typeof button, typeof buttons, typeof backButton, typeof menuButton, typeof tabBar, typeof segment, typeof fab];
|
|
11
|
+
export type NativeUIComponent = (typeof components)[number]['tag'];
|
|
12
|
+
export declare const selector: string;
|
|
13
|
+
export declare const shadowSelector: string;
|
|
14
|
+
export declare const motionSelector: string;
|
|
15
|
+
export declare const isVerticalBarsCandidate: (element: HTMLElement) => boolean;
|
|
16
|
+
export declare const readCandidate: (element: HTMLElement, id: Identify, options?: VerticalControlAreaOptions) => Candidate | undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as button from './ion-button.js';
|
|
2
|
+
import * as buttons from './ion-buttons.js';
|
|
3
|
+
import * as backButton from './ion-back-button.js';
|
|
4
|
+
import * as menuButton from './ion-menu-button.js';
|
|
5
|
+
import * as tabBar from './ion-tab-bar.js';
|
|
6
|
+
import * as segment from './ion-segment.js';
|
|
7
|
+
import * as fab from './ion-fab.js';
|
|
8
|
+
import { isVerticalBarsSource, visible, withoutPrehide } from '../shared/dom.js';
|
|
9
|
+
// Static composition only. Each component declares its own tag, discovery and reader.
|
|
10
|
+
export const components = [button, buttons, backButton, menuButton, tabBar, segment, fab];
|
|
11
|
+
export const selector = components
|
|
12
|
+
.map((component) => ('selector' in component ? component.selector : component.tag))
|
|
13
|
+
.filter(Boolean)
|
|
14
|
+
.join(', ');
|
|
15
|
+
export const shadowSelector = [
|
|
16
|
+
'ion-icon',
|
|
17
|
+
...components.flatMap((component) => ('shadowSelector' in component ? [component.shadowSelector] : [])),
|
|
18
|
+
].join(', ');
|
|
19
|
+
export const motionSelector = [
|
|
20
|
+
'.ion-page',
|
|
21
|
+
'ion-header',
|
|
22
|
+
'ion-footer',
|
|
23
|
+
'ion-toolbar',
|
|
24
|
+
...components.filter((component) => 'tracksMotion' in component && component.tracksMotion).map((component) => component.tag),
|
|
25
|
+
].join(', ');
|
|
26
|
+
export const isVerticalBarsCandidate = isVerticalBarsSource;
|
|
27
|
+
export const readCandidate = (element, id, options = {}) => {
|
|
28
|
+
const verticalBars = isVerticalBarsCandidate(element);
|
|
29
|
+
if ((!element.classList.contains('ios') && !verticalBars) ||
|
|
30
|
+
!visible(element, verticalBars) ||
|
|
31
|
+
element.closest('ion-popover') ||
|
|
32
|
+
(element.closest('ion-modal') && !verticalBars))
|
|
33
|
+
return;
|
|
34
|
+
if (element.contains(element.ownerDocument.activeElement))
|
|
35
|
+
return;
|
|
36
|
+
// Read icon and control geometry before restoring prehide, which can collapse the toolbar.
|
|
37
|
+
return withoutPrehide(element, () => components.find((component) => component.tag === element.localName)?.read(element, id, options));
|
|
38
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { VerticalControlAreaOptions } from '../definitions.js';
|
|
2
|
+
import type { Candidate, Identify } from '../shared/candidate.js';
|
|
3
|
+
export declare const tag = "ion-back-button";
|
|
4
|
+
export declare const read: (element: HTMLElement, id: Identify, options?: VerticalControlAreaOptions) => Candidate | undefined;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { horizontalSelected, inVerticalToolbarLayout } from '../shared/toolbar-options.js';
|
|
2
|
+
import { createCandidate, appendItem } from '../shared/candidate.js';
|
|
3
|
+
import { inFixedToolbar, isVerticalBarsSource } from '../shared/dom.js';
|
|
4
|
+
export const tag = 'ion-back-button';
|
|
5
|
+
export const read = (element, id, options = {}) => {
|
|
6
|
+
const button = element;
|
|
7
|
+
if ((inVerticalToolbarLayout(element) ? !isVerticalBarsSource(element) : !horizontalSelected(element, options)) ||
|
|
8
|
+
!button.shadowRoot ||
|
|
9
|
+
button.icon !== undefined ||
|
|
10
|
+
button.color !== undefined ||
|
|
11
|
+
(!isVerticalBarsSource(element) && !inFixedToolbar(element)))
|
|
12
|
+
return;
|
|
13
|
+
const candidate = createCandidate(element, tag, id);
|
|
14
|
+
const label = button.shadowRoot.querySelector('[part="text"]')?.textContent?.trim() ?? '';
|
|
15
|
+
return appendItem(candidate, element, id, button.shadowRoot, label) ? candidate : undefined;
|
|
16
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { VerticalControlAreaOptions } from '../definitions.js';
|
|
2
|
+
import type { Candidate, Identify } from '../shared/candidate.js';
|
|
3
|
+
export declare const tag = "ion-button";
|
|
4
|
+
export declare const read: (element: HTMLElement, id: Identify, options?: VerticalControlAreaOptions) => Candidate | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { horizontalSelected, horizontalToolbarGroup, inVerticalToolbarLayout } from '../shared/toolbar-options.js';
|
|
2
|
+
import { createCandidate, appendItem } from '../shared/candidate.js';
|
|
3
|
+
import { inFixedToolbar, isVerticalBarsToolbarAction, isVerticalBarsToolbarGroup } from '../shared/dom.js';
|
|
4
|
+
export const tag = 'ion-button';
|
|
5
|
+
export const read = (element, id, options = {}) => {
|
|
6
|
+
const button = element;
|
|
7
|
+
const verticalBars = inVerticalToolbarLayout(element);
|
|
8
|
+
if (verticalBars && element.parentElement && isVerticalBarsToolbarGroup(element.parentElement))
|
|
9
|
+
return;
|
|
10
|
+
const fill = button.fill ?? 'default';
|
|
11
|
+
const group = element.parentElement?.matches('ion-buttons') ? element.parentElement : undefined;
|
|
12
|
+
if (!verticalBars && (!horizontalSelected(element, options) || (group && horizontalToolbarGroup(group, options))))
|
|
13
|
+
return;
|
|
14
|
+
if (!inFixedToolbar(element) ||
|
|
15
|
+
(verticalBars && !isVerticalBarsToolbarAction(element)) ||
|
|
16
|
+
(!verticalBars && fill !== 'default' && !(group && fill === 'clear')) ||
|
|
17
|
+
(!verticalBars && button.classList.contains('ion-color')))
|
|
18
|
+
return;
|
|
19
|
+
const candidate = createCandidate(element, tag, id, options);
|
|
20
|
+
const item = appendItem(candidate, element, id);
|
|
21
|
+
if (!item)
|
|
22
|
+
return;
|
|
23
|
+
if (!verticalBars && (fill === 'clear' || (group && button.fill === undefined)))
|
|
24
|
+
item.buttonFill = 'clear';
|
|
25
|
+
return candidate;
|
|
26
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { VerticalControlAreaOptions } from '../definitions.js';
|
|
2
|
+
import type { Candidate, Identify } from '../shared/candidate.js';
|
|
3
|
+
export declare const tag = "ion-buttons";
|
|
4
|
+
export declare const tracksMotion = true;
|
|
5
|
+
export declare const read: (element: HTMLElement, id: Identify, options?: VerticalControlAreaOptions) => Candidate | undefined;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { horizontalToolbarGroup, inVerticalToolbarLayout } from '../shared/toolbar-options.js';
|
|
2
|
+
import { createCandidate, appendItem } from '../shared/candidate.js';
|
|
3
|
+
import { childElements, verticalBarsToolbarActions, inFixedToolbar, isVerticalBarsToolbarGroup } from '../shared/dom.js';
|
|
4
|
+
import * as menuButton from './ion-menu-button.js';
|
|
5
|
+
export const tag = 'ion-buttons';
|
|
6
|
+
export const tracksMotion = true;
|
|
7
|
+
export const read = (element, id, options = {}) => {
|
|
8
|
+
if (!inFixedToolbar(element))
|
|
9
|
+
return;
|
|
10
|
+
let children = childElements(element);
|
|
11
|
+
const verticalBars = inVerticalToolbarLayout(element);
|
|
12
|
+
if (verticalBars ? !isVerticalBarsToolbarGroup(element) : !horizontalToolbarGroup(element, options))
|
|
13
|
+
return;
|
|
14
|
+
if (children.length === 1 && (!verticalBars || children[0].matches(menuButton.tag)))
|
|
15
|
+
return menuButton.read(element, id, options);
|
|
16
|
+
if (verticalBars)
|
|
17
|
+
children = verticalBarsToolbarActions(element);
|
|
18
|
+
if (!children.length ||
|
|
19
|
+
children.some((child) => !child.matches(`${menuButton.tag}${verticalBars ? '' : '.ios'}`) &&
|
|
20
|
+
(!child.matches(`ion-button${verticalBars ? '' : '.ios.button-clear'}`) ||
|
|
21
|
+
(!verticalBars && (child.fill ?? 'clear') !== 'clear'))))
|
|
22
|
+
return;
|
|
23
|
+
const candidate = createCandidate(element, tag, id, options);
|
|
24
|
+
if (verticalBars && children.length !== childElements(element).length)
|
|
25
|
+
candidate.sources = children;
|
|
26
|
+
for (const child of children) {
|
|
27
|
+
const supported = child.matches(menuButton.tag)
|
|
28
|
+
? menuButton.append(candidate, child, id)
|
|
29
|
+
: appendItem(candidate, child, id);
|
|
30
|
+
if (!supported)
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
return candidate;
|
|
34
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Candidate } from '../shared/candidate.js';
|
|
2
|
+
export declare const tag = "ion-fab";
|
|
3
|
+
export declare const shadowSelector = "ion-fab-button, ion-fab-list";
|
|
4
|
+
export declare const read: (fab: HTMLElement, id: (element: HTMLElement) => string) => Candidate | undefined;
|