@symbiote-native/splash-screen 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +145 -0
  3. package/build-ngc/angular/index.d.ts +3 -0
  4. package/build-ngc/angular/index.js +6 -0
  5. package/build-ngc/angular/index.js.map +1 -0
  6. package/build-ngc/angular/services/hide-animation.service.d.ts +9 -0
  7. package/build-ngc/angular/services/hide-animation.service.js +32 -0
  8. package/build-ngc/angular/services/hide-animation.service.js.map +1 -0
  9. package/build-ngc/core/compute-hide-animation-styles.d.ts +3 -0
  10. package/build-ngc/core/compute-hide-animation-styles.js +78 -0
  11. package/build-ngc/core/compute-hide-animation-styles.js.map +1 -0
  12. package/build-ngc/core/hide-animation-controller.d.ts +10 -0
  13. package/build-ngc/core/hide-animation-controller.js +51 -0
  14. package/build-ngc/core/hide-animation-controller.js.map +1 -0
  15. package/build-ngc/core/hide.d.ts +1 -0
  16. package/build-ngc/core/hide.js +5 -0
  17. package/build-ngc/core/hide.js.map +1 -0
  18. package/build-ngc/core/index.d.ts +5 -0
  19. package/build-ngc/core/index.js +5 -0
  20. package/build-ngc/core/index.js.map +1 -0
  21. package/build-ngc/core/native-module.d.ts +2 -0
  22. package/build-ngc/core/native-module.js +13 -0
  23. package/build-ngc/core/native-module.js.map +1 -0
  24. package/build-ngc/core/types.d.ts +51 -0
  25. package/build-ngc/core/types.js +2 -0
  26. package/build-ngc/core/types.js.map +1 -0
  27. package/cli.cjs +23 -0
  28. package/package.json +118 -0
  29. package/react-native.config.cjs +39 -0
  30. package/src/angular/index.ts +6 -0
  31. package/src/angular/services/hide-animation.service.test.ts +205 -0
  32. package/src/angular/services/hide-animation.service.ts +35 -0
  33. package/src/core/compute-hide-animation-styles.ts +111 -0
  34. package/src/core/hide-animation-controller.ts +70 -0
  35. package/src/core/hide.ts +4 -0
  36. package/src/core/index.ts +13 -0
  37. package/src/core/native-module.ts +19 -0
  38. package/src/core/types.ts +62 -0
  39. package/src/react/hooks/use-hide-animation.test.tsx +114 -0
  40. package/src/react/hooks/use-hide-animation.ts +40 -0
  41. package/src/react/index.ts +9 -0
  42. package/src/vue/composables/use-hide-animation.test.ts +199 -0
  43. package/src/vue/composables/use-hide-animation.ts +32 -0
  44. package/src/vue/index.ts +9 -0
  45. package/symbiote-splash-screen.podspec +64 -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,145 @@
