@symbiote-native/navigation-bar 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/build/angular/index.d.ts +3 -0
  4. package/build/angular/index.js +3 -0
  5. package/build/angular/navigation-bar-visibility.service.d.ts +5 -0
  6. package/build/angular/navigation-bar-visibility.service.js +73 -0
  7. package/build/angular/navigation-bar.d.ts +13 -0
  8. package/build/angular/navigation-bar.js +107 -0
  9. package/build/core/entries-stack.d.ts +8 -0
  10. package/build/core/entries-stack.js +54 -0
  11. package/build/core/index.d.ts +3 -0
  12. package/build/core/index.js +2 -0
  13. package/build/core/native-module/index.android.d.ts +2 -0
  14. package/build/core/native-module/index.android.js +2 -0
  15. package/build/core/native-module/index.d.ts +2 -0
  16. package/build/core/native-module/index.js +3 -0
  17. package/build/core/native-module/shared.d.ts +8 -0
  18. package/build/core/native-module/shared.js +1 -0
  19. package/build/core/navigation-bar.d.ts +16 -0
  20. package/build/core/navigation-bar.js +69 -0
  21. package/build/core/types.d.ts +14 -0
  22. package/build/core/types.js +2 -0
  23. package/build/react/index.d.ts +3 -0
  24. package/build/react/index.js +3 -0
  25. package/build/react/navigation-bar.d.ts +3 -0
  26. package/build/react/navigation-bar.js +23 -0
  27. package/build/react/use-visibility.d.ts +3 -0
  28. package/build/react/use-visibility.js +22 -0
  29. package/build/solid/create-visibility.d.ts +4 -0
  30. package/build/solid/create-visibility.js +26 -0
  31. package/build/solid/index.d.ts +3 -0
  32. package/build/solid/index.js +3 -0
  33. package/build/solid/navigation-bar.d.ts +2 -0
  34. package/build/solid/navigation-bar.js +24 -0
  35. package/build/svelte/NavigationBar.svelte +28 -0
  36. package/build/svelte/NavigationBar.svelte.d.ts +4 -0
  37. package/build/svelte/index.d.ts +3 -0
  38. package/build/svelte/index.js +3 -0
  39. package/build/svelte/use-visibility.svelte.d.ts +4 -0
  40. package/build/svelte/use-visibility.svelte.js +26 -0
  41. package/build/vue/index.d.ts +3 -0
  42. package/build/vue/index.js +3 -0
  43. package/build/vue/navigation-bar.d.ts +2 -0
  44. package/build/vue/navigation-bar.js +23 -0
  45. package/build/vue/use-visibility.d.ts +4 -0
  46. package/build/vue/use-visibility.js +24 -0
  47. package/build-ngc/angular/index.d.ts +3 -0
  48. package/build-ngc/angular/index.js +4 -0
  49. package/build-ngc/angular/index.js.map +1 -0
  50. package/build-ngc/angular/navigation-bar-visibility.service.d.ts +8 -0
  51. package/build-ngc/angular/navigation-bar-visibility.service.js +32 -0
  52. package/build-ngc/angular/navigation-bar-visibility.service.js.map +1 -0
  53. package/build-ngc/angular/navigation-bar.d.ts +16 -0
  54. package/build-ngc/angular/navigation-bar.js +59 -0
  55. package/build-ngc/angular/navigation-bar.js.map +1 -0
  56. package/build-ngc/core/entries-stack.d.ts +8 -0
  57. package/build-ngc/core/entries-stack.js +55 -0
  58. package/build-ngc/core/entries-stack.js.map +1 -0
  59. package/build-ngc/core/index.d.ts +3 -0
  60. package/build-ngc/core/index.js +3 -0
  61. package/build-ngc/core/index.js.map +1 -0
  62. package/build-ngc/core/native-module/index.android.d.ts +2 -0
  63. package/build-ngc/core/native-module/index.android.js +3 -0
  64. package/build-ngc/core/native-module/index.android.js.map +1 -0
  65. package/build-ngc/core/native-module/index.d.ts +2 -0
  66. package/build-ngc/core/native-module/index.js +4 -0
  67. package/build-ngc/core/native-module/index.js.map +1 -0
  68. package/build-ngc/core/native-module/shared.d.ts +8 -0
  69. package/build-ngc/core/native-module/shared.js +2 -0
  70. package/build-ngc/core/native-module/shared.js.map +1 -0
  71. package/build-ngc/core/navigation-bar.d.ts +16 -0
  72. package/build-ngc/core/navigation-bar.js +70 -0
  73. package/build-ngc/core/navigation-bar.js.map +1 -0
  74. package/build-ngc/core/types.d.ts +14 -0
  75. package/build-ngc/core/types.js +3 -0
  76. package/build-ngc/core/types.js.map +1 -0
  77. package/native-link.json +12 -0
  78. package/package.json +150 -0
  79. package/src/angular/index.ts +3 -0
  80. package/src/angular/navigation-bar-visibility.service.ts +29 -0
  81. package/src/angular/navigation-bar.ts +67 -0
  82. package/src/core/entries-stack.ts +76 -0
  83. package/src/core/index.ts +19 -0
  84. package/src/core/native-module/index.android.ts +5 -0
  85. package/src/core/native-module/index.ts +5 -0
  86. package/src/core/native-module/shared.ts +15 -0
  87. package/src/core/navigation-bar.ts +90 -0
  88. package/src/core/types.ts +19 -0
  89. package/src/react/index.ts +3 -0
  90. package/src/react/navigation-bar.tsx +38 -0
  91. package/src/react/use-visibility.ts +29 -0
  92. package/src/solid/create-visibility.ts +32 -0
  93. package/src/solid/index.ts +3 -0
  94. package/src/solid/navigation-bar.ts +35 -0
  95. package/src/svelte/NavigationBar.svelte +28 -0
  96. package/src/svelte/index.ts +3 -0
  97. package/src/svelte/svelte-ambient.d.ts +3 -0
  98. package/src/svelte/svelte-compile.test-helper.ts +7 -0
  99. package/src/svelte/use-visibility.svelte.ts +37 -0
  100. package/src/vue/index.ts +3 -0
  101. package/src/vue/navigation-bar.ts +42 -0
  102. package/src/vue/use-visibility.ts +28 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 A. Prokopenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # @symbiote-native/navigation-bar
