@symbiote-native/splash-screen 0.0.0-canary-20260724081123
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/LICENSE +21 -0
- package/README.md +145 -0
- package/build/angular/index.d.ts +3 -0
- package/build/angular/index.js +5 -0
- package/build/angular/services/hide-animation.service/index.d.ts +6 -0
- package/build/angular/services/hide-animation.service/index.js +74 -0
- package/build/core/compute-hide-animation-styles.d.ts +3 -0
- package/build/core/compute-hide-animation-styles.js +77 -0
- package/build/core/hide-animation-controller.d.ts +10 -0
- package/build/core/hide-animation-controller.js +50 -0
- package/build/core/hide.d.ts +1 -0
- package/build/core/hide.js +4 -0
- package/build/core/index.d.ts +5 -0
- package/build/core/index.js +4 -0
- package/build/core/native-module.d.ts +2 -0
- package/build/core/native-module.js +12 -0
- package/build/core/types.d.ts +51 -0
- package/build/core/types.js +1 -0
- package/build/react/hooks/use-hide-animation/index.d.ts +2 -0
- package/build/react/hooks/use-hide-animation/index.js +29 -0
- package/build/react/index.d.ts +3 -0
- package/build/react/index.js +7 -0
- package/build/vue/composables/use-hide-animation/index.d.ts +3 -0
- package/build/vue/composables/use-hide-animation/index.js +21 -0
- package/build/vue/index.d.ts +3 -0
- package/build/vue/index.js +7 -0
- package/build-ngc/angular/index.d.ts +3 -0
- package/build-ngc/angular/index.js +6 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/angular/services/hide-animation.service/index.d.ts +9 -0
- package/build-ngc/angular/services/hide-animation.service/index.js +32 -0
- package/build-ngc/angular/services/hide-animation.service/index.js.map +1 -0
- package/build-ngc/core/compute-hide-animation-styles.d.ts +3 -0
- package/build-ngc/core/compute-hide-animation-styles.js +78 -0
- package/build-ngc/core/compute-hide-animation-styles.js.map +1 -0
- package/build-ngc/core/hide-animation-controller.d.ts +10 -0
- package/build-ngc/core/hide-animation-controller.js +51 -0
- package/build-ngc/core/hide-animation-controller.js.map +1 -0
- package/build-ngc/core/hide.d.ts +1 -0
- package/build-ngc/core/hide.js +5 -0
- package/build-ngc/core/hide.js.map +1 -0
- package/build-ngc/core/index.d.ts +5 -0
- package/build-ngc/core/index.js +5 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +2 -0
- package/build-ngc/core/native-module.js +13 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/types.d.ts +51 -0
- package/build-ngc/core/types.js +2 -0
- package/build-ngc/core/types.js.map +1 -0
- package/cli.cjs +23 -0
- package/codegen-specs/NativeRNBootSplash.ts +15 -0
- package/codegen-specs/NativeRNBootSplash.web.ts +47 -0
- package/package.json +122 -0
- package/react-native.config.cjs +39 -0
- package/src/angular/index.ts +6 -0
- package/src/angular/services/hide-animation.service/hide-animation.service.test.ts +205 -0
- package/src/angular/services/hide-animation.service/index.ts +35 -0
- package/src/core/compute-hide-animation-styles.ts +111 -0
- package/src/core/hide-animation-controller.ts +70 -0
- package/src/core/hide.ts +4 -0
- package/src/core/index.ts +13 -0
- package/src/core/native-module.ts +19 -0
- package/src/core/types.ts +62 -0
- package/src/react/hooks/use-hide-animation/index.ts +40 -0
- package/src/react/hooks/use-hide-animation/use-hide-animation.test.tsx +114 -0
- package/src/react/index.ts +9 -0
- package/src/vue/composables/use-hide-animation/index.ts +32 -0
- package/src/vue/composables/use-hide-animation/use-hide-animation.test.ts +199 -0
- package/src/vue/index.ts +9 -0
- package/symbiote-splash-screen.podspec +67 -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,5 @@
|
|
|
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/index.js';
|
|
5
|
+
export { HideAnimationService } from './services/hide-animation.service/index.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Signal } from '@angular/core';
|
|
2
|
+
import { type IHideAnimationConfig, type IHideAnimationResult } from '../../../core';
|
|
3
|
+
export declare class HideAnimationService {
|
|
4
|
+
private readonly injector;
|
|
5
|
+
connect(getConfig: () => IHideAnimationConfig): Signal<IHideAnimationResult>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
3
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
4
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
5
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
6
|
+
var _, done = false;
|
|
7
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
8
|
+
var context = {};
|
|
9
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
10
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
11
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
12
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
13
|
+
if (kind === "accessor") {
|
|
14
|
+
if (result === void 0) continue;
|
|
15
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
16
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
17
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
18
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
19
|
+
}
|
|
20
|
+
else if (_ = accept(result)) {
|
|
21
|
+
if (kind === "field") initializers.unshift(_);
|
|
22
|
+
else descriptor[key] = _;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
26
|
+
done = true;
|
|
27
|
+
};
|
|
28
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
29
|
+
var useValue = arguments.length > 2;
|
|
30
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
31
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
32
|
+
}
|
|
33
|
+
return useValue ? value : void 0;
|
|
34
|
+
};
|
|
35
|
+
import { computed, effect, inject, Injectable, Injector } from '@angular/core';
|
|
36
|
+
import { computeHideAnimationStyles, getHideAnimationConstants, HideAnimationController, } from '../../../core/index.js';
|
|
37
|
+
// Angular twin of React's `useHideAnimation` hook and Vue's `useHideAnimation` composable.
|
|
38
|
+
// Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
|
|
39
|
+
// stands in for the hook's role: call it ONCE (typically from a component's field initializer,
|
|
40
|
+
// inside an injection context) with a GETTER, so it can keep reading the caller's own signals —
|
|
41
|
+
// the same reason Vue's composable also takes a getter rather than a value: there is no
|
|
42
|
+
// re-render/re-setup cycle here to hang a fresh read off of.
|
|
43
|
+
//
|
|
44
|
+
// readonly hideAnimation = inject(HideAnimationService).connect(() => this.config());
|
|
45
|
+
// // template: [style]="hideAnimation().container.style", (layout)="..." etc.
|
|
46
|
+
let HideAnimationService = (() => {
|
|
47
|
+
let _classDecorators = [Injectable({ providedIn: 'root' })];
|
|
48
|
+
let _classDescriptor;
|
|
49
|
+
let _classExtraInitializers = [];
|
|
50
|
+
let _classThis;
|
|
51
|
+
var HideAnimationService = class {
|
|
52
|
+
static { _classThis = this; }
|
|
53
|
+
static {
|
|
54
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
55
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
56
|
+
HideAnimationService = _classThis = _classDescriptor.value;
|
|
57
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
58
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
59
|
+
}
|
|
60
|
+
// Captured in the constructor (itself always run inside an injection context by Angular's
|
|
61
|
+
// own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
|
|
62
|
+
// code that is not, on its own, an active injection context — mirrors create-tunnel.ts's
|
|
63
|
+
// `TunnelOut`, which passes its own captured `Injector` to `effect()` for the same reason.
|
|
64
|
+
injector = inject(Injector);
|
|
65
|
+
connect(getConfig) {
|
|
66
|
+
const controller = new HideAnimationController(getConfig());
|
|
67
|
+
const constants = getHideAnimationConstants();
|
|
68
|
+
effect(() => controller.updateConfig(getConfig()), { injector: this.injector });
|
|
69
|
+
return computed(() => computeHideAnimationStyles(getConfig(), constants, controller));
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
return HideAnimationService = _classThis;
|
|
73
|
+
})();
|
|
74
|
+
export { HideAnimationService };
|
|
@@ -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,77 @@
|
|
|
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
|
+
}
|
|
@@ -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,50 @@
|
|
|
1
|
+
import { hide } from './hide.js';
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { hide, isVisible } from 'react-native-bootsplash';
|
|
@@ -0,0 +1,4 @@
|
|
|
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';
|
|
@@ -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,12 @@
|
|
|
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
|
+
}
|
|
@@ -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 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// React lifecycle wiring over the framework-agnostic HideAnimationController + style
|
|
2
|
+
// computation (core/) — mirrors the lifecycle-bucket naming convention of
|
|
3
|
+
// adapters/react/src/hooks. Faithful port of react-native-bootsplash's own useHideAnimation:
|
|
4
|
+
// the controller is constructed lazily ONCE (a useRef factory, never reconstructed across
|
|
5
|
+
// re-renders), native constants are read once via useState's lazy initializer (they never
|
|
6
|
+
// change), and an effect with NO dependency array re-syncs the controller's config after
|
|
7
|
+
// EVERY render — intentional, since `ready` flipping true only gets picked up this way.
|
|
8
|
+
//
|
|
9
|
+
// No useMemo around the style computation: upstream lists ~15 individual primitive fields
|
|
10
|
+
// (manifest.logo.width, backgroundColor, ...) as its memo's deps specifically so Object.is
|
|
11
|
+
// compares by VALUE, not by the `config`/`manifest` object's reference — callers construct
|
|
12
|
+
// that object fresh on every render (the normal way to call this hook), so keying a memo off
|
|
13
|
+
// the whole object would recompute every render anyway while looking like it memoizes.
|
|
14
|
+
// Reproducing the fine-grained list isn't worth it for a splash screen shown for a couple of
|
|
15
|
+
// renders at boot; computing plainly is honest about the actual cost and simpler to read.
|
|
16
|
+
import { useEffect, useRef, useState } from 'react';
|
|
17
|
+
import { computeHideAnimationStyles, getHideAnimationConstants, HideAnimationController, } from '../../../core/index.js';
|
|
18
|
+
export function useHideAnimation(config) {
|
|
19
|
+
const controllerRef = useRef(null);
|
|
20
|
+
if (controllerRef.current == null) {
|
|
21
|
+
controllerRef.current = new HideAnimationController(config);
|
|
22
|
+
}
|
|
23
|
+
const controller = controllerRef.current;
|
|
24
|
+
const [constants] = useState(() => getHideAnimationConstants());
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
controller.updateConfig(config);
|
|
27
|
+
});
|
|
28
|
+
return computeHideAnimationStyles(config, constants, controller);
|
|
29
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @symbiote-native/splash-screen/react: the React entry over the framework-agnostic core.
|
|
2
|
+
// hide/isVisible carry zero lifecycle and are re-exported verbatim; useHideAnimation wraps
|
|
3
|
+
// HideAnimationController + computeHideAnimationStyles with React's own lifecycle
|
|
4
|
+
// (hooks/use-hide-animation.ts) — mirrors the lifecycle-bucket naming convention of
|
|
5
|
+
// adapters/react/src/hooks (never `composables`, that's Vue's term).
|
|
6
|
+
export { hide, isVisible } from '../core/index.js';
|
|
7
|
+
export { useHideAnimation } from './hooks/use-hide-animation/index.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Vue lifecycle wiring over the framework-agnostic HideAnimationController + style
|
|
2
|
+
// computation (core/). A Vue composable's setup body runs ONCE (unlike a React hook,
|
|
3
|
+
// which re-runs every render), so this takes a config GETTER, not a plain value — Vue's
|
|
4
|
+
// reactivity tracks whatever reactive refs the getter reads internally and re-runs the
|
|
5
|
+
// watchEffect/computed below on their change, mirroring how a React consumer would pass a
|
|
6
|
+
// fresh config object on every render.
|
|
7
|
+
//
|
|
8
|
+
// The controller is a plain local, not a Vue ref: nothing needs Vue to react to the
|
|
9
|
+
// controller reference itself (only its methods are called), so wrapping it would only
|
|
10
|
+
// add an unnecessary reactive Proxy — same identity discipline as
|
|
11
|
+
// `use-color-scheme.ts`'s subscription handle.
|
|
12
|
+
import { computed, watchEffect } from '@vue/runtime-core';
|
|
13
|
+
import { computeHideAnimationStyles, getHideAnimationConstants, HideAnimationController, } from '../../../core/index.js';
|
|
14
|
+
export function useHideAnimation(getConfig) {
|
|
15
|
+
const controller = new HideAnimationController(getConfig());
|
|
16
|
+
const constants = getHideAnimationConstants();
|
|
17
|
+
watchEffect(() => {
|
|
18
|
+
controller.updateConfig(getConfig());
|
|
19
|
+
});
|
|
20
|
+
return computed(() => computeHideAnimationStyles(getConfig(), constants, controller));
|
|
21
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @symbiote-native/splash-screen/vue: the Vue entry over the framework-agnostic core.
|
|
2
|
+
// hide/isVisible carry zero lifecycle and are re-exported verbatim; useHideAnimation
|
|
3
|
+
// wraps HideAnimationController + computeHideAnimationStyles with Vue's own reactivity
|
|
4
|
+
// (composables/use-hide-animation.ts) — mirrors the lifecycle-bucket naming convention
|
|
5
|
+
// of adapters/vue/src/composables (never `hooks/`, that's React's term).
|
|
6
|
+
export { hide, isVisible } from '../core/index.js';
|
|
7
|
+
export { useHideAnimation } from './composables/use-hide-animation/index.js';
|
|
@@ -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
|
+
}
|