1
+ # @symbiote-native/splash-screen
2
+
3
+ A wrapper package for [SymbioteNative](../../README.md) that makes
4
+ [`react-native-bootsplash`](https://github.com/zoontek/react-native-bootsplash) usable from
5
+ **every** adapter — React, Vue, and Angular — not just React. Unlike a native-*view* wrapper
6
+ (see `@symbiote-native/slider`), bootsplash exposes an imperative TurboModule (`hide`/`isVisible`)
7
+ plus a React hook (`useHideAnimation`) that composes a fade-out overlay from plain `View`/`Image`
8
+ primitives — there is no `ViewConfig` to register, only lifecycle to port per adapter.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @symbiote-native/splash-screen
14
+ ```
15
+
16
+ Only this package — never `react-native-bootsplash` directly. `@symbiote-native/splash-screen`
17
+ depends on it and ships as the sole autolinked native proxy (`react-native.config.cjs` +
18
+ `symbiote-splash-screen.podspec`), the same one-dependency packaging as `@symbiote-native/slider`
19
+ (see the `symbiote-third-party-native-view` skill).
20
+
21
+ ## Shape
22
+
23
+ ```
24
+ src/core/ hide()/isVisible() (re-exported as-is — zero React upstream), the readiness-gate
25
+ HideAnimationController, and computeHideAnimationStyles: a faithful port of
26
+ react-native-bootsplash's useHideAnimation body, framework-agnostic
27
+ src/react/ @symbiote-native/splash-screen/react — useHideAnimation hook
28
+ src/vue/ @symbiote-native/splash-screen/vue — useHideAnimation composable
29
+ src/angular/ @symbiote-native/splash-screen/angular — HideAnimationService (signals)
30
+ ```
31
+
32
+ `hide()`/`isVisible()` are re-exported straight from `react-native-bootsplash` since the upstream
33
+ functions already have zero React dependency. `useHideAnimation` returns `{ container, logo,
34
+ brand }` prop bags the app binds to its own `View`/`Image` — the same contract upstream ships,
35
+ kept identical across adapters (Angular's service returns the same three values as Signals).
36
+
37
+ ## Required manual step: wiring the native init call
38
+
39
+ The upstream asset generator (see below) does **not** automate this — you must add it yourself,
40
+ in every app that uses this package:
41
+
42
+ **iOS** — `ios/YourApp/AppDelegate.swift`, inside `customize(_ rootView:)`:
43
+
44
+ ```swift
45
+ import RNBootSplash
46
+
47
+ override func customize(_ rootView: RCTRootView) {
48
+ super.customize(rootView)
49
+ RNBootSplash.initWithStoryboard("BootSplash", rootView: rootView)
50
+ }
51
+ ```
52
+
53
+ **Android** — `android/app/src/main/java/.../MainActivity.kt`, inside `onCreate`:
54
+
55
+ ```kotlin
56
+ import com.zoontek.rnbootsplash.RNBootSplash
57
+
58
+ override fun onCreate(savedInstanceState: Bundle?) {
59
+ RNBootSplash.init(this, R.style.BootTheme)
60
+ super.onCreate(savedInstanceState)
61
+ }
62
+ ```
63
+
64
+ Skipping this step means the native splash is never shown, and `hide()`/`useHideAnimation` have
65
+ nothing to hide.
66
+
67
+ ## Asset generation CLI
68
+
69
+ ```bash
70
+ npx symbiote-splash-screen generate <logo> [options]
71
+ ```
72
+
73
+ A thin rebrand of `react-native-bootsplash`'s own generator (`bin: symbiote-splash-screen`,
74
+ spawned as a child process of its real `cli.js` since that script sits outside upstream's
75
+ `exports` map) — it writes native Android/iOS/web project files (drawables, `styles.xml`'s
76
+ `Theme.BootSplash`, `LaunchScreen.storyboard`, `Info.plist`'s launch-screen key) plus
77
+ `assets/bootsplash/manifest.json` (the same shape `IManifest` expects: `background`,
78
+ `logo.{width,height}`, optional `darkBackground`/`brand`), independent of which adapter the app
79
+ uses. Zero reimplementation; run `--help` for the full flag list (`--brand`, `--dark-*`, etc. —
80
+ the multi-density/dark-mode addon flags require a paid `--license-key` from the upstream author;
81
+ pass it through as-is, this package doesn't attempt to replicate it).
82
+
83
+ ## Use it
84
+
85
+ `hide()` is the simple case — call it once your JS tree has mounted:
86
+
87
+ ```tsx
88
+ // React — examples/react/App.tsx
89
+ import { hide } from '@symbiote-native/splash-screen/react';
90
+
91
+ useEffect(() => {
92
+ hide();
93
+ }, []);
94
+ ```
95
+
96
+ ```vue
97
+ <!-- Vue — examples/vue-sfc/App.vue -->
98
+ <script setup lang="ts">
99
+ import { onMounted } from 'vue';
100
+ import { hide } from '@symbiote-native/splash-screen/vue';
101
+
102
+ onMounted(() => hide());
103
+ </script>
104
+ ```
105
+
106
+ ```ts
107
+ // Angular — examples/angular/App.ts
108
+ import { hide } from '@symbiote-native/splash-screen/angular';
109
+ // call once from the root component's constructor/ngOnInit.
110
+ ```
111
+
112
+ For a fade transition gated on real readiness (layout committed + logo/brand images loaded +
113
+ your own `ready` flag) instead of an immediate `hide()`, use `useHideAnimation` — it returns the
114
+ same `{ container, logo, brand }` prop bags upstream's hook does, which you bind onto your own
115
+ `View`/`Image`:
116
+
117
+ ```tsx
118
+ import { useHideAnimation } from '@symbiote-native/splash-screen/react';
119
+ import manifest from '../assets/bootsplash/manifest.json';
120
+
121
+ const { container, logo } = useHideAnimation({
122
+ manifest,
123
+ logo: require('../assets/bootsplash/logo.png'),
124
+ animate: () => {
125
+ /* your own fade-out, e.g. an Animated.timing to opacity 0 */
126
+ },
127
+ });
128
+ ```
129
+
130
+ See the docs-site package page (`docs/packages/splash-screen`) for the full config surface and
131
+ the Vue/Angular equivalents.
132
+
133
+ ## Test it
134
+
135
+ Headless hook/composable tests live next to each adapter entry
136
+ (`src/{react,vue}/{hooks,composables}/use-hide-animation.test.{ts,tsx}`) and mock
137
+ `react-native-bootsplash`'s `hide`/`isVisible` plus a fake `__turboModuleProxy` for
138
+ `getConstants()`, so they run without a real Fabric host. Native asset generation and the
139
+ Android/iOS wiring above are verified on-device (see the parent [README](../../README.md) for the
140
+ project's testing model).
141
+
142
+ ## Out of scope
143
+
144
+ - The Expo config plugin (`app.plugin.js`, upstream's `/expo` entry) — this repo doesn't target
145
+ Expo.
@@ -0,0 +1,3 @@
1
+ export { hide, isVisible } from '../core';
2
+ export { HideAnimationService } from './services/hide-animation.service';
3
+ export type { IHideAnimationConfig, IHideAnimationResult, IManifest, IHideConfig } from '../core';
@@ -0,0 +1,6 @@
1
+ // hide()/isVisible() carry zero framework dependency — re-exported verbatim from core, same as
2
+ // every other adapter. HideAnimationService is the Angular-only lifecycle half; the state
3
+ // machine, style computation, and native-constants read all live in core, shared with React/Vue.
4
+ export { hide, isVisible } from '../core';
5
+ export { HideAnimationService } from './services/hide-animation.service';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/angular/index.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,0FAA0F;AAC1F,iGAAiG;AACjG,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type Signal } from '@angular/core';
2
+ import { type IHideAnimationConfig, type IHideAnimationResult } from '../../core';
3
+ import * as i0 from "@angular/core";
4
+ export declare class HideAnimationService {
5
+ private readonly injector;
6
+ connect(getConfig: () => IHideAnimationConfig): Signal<IHideAnimationResult>;
7
+ static ɵfac: i0.ɵɵFactoryDeclaration<HideAnimationService, never>;
8
+ static ɵprov: i0.ɵɵInjectableDeclaration<HideAnimationService>;
9
+ }
@@ -0,0 +1,32 @@
1
+ import { computed, effect, inject, Injectable, Injector } from '@angular/core';
2
+ import { computeHideAnimationStyles, getHideAnimationConstants, HideAnimationController, } from '../../core';
3
+ import * as i0 from "@angular/core";
4
+ // Angular twin of React's `useHideAnimation` hook and Vue's `useHideAnimation` composable.
5
+ // Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
6
+ // stands in for the hook's role: call it ONCE (typically from a component's field initializer,
7
+ // inside an injection context) with a GETTER, so it can keep reading the caller's own signals —
8
+ // the same reason Vue's composable also takes a getter rather than a value: there is no
9
+ // re-render/re-setup cycle here to hang a fresh read off of.
10
+ //
11
+ // readonly hideAnimation = inject(HideAnimationService).connect(() => this.config());
12
+ // // template: [style]="hideAnimation().container.style", (layout)="..." etc.
13
+ export class HideAnimationService {
14
+ // Captured in the constructor (itself always run inside an injection context by Angular's
15
+ // own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
16
+ // code that is not, on its own, an active injection context — mirrors create-tunnel.ts's
17
+ // `TunnelOut`, which passes its own captured `Injector` to `effect()` for the same reason.
18
+ injector = inject(Injector);
19
+ connect(getConfig) {
20
+ const controller = new HideAnimationController(getConfig());
21
+ const constants = getHideAnimationConstants();
22
+ effect(() => controller.updateConfig(getConfig()), { injector: this.injector });
23
+ return computed(() => computeHideAnimationStyles(getConfig(), constants, controller));
24
+ }
25
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: HideAnimationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
26
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: HideAnimationService, providedIn: 'root' });
27
+ }
28
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: HideAnimationService, decorators: [{
29
+ type: Injectable,
30
+ args: [{ providedIn: 'root' }]
31
+ }] });
32
+ //# sourceMappingURL=hide-animation.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hide-animation.service.js","sourceRoot":"","sources":["../../../src/angular/services/hide-animation.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAe,MAAM,eAAe,CAAC;AAC5F,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,GAGxB,MAAM,YAAY,CAAC;;AAEpB,2FAA2F;AAC3F,4FAA4F;AAC5F,+FAA+F;AAC/F,gGAAgG;AAChG,wFAAwF;AACxF,6DAA6D;AAC7D,EAAE;AACF,wFAAwF;AACxF,gFAAgF;AAEhF,MAAM,OAAO,oBAAoB;IAC/B,0FAA0F;IAC1F,gGAAgG;IAChG,yFAAyF;IACzF,2FAA2F;IAC1E,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO,CAAC,SAAqC;QAC3C,MAAM,UAAU,GAAG,IAAI,uBAAuB,CAAC,SAAS,EAAE,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,yBAAyB,EAAE,CAAC;QAE9C,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEhF,OAAO,QAAQ,CAAC,GAAG,EAAE,CAAC,0BAA0B,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IACxF,CAAC;uGAdU,oBAAoB;2GAApB,oBAAoB,cADP,MAAM;;2FACnB,oBAAoB;kBADhC,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
@@ -0,0 +1,3 @@
1
+ import type { HideAnimationController } from './hide-animation-controller';
2
+ import type { IHideAnimationConfig, IHideAnimationConstants, IHideAnimationResult } from './types';
3
+ export declare function computeHideAnimationStyles(config: IHideAnimationConfig, constants: IHideAnimationConstants, controller: HideAnimationController): IHideAnimationResult;
@@ -0,0 +1,78 @@
1
+ import { Platform } from '@symbiote-native/engine';
2
+ import { isEdgeToEdge } from 'react-native-is-edge-to-edge';
3
+ // react-native-bootsplash also branches on Platform.OS === 'web' here (react-native-web
4
+ // support); SymbioteNative ships iOS + Android only, so that branch is dropped rather than
5
+ // ported dead.
6
+ const IS_EDGE_TO_EDGE = isEdgeToEdge();
7
+ const SKIPPED_IMAGE_SOURCE = -1;
8
+ // Faithful port of react-native-bootsplash's useHideAnimation style computation (its
9
+ // src/index.ts useMemo body): container/logo/brand prop bags the app binds to its own
10
+ // View/Image, unchanged across React/Vue/Angular since none of this touches lifecycle.
11
+ export function computeHideAnimationStyles(config, constants, controller) {
12
+ const { manifest, statusBarTranslucent, navigationBarTranslucent } = config;
13
+ const { darkModeEnabled, logoSizeRatio = 1, navigationBarHeight = 0, statusBarHeight = 0, } = constants;
14
+ const skipLogo = config.logo == null;
15
+ const skipBrand = manifest.brand == null || config.brand == null;
16
+ const backgroundColor = darkModeEnabled && manifest.darkBackground != null
17
+ ? manifest.darkBackground
18
+ : manifest.background;
19
+ const logoSource = skipLogo
20
+ ? undefined
21
+ : darkModeEnabled && config.darkLogo != null
22
+ ? config.darkLogo
23
+ : config.logo;
24
+ const brandSource = skipBrand
25
+ ? undefined
26
+ : darkModeEnabled && config.darkBrand != null
27
+ ? config.darkBrand
28
+ : config.brand;
29
+ const isAndroid = Platform.OS === 'android';
30
+ const container = {
31
+ style: {
32
+ alignItems: 'center',
33
+ backgroundColor,
34
+ justifyContent: 'center',
35
+ position: 'absolute',
36
+ left: 0,
37
+ right: 0,
38
+ top: 0,
39
+ bottom: 0,
40
+ marginTop: isAndroid && !IS_EDGE_TO_EDGE && !(statusBarTranslucent ?? false)
41
+ ? -statusBarHeight
42
+ : undefined,
43
+ marginBottom: isAndroid && !IS_EDGE_TO_EDGE && !(navigationBarTranslucent ?? false)
44
+ ? -navigationBarHeight
45
+ : undefined,
46
+ },
47
+ onLayout: controller.onContainerLayout,
48
+ };
49
+ const logoSizeMultiplier = isAndroid ? logoSizeRatio : 1;
50
+ const logo = logoSource == null
51
+ ? { source: SKIPPED_IMAGE_SOURCE }
52
+ : {
53
+ source: logoSource,
54
+ fadeDuration: 0,
55
+ resizeMode: 'contain',
56
+ style: {
57
+ width: manifest.logo.width * logoSizeMultiplier,
58
+ height: manifest.logo.height * logoSizeMultiplier,
59
+ },
60
+ onLoadEnd: controller.onLogoLoadEnd,
61
+ };
62
+ const brand = brandSource == null || manifest.brand == null
63
+ ? { source: SKIPPED_IMAGE_SOURCE }
64
+ : {
65
+ source: brandSource,
66
+ fadeDuration: 0,
67
+ resizeMode: 'contain',
68
+ style: {
69
+ position: 'absolute',
70
+ bottom: manifest.brand.bottom,
71
+ width: manifest.brand.width,
72
+ height: manifest.brand.height,
73
+ },
74
+ onLoadEnd: controller.onBrandLoadEnd,
75
+ };
76
+ return { container, logo, brand };
77
+ }
78
+ //# sourceMappingURL=compute-hide-animation-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute-hide-animation-styles.js","sourceRoot":"","sources":["../../src/core/compute-hide-animation-styles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { IHideAnimationConfig } from './types';
2
+ export declare class HideAnimationController {
3
+ private readonly readiness;
4
+ constructor(config: IHideAnimationConfig);
5
+ updateConfig(config: IHideAnimationConfig): void;
6
+ readonly onContainerLayout: () => void;
7
+ readonly onLogoLoadEnd: () => void;
8
+ readonly onBrandLoadEnd: () => void;
9
+ private maybeRunAnimate;
10
+ }
@@ -0,0 +1,51 @@
1
+ import { hide } from './hide';
2
+ // Faithful port of react-native-bootsplash's useHideAnimation readiness gate (its
3
+ // src/index.ts): hide() fires exactly once, after layout + both images (if requested) +
4
+ // the caller all report ready, then the caller's own fade-out `animate()` runs. logoReady/
5
+ // brandReady are captured ONCE at construction (mirrors the original's useRef factory,
6
+ // evaluated only on first render) — a config that later drops its logo/brand source does
7
+ // NOT retroactively flip readiness back on, only updateConfig's animate/userReady do.
8
+ export class HideAnimationController {
9
+ readiness;
10
+ constructor(config) {
11
+ this.readiness = {
12
+ layoutReady: false,
13
+ logoReady: config.logo == null,
14
+ brandReady: config.manifest.brand == null || config.brand == null,
15
+ userReady: config.ready ?? true,
16
+ animate: config.animate,
17
+ animateHasBeenCalled: false,
18
+ };
19
+ }
20
+ updateConfig(config) {
21
+ this.readiness.animate = config.animate;
22
+ this.readiness.userReady = config.ready ?? true;
23
+ this.maybeRunAnimate();
24
+ }
25
+ onContainerLayout = () => {
26
+ this.readiness.layoutReady = true;
27
+ this.maybeRunAnimate();
28
+ };
29
+ onLogoLoadEnd = () => {
30
+ this.readiness.logoReady = true;
31
+ this.maybeRunAnimate();
32
+ };
33
+ onBrandLoadEnd = () => {
34
+ this.readiness.brandReady = true;
35
+ this.maybeRunAnimate();
36
+ };
37
+ maybeRunAnimate() {
38
+ const state = this.readiness;
39
+ if (state.layoutReady &&
40
+ state.logoReady &&
41
+ state.brandReady &&
42
+ state.userReady &&
43
+ !state.animateHasBeenCalled) {
44
+ state.animateHasBeenCalled = true;
45
+ hide({ fade: false })
46
+ .then(() => state.animate())
47
+ .catch(() => { });
48
+ }
49
+ }
50
+ }
51
+ //# sourceMappingURL=hide-animation-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hide-animation-controller.js","sourceRoot":"","sources":["../../src/core/hide-animation-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAY9B,kFAAkF;AAClF,wFAAwF;AACxF,2FAA2F;AAC3F,uFAAuF;AACvF,yFAAyF;AACzF,sFAAsF;AACtF,MAAM,OAAO,uBAAuB;IACjB,SAAS,CAAkB;IAE5C,YAAY,MAA4B;QACtC,IAAI,CAAC,SAAS,GAAG;YACf,WAAW,EAAE,KAAK;YAClB,SAAS,EAAE,MAAM,CAAC,IAAI,IAAI,IAAI;YAC9B,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC,KAAK,IAAI,IAAI;YACjE,SAAS,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI;YAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,oBAAoB,EAAE,KAAK;SAC5B,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,MAA4B;QACvC,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QACxC,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC;QAChD,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAEQ,iBAAiB,GAAG,GAAS,EAAE;QACtC,IAAI,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC;QAClC,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC,CAAC;IAEO,aAAa,GAAG,GAAS,EAAE;QAClC,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,IAAI,CAAC;QAChC,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC,CAAC;IAEO,cAAc,GAAG,GAAS,EAAE;QACnC,IAAI,CAAC,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC,CAAC;IAEM,eAAe;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;QAE7B,IACE,KAAK,CAAC,WAAW;YACjB,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,UAAU;YAChB,KAAK,CAAC,SAAS;YACf,CAAC,KAAK,CAAC,oBAAoB,EAC3B,CAAC;YACD,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAClC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;iBAClB,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;iBAC3B,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1 @@
1
+ export { hide, isVisible } from 'react-native-bootsplash';
@@ -0,0 +1,5 @@
1
+ // hide()/isVisible() carry zero React dependency upstream (react-native-bootsplash's own
2
+ // src/index.ts calls straight into its TurboModule) — framework-agnostic as-is, so every
3
+ // adapter re-exports this verbatim rather than re-wrapping it.
4
+ export { hide, isVisible } from 'react-native-bootsplash';
5
+ //# sourceMappingURL=hide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hide.js","sourceRoot":"","sources":["../../src/core/hide.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,yFAAyF;AACzF,+DAA+D;AAC/D,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { hide, isVisible } from './hide';
2
+ export { HideAnimationController } from './hide-animation-controller';
3
+ export { computeHideAnimationStyles } from './compute-hide-animation-styles';
4
+ export { getHideAnimationConstants } from './native-module';
5
+ export type { IHideConfig, IManifest, IHideAnimationConfig, IHideAnimationContainerProps, IHideAnimationImageProps, IHideAnimationResult, IHideAnimationConstants, } from './types';
@@ -0,0 +1,5 @@
1
+ export { hide, isVisible } from './hide';
2
+ export { HideAnimationController } from './hide-animation-controller';
3
+ export { computeHideAnimationStyles } from './compute-hide-animation-styles';
4
+ export { getHideAnimationConstants } from './native-module';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { IHideAnimationConstants } from './types';
2
+ export declare function getHideAnimationConstants(): IHideAnimationConstants;
@@ -0,0 +1,13 @@
1
+ // react-native-bootsplash's package.json ships an `exports` map that only opens `.`,
2
+ // `./expo`, `./package.json`, `./app.plugin.js` — its internal TurboModule spec
3
+ // (dist/commonjs/specs/NativeRNBootSplash) is NOT a published subpath, so a deep import
4
+ // would throw ERR_PACKAGE_PATH_NOT_EXPORTED. getEnforcingNativeModule reads the same
5
+ // global TurboModule proxy the spec itself reads (by name), sidestepping the exports map
6
+ // entirely — this is how we reach getConstants(), which the package's public JS API
7
+ // (hide/isVisible/useHideAnimation) never exposes directly.
8
+ import { getEnforcingNativeModule } from '@symbiote-native/engine';
9
+ const RN_BOOT_SPLASH_MODULE_NAME = 'RNBootSplash';
10
+ export function getHideAnimationConstants() {
11
+ return getEnforcingNativeModule(RN_BOOT_SPLASH_MODULE_NAME).getConstants();
12
+ }
13
+ //# sourceMappingURL=native-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,gFAAgF;AAChF,wFAAwF;AACxF,qFAAqF;AACrF,yFAAyF;AACzF,oFAAoF;AACpF,4DAA4D;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAGnE,MAAM,0BAA0B,GAAG,cAAc,CAAC;AAMlD,MAAM,UAAU,yBAAyB;IACvC,OAAO,wBAAwB,CAAoB,0BAA0B,CAAC,CAAC,YAAY,EAAE,CAAC;AAChG,CAAC"}
@@ -0,0 +1,51 @@
1
+ import type { IImageSourceProp, IResizeMode } from '@symbiote-native/components';
2
+ import type { IStyleProp, IViewStyle } from '@symbiote-native/engine';
3
+ export type IHideConfig = {
4
+ fade?: boolean;
5
+ };
6
+ export type IManifest = {
7
+ background: string;
8
+ darkBackground?: string;
9
+ logo: {
10
+ width: number;
11
+ height: number;
12
+ };
13
+ brand?: {
14
+ bottom: number;
15
+ width: number;
16
+ height: number;
17
+ };
18
+ };
19
+ export type IHideAnimationConfig = {
20
+ manifest: IManifest;
21
+ ready?: boolean;
22
+ logo?: IImageSourceProp;
23
+ darkLogo?: IImageSourceProp;
24
+ brand?: IImageSourceProp;
25
+ darkBrand?: IImageSourceProp;
26
+ animate: () => void;
27
+ statusBarTranslucent?: boolean;
28
+ navigationBarTranslucent?: boolean;
29
+ };
30
+ export type IHideAnimationContainerProps = {
31
+ style: IStyleProp<IViewStyle>;
32
+ onLayout: () => void;
33
+ };
34
+ export type IHideAnimationImageProps = {
35
+ source: IImageSourceProp;
36
+ fadeDuration?: number;
37
+ resizeMode?: IResizeMode;
38
+ style?: IStyleProp<IViewStyle>;
39
+ onLoadEnd?: () => void;
40
+ };
41
+ export type IHideAnimationResult = {
42
+ container: IHideAnimationContainerProps;
43
+ logo: IHideAnimationImageProps;
44
+ brand: IHideAnimationImageProps;
45
+ };
46
+ export type IHideAnimationConstants = {
47
+ darkModeEnabled: boolean;
48
+ logoSizeRatio?: number;
49
+ navigationBarHeight?: number;
50
+ statusBarHeight?: number;
51
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
package/cli.cjs ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Thin rebrand of react-native-bootsplash's own asset-generation CLI — zero reimplementation.
4
+ // Its `generate()` only writes native android/ios/web project files (drawables, storyboards,
5
+ // AndroidManifest/Info.plist theme wiring); it has no idea which JS renderer the app uses, so
6
+ // delegating is exact. We can't `require()` its internals directly: its package.json `exports`
7
+ // map only opens ".", "./expo", "./package.json", "./app.plugin.js" — the CLI script and the
8
+ // generate module live outside that map. Resolving `cli.js` as a sibling of the (exported)
9
+ // package.json and spawning it as its own process sidesteps the exports encapsulation
10
+ // entirely, since we never `require()` into the package — we just run its bin file.
11
+ const path = require('path');
12
+ const { spawnSync } = require('child_process');
13
+
14
+ const nativeCliPath = path.join(
15
+ path.dirname(require.resolve('react-native-bootsplash/package.json')),
16
+ 'cli.js',
17
+ );
18
+
19
+ const result = spawnSync(process.execPath, [nativeCliPath, ...process.argv.slice(2)], {
20
+ stdio: 'inherit',
21
+ });
22
+
23
+ process.exit(result.status ?? 1);
package/package.json ADDED
@@ -0,0 +1,118 @@
1
+ {
2
+ "name": "@symbiote-native/splash-screen",
3
+ "version": "0.1.1",
4
+ "description": "Third-party splash-screen native module wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/OneEyed1366/symbiote-native.git",
8
+ "directory": "packages/splash-screen"
9
+ },
10
+ "homepage": "https://github.com/OneEyed1366/symbiote-native/tree/master/packages/splash-screen#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/OneEyed1366/symbiote-native/issues"
13
+ },
14
+ "author": "Andrey Prokopenko <psevdoproger@gmail.com>",
15
+ "type": "module",
16
+ "main": "./build/core/index.js",
17
+ "module": "./build/core/index.js",
18
+ "types": "./build/core/index.d.ts",
19
+ "bin": {
20
+ "symbiote-splash-screen": "./cli.cjs"
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "types": "./build/core/index.d.ts",
25
+ "default": "./build/core/index.js"
26
+ },
27
+ "./vue": {
28
+ "types": "./build/vue/index.d.ts",
29
+ "default": "./build/vue/index.js"
30
+ },
31
+ "./react": {
32
+ "types": "./build/react/index.d.ts",
33
+ "default": "./build/react/index.js"
34
+ },
35
+ "./angular": {
36
+ "types": "./build-ngc/angular/index.d.ts",
37
+ "react-native": "./build-ngc/angular/index.js",
38
+ "default": "./src/angular/index.ts"
39
+ }
40
+ },
41
+ "files": [
42
+ "src",
43
+ "build",
44
+ "build-ngc",
45
+ "cli.cjs",
46
+ "react-native.config.cjs",
47
+ "symbiote-splash-screen.podspec"
48
+ ],
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "codegenConfig": {
53
+ "name": "RNBootSplashSpec",
54
+ "type": "modules",
55
+ "jsSrcsDir": "node_modules/react-native-bootsplash/src/specs",
56
+ "android": {
57
+ "javaPackageName": "com.zoontek.rnbootsplash"
58
+ }
59
+ },
60
+ "dependencies": {
61
+ "react-native-bootsplash": "^7.3.2",
62
+ "react-native-is-edge-to-edge": "^1.3.1",
63
+ "@symbiote-native/components": "0.2.2",
64
+ "@symbiote-native/engine": "0.1.3"
65
+ },
66
+ "peerDependencies": {
67
+ "@angular/core": ">=20",
68
+ "@vue/runtime-core": "^3.5.13",
69
+ "react": ">=19.0.0",
70
+ "react-native": ">=0.86",
71
+ "vue": ">=3.5.0",
72
+ "@symbiote-native/angular": "0.3.2",
73
+ "@symbiote-native/vue": "0.3.2",
74
+ "@symbiote-native/react": "0.2.3"
75
+ },
76
+ "peerDependenciesMeta": {
77
+ "@symbiote-native/angular": {
78
+ "optional": true
79
+ },
80
+ "@symbiote-native/react": {
81
+ "optional": true
82
+ },
83
+ "@symbiote-native/vue": {
84
+ "optional": true
85
+ },
86
+ "@angular/core": {
87
+ "optional": true
88
+ },
89
+ "@vue/runtime-core": {
90
+ "optional": true
91
+ },
92
+ "react": {
93
+ "optional": true
94
+ },
95
+ "vue": {
96
+ "optional": true
97
+ }
98
+ },
99
+ "devDependencies": {
100
+ "@angular/compiler": "^22",
101
+ "@angular/compiler-cli": "^22",
102
+ "@angular/core": "^22",
103
+ "@types/node": "^26.0.0",
104
+ "@types/react": "^19.2.0",
105
+ "@vue/runtime-core": "^3.5.13",
106
+ "react": "19.2.3",
107
+ "typescript": "~6.0.0",
108
+ "@symbiote-native/react": "0.2.3",
109
+ "@symbiote-native/angular": "0.3.2",
110
+ "@symbiote-native/test-utils": "0.1.5",
111
+ "@symbiote-native/vue": "0.3.2"
112
+ },
113
+ "scripts": {
114
+ "typecheck": "tsc --build",
115
+ "ng:build": "ngc -p tsconfig.angular.json",
116
+ "format": "prettier --write \"src/**/*.{ts,tsx}\""
117
+ }
118
+ }