@symbiote-native/screen-orientation 0.0.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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +171 -0
  3. package/build/angular/index.d.ts +2 -0
  4. package/build/angular/index.js +4 -0
  5. package/build/angular/services/screen-orientation.service/index.d.ts +6 -0
  6. package/build/angular/services/screen-orientation.service/index.js +75 -0
  7. package/build/core/index.d.ts +3 -0
  8. package/build/core/index.js +2 -0
  9. package/build/core/native-module.d.ts +13 -0
  10. package/build/core/native-module.js +3 -0
  11. package/build/core/screen-orientation.d.ts +24 -0
  12. package/build/core/screen-orientation.js +152 -0
  13. package/build/core/types.d.ts +85 -0
  14. package/build/core/types.js +64 -0
  15. package/build/react/hooks/use-screen-orientation/index.d.ts +2 -0
  16. package/build/react/hooks/use-screen-orientation/index.js +20 -0
  17. package/build/react/index.d.ts +2 -0
  18. package/build/react/index.js +3 -0
  19. package/build/vue/composables/use-screen-orientation/index.d.ts +3 -0
  20. package/build/vue/composables/use-screen-orientation/index.js +27 -0
  21. package/build/vue/index.d.ts +2 -0
  22. package/build/vue/index.js +3 -0
  23. package/build-ngc/angular/index.d.ts +2 -0
  24. package/build-ngc/angular/index.js +5 -0
  25. package/build-ngc/angular/index.js.map +1 -0
  26. package/build-ngc/angular/services/screen-orientation.service/index.d.ts +9 -0
  27. package/build-ngc/angular/services/screen-orientation.service/index.js +34 -0
  28. package/build-ngc/angular/services/screen-orientation.service/index.js.map +1 -0
  29. package/build-ngc/core/index.d.ts +3 -0
  30. package/build-ngc/core/index.js +3 -0
  31. package/build-ngc/core/index.js.map +1 -0
  32. package/build-ngc/core/native-module.d.ts +13 -0
  33. package/build-ngc/core/native-module.js +4 -0
  34. package/build-ngc/core/native-module.js.map +1 -0
  35. package/build-ngc/core/screen-orientation.d.ts +24 -0
  36. package/build-ngc/core/screen-orientation.js +153 -0
  37. package/build-ngc/core/screen-orientation.js.map +1 -0
  38. package/build-ngc/core/types.d.ts +85 -0
  39. package/build-ngc/core/types.js +65 -0
  40. package/build-ngc/core/types.js.map +1 -0
  41. package/native-link.json +12 -0
  42. package/package.json +107 -0
  43. package/src/angular/index.ts +26 -0
  44. package/src/angular/services/screen-orientation.service/index.ts +45 -0
  45. package/src/angular/services/screen-orientation.service/screen-orientation.service.test.ts +101 -0
  46. package/src/core/index.ts +25 -0
  47. package/src/core/native-module.ts +36 -0
  48. package/src/core/screen-orientation.test.ts +307 -0
  49. package/src/core/screen-orientation.ts +186 -0
  50. package/src/core/types.ts +98 -0
  51. package/src/react/hooks/use-screen-orientation/index.ts +34 -0
  52. package/src/react/hooks/use-screen-orientation/use-screen-orientation.test.tsx +93 -0
  53. package/src/react/index.ts +26 -0
  54. package/src/vue/composables/use-screen-orientation/index.ts +42 -0
  55. package/src/vue/composables/use-screen-orientation/use-screen-orientation.test.ts +98 -0
  56. package/src/vue/index.ts +26 -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,171 @@