2
+
3
+ Make the Android navigation bar match your screen: light or dark buttons, hidden for an immersive
4
+ view. One API for every [SymbioteNative](../../README.md) adapter (React, Vue, Svelte, Solid and
5
+ Angular).
6
+
7
+ It wraps [`expo-navigation-bar`](https://github.com/expo/expo/tree/main/packages/expo-navigation-bar).
8
+ **Android only**: upstream ships no iOS implementation, so every function throws
9
+ `UnavailabilityError` off Android, matching this repo's normalization for platform-gated packages.
10
+ Built the same way as [`@symbiote-native/intent-launcher`](../intent-launcher): an
11
+ `expo-modules-core`-based wrapper (see the `symbiote-expo-native-module` project skill for the full
12
+ mechanism).
13
+
14
+ ## Install
15
+
16
+ **New app:**
17
+
18
+ ```bash
19
+ npx @symbiote-native/cli new my-app --navigation-bar
20
+ ```
21
+
22
+ **Existing SymbioteNative app:**
23
+
24
+ ```bash
25
+ npx @symbiote-native/cli add --navigation-bar
26
+ ```
27
+
28
+ Either way: installs `@symbiote-native/navigation-bar` and wires the native autolinking
29
+ automatically, see [`@symbiote-native/cli`](../cli).
30
+
31
+ <details>
32
+ <summary>Manual install (no CLI, wiring native autolinking by hand)</summary>
33
+
34
+ ```bash
35
+ npm install @symbiote-native/navigation-bar
36
+ ```
37
+
38
+ `expo-navigation-bar` and `expo-modules-core` come along as regular, pinned dependencies, never
39
+ install either yourself, and never add the `expo` meta-package to this project (it bundles its
40
+ own Metro/Babel pipeline that conflicts with this project's own).
41
+
42
+ ### Required one-time step: native autolinking wiring
43
+
44
+ Unlike a plain RN native module, `expo-navigation-bar`'s native code is discovered by
45
+ `expo-modules-autolinking`, wired into the native host app **once**, covering this package and
46
+ every other `expo-modules-core` package with zero further changes. Full mechanics live in the
47
+ `symbiote-expo-native-module` skill.
48
+
49
+ ### No manifest permissions, config plugin not carried over
50
+
51
+ - **No runtime permission, no manifest edit.** `expo-navigation-bar`'s own `AndroidManifest.xml`
52
+ declares nothing.
53
+ - **The config plugin was checked and produces no diff with default props.** Upstream's
54
+ `withNavigationBar` only touches `styles.xml` (`android:windowLightNavigationBar`,
55
+ `android:enforceNavigationBarContrast`) when the app passes explicit plugin props
56
+ (`hidden`/`style`/`enforceContrast`); with none given it returns the config unchanged, and this
57
+ repo has no `app.json` for a plugin to read props from anyway. Setting an initial style/hidden
58
+ value before first paint (rather than calling `setStyle`/`setHidden` after mount) needs those
59
+ `styles.xml` edits applied by hand - not something `native-link.json` covers, since it has no
60
+ style-resource field, only manifest attributes and iOS Info.plist array keys.
61
+
62
+ </details>
63
+
64
+ ## Shape
65
+
66
+ ```
67
+ src/core/ setStyle + setHidden + addVisibilityListener + setVisibilityAsync +
68
+ getVisibilityAsync + the entries-stack merge core. The native module
69
+ resolution is Android-only (native-module/index.android.ts); the base
70
+ native-module/index.ts is an empty stub, same as upstream's own
71
+ non-Android ExpoNavigationBar.ts
72
+ src/react/ NavigationBar + useVisibility (hooks)
73
+ src/vue/ NavigationBar + useVisibility (composable)
74
+ src/solid/ NavigationBar + createVisibility (primitive)
75
+ src/svelte/ NavigationBar.svelte + use-visibility.svelte.ts (rune)
76
+ src/angular/ NavigationBar + NavigationBarVisibilityService (DI)
77
+ ```
78
+
79
+ Upstream's declarative `<NavigationBar>` component and `useVisibility` hook are ported to
80
+ **every** adapter, not just React: the merge-stack logic lives once in `src/core/entries-stack.ts`
81
+ (`pushStackEntry`/`popStackEntry`/`replaceStackEntry`), and each adapter supplies only its own
82
+ lifecycle glue over that shared core, per this project's `components_split_logic_view_lifecycle`
83
+ convention.
84
+
85
+ ## Use it
86
+
87
+ ```ts
88
+ import { setHidden, setStyle } from '@symbiote-native/navigation-bar';
89
+
90
+ setStyle('dark');
91
+ setHidden(true);
92
+ ```
93
+
94
+ ```ts
95
+ import { addVisibilityListener, getVisibilityAsync } from '@symbiote-native/navigation-bar';
96
+
97
+ const visibility = await getVisibilityAsync();
98
+ const subscription = addVisibilityListener(({ visibility }) => {
99
+ console.log(visibility);
100
+ });
101
+ ```
102
+
103
+ Declaratively, per adapter:
104
+
105
+ ```tsx
106
+ // React / Vue / Solid: <NavigationBar style="dark" hidden={false} />
107
+ // Svelte: <NavigationBar style="dark" hidden={false} />
108
+ // Angular: <navigation-bar [style]="'dark'" [hidden]="false" />
109
+ ```
110
+
111
+ The deepest mounted `<NavigationBar>` wins on shared fields (upstream's own priority rule);
112
+ unmounting the last one falls back to `defaultNavigationBarProps`.
113
+
114
+ ## API
115
+
116
+ | Export | Signature | Notes |
117
+ | --- | --- | --- |
118
+ | `setStyle` | `(style: INavigationBarStyle) => void` | `'auto'`/`'inverted'` resolve against `Appearance.getColorScheme()`; skips a redundant native call when the resolved style is unchanged |
119
+ | `setHidden` | `(hidden: boolean) => void` | Skips a redundant native call when the value is unchanged |
120
+ | `addVisibilityListener` | `(listener: (event: INavigationBarVisibilityEvent) => void) => EventSubscription` | |
121
+ | `setVisibilityAsync` | `(visibility: INavigationBarVisibility) => Promise<void>` | Calls the native `setHidden` directly, bypassing `setHidden`'s own dedupe |
122
+ | `getVisibilityAsync` | `() => Promise<INavigationBarVisibility>` | |
123
+
124
+ ## Notes
125
+
126
+ - **Every function throws `UnavailabilityError` off Android**, replacing upstream's own
127
+ `console.warn`-and-no-op fallback - the same normalization this repo's other platform-gated
128
+ packages already apply.
129
+ - **`setVisibilityAsync` forwards straight to the native module**, not through this package's own
130
+ `setHidden`, matching upstream's exact observable behavior (and the ported test asserting it).
131
+
132
+ ## Common questions
133
+
134
+ - **`setBackgroundColorAsync` has no effect.** Reported on Expo Go; test in a development build.
135
+ - **"The current activity is no longer available".** A setter ran with no activity attached; retry
136
+ once the app is foregrounded.
137
+ - **`PlatformColor` fails.** Pass a plain color string.
138
+ - **Black bar with `inset-swipe`.** Reported on phones that hide the bottom bar by default.
139
+
140
+ Sources: [expo/expo#36814](https://github.com/expo/expo/issues/36814),
141
+ [expo/expo#33950](https://github.com/expo/expo/issues/33950),
142
+ [expo/expo#18515](https://github.com/expo/expo/issues/18515).
143
+
144
+ ## Test it
145
+
146
+ ```bash
147
+ pnpm vitest run packages/navigation-bar
148
+ ```
149
+
150
+ The core tests fake the native module in place of `requireNativeModule`'s runtime resolution -
151
+ `ExpoNavigationBar` only exists on a real Android device, so a headless run would otherwise throw
152
+ at import. Upstream's own three tests (`resolves the visibility from the native module`,
153
+ `calls setHidden from setVisibilityAsync`, `adds and removes a visibility listener`) are all
154
+ ported, translated onto this package's own fake-native-module harness; `setStyle`/`setHidden`'s
155
+ resolve-and-dedupe behavior is net-new coverage upstream never exercised directly. Each adapter's
156
+ `NavigationBar`/visibility port has its own lifecycle test (push/replace/pop the stack entry,
157
+ resolve/track/clean up visibility), 53 tests total across the package.
@@ -0,0 +1,3 @@
1
+ export * from '../core';
2
+ export { NavigationBar } from './navigation-bar';
3
+ export { NavigationBarVisibilityService } from './navigation-bar-visibility.service';
@@ -0,0 +1,3 @@
1
+ export * from '../core/index.js';
2
+ export { NavigationBar } from './navigation-bar.js';
3
+ export { NavigationBarVisibilityService } from './navigation-bar-visibility.service.js';
@@ -0,0 +1,5 @@
1
+ import type { INavigationBarVisibility } from '../core';
2
+ export declare class NavigationBarVisibilityService {
3
+ readonly visibility: import("@angular/core").WritableSignal<INavigationBarVisibility | null>;
4
+ constructor();
5
+ }
@@ -0,0 +1,73 @@
1
+ // Angular twin of `../react`/`../vue`/`../solid`/`../svelte`'s visibility hook, DI shape
2
+ // matching `ColorSchemeService` (`components_split_logic_view_lifecycle`)
3
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
4
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
5
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
6
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
7
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
8
+ var _, done = false;
9
+ for (var i = decorators.length - 1; i >= 0; i--) {
10
+ var context = {};
11
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
12
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
13
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
14
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
15
+ if (kind === "accessor") {
16
+ if (result === void 0) continue;
17
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
18
+ if (_ = accept(result.get)) descriptor.get = _;
19
+ if (_ = accept(result.set)) descriptor.set = _;
20
+ if (_ = accept(result.init)) initializers.unshift(_);
21
+ }
22
+ else if (_ = accept(result)) {
23
+ if (kind === "field") initializers.unshift(_);
24
+ else descriptor[key] = _;
25
+ }
26
+ }
27
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
28
+ done = true;
29
+ };
30
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
31
+ var useValue = arguments.length > 2;
32
+ for (var i = 0; i < initializers.length; i++) {
33
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
34
+ }
35
+ return useValue ? value : void 0;
36
+ };
37
+ import { DestroyRef, inject, Injectable, signal } from '@angular/core';
38
+ import { addVisibilityListener, getVisibilityAsync } from '../core/index.js';
39
+ let NavigationBarVisibilityService = (() => {
40
+ let _classDecorators = [Injectable({ providedIn: 'root' })];
41
+ let _classDescriptor;
42
+ let _classExtraInitializers = [];
43
+ let _classThis;
44
+ var NavigationBarVisibilityService = class {
45
+ static { _classThis = this; }
46
+ static {
47
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
48
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
49
+ NavigationBarVisibilityService = _classThis = _classDescriptor.value;
50
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
51
+ __runInitializers(_classThis, _classExtraInitializers);
52
+ }
53
+ visibility = signal(null);
54
+ constructor() {
55
+ const destroyRef = inject(DestroyRef);
56
+ let isMounted = true;
57
+ getVisibilityAsync().then(value => {
58
+ if (isMounted)
59
+ this.visibility.set(value);
60
+ });
61
+ const subscription = addVisibilityListener(({ visibility: next }) => {
62
+ if (isMounted)
63
+ this.visibility.set(next);
64
+ });
65
+ destroyRef.onDestroy(() => {
66
+ subscription.remove();
67
+ isMounted = false;
68
+ });
69
+ }
70
+ };
71
+ return NavigationBarVisibilityService = _classThis;
72
+ })();
73
+ export { NavigationBarVisibilityService };
@@ -0,0 +1,13 @@
1
+ import { type OnChanges, type OnDestroy, type OnInit } from '@angular/core';
2
+ import { type INavigationBarStyle } from '../core';
3
+ export declare class NavigationBar implements OnInit, OnChanges, OnDestroy {
4
+ style?: INavigationBarStyle;
5
+ hidden?: boolean;
6
+ private readonly colorScheme;
7
+ private stackEntry;
8
+ private isInitialized;
9
+ constructor();
10
+ ngOnInit(): void;
11
+ ngOnChanges(): void;
12
+ ngOnDestroy(): void;
13
+ }
@@ -0,0 +1,107 @@
1
+ // Angular twin of `../react`/`../vue`/`../solid`/`../svelte`'s `NavigationBar`, same shared
2
+ // stack core - only the lifecycle wiring differs per adapter
3
+ // (`components_split_logic_view_lifecycle`)
4
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
5
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
6
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
7
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
8
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
9
+ var _, done = false;
10
+ for (var i = decorators.length - 1; i >= 0; i--) {
11
+ var context = {};
12
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
13
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
14
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
15
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
16
+ if (kind === "accessor") {
17
+ if (result === void 0) continue;
18
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
19
+ if (_ = accept(result.get)) descriptor.get = _;
20
+ if (_ = accept(result.set)) descriptor.set = _;
21
+ if (_ = accept(result.init)) initializers.unshift(_);
22
+ }
23
+ else if (_ = accept(result)) {
24
+ if (kind === "field") initializers.unshift(_);
25
+ else descriptor[key] = _;
26
+ }
27
+ }
28
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
29
+ done = true;
30
+ };
31
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
32
+ var useValue = arguments.length > 2;
33
+ for (var i = 0; i < initializers.length; i++) {
34
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
35
+ }
36
+ return useValue ? value : void 0;
37
+ };
38
+ import { Component, effect, inject, Input, } from '@angular/core';
39
+ import { ColorSchemeService } from '@symbiote-native/angular';
40
+ import { popStackEntry, pushStackEntry, replaceStackEntry, } from '../core/index.js';
41
+ let NavigationBar = (() => {
42
+ let _classDecorators = [Component({
43
+ selector: 'navigation-bar',
44
+ standalone: true,
45
+ template: '',
46
+ })];
47
+ let _classDescriptor;
48
+ let _classExtraInitializers = [];
49
+ let _classThis;
50
+ let _style_decorators;
51
+ let _style_initializers = [];
52
+ let _style_extraInitializers = [];
53
+ let _hidden_decorators;
54
+ let _hidden_initializers = [];
55
+ let _hidden_extraInitializers = [];
56
+ var NavigationBar = class {
57
+ static { _classThis = this; }
58
+ static {
59
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
60
+ _style_decorators = [Input()];
61
+ _hidden_decorators = [Input()];
62
+ __esDecorate(null, null, _style_decorators, { kind: "field", name: "style", static: false, private: false, access: { has: obj => "style" in obj, get: obj => obj.style, set: (obj, value) => { obj.style = value; } }, metadata: _metadata }, _style_initializers, _style_extraInitializers);
63
+ __esDecorate(null, null, _hidden_decorators, { kind: "field", name: "hidden", static: false, private: false, access: { has: obj => "hidden" in obj, get: obj => obj.hidden, set: (obj, value) => { obj.hidden = value; } }, metadata: _metadata }, _hidden_initializers, _hidden_extraInitializers);
64
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
65
+ NavigationBar = _classThis = _classDescriptor.value;
66
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
67
+ __runInitializers(_classThis, _classExtraInitializers);
68
+ }
69
+ style = __runInitializers(this, _style_initializers, void 0);
70
+ hidden = (__runInitializers(this, _style_extraInitializers), __runInitializers(this, _hidden_initializers, void 0));
71
+ colorScheme = (__runInitializers(this, _hidden_extraInitializers), inject(ColorSchemeService).colorScheme);
72
+ stackEntry = null;
73
+ isInitialized = false;
74
+ constructor() {
75
+ effect(() => {
76
+ this.colorScheme();
77
+ if (this.stackEntry) {
78
+ this.stackEntry = replaceStackEntry(this.stackEntry, {
79
+ style: this.style,
80
+ hidden: this.hidden,
81
+ });
82
+ }
83
+ });
84
+ }
85
+ ngOnInit() {
86
+ this.stackEntry = pushStackEntry({
87
+ style: this.style,
88
+ hidden: this.hidden,
89
+ });
90
+ this.isInitialized = true;
91
+ }
92
+ ngOnChanges() {
93
+ if (!this.isInitialized || !this.stackEntry)
94
+ return;
95
+ this.stackEntry = replaceStackEntry(this.stackEntry, {
96
+ style: this.style,
97
+ hidden: this.hidden,
98
+ });
99
+ }
100
+ ngOnDestroy() {
101
+ if (this.stackEntry)
102
+ popStackEntry(this.stackEntry);
103
+ }
104
+ };
105
+ return NavigationBar = _classThis;
106
+ })();
107
+ export { NavigationBar };
@@ -0,0 +1,8 @@
1
+ import type { INavigationBarProps } from './types';
2
+ export type INavigationBarStackEntry = INavigationBarProps;
3
+ /** Registers a mounted `<NavigationBar>` entry */
4
+ export declare function pushStackEntry(props: INavigationBarProps): INavigationBarStackEntry;
5
+ /** Unregisters an entry, falling back to the previous entries or the last imperative call */
6
+ export declare function popStackEntry(entry: INavigationBarStackEntry): void;
7
+ /** Swaps an entry's props in place, for a mounted component's own prop changes */
8
+ export declare function replaceStackEntry(entry: INavigationBarStackEntry, props: INavigationBarProps): INavigationBarStackEntry;
@@ -0,0 +1,54 @@
1
+ // Framework-agnostic merge-stack over multiple mounted `<NavigationBar>` declarative components,
2
+ // ported from expo-navigation-bar's NavigationBar.android.ts - the last pushed (or deepest in the
3
+ // view hierarchy) entry wins on shared fields, matching upstream's own priority rule
4
+ import { defaultNavigationBarProps, setHidden, setStyle, } from './navigation-bar.js';
5
+ const entriesStack = [];
6
+ let updateImmediate = null;
7
+ function mergeEntriesStack(stack) {
8
+ return stack.reduce((prev, cur) => ({
9
+ style: cur.style ?? prev.style,
10
+ hidden: cur.hidden ?? prev.hidden,
11
+ }), {});
12
+ }
13
+ function createStackEntry(props) {
14
+ return { style: props.style, hidden: props.hidden };
15
+ }
16
+ function updateEntriesStack() {
17
+ if (updateImmediate != null)
18
+ clearImmediate(updateImmediate);
19
+ updateImmediate = setImmediate(() => {
20
+ if (entriesStack.length === 0) {
21
+ setStyle(defaultNavigationBarProps.style);
22
+ setHidden(defaultNavigationBarProps.hidden);
23
+ return;
24
+ }
25
+ const { style, hidden } = mergeEntriesStack(entriesStack);
26
+ if (style != null)
27
+ setStyle(style);
28
+ if (hidden != null)
29
+ setHidden(hidden);
30
+ });
31
+ }
32
+ /** Registers a mounted `<NavigationBar>` entry */
33
+ export function pushStackEntry(props) {
34
+ const entry = createStackEntry(props);
35
+ entriesStack.push(entry);
36
+ updateEntriesStack();
37
+ return entry;
38
+ }
39
+ /** Unregisters an entry, falling back to the previous entries or the last imperative call */
40
+ export function popStackEntry(entry) {
41
+ const index = entriesStack.indexOf(entry);
42
+ if (index !== -1)
43
+ entriesStack.splice(index, 1);
44
+ updateEntriesStack();
45
+ }
46
+ /** Swaps an entry's props in place, for a mounted component's own prop changes */
47
+ export function replaceStackEntry(entry, props) {
48
+ const newEntry = createStackEntry(props);
49
+ const index = entriesStack.indexOf(entry);
50
+ if (index !== -1)
51
+ entriesStack[index] = newEntry;
52
+ updateEntriesStack();
53
+ return newEntry;
54
+ }
@@ -0,0 +1,3 @@
1
+ export { addVisibilityListener, getVisibilityAsync, setHidden, setStyle, setVisibilityAsync, } from './navigation-bar';
2
+ export { popStackEntry, pushStackEntry, replaceStackEntry, type INavigationBarStackEntry, } from './entries-stack';
3
+ export type { INavigationBarProps, INavigationBarStyle, INavigationBarVisibility, INavigationBarVisibilityEvent, } from './types';
@@ -0,0 +1,2 @@
1
+ export { addVisibilityListener, getVisibilityAsync, setHidden, setStyle, setVisibilityAsync, } from './navigation-bar.js';
2
+ export { popStackEntry, pushStackEntry, replaceStackEntry, } from './entries-stack.js';
@@ -0,0 +1,2 @@
1
+ import type { INativeNavigationBarModule } from './shared';
2
+ export declare const expoNavigationBar: INativeNavigationBarModule;
@@ -0,0 +1,2 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ export const expoNavigationBar = requireNativeModule('ExpoNavigationBar');
@@ -0,0 +1,2 @@
1
+ import type { INativeNavigationBarModule } from './shared';
2
+ export declare const expoNavigationBar: Partial<INativeNavigationBarModule>;
@@ -0,0 +1,3 @@
1
+ // Android-only native module - upstream's own non-android entry types an empty stub, so every
2
+ // function guards a missing method with UnavailabilityError rather than calling this
3
+ export const expoNavigationBar = {};
@@ -0,0 +1,8 @@
1
+ import type { EventSubscription } from 'expo-modules-core';
2
+ import type { INavigationBarVisibility, INavigationBarVisibilityEvent } from '../types';
3
+ export type INativeNavigationBarModule = {
4
+ addListener(event: 'ExpoNavigationBar.didChange', listener: (event: INavigationBarVisibilityEvent) => void): EventSubscription;
5
+ setStyle(style: 'light' | 'dark'): Promise<void>;
6
+ setHidden(hidden: boolean): Promise<void>;
7
+ getVisibilityAsync(): Promise<INavigationBarVisibility>;
8
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import { type EventSubscription } from 'expo-modules-core';
2
+ import type { INavigationBarStyle, INavigationBarVisibility, INavigationBarVisibilityEvent } from './types';
3
+ export declare const defaultNavigationBarProps: {
4
+ style: INavigationBarStyle;
5
+ hidden: boolean;
6
+ };
7
+ /** Sets the navigation bar buttons' style */
8
+ export declare function setStyle(style: INavigationBarStyle): void;
9
+ /** Shows or hides the navigation bar */
10
+ export declare function setHidden(hidden: boolean): void;
11
+ /** Observes changes to the system navigation bar's visibility */
12
+ export declare function addVisibilityListener(listener: (event: INavigationBarVisibilityEvent) => void): EventSubscription;
13
+ /** Sets the navigation bar's visibility */
14
+ export declare function setVisibilityAsync(visibility: INavigationBarVisibility): Promise<void>;
15
+ /** Gets the navigation bar's current visibility */
16
+ export declare function getVisibilityAsync(): Promise<INavigationBarVisibility>;
@@ -0,0 +1,69 @@
1
+ import { UnavailabilityError } from 'expo-modules-core';
2
+ import { Appearance } from '@symbiote-native/engine';
3
+ import { expoNavigationBar } from './native-module';
4
+ function isLightColorScheme() {
5
+ return (Appearance.getColorScheme() ?? 'light') === 'light';
6
+ }
7
+ function resolveStyle(style) {
8
+ switch (style) {
9
+ case 'auto':
10
+ return isLightColorScheme() ? 'dark' : 'light';
11
+ case 'inverted':
12
+ return isLightColorScheme() ? 'light' : 'dark';
13
+ default:
14
+ return style;
15
+ }
16
+ }
17
+ let currentStyle;
18
+ let currentHidden;
19
+ // The baseline the declarative `<NavigationBar>` stack falls back to once every mounted
20
+ // entry unmounts, kept in sync with every imperative `setStyle`/`setHidden` call too -
21
+ // mirrors upstream's own `defaultProps`, read by ./entries-stack.ts
22
+ export const defaultNavigationBarProps = {
23
+ style: 'light',
24
+ hidden: false,
25
+ };
26
+ /** Sets the navigation bar buttons' style */
27
+ export function setStyle(style) {
28
+ if (!expoNavigationBar.setStyle) {
29
+ throw new UnavailabilityError('NavigationBar', 'setStyle');
30
+ }
31
+ defaultNavigationBarProps.style = style;
32
+ const resolved = resolveStyle(style);
33
+ if (resolved === currentStyle)
34
+ return;
35
+ currentStyle = resolved;
36
+ expoNavigationBar.setStyle(resolved);
37
+ }
38
+ /** Shows or hides the navigation bar */
39
+ export function setHidden(hidden) {
40
+ if (!expoNavigationBar.setHidden) {
41
+ throw new UnavailabilityError('NavigationBar', 'setHidden');
42
+ }
43
+ defaultNavigationBarProps.hidden = hidden;
44
+ if (hidden === currentHidden)
45
+ return;
46
+ currentHidden = hidden;
47
+ expoNavigationBar.setHidden(hidden);
48
+ }
49
+ /** Observes changes to the system navigation bar's visibility */
50
+ export function addVisibilityListener(listener) {
51
+ if (!expoNavigationBar.addListener) {
52
+ throw new UnavailabilityError('NavigationBar', 'addVisibilityListener');
53
+ }
54
+ return expoNavigationBar.addListener('ExpoNavigationBar.didChange', listener);
55
+ }
56
+ /** Sets the navigation bar's visibility */
57
+ export async function setVisibilityAsync(visibility) {
58
+ if (!expoNavigationBar.setHidden) {
59
+ throw new UnavailabilityError('NavigationBar', 'setVisibilityAsync');
60
+ }
61
+ await expoNavigationBar.setHidden(visibility === 'hidden');
62
+ }
63
+ /** Gets the navigation bar's current visibility */
64
+ export async function getVisibilityAsync() {
65
+ if (!expoNavigationBar.getVisibilityAsync) {
66
+ throw new UnavailabilityError('NavigationBar', 'getVisibilityAsync');
67
+ }
68
+ return expoNavigationBar.getVisibilityAsync();
69
+ }
@@ -0,0 +1,14 @@
1
+ export type INavigationBarVisibility = 'visible' | 'hidden';
2
+ export type INavigationBarVisibilityEvent = {
3
+ /** Current navigation bar visibility */
4
+ visibility: INavigationBarVisibility;
5
+ /** Native Android system UI visibility state, from `setOnSystemUiVisibilityChangeListener` */
6
+ rawVisibility: number;
7
+ };
8
+ /** `auto`/`inverted` resolve against the current color scheme, `light`/`dark` are fixed */
9
+ export type INavigationBarStyle = 'auto' | 'inverted' | 'light' | 'dark';
10
+ export type INavigationBarProps = {
11
+ /** @default 'auto' */
12
+ style?: INavigationBarStyle;
13
+ hidden?: boolean;
14
+ };
@@ -0,0 +1,2 @@
1
+ // Ported from expo-navigation-bar's NavigationBar.types.ts (sdk-57)
2
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from '../core';
2
+ export { NavigationBar } from './navigation-bar';
3
+ export { useVisibility } from './use-visibility';
@@ -0,0 +1,3 @@
1
+ export * from '../core/index.js';
2
+ export { NavigationBar } from './navigation-bar.js';
3
+ export { useVisibility } from './use-visibility.js';
@@ -0,0 +1,3 @@
1
+ import { type INavigationBarProps } from '../core';
2
+ /** Declaratively configures the navigation bar - several mounted instances merge, last wins */
3
+ export declare function NavigationBar({ style, hidden }: INavigationBarProps): null;
@@ -0,0 +1,23 @@
1
+ import { useEffect, useMemo, useRef } from 'react';
2
+ import { useColorScheme } from '@symbiote-native/react';
3
+ import { popStackEntry, pushStackEntry, replaceStackEntry, } from '../core/index.js';
4
+ /** Declaratively configures the navigation bar - several mounted instances merge, last wins */
5
+ export function NavigationBar({ style, hidden }) {
6
+ const colorScheme = useColorScheme();
7
+ const stableProps = useMemo(() => ({ style, hidden }), [style, hidden]);
8
+ const stackEntryRef = useRef(null);
9
+ useEffect(() => {
10
+ stackEntryRef.current = pushStackEntry(stableProps);
11
+ return () => {
12
+ if (stackEntryRef.current)
13
+ popStackEntry(stackEntryRef.current);
14
+ };
15
+ // eslint-disable-next-line react-hooks/exhaustive-deps
16
+ }, []);
17
+ useEffect(() => {
18
+ if (stackEntryRef.current) {
19
+ stackEntryRef.current = replaceStackEntry(stackEntryRef.current, stableProps);
20
+ }
21
+ }, [colorScheme, stableProps]);
22
+ return null;
23
+ }
@@ -0,0 +1,3 @@
1
+ import type { INavigationBarVisibility } from '../core';
2
+ /** Statefully tracks the system navigation bar's visibility, `null` during async initialization */
3
+ export declare function useVisibility(): INavigationBarVisibility | null;