1
+ # @symbiote-native/screen-orientation
2
+
3
+ Port of [`expo-screen-orientation`](https://docs.expo.dev/versions/latest/sdk/screen-orientation/)
4
+ for [SymbioteNative](../../README.md) — orientation locking (`lockAsync`/`lockPlatformAsync`/
5
+ `unlockAsync`), reading the current orientation and lock, and an auto-updating orientation-change
6
+ subscription, reachable from every adapter (React, Vue, Angular), not just React.
7
+
8
+ Built the same way as [`@symbiote-native/network`](../network) and
9
+ [`@symbiote-native/device`](../device), an `expo-modules-core`-based wrapper (see the
10
+ `symbiote-expo-native-module` project skill for the full mechanism: why `expo-modules-core` is
11
+ depended on directly and never the `expo` meta-package, why the upstream JS is hand-ported into
12
+ `core/` rather than imported, and how autolinking picks up the native module).
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install @symbiote-native/screen-orientation
18
+ ```
19
+
20
+ Depends on `expo-screen-orientation` and `expo-modules-core` directly (regular dependencies,
21
+ pinned to exact versions — never a caret range, since this package's `core/` is hand-ported against
22
+ one specific native API shape and a newer resolve could silently drift the two apart). Never
23
+ install `expo-screen-orientation` yourself, and never add the `expo` package to this project — it
24
+ bundles its own Metro/Babel pipeline that conflicts with this project's own.
25
+
26
+ ### Required one-time step: native autolinking wiring
27
+
28
+ Unlike a plain RN native module, `expo-screen-orientation`'s native code is discovered by
29
+ `expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism — this needs wiring
30
+ into the native host app **once**, covering this package and every other `expo-modules-core`
31
+ package (`@symbiote-native/network`, `@symbiote-native/device`, `@symbiote-native/sensors`, ...)
32
+ with zero further changes:
33
+
34
+ | Platform | Touches |
35
+ |---|---|
36
+ | iOS | `ios/Podfile` — add `use_expo_modules!` |
37
+ | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
38
+ | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
39
+ | Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
40
+
41
+ Full mechanics — the Podfile pieces that normally ship inside the `expo` package, the `expo`
42
+ peer-dependency exclusion list — live in the `symbiote-expo-native-module` skill. Reference
43
+ implementation: `.examples/expo-react/ios/Podfile` and
44
+ `.examples/expo-react/android/app/src/main/java/com/canary/MainApplication.kt`.
45
+
46
+ No platform permission string is needed for screen-orientation's surface — locking and reading the
47
+ current orientation read/write system state with no runtime permission prompt on either platform.
48
+
49
+ ## Shape
50
+
51
+ ```
52
+ src/core/ screen-orientation.ts — lock*/unlock/get* functions +
53
+ addOrientationChangeListener subscription. native-module.ts resolves the
54
+ native module through expo-modules-core's requireNativeModule. types.ts —
55
+ Orientation/OrientationLock/SizeClassIOS/WebOrientationLock/WebOrientation
56
+ enums, PlatformOrientationInfo, ScreenOrientationInfo,
57
+ OrientationChangeEvent, hand-ported from ScreenOrientation.types.ts, plus
58
+ ScreenOrientationState (this package's own hook/composable/service shape).
59
+ src/react/hooks/ @symbiote-native/screen-orientation/react — useScreenOrientation
60
+ src/vue/composables/ @symbiote-native/screen-orientation/vue — useScreenOrientation (same name)
61
+ src/angular/services/ @symbiote-native/screen-orientation/angular — ScreenOrientationService
62
+ (`.connect()` returns a Signal)
63
+ ```
64
+
65
+ Each adapter's hook/composable/service is a thin lifecycle wrapper (seed from one-shot
66
+ `getOrientationAsync()`/`getOrientationLockAsync()` calls, subscribe to
67
+ `addOrientationChangeListener`, unsubscribe on unmount) over the same `core` functions — the
68
+ subscription logic is written once and shared by all three.
69
+
70
+ ## Use it
71
+
72
+ ```tsx
73
+ // React — examples/expo-react/screens/ScreenOrientationScreen.tsx
74
+ import { lockAsync, OrientationLock } from '@symbiote-native/screen-orientation';
75
+ import { useScreenOrientation } from '@symbiote-native/screen-orientation/react';
76
+
77
+ function ScreenOrientationScreen() {
78
+ const { orientation, orientationLock } = useScreenOrientation();
79
+
80
+ return (
81
+ <>
82
+ <Text>Orientation: {orientation}</Text>
83
+ <Text>Lock: {orientationLock}</Text>
84
+ <Button title="Lock landscape" onPress={() => lockAsync(OrientationLock.LANDSCAPE)} />
85
+ </>
86
+ );
87
+ }
88
+ ```
89
+
90
+ ```vue
91
+ <!-- Vue — examples/expo-vue-sfc/screens/ScreenOrientationScreen.vue -->
92
+ <script setup lang="ts">
93
+ import { Text } from '@symbiote-native/vue';
94
+ import { lockAsync, OrientationLock } from '@symbiote-native/screen-orientation';
95
+ import { useScreenOrientation } from '@symbiote-native/screen-orientation/vue';
96
+
97
+ const screenOrientation = useScreenOrientation(); // Ref<ScreenOrientationState>
98
+ </script>
99
+ <template>
100
+ <Text>Orientation: {{ screenOrientation.orientation }}</Text>
101
+ <Text>Lock: {{ screenOrientation.orientationLock }}</Text>
102
+ </template>
103
+ ```
104
+
105
+ ```ts
106
+ // Angular — examples/expo-angular/src/screens/ScreenOrientationScreen.ts
107
+ import { Component, inject } from '@angular/core';
108
+ import { Text } from '@symbiote-native/angular';
109
+ import { ScreenOrientationService } from '@symbiote-native/screen-orientation/angular';
110
+
111
+ @Component({
112
+ standalone: true,
113
+ imports: [Text],
114
+ template: `<Text>Orientation: {{ screenOrientation().orientation }}</Text>`,
115
+ })
116
+ export class ScreenOrientationScreen {
117
+ readonly screenOrientation = inject(ScreenOrientationService).connect(); // Signal<ScreenOrientationState>
118
+ }
119
+ ```
120
+
121
+ ## API
122
+
123
+ A mix of stateless async functions and one listener-based subscription
124
+ (`addOrientationChangeListener`), with its own adapter-level lifecycle hook.
125
+
126
+ ```ts
127
+ lockAsync(orientationLock): Promise<void> // locks to one of the OrientationLock values
128
+ lockPlatformAsync(options): Promise<void> // locks via a platform-specific param
129
+ unlockAsync(): Promise<void> // unlocks back to OrientationLock.DEFAULT
130
+ getOrientationAsync(): Promise<Orientation> // the device's current orientation
131
+ getOrientationLockAsync(): Promise<OrientationLock> // the current lock
132
+ getPlatformOrientationLockAsync(): Promise<PlatformOrientationInfo>
133
+ supportsOrientationLockAsync(orientationLock): Promise<boolean>
134
+ addOrientationChangeListener(listener): EventSubscription // fires whenever orientation/lock changes
135
+ ```
136
+
137
+ Plus `Orientation` (enum: `UNKNOWN`/`PORTRAIT_UP`/`PORTRAIT_DOWN`/`LANDSCAPE_LEFT`/
138
+ `LANDSCAPE_RIGHT`), `OrientationLock` (enum: `DEFAULT`/`ALL`/`PORTRAIT`/`PORTRAIT_UP`/
139
+ `PORTRAIT_DOWN`/`LANDSCAPE`/`LANDSCAPE_LEFT`/`LANDSCAPE_RIGHT`/`OTHER`/`UNKNOWN`), `SizeClassIOS`,
140
+ `WebOrientationLock`, `WebOrientation`, `PlatformOrientationInfo`, `ScreenOrientationInfo`,
141
+ `OrientationChangeEvent` — ported from upstream's `ScreenOrientation.types.ts`.
142
+
143
+ ```ts
144
+ import { getOrientationAsync, addOrientationChangeListener } from '@symbiote-native/screen-orientation';
145
+
146
+ // framework-scoped entry points re-export the same free functions, plus a lifecycle
147
+ // hook/composable/service:
148
+ import { useScreenOrientation } from '@symbiote-native/screen-orientation/react';
149
+ import { useScreenOrientation } from '@symbiote-native/screen-orientation/vue';
150
+ import { ScreenOrientationService } from '@symbiote-native/screen-orientation/angular';
151
+ ```
152
+
153
+ `useScreenOrientation` seeds its initial value (`{ orientation: Orientation.UNKNOWN,
154
+ orientationLock: OrientationLock.UNKNOWN }`) from one-shot `getOrientationAsync()`/
155
+ `getOrientationLockAsync()` calls, then subscribes to `addOrientationChangeListener` for updates,
156
+ and unsubscribes on unmount.
157
+
158
+ **Note (Android):** `expo-screen-orientation` doesn't emit its own `expoDidUpdateDimensions`
159
+ event on Android — the module piggybacks on RN's own `Dimensions.addEventListener('change', ...)`
160
+ there instead, re-fetching the lock and orientation on every dimensions change. iOS and web both
161
+ subscribe to the native `expoDidUpdateDimensions` event directly.
162
+
163
+ ## Test it
164
+
165
+ No Fabric/Descriptor angle at all — screen-orientation is a pure async-function + `EventEmitter`
166
+ listener surface, never a view. Tests inject a fake native-module object in place of the real
167
+ `requireNativeModule` resolution (`src/core/screen-orientation.test.ts`,
168
+ `src/{react,vue,angular}/**/*.test.{ts,tsx}`, `vitest`), the same pattern
169
+ `@symbiote-native/network`/`@symbiote-native/device`/`@symbiote-native/sensors` use — no
170
+ `installFabric()`, no ViewConfig. Native rendering itself is verified on-device — see the parent
171
+ [README](../../README.md).
@@ -0,0 +1,2 @@
1
+ export { ScreenOrientationService } from './services/screen-orientation.service';
2
+ export { lockAsync, lockPlatformAsync, unlockAsync, getOrientationAsync, getOrientationLockAsync, getPlatformOrientationLockAsync, supportsOrientationLockAsync, addOrientationChangeListener, removeOrientationChangeListeners, removeOrientationChangeListener, Orientation, OrientationLock, SizeClassIOS, WebOrientationLock, WebOrientation, type PlatformOrientationInfo, type ScreenOrientationInfo, type OrientationChangeEvent, type OrientationChangeListener, type ScreenOrientationState, type EventSubscription, } from '../core';
@@ -0,0 +1,4 @@
1
+ // ScreenOrientationService is the Angular-only lifecycle half; the free functions and event
2
+ // subscription plumbing all live in core, shared with React/Vue.
3
+ export { ScreenOrientationService } from './services/screen-orientation.service';
4
+ export { lockAsync, lockPlatformAsync, unlockAsync, getOrientationAsync, getOrientationLockAsync, getPlatformOrientationLockAsync, supportsOrientationLockAsync, addOrientationChangeListener, removeOrientationChangeListeners, removeOrientationChangeListener, Orientation, OrientationLock, SizeClassIOS, WebOrientationLock, WebOrientation, } from '../core';
@@ -0,0 +1,6 @@
1
+ import { type Signal } from '@angular/core';
2
+ import { type ScreenOrientationState } from '../../../core';
3
+ export declare class ScreenOrientationService {
4
+ private readonly injector;
5
+ connect(): Signal<ScreenOrientationState>;
6
+ }
@@ -0,0 +1,75 @@
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 { effect, inject, Injectable, Injector, signal } from '@angular/core';
36
+ import { addOrientationChangeListener, getOrientationAsync, getOrientationLockAsync, Orientation, OrientationLock, } from '../../../core';
37
+ // Angular twin of React's `useScreenOrientation` hook and Vue's `useScreenOrientation`
38
+ // composable.
39
+ //
40
+ // readonly screenOrientation = inject(ScreenOrientationService).connect();
41
+ // // template: {{ screenOrientation().orientation }}
42
+ let ScreenOrientationService = (() => {
43
+ let _classDecorators = [Injectable({ providedIn: 'root' })];
44
+ let _classDescriptor;
45
+ let _classExtraInitializers = [];
46
+ let _classThis;
47
+ var ScreenOrientationService = class {
48
+ static { _classThis = this; }
49
+ static {
50
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
51
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
52
+ ScreenOrientationService = _classThis = _classDescriptor.value;
53
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
54
+ __runInitializers(_classThis, _classExtraInitializers);
55
+ }
56
+ injector = inject(Injector);
57
+ connect() {
58
+ const screenOrientation = signal({
59
+ orientation: Orientation.UNKNOWN,
60
+ orientationLock: OrientationLock.UNKNOWN,
61
+ });
62
+ effect(onCleanup => {
63
+ Promise.all([getOrientationAsync(), getOrientationLockAsync()]).then(([orientation, orientationLock]) => screenOrientation.set({ orientation, orientationLock }));
64
+ const subscription = addOrientationChangeListener(event => screenOrientation.set({
65
+ orientation: event.orientationInfo.orientation,
66
+ orientationLock: event.orientationLock,
67
+ }));
68
+ onCleanup(() => subscription.remove());
69
+ }, { injector: this.injector });
70
+ return screenOrientation.asReadonly();
71
+ }
72
+ };
73
+ return ScreenOrientationService = _classThis;
74
+ })();
75
+ export { ScreenOrientationService };
@@ -0,0 +1,3 @@
1
+ export { lockAsync, lockPlatformAsync, unlockAsync, getOrientationAsync, getOrientationLockAsync, getPlatformOrientationLockAsync, supportsOrientationLockAsync, addOrientationChangeListener, removeOrientationChangeListeners, removeOrientationChangeListener, } from './screen-orientation';
2
+ export { Orientation, OrientationLock, SizeClassIOS, WebOrientationLock, WebOrientation, type PlatformOrientationInfo, type ScreenOrientationInfo, type OrientationChangeEvent, type OrientationChangeListener, type ScreenOrientationState, } from './types';
3
+ export type { EventSubscription } from 'expo-modules-core';
@@ -0,0 +1,2 @@
1
+ export { lockAsync, lockPlatformAsync, unlockAsync, getOrientationAsync, getOrientationLockAsync, getPlatformOrientationLockAsync, supportsOrientationLockAsync, addOrientationChangeListener, removeOrientationChangeListeners, removeOrientationChangeListener, } from './screen-orientation';
2
+ export { Orientation, OrientationLock, SizeClassIOS, WebOrientationLock, WebOrientation, } from './types';
@@ -0,0 +1,13 @@
1
+ import type { EventSubscription } from 'expo-modules-core';
2
+ import type { Orientation, OrientationChangeEvent, OrientationLock, WebOrientationLock } from './types';
3
+ export type IPlatformOrientationParam = number | Orientation[] | WebOrientationLock;
4
+ export type INativeScreenOrientationModule = {
5
+ lockAsync?(orientationLock: OrientationLock): Promise<void>;
6
+ lockPlatformAsync?(platformOrientationParam: IPlatformOrientationParam): Promise<void>;
7
+ getOrientationAsync?(): Promise<Orientation>;
8
+ getOrientationLockAsync?(): Promise<OrientationLock>;
9
+ getPlatformOrientationLockAsync?(): Promise<IPlatformOrientationParam>;
10
+ supportsOrientationLockAsync?(orientationLock: OrientationLock): Promise<boolean>;
11
+ addListener(eventName: 'expoDidUpdateDimensions', listener: (event: OrientationChangeEvent) => void): EventSubscription;
12
+ };
13
+ export declare const expoScreenOrientation: INativeScreenOrientationModule;
@@ -0,0 +1,3 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ const EXPO_SCREEN_ORIENTATION_MODULE_NAME = 'ExpoScreenOrientation';
3
+ export const expoScreenOrientation = requireNativeModule(EXPO_SCREEN_ORIENTATION_MODULE_NAME);
@@ -0,0 +1,24 @@
1
+ import { type EventSubscription } from 'expo-modules-core';
2
+ import { Orientation, OrientationLock, type OrientationChangeListener, type PlatformOrientationInfo } from './types';
3
+ /** Locks the screen to the given orientation. */
4
+ export declare function lockAsync(orientationLock: OrientationLock): Promise<void>;
5
+ /** Locks the screen to a platform-specific orientation param — Android constant, iOS
6
+ * orientation array, or web `WebOrientationLock`. */
7
+ export declare function lockPlatformAsync(options: PlatformOrientationInfo): Promise<void>;
8
+ /** Unlocks the screen orientation back to the system default. */
9
+ export declare function unlockAsync(): Promise<void>;
10
+ /** Gets the device's current screen orientation. */
11
+ export declare function getOrientationAsync(): Promise<Orientation>;
12
+ /** Gets the current orientation lock, falling back to the last value set via `lockAsync()`/
13
+ * `lockPlatformAsync()` when the native method itself isn't available. */
14
+ export declare function getOrientationLockAsync(): Promise<OrientationLock>;
15
+ /** Gets the current orientation lock as a platform-specific value. */
16
+ export declare function getPlatformOrientationLockAsync(): Promise<PlatformOrientationInfo>;
17
+ /** Whether the given orientation lock is supported on this device. */
18
+ export declare function supportsOrientationLockAsync(orientationLock: OrientationLock): Promise<boolean>;
19
+ /** Subscribes to orientation-change events. */
20
+ export declare function addOrientationChangeListener(listener: OrientationChangeListener): EventSubscription;
21
+ /** Removes every orientation-change listener registered via `addOrientationChangeListener()`. */
22
+ export declare function removeOrientationChangeListeners(): void;
23
+ /** Removes a single orientation-change listener's subscription. */
24
+ export declare function removeOrientationChangeListener(subscription: EventSubscription): void;
@@ -0,0 +1,152 @@
1
+ // Hand-ported from .vendors/expo/packages/expo-screen-orientation/src/ScreenOrientation.ts
2
+ // (sdk-57). Unlike battery, upstream ScreenOrientation throws UnavailabilityError when the
3
+ // native method is absent — same convention as packages/network/src/core/network.ts.
4
+ import { Platform, UnavailabilityError } from 'expo-modules-core';
5
+ import { Dimensions } from 'react-native';
6
+ import { expoScreenOrientation } from './native-module';
7
+ import { Orientation, OrientationLock, WebOrientationLock, } from './types';
8
+ const NATIVE_MODULE_NAME = 'ScreenOrientation';
9
+ let _orientationChangeSubscribers = [];
10
+ let _lastOrientationLock = OrientationLock.UNKNOWN;
11
+ /** Locks the screen to the given orientation. */
12
+ export async function lockAsync(orientationLock) {
13
+ if (!expoScreenOrientation.lockAsync) {
14
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'lockAsync');
15
+ }
16
+ const orientationLocks = Object.values(OrientationLock);
17
+ if (!orientationLocks.includes(orientationLock)) {
18
+ throw new TypeError(`Invalid Orientation Lock: ${orientationLock}`);
19
+ }
20
+ if (orientationLock === OrientationLock.OTHER) {
21
+ return;
22
+ }
23
+ await expoScreenOrientation.lockAsync(orientationLock);
24
+ _lastOrientationLock = orientationLock;
25
+ }
26
+ /** Locks the screen to a platform-specific orientation param — Android constant, iOS
27
+ * orientation array, or web `WebOrientationLock`. */
28
+ export async function lockPlatformAsync(options) {
29
+ if (!expoScreenOrientation.lockPlatformAsync) {
30
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'lockPlatformAsync');
31
+ }
32
+ const { screenOrientationConstantAndroid, screenOrientationArrayIOS, screenOrientationLockWeb } = options;
33
+ let platformOrientationParam;
34
+ if (Platform.OS === 'android' && screenOrientationConstantAndroid) {
35
+ if (isNaN(screenOrientationConstantAndroid)) {
36
+ throw new TypeError(`lockPlatformAsync Android platform: screenOrientationConstantAndroid cannot be called with ${screenOrientationConstantAndroid}`);
37
+ }
38
+ platformOrientationParam = screenOrientationConstantAndroid;
39
+ }
40
+ else if (Platform.OS === 'ios' && screenOrientationArrayIOS) {
41
+ if (!Array.isArray(screenOrientationArrayIOS)) {
42
+ throw new TypeError(`lockPlatformAsync iOS platform: screenOrientationArrayIOS cannot be called with ${screenOrientationArrayIOS}`);
43
+ }
44
+ const orientations = Object.values(Orientation);
45
+ for (const orientation of screenOrientationArrayIOS) {
46
+ if (!orientations.includes(orientation)) {
47
+ throw new TypeError(`lockPlatformAsync iOS platform: ${orientation} is not a valid Orientation`);
48
+ }
49
+ }
50
+ platformOrientationParam = screenOrientationArrayIOS;
51
+ }
52
+ else if (Platform.OS === 'web' && screenOrientationLockWeb) {
53
+ const webOrientationLocks = Object.values(WebOrientationLock);
54
+ if (!webOrientationLocks.includes(screenOrientationLockWeb)) {
55
+ throw new TypeError(`Invalid Web Orientation Lock: ${screenOrientationLockWeb}`);
56
+ }
57
+ platformOrientationParam = screenOrientationLockWeb;
58
+ }
59
+ if (!platformOrientationParam) {
60
+ throw new TypeError('lockPlatformAsync cannot be called with undefined option properties');
61
+ }
62
+ await expoScreenOrientation.lockPlatformAsync(platformOrientationParam);
63
+ _lastOrientationLock = OrientationLock.OTHER;
64
+ }
65
+ /** Unlocks the screen orientation back to the system default. */
66
+ export async function unlockAsync() {
67
+ if (!expoScreenOrientation.lockAsync) {
68
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'lockAsync');
69
+ }
70
+ await expoScreenOrientation.lockAsync(OrientationLock.DEFAULT);
71
+ }
72
+ /** Gets the device's current screen orientation. */
73
+ export async function getOrientationAsync() {
74
+ if (!expoScreenOrientation.getOrientationAsync) {
75
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getOrientationAsync');
76
+ }
77
+ return expoScreenOrientation.getOrientationAsync();
78
+ }
79
+ /** Gets the current orientation lock, falling back to the last value set via `lockAsync()`/
80
+ * `lockPlatformAsync()` when the native method itself isn't available. */
81
+ export async function getOrientationLockAsync() {
82
+ if (!expoScreenOrientation.getOrientationLockAsync) {
83
+ return _lastOrientationLock;
84
+ }
85
+ return expoScreenOrientation.getOrientationLockAsync();
86
+ }
87
+ /** Gets the current orientation lock as a platform-specific value. */
88
+ export async function getPlatformOrientationLockAsync() {
89
+ const platformOrientationLock = await expoScreenOrientation.getPlatformOrientationLockAsync?.();
90
+ if (Platform.OS === 'android' && typeof platformOrientationLock === 'number') {
91
+ return { screenOrientationConstantAndroid: platformOrientationLock };
92
+ }
93
+ if (Platform.OS === 'ios' && Array.isArray(platformOrientationLock)) {
94
+ return { screenOrientationArrayIOS: platformOrientationLock };
95
+ }
96
+ if (Platform.OS === 'web' && typeof platformOrientationLock === 'string') {
97
+ return { screenOrientationLockWeb: platformOrientationLock };
98
+ }
99
+ return {};
100
+ }
101
+ /** Whether the given orientation lock is supported on this device. */
102
+ export async function supportsOrientationLockAsync(orientationLock) {
103
+ if (!expoScreenOrientation.supportsOrientationLockAsync) {
104
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'supportsOrientationLockAsync');
105
+ }
106
+ const orientationLocks = Object.values(OrientationLock);
107
+ if (!orientationLocks.includes(orientationLock)) {
108
+ throw new TypeError(`Invalid Orientation Lock: ${orientationLock}`);
109
+ }
110
+ return expoScreenOrientation.supportsOrientationLockAsync(orientationLock);
111
+ }
112
+ /** Subscribes to orientation-change events. */
113
+ export function addOrientationChangeListener(listener) {
114
+ if (typeof listener !== 'function') {
115
+ throw new TypeError(`addOrientationChangeListener cannot be called with ${listener}`);
116
+ }
117
+ const subscription = createDidUpdateDimensionsSubscription(listener);
118
+ _orientationChangeSubscribers.push(subscription);
119
+ return subscription;
120
+ }
121
+ /** Removes every orientation-change listener registered via `addOrientationChangeListener()`. */
122
+ export function removeOrientationChangeListeners() {
123
+ let i = _orientationChangeSubscribers.length;
124
+ while (i--) {
125
+ _orientationChangeSubscribers[i]?.remove();
126
+ _orientationChangeSubscribers.pop();
127
+ }
128
+ }
129
+ /** Removes a single orientation-change listener's subscription. */
130
+ export function removeOrientationChangeListener(subscription) {
131
+ if (!subscription || !subscription.remove) {
132
+ throw new TypeError('Must pass in a valid subscription');
133
+ }
134
+ subscription.remove();
135
+ _orientationChangeSubscribers = _orientationChangeSubscribers.filter(sub => sub !== subscription);
136
+ }
137
+ // IMPORTANT: Android doesn't emit `expoDidUpdateDimensions` — RN's own Dimensions module does,
138
+ // so on Android we piggyback on RN's own `Dimensions.addEventListener('change', ...)` instead.
139
+ function createDidUpdateDimensionsSubscription(listener) {
140
+ if (Platform.OS === 'web' || Platform.OS === 'ios') {
141
+ return expoScreenOrientation.addListener('expoDidUpdateDimensions', async (update) => {
142
+ listener(update);
143
+ });
144
+ }
145
+ return Dimensions.addEventListener('change', async () => {
146
+ const [orientationLock, orientation] = await Promise.all([
147
+ getOrientationLockAsync(),
148
+ getOrientationAsync(),
149
+ ]);
150
+ listener({ orientationInfo: { orientation }, orientationLock });
151
+ });
152
+ }
@@ -0,0 +1,85 @@
1
+ export declare enum Orientation {
2
+ UNKNOWN = 0,
3
+ PORTRAIT_UP = 1,
4
+ PORTRAIT_DOWN = 2,
5
+ LANDSCAPE_LEFT = 3,
6
+ LANDSCAPE_RIGHT = 4
7
+ }
8
+ /**
9
+ * Values that can be passed to `lockAsync()`. `ALL`/`PORTRAIT` are invalid on devices that
10
+ * don't support `PORTRAIT_DOWN`.
11
+ */
12
+ export declare enum OrientationLock {
13
+ /** iOS: all orientations except `PORTRAIT_DOWN`. Android: system decides. */
14
+ DEFAULT = 0,
15
+ /** All four possible orientations. */
16
+ ALL = 1,
17
+ /** Any portrait orientation. */
18
+ PORTRAIT = 2,
19
+ /** Right-side-up portrait only. */
20
+ PORTRAIT_UP = 3,
21
+ /** Upside-down portrait only. */
22
+ PORTRAIT_DOWN = 4,
23
+ /** Any landscape orientation. */
24
+ LANDSCAPE = 5,
25
+ /** Left landscape only. */
26
+ LANDSCAPE_LEFT = 6,
27
+ /** Right landscape only. */
28
+ LANDSCAPE_RIGHT = 7,
29
+ /** A platform-specific orientation — not a valid `lockAsync()` policy. */
30
+ OTHER = 8,
31
+ /** Unknown lock — not a valid `lockAsync()` policy. */
32
+ UNKNOWN = 9
33
+ }
34
+ /** Each iOS device has a default set of UIKit size classes. */
35
+ export declare enum SizeClassIOS {
36
+ UNKNOWN = 0,
37
+ COMPACT = 1,
38
+ REGULAR = 2
39
+ }
40
+ export declare enum WebOrientationLock {
41
+ PORTRAIT_PRIMARY = "portrait-primary",
42
+ PORTRAIT_SECONDARY = "portrait-secondary",
43
+ PORTRAIT = "portrait",
44
+ LANDSCAPE_PRIMARY = "landscape-primary",
45
+ LANDSCAPE_SECONDARY = "landscape-secondary",
46
+ LANDSCAPE = "landscape",
47
+ ANY = "any",
48
+ NATURAL = "natural",
49
+ UNKNOWN = "unknown"
50
+ }
51
+ export declare enum WebOrientation {
52
+ PORTRAIT_PRIMARY = "portrait-primary",
53
+ PORTRAIT_SECONDARY = "portrait-secondary",
54
+ LANDSCAPE_PRIMARY = "landscape-primary",
55
+ LANDSCAPE_SECONDARY = "landscape-secondary"
56
+ }
57
+ export type PlatformOrientationInfo = {
58
+ /** @platform android */
59
+ screenOrientationConstantAndroid?: number;
60
+ /** @platform ios */
61
+ screenOrientationArrayIOS?: Orientation[];
62
+ /** @platform web */
63
+ screenOrientationLockWeb?: WebOrientationLock;
64
+ };
65
+ export type ScreenOrientationInfo = {
66
+ orientation: Orientation;
67
+ /** @platform ios */
68
+ verticalSizeClass?: SizeClassIOS;
69
+ /** @platform ios */
70
+ horizontalSizeClass?: SizeClassIOS;
71
+ };
72
+ export type OrientationChangeEvent = {
73
+ orientationLock: OrientationLock;
74
+ orientationInfo: ScreenOrientationInfo;
75
+ };
76
+ export type OrientationChangeListener = (event: OrientationChangeEvent) => void;
77
+ /**
78
+ * The shape shared by every adapter's `useScreenOrientation()` hook/composable/service — not an
79
+ * upstream expo-screen-orientation type (upstream ships no reactive hook of its own), defined once
80
+ * here so React/Vue/Angular all consume the identical shape instead of three ad-hoc declarations.
81
+ */
82
+ export type ScreenOrientationState = {
83
+ orientation: Orientation;
84
+ orientationLock: OrientationLock;
85
+ };
@@ -0,0 +1,64 @@
1
+ // Hand-ported from .vendors/expo/packages/expo-screen-orientation/src/ScreenOrientation.types.ts
2
+ // (sdk-57) — plain enums/data shapes, no `expo` meta-package import to swap out (same as
3
+ // packages/network's types.ts).
4
+ export var Orientation;
5
+ (function (Orientation) {
6
+ Orientation[Orientation["UNKNOWN"] = 0] = "UNKNOWN";
7
+ Orientation[Orientation["PORTRAIT_UP"] = 1] = "PORTRAIT_UP";
8
+ Orientation[Orientation["PORTRAIT_DOWN"] = 2] = "PORTRAIT_DOWN";
9
+ Orientation[Orientation["LANDSCAPE_LEFT"] = 3] = "LANDSCAPE_LEFT";
10
+ Orientation[Orientation["LANDSCAPE_RIGHT"] = 4] = "LANDSCAPE_RIGHT";
11
+ })(Orientation || (Orientation = {}));
12
+ /**
13
+ * Values that can be passed to `lockAsync()`. `ALL`/`PORTRAIT` are invalid on devices that
14
+ * don't support `PORTRAIT_DOWN`.
15
+ */
16
+ export var OrientationLock;
17
+ (function (OrientationLock) {
18
+ /** iOS: all orientations except `PORTRAIT_DOWN`. Android: system decides. */
19
+ OrientationLock[OrientationLock["DEFAULT"] = 0] = "DEFAULT";
20
+ /** All four possible orientations. */
21
+ OrientationLock[OrientationLock["ALL"] = 1] = "ALL";
22
+ /** Any portrait orientation. */
23
+ OrientationLock[OrientationLock["PORTRAIT"] = 2] = "PORTRAIT";
24
+ /** Right-side-up portrait only. */
25
+ OrientationLock[OrientationLock["PORTRAIT_UP"] = 3] = "PORTRAIT_UP";
26
+ /** Upside-down portrait only. */
27
+ OrientationLock[OrientationLock["PORTRAIT_DOWN"] = 4] = "PORTRAIT_DOWN";
28
+ /** Any landscape orientation. */
29
+ OrientationLock[OrientationLock["LANDSCAPE"] = 5] = "LANDSCAPE";
30
+ /** Left landscape only. */
31
+ OrientationLock[OrientationLock["LANDSCAPE_LEFT"] = 6] = "LANDSCAPE_LEFT";
32
+ /** Right landscape only. */
33
+ OrientationLock[OrientationLock["LANDSCAPE_RIGHT"] = 7] = "LANDSCAPE_RIGHT";
34
+ /** A platform-specific orientation — not a valid `lockAsync()` policy. */
35
+ OrientationLock[OrientationLock["OTHER"] = 8] = "OTHER";
36
+ /** Unknown lock — not a valid `lockAsync()` policy. */
37
+ OrientationLock[OrientationLock["UNKNOWN"] = 9] = "UNKNOWN";
38
+ })(OrientationLock || (OrientationLock = {}));
39
+ /** Each iOS device has a default set of UIKit size classes. */
40
+ export var SizeClassIOS;
41
+ (function (SizeClassIOS) {
42
+ SizeClassIOS[SizeClassIOS["UNKNOWN"] = 0] = "UNKNOWN";
43
+ SizeClassIOS[SizeClassIOS["COMPACT"] = 1] = "COMPACT";
44
+ SizeClassIOS[SizeClassIOS["REGULAR"] = 2] = "REGULAR";
45
+ })(SizeClassIOS || (SizeClassIOS = {}));
46
+ export var WebOrientationLock;
47
+ (function (WebOrientationLock) {
48
+ WebOrientationLock["PORTRAIT_PRIMARY"] = "portrait-primary";
49
+ WebOrientationLock["PORTRAIT_SECONDARY"] = "portrait-secondary";
50
+ WebOrientationLock["PORTRAIT"] = "portrait";
51
+ WebOrientationLock["LANDSCAPE_PRIMARY"] = "landscape-primary";
52
+ WebOrientationLock["LANDSCAPE_SECONDARY"] = "landscape-secondary";
53
+ WebOrientationLock["LANDSCAPE"] = "landscape";
54
+ WebOrientationLock["ANY"] = "any";
55
+ WebOrientationLock["NATURAL"] = "natural";
56
+ WebOrientationLock["UNKNOWN"] = "unknown";
57
+ })(WebOrientationLock || (WebOrientationLock = {}));
58
+ export var WebOrientation;
59
+ (function (WebOrientation) {
60
+ WebOrientation["PORTRAIT_PRIMARY"] = "portrait-primary";
61
+ WebOrientation["PORTRAIT_SECONDARY"] = "portrait-secondary";
62
+ WebOrientation["LANDSCAPE_PRIMARY"] = "landscape-primary";
63
+ WebOrientation["LANDSCAPE_SECONDARY"] = "landscape-secondary";
64
+ })(WebOrientation || (WebOrientation = {}));
@@ -0,0 +1,2 @@
1
+ import { type ScreenOrientationState } from '../../../core';
2
+ export declare function useScreenOrientation(): ScreenOrientationState;