rn-system-bar 3.1.7 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +655 -125
- package/android/src/main/java/com/systembar/SystemBarModule.kt +288 -13
- package/index.ts +23 -2
- package/ios/SystemBarModule.m +13 -6
- package/ios/SystemBarModule.swift +44 -9
- package/lib/index.d.ts +3 -2
- package/lib/index.js +13 -2
- package/lib/specs/NativeSystemBar.d.ts +9 -0
- package/lib/src/SystemBar.d.ts +83 -5
- package/lib/src/SystemBar.js +172 -13
- package/lib/src/types.d.ts +61 -1
- package/lib/src/useSystemBar.d.ts +34 -3
- package/lib/src/useSystemBar.js +96 -6
- package/lib/src/useTheme.d.ts +24 -0
- package/lib/src/useTheme.js +81 -0
- package/package.json +3 -2
- package/specs/NativeSystemBar.ts +21 -6
- package/src/SystemBar.ts +209 -23
- package/src/types.ts +85 -1
- package/src/useSystemBar.ts +234 -26
- package/src/useTheme.ts +95 -0
package/lib/src/SystemBar.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// All APIs → native Kotlin / iOS Swift.
|
|
6
6
|
// ─────────────────────────────────────────────
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.onScreencastChange = exports.getScreencastInfo = exports.setOrientation = exports.setSecureScreen = exports.immersiveMode = exports.keepScreenOn = exports.onVolumeChange = exports.setVolumeHUDVisible = exports.getVolume = exports.setVolume = exports.onBrightnessChange = exports.getBrightness = exports.setBrightness = exports.setStatusBarVisibility = exports.setStatusBarStyle = exports.setNavigationBarBehavior = exports.setNavigationBarStyle = exports.setNavigationBarButtonStyle = exports.setNavigationBarVisibility = exports.setNavigationBarColor = void 0;
|
|
8
|
+
exports.onAppCastChange = exports.getAppCastInfo = exports.disconnectAppCast = exports.connectAppCast = exports.stopAppCastScan = exports.startAppCastScan = exports.onScreencastChange = exports.getScreencastInfo = exports.onSystemScreencastChange = exports.getSystemScreencastInfo = exports.setOrientation = exports.setSecureScreen = exports.immersiveMode = exports.keepScreenOn = exports.onVolumeChange = exports.setVolumeHUDVisible = exports.getVolume = exports.setVolume = exports.onBrightnessChange = exports.getBrightness = exports.setBrightness = exports.setStatusBarVisibility = exports.setStatusBarStyle = exports.setStatusBarColor = exports.setNavigationBarBehavior = exports.setNavigationBarStyle = exports.setNavigationBarButtonStyle = exports.setNavigationBarVisibility = exports.setNavigationBarColor = void 0;
|
|
9
9
|
const react_native_1 = require("react-native");
|
|
10
10
|
const { SystemBar: Native } = react_native_1.NativeModules;
|
|
11
11
|
const isAndroid = react_native_1.Platform.OS === "android";
|
|
@@ -17,20 +17,40 @@ const androidOnly = (name) => {
|
|
|
17
17
|
}
|
|
18
18
|
return true;
|
|
19
19
|
};
|
|
20
|
-
const checkNative = () => {
|
|
20
|
+
const checkNative = (method) => {
|
|
21
21
|
if (!Native)
|
|
22
22
|
throw new Error("[rn-system-bar] Native module not found. Rebuild your project.");
|
|
23
|
+
if (method && typeof Native[method] !== "function")
|
|
24
|
+
throw new Error(`[rn-system-bar] Native.${method} not found. Rebuild your project.`);
|
|
23
25
|
};
|
|
24
26
|
// ═══════════════════════════════════════════════
|
|
25
27
|
// NAVIGATION BAR (Android — 100% native)
|
|
26
28
|
// ═══════════════════════════════════════════════
|
|
29
|
+
/**
|
|
30
|
+
* Set the navigation bar background colour.
|
|
31
|
+
*
|
|
32
|
+
* @param color
|
|
33
|
+
* - Any hex string → solid colour e.g. `"#1a1a2e"`
|
|
34
|
+
* - `"transparent"` → fully transparent (content draws behind bar)
|
|
35
|
+
* - `"translucent"` → semi-transparent (system scrim over content)
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* setNavigationBarColor("#000000"); // solid black
|
|
39
|
+
* setNavigationBarColor("transparent"); // glass / edge-to-edge
|
|
40
|
+
* setNavigationBarColor("translucent"); // frosted glass
|
|
41
|
+
*/
|
|
27
42
|
const setNavigationBarColor = (color) => {
|
|
28
43
|
if (!androidOnly("setNavigationBarColor"))
|
|
29
44
|
return;
|
|
30
45
|
checkNative();
|
|
31
|
-
Native.setNavigationBarColor(color);
|
|
46
|
+
Native.setNavigationBarColor(color); // Kotlin handles the special values
|
|
32
47
|
};
|
|
33
48
|
exports.setNavigationBarColor = setNavigationBarColor;
|
|
49
|
+
/**
|
|
50
|
+
* Hide or show the navigation bar.
|
|
51
|
+
*
|
|
52
|
+
* @param mode `"visible"` | `"hidden"`
|
|
53
|
+
*/
|
|
34
54
|
const setNavigationBarVisibility = (mode) => {
|
|
35
55
|
if (!androidOnly("setNavigationBarVisibility"))
|
|
36
56
|
return;
|
|
@@ -62,6 +82,21 @@ exports.setNavigationBarBehavior = setNavigationBarBehavior;
|
|
|
62
82
|
// ═══════════════════════════════════════════════
|
|
63
83
|
// STATUS BAR (native — no RN StatusBar)
|
|
64
84
|
// ═══════════════════════════════════════════════
|
|
85
|
+
/**
|
|
86
|
+
* Set the status bar background colour (Android only).
|
|
87
|
+
*
|
|
88
|
+
* @param color
|
|
89
|
+
* - Any hex string → solid colour
|
|
90
|
+
* - `"transparent"` → fully transparent
|
|
91
|
+
* - `"translucent"` → semi-transparent
|
|
92
|
+
*/
|
|
93
|
+
const setStatusBarColor = (color) => {
|
|
94
|
+
if (!androidOnly("setStatusBarColor"))
|
|
95
|
+
return;
|
|
96
|
+
checkNative();
|
|
97
|
+
Native.setStatusBarColor(color);
|
|
98
|
+
};
|
|
99
|
+
exports.setStatusBarColor = setStatusBarColor;
|
|
65
100
|
const setStatusBarStyle = (style) => {
|
|
66
101
|
checkNative();
|
|
67
102
|
Native.setStatusBarStyle(style);
|
|
@@ -86,7 +121,7 @@ const getBrightness = () => {
|
|
|
86
121
|
};
|
|
87
122
|
exports.getBrightness = getBrightness;
|
|
88
123
|
/**
|
|
89
|
-
* Subscribe to system brightness changes (polls every
|
|
124
|
+
* Subscribe to system brightness changes (polls every 500 ms on Android).
|
|
90
125
|
* @returns unsubscribe function
|
|
91
126
|
*/
|
|
92
127
|
const onBrightnessChange = (callback) => {
|
|
@@ -166,21 +201,145 @@ const setOrientation = (mode) => {
|
|
|
166
201
|
};
|
|
167
202
|
exports.setOrientation = setOrientation;
|
|
168
203
|
// ═══════════════════════════════════════════════
|
|
169
|
-
// SCREENCAST
|
|
204
|
+
// SYSTEM SCREENCAST (external display / HDMI / Miracast)
|
|
205
|
+
// Reads DisplayManager — detects any externally mirrored display.
|
|
206
|
+
// ═══════════════════════════════════════════════
|
|
207
|
+
/**
|
|
208
|
+
* One-shot snapshot of system-level external display state.
|
|
209
|
+
* Works on both Android (DisplayManager) and iOS (UIScreen.screens).
|
|
210
|
+
*/
|
|
211
|
+
const getSystemScreencastInfo = () => {
|
|
212
|
+
if (!Native || typeof Native.getSystemScreencastInfo !== "function") {
|
|
213
|
+
if (__DEV__)
|
|
214
|
+
console.warn("[rn-system-bar] getSystemScreencastInfo not available on this build.");
|
|
215
|
+
return Promise.resolve({
|
|
216
|
+
isCasting: false,
|
|
217
|
+
displayName: null,
|
|
218
|
+
displays: [],
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return Native.getSystemScreencastInfo();
|
|
222
|
+
};
|
|
223
|
+
exports.getSystemScreencastInfo = getSystemScreencastInfo;
|
|
224
|
+
/**
|
|
225
|
+
* Subscribe to system external-display changes.
|
|
226
|
+
* Fires when an HDMI / Miracast / AirPlay display connects or disconnects.
|
|
227
|
+
* @returns unsubscribe function
|
|
228
|
+
*/
|
|
229
|
+
const onSystemScreencastChange = (callback) => {
|
|
230
|
+
checkNative();
|
|
231
|
+
const { DeviceEventEmitter } = require("react-native");
|
|
232
|
+
Native.startSystemScreencastListener();
|
|
233
|
+
const sub = DeviceEventEmitter.addListener("SystemBar_SystemScreencastChange", callback);
|
|
234
|
+
return () => {
|
|
235
|
+
sub.remove();
|
|
236
|
+
Native.stopSystemScreencastListener();
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
exports.onSystemScreencastChange = onSystemScreencastChange;
|
|
240
|
+
// ─────────────────────────────────────────────
|
|
241
|
+
// Legacy aliases (backward compat)
|
|
242
|
+
// ─────────────────────────────────────────────
|
|
243
|
+
/** @deprecated Use getSystemScreencastInfo() */
|
|
244
|
+
exports.getScreencastInfo = exports.getSystemScreencastInfo;
|
|
245
|
+
/** @deprecated Use onSystemScreencastChange() */
|
|
246
|
+
exports.onScreencastChange = exports.onSystemScreencastChange;
|
|
247
|
+
// ═══════════════════════════════════════════════
|
|
248
|
+
// APP-ONLY CAST (MediaRouter — Chromecast / TV)
|
|
249
|
+
// Mirrors only this app's screen — NOT the whole system.
|
|
250
|
+
// Flow: startAppCastScan() → onAppCastChange (devices arrive)
|
|
251
|
+
// → connectAppCast(deviceId) → onAppCastChange (state = "connected")
|
|
252
|
+
// → disconnectAppCast()
|
|
170
253
|
// ═══════════════════════════════════════════════
|
|
171
|
-
|
|
254
|
+
/**
|
|
255
|
+
* Start scanning for nearby castable devices (Chromecast, TV, etc.).
|
|
256
|
+
* Listen for results via `onAppCastChange`.
|
|
257
|
+
* Android: uses MediaRouter. iOS: no-op (AirPlay is system-only).
|
|
258
|
+
*/
|
|
259
|
+
const startAppCastScan = () => {
|
|
260
|
+
if (!androidOnly("startAppCastScan"))
|
|
261
|
+
return;
|
|
262
|
+
checkNative();
|
|
263
|
+
Native.startAppCastScan();
|
|
264
|
+
};
|
|
265
|
+
exports.startAppCastScan = startAppCastScan;
|
|
266
|
+
/**
|
|
267
|
+
* Stop the device discovery scan.
|
|
268
|
+
*/
|
|
269
|
+
const stopAppCastScan = () => {
|
|
270
|
+
if (!androidOnly("stopAppCastScan"))
|
|
271
|
+
return;
|
|
272
|
+
checkNative();
|
|
273
|
+
Native.stopAppCastScan();
|
|
274
|
+
};
|
|
275
|
+
exports.stopAppCastScan = stopAppCastScan;
|
|
276
|
+
/**
|
|
277
|
+
* Connect to a discovered device and begin casting this app's screen.
|
|
278
|
+
* @param deviceId The `id` field from `AppCastDevice` (MediaRouter route ID).
|
|
279
|
+
* @param pairingPin Optional PIN string if `requiresPairing` is true.
|
|
280
|
+
*/
|
|
281
|
+
const connectAppCast = (deviceId, pairingPin) => {
|
|
282
|
+
if (!androidOnly("connectAppCast"))
|
|
283
|
+
return;
|
|
284
|
+
checkNative();
|
|
285
|
+
Native.connectAppCast(deviceId, pairingPin !== null && pairingPin !== void 0 ? pairingPin : null);
|
|
286
|
+
};
|
|
287
|
+
exports.connectAppCast = connectAppCast;
|
|
288
|
+
/**
|
|
289
|
+
* Disconnect the active in-app cast session.
|
|
290
|
+
*/
|
|
291
|
+
const disconnectAppCast = () => {
|
|
292
|
+
if (!androidOnly("disconnectAppCast"))
|
|
293
|
+
return;
|
|
294
|
+
checkNative();
|
|
295
|
+
Native.disconnectAppCast();
|
|
296
|
+
};
|
|
297
|
+
exports.disconnectAppCast = disconnectAppCast;
|
|
298
|
+
/**
|
|
299
|
+
* Get the current in-app cast snapshot (state + device list).
|
|
300
|
+
*/
|
|
301
|
+
const getAppCastInfo = () => {
|
|
302
|
+
if (!isAndroid) {
|
|
303
|
+
return Promise.resolve({
|
|
304
|
+
state: "idle",
|
|
305
|
+
devices: [],
|
|
306
|
+
connectedDevice: null,
|
|
307
|
+
error: null,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
172
310
|
checkNative();
|
|
173
|
-
return Native.
|
|
311
|
+
return Native.getAppCastInfo();
|
|
174
312
|
};
|
|
175
|
-
exports.
|
|
176
|
-
|
|
313
|
+
exports.getAppCastInfo = getAppCastInfo;
|
|
314
|
+
/**
|
|
315
|
+
* Subscribe to in-app cast state changes.
|
|
316
|
+
* Fires on: device discovered/lost, state changes (scanning → connecting → connected),
|
|
317
|
+
* pairing requests, errors.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* const unsub = onAppCastChange((info) => {
|
|
321
|
+
* if (info.state === "connected") console.log("Casting to", info.connectedDevice?.name);
|
|
322
|
+
* if (info.error) console.warn("Cast error:", info.error);
|
|
323
|
+
* });
|
|
324
|
+
*
|
|
325
|
+
* @returns unsubscribe function
|
|
326
|
+
*/
|
|
327
|
+
const onAppCastChange = (callback) => {
|
|
328
|
+
if (!isAndroid) {
|
|
329
|
+
// iOS: immediately call with idle state, return no-op
|
|
330
|
+
callback({
|
|
331
|
+
state: "idle",
|
|
332
|
+
devices: [],
|
|
333
|
+
connectedDevice: null,
|
|
334
|
+
error: null,
|
|
335
|
+
});
|
|
336
|
+
return () => { };
|
|
337
|
+
}
|
|
177
338
|
checkNative();
|
|
178
339
|
const { DeviceEventEmitter } = require("react-native");
|
|
179
|
-
|
|
180
|
-
const sub = DeviceEventEmitter.addListener("SystemBar_ScreencastChange", callback);
|
|
340
|
+
const sub = DeviceEventEmitter.addListener("SystemBar_AppCastChange", callback);
|
|
181
341
|
return () => {
|
|
182
342
|
sub.remove();
|
|
183
|
-
Native.stopScreencastListener();
|
|
184
343
|
};
|
|
185
344
|
};
|
|
186
|
-
exports.
|
|
345
|
+
exports.onAppCastChange = onAppCastChange;
|
package/lib/src/types.d.ts
CHANGED
|
@@ -3,6 +3,37 @@ export type NavigationBarButtonStyle = "light" | "dark";
|
|
|
3
3
|
export type NavigationBarStyle = "auto" | "inverted" | "light" | "dark";
|
|
4
4
|
export type NavigationBarVisibility = "visible" | "hidden";
|
|
5
5
|
export type StatusBarStyle = "light" | "dark";
|
|
6
|
+
/**
|
|
7
|
+
* Navigation bar color value.
|
|
8
|
+
*
|
|
9
|
+
* - Any CSS hex string → solid colour e.g. "#1a1a2e", "#000"
|
|
10
|
+
* - `"transparent"` → fully transparent (FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS +
|
|
11
|
+
* transparent color; content draws behind bar)
|
|
12
|
+
* - `"translucent"` → semi-transparent (FLAG_TRANSLUCENT_NAVIGATION)
|
|
13
|
+
*/
|
|
14
|
+
export type NavigationBarColorValue = string | "transparent" | "translucent";
|
|
15
|
+
/**
|
|
16
|
+
* Status bar background color value.
|
|
17
|
+
*
|
|
18
|
+
* - Any CSS hex string → solid colour
|
|
19
|
+
* - `"transparent"` → fully transparent (draws behind content)
|
|
20
|
+
* - `"translucent"` → semi-transparent
|
|
21
|
+
*/
|
|
22
|
+
export type StatusBarColorValue = string | "transparent" | "translucent";
|
|
23
|
+
/**
|
|
24
|
+
* "system" → follow the OS Appearance (dark/light).
|
|
25
|
+
* "dark" → force dark.
|
|
26
|
+
* "light" → force light.
|
|
27
|
+
*/
|
|
28
|
+
export type ThemeMode = "system" | "dark" | "light";
|
|
29
|
+
export interface ThemeState {
|
|
30
|
+
/** Resolved dark-mode flag. Always a concrete boolean. */
|
|
31
|
+
isDark: boolean;
|
|
32
|
+
/** Currently active override ("system" = following OS). */
|
|
33
|
+
mode: ThemeMode;
|
|
34
|
+
/** Set the override mode. Pass "system" to revert to OS. */
|
|
35
|
+
setMode: (mode: ThemeMode) => void;
|
|
36
|
+
}
|
|
6
37
|
export type Orientation = "portrait" | "landscape" | "landscape-left" | "landscape-right" | "auto";
|
|
7
38
|
export type VolumeStream = "music" | "ring" | "notification" | "alarm" | "system";
|
|
8
39
|
export interface ScreencastDisplay {
|
|
@@ -10,8 +41,37 @@ export interface ScreencastDisplay {
|
|
|
10
41
|
name: string;
|
|
11
42
|
isValid: boolean;
|
|
12
43
|
}
|
|
13
|
-
|
|
44
|
+
/** Result of getSystemScreencastInfo() — physical/HDMI/Miracast external display. */
|
|
45
|
+
export interface SystemScreencastInfo {
|
|
14
46
|
isCasting: boolean;
|
|
15
47
|
displayName: string | null;
|
|
16
48
|
displays: ScreencastDisplay[];
|
|
17
49
|
}
|
|
50
|
+
/** Connection state of the in-app cast session. */
|
|
51
|
+
export type AppCastState = "idle" | "scanning" | "connecting" | "connected" | "disconnecting";
|
|
52
|
+
/** A discovered castable device (TV, Chromecast, etc.). */
|
|
53
|
+
export interface AppCastDevice {
|
|
54
|
+
/** Unique route ID from MediaRouter. */
|
|
55
|
+
id: string;
|
|
56
|
+
/** Human-readable device name e.g. "Living Room TV". */
|
|
57
|
+
name: string;
|
|
58
|
+
/** Device description / model string (may be null). */
|
|
59
|
+
description: string | null;
|
|
60
|
+
/** Signal strength 0–100, or null if unavailable. */
|
|
61
|
+
signalStrength: number | null;
|
|
62
|
+
/** Whether a pairing/PIN step is required. */
|
|
63
|
+
requiresPairing: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** Full snapshot of in-app cast state. */
|
|
66
|
+
export interface AppCastInfo {
|
|
67
|
+
state: AppCastState;
|
|
68
|
+
/** Devices found during the last scan. */
|
|
69
|
+
devices: AppCastDevice[];
|
|
70
|
+
/** The device currently connected (or connecting). */
|
|
71
|
+
connectedDevice: AppCastDevice | null;
|
|
72
|
+
/** Error message from the last failed operation. */
|
|
73
|
+
error: string | null;
|
|
74
|
+
}
|
|
75
|
+
/** @deprecated Use SystemScreencastInfo */
|
|
76
|
+
export interface ScreencastInfo extends SystemScreencastInfo {
|
|
77
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { NavigationBarBehavior, NavigationBarButtonStyle, NavigationBarStyle, NavigationBarVisibility, Orientation, ScreencastInfo, StatusBarStyle } from "./types";
|
|
1
|
+
import type { AppCastInfo, NavigationBarBehavior, NavigationBarButtonStyle, NavigationBarColorValue, NavigationBarStyle, NavigationBarVisibility, Orientation, ScreencastInfo, StatusBarColorValue, StatusBarStyle, SystemScreencastInfo, ThemeMode, ThemeState } from "./types";
|
|
2
2
|
export interface SystemBarConfig {
|
|
3
|
-
navigationBarColor?:
|
|
3
|
+
navigationBarColor?: NavigationBarColorValue;
|
|
4
4
|
navigationBarVisibility?: NavigationBarVisibility;
|
|
5
5
|
navigationBarButtonStyle?: NavigationBarButtonStyle;
|
|
6
6
|
navigationBarStyle?: NavigationBarStyle;
|
|
7
7
|
navigationBarBehavior?: NavigationBarBehavior;
|
|
8
|
-
statusBarColor?:
|
|
8
|
+
statusBarColor?: StatusBarColorValue;
|
|
9
9
|
statusBarStyle?: StatusBarStyle;
|
|
10
10
|
statusBarVisible?: boolean;
|
|
11
11
|
statusBarAnimated?: boolean;
|
|
@@ -16,4 +16,35 @@ export interface SystemBarConfig {
|
|
|
16
16
|
secureScreen?: boolean;
|
|
17
17
|
}
|
|
18
18
|
export declare const useSystemBar: (config: SystemBarConfig) => void;
|
|
19
|
+
export interface ThemedSystemBarConfig {
|
|
20
|
+
/** Config applied when the resolved theme is DARK. */
|
|
21
|
+
dark?: SystemBarConfig;
|
|
22
|
+
/** Config applied when the resolved theme is LIGHT. */
|
|
23
|
+
light?: SystemBarConfig;
|
|
24
|
+
/**
|
|
25
|
+
* Static config merged under both themes.
|
|
26
|
+
* Theme-specific (dark/light) values always win.
|
|
27
|
+
*/
|
|
28
|
+
base?: SystemBarConfig;
|
|
29
|
+
}
|
|
30
|
+
export declare const useThemeSystemBar: (config: ThemedSystemBarConfig) => ThemeState;
|
|
31
|
+
export { useTheme } from "./useTheme";
|
|
32
|
+
export type { ThemeMode, ThemeState };
|
|
33
|
+
export declare const useSystemScreencast: () => SystemScreencastInfo;
|
|
34
|
+
/** @deprecated Renamed to useSystemScreencast() */
|
|
19
35
|
export declare const useScreencast: () => ScreencastInfo;
|
|
36
|
+
export interface UseAppCastReturn extends AppCastInfo {
|
|
37
|
+
/** Start scanning for nearby castable devices (TV, Chromecast, etc.). */
|
|
38
|
+
scan: () => void;
|
|
39
|
+
/** Stop the device discovery scan. */
|
|
40
|
+
stopScan: () => void;
|
|
41
|
+
/**
|
|
42
|
+
* Connect to a discovered device and begin casting this app's screen.
|
|
43
|
+
* @param deviceId The `id` field from `AppCastDevice`.
|
|
44
|
+
* @param pairingPin Optional PIN string if `requiresPairing` is true.
|
|
45
|
+
*/
|
|
46
|
+
connect: (deviceId: string, pairingPin?: string) => void;
|
|
47
|
+
/** End the active cast session and return to idle. */
|
|
48
|
+
disconnect: () => void;
|
|
49
|
+
}
|
|
50
|
+
export declare const useAppCast: () => UseAppCastReturn;
|
package/lib/src/useSystemBar.js
CHANGED
|
@@ -36,9 +36,22 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
36
36
|
};
|
|
37
37
|
})();
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.useScreencast = exports.useSystemBar = void 0;
|
|
39
|
+
exports.useAppCast = exports.useScreencast = exports.useSystemScreencast = exports.useTheme = exports.useThemeSystemBar = exports.useSystemBar = void 0;
|
|
40
40
|
const react_1 = require("react");
|
|
41
41
|
const SystemBar = __importStar(require("./SystemBar"));
|
|
42
|
+
const useTheme_1 = require("./useTheme");
|
|
43
|
+
// ─────────────────────────────────────────────
|
|
44
|
+
// useSystemBar
|
|
45
|
+
// Apply system bar settings declaratively.
|
|
46
|
+
//
|
|
47
|
+
// @example
|
|
48
|
+
// useSystemBar({
|
|
49
|
+
// navigationBarColor: "transparent",
|
|
50
|
+
// navigationBarButtonStyle: "light",
|
|
51
|
+
// statusBarStyle: "light",
|
|
52
|
+
// keepScreenOn: true,
|
|
53
|
+
// });
|
|
54
|
+
// ─────────────────────────────────────────────
|
|
42
55
|
const useSystemBar = (config) => {
|
|
43
56
|
const configRef = (0, react_1.useRef)("");
|
|
44
57
|
const configStr = JSON.stringify(config);
|
|
@@ -47,7 +60,6 @@ const useSystemBar = (config) => {
|
|
|
47
60
|
return;
|
|
48
61
|
configRef.current = configStr;
|
|
49
62
|
const apply = async () => {
|
|
50
|
-
// All nav bar calls are now sync (no expo-navigation-bar)
|
|
51
63
|
if (config.navigationBarColor !== undefined)
|
|
52
64
|
SystemBar.setNavigationBarColor(config.navigationBarColor);
|
|
53
65
|
if (config.navigationBarVisibility !== undefined)
|
|
@@ -58,6 +70,8 @@ const useSystemBar = (config) => {
|
|
|
58
70
|
SystemBar.setNavigationBarStyle(config.navigationBarStyle);
|
|
59
71
|
if (config.navigationBarBehavior !== undefined)
|
|
60
72
|
SystemBar.setNavigationBarBehavior(config.navigationBarBehavior);
|
|
73
|
+
if (config.statusBarColor !== undefined)
|
|
74
|
+
SystemBar.setStatusBarColor(config.statusBarColor);
|
|
61
75
|
if (config.statusBarStyle !== undefined)
|
|
62
76
|
SystemBar.setStatusBarStyle(config.statusBarStyle);
|
|
63
77
|
if (config.statusBarVisible !== undefined)
|
|
@@ -81,20 +95,96 @@ const useSystemBar = (config) => {
|
|
|
81
95
|
}, [configStr]);
|
|
82
96
|
};
|
|
83
97
|
exports.useSystemBar = useSystemBar;
|
|
98
|
+
const useThemeSystemBar = (config) => {
|
|
99
|
+
var _a, _b, _c;
|
|
100
|
+
const theme = (0, useTheme_1.useTheme)();
|
|
101
|
+
const resolved = Object.assign(Object.assign({}, ((_a = config.base) !== null && _a !== void 0 ? _a : {})), (theme.isDark ? ((_b = config.dark) !== null && _b !== void 0 ? _b : {}) : ((_c = config.light) !== null && _c !== void 0 ? _c : {})));
|
|
102
|
+
(0, exports.useSystemBar)(resolved);
|
|
103
|
+
return theme;
|
|
104
|
+
};
|
|
105
|
+
exports.useThemeSystemBar = useThemeSystemBar;
|
|
84
106
|
// ─────────────────────────────────────────────
|
|
85
|
-
//
|
|
107
|
+
// Re-export useTheme + theme types
|
|
86
108
|
// ─────────────────────────────────────────────
|
|
87
|
-
|
|
109
|
+
var useTheme_2 = require("./useTheme");
|
|
110
|
+
Object.defineProperty(exports, "useTheme", { enumerable: true, get: function () { return useTheme_2.useTheme; } });
|
|
111
|
+
// ─────────────────────────────────────────────
|
|
112
|
+
// useSystemScreencast
|
|
113
|
+
// Tracks OS-level external display state.
|
|
114
|
+
// Android: DisplayManager (HDMI / Miracast / Presentation display).
|
|
115
|
+
// iOS: UIScreen.screens (AirPlay mirror).
|
|
116
|
+
//
|
|
117
|
+
// @example
|
|
118
|
+
// const { isCasting, displayName, displays } = useSystemScreencast();
|
|
119
|
+
// ─────────────────────────────────────────────
|
|
120
|
+
const useSystemScreencast = () => {
|
|
88
121
|
const [info, setInfo] = (0, react_1.useState)({
|
|
89
122
|
isCasting: false,
|
|
90
123
|
displayName: null,
|
|
91
124
|
displays: [],
|
|
92
125
|
});
|
|
93
126
|
(0, react_1.useEffect)(() => {
|
|
94
|
-
SystemBar.
|
|
95
|
-
|
|
127
|
+
SystemBar.getSystemScreencastInfo()
|
|
128
|
+
.then(setInfo)
|
|
129
|
+
.catch(() => { });
|
|
130
|
+
const unsub = SystemBar.onSystemScreencastChange(setInfo);
|
|
96
131
|
return () => unsub();
|
|
97
132
|
}, []);
|
|
98
133
|
return info;
|
|
99
134
|
};
|
|
135
|
+
exports.useSystemScreencast = useSystemScreencast;
|
|
136
|
+
/** @deprecated Renamed to useSystemScreencast() */
|
|
137
|
+
// useSystemBar.ts — useScreencast
|
|
138
|
+
const useScreencast = () => {
|
|
139
|
+
const [info, setInfo] = (0, react_1.useState)({
|
|
140
|
+
isCasting: false,
|
|
141
|
+
displayName: null,
|
|
142
|
+
displays: [],
|
|
143
|
+
});
|
|
144
|
+
(0, react_1.useEffect)(() => {
|
|
145
|
+
// Guard: if method doesn't exist on native module, bail silently
|
|
146
|
+
SystemBar.getScreencastInfo()
|
|
147
|
+
.then(setInfo)
|
|
148
|
+
.catch(() => {
|
|
149
|
+
if (__DEV__)
|
|
150
|
+
console.warn("[rn-system-bar] getSystemScreencastInfo not available. Rebuild native.");
|
|
151
|
+
});
|
|
152
|
+
let unsub;
|
|
153
|
+
try {
|
|
154
|
+
unsub = SystemBar.onScreencastChange(setInfo);
|
|
155
|
+
}
|
|
156
|
+
catch (_a) {
|
|
157
|
+
// native listener not available
|
|
158
|
+
}
|
|
159
|
+
return () => unsub === null || unsub === void 0 ? void 0 : unsub();
|
|
160
|
+
}, []);
|
|
161
|
+
return info;
|
|
162
|
+
};
|
|
100
163
|
exports.useScreencast = useScreencast;
|
|
164
|
+
const useAppCast = () => {
|
|
165
|
+
const [info, setInfo] = (0, react_1.useState)({
|
|
166
|
+
state: "idle",
|
|
167
|
+
devices: [],
|
|
168
|
+
connectedDevice: null,
|
|
169
|
+
error: null,
|
|
170
|
+
});
|
|
171
|
+
(0, react_1.useEffect)(() => {
|
|
172
|
+
// Sync initial state
|
|
173
|
+
SystemBar.getAppCastInfo()
|
|
174
|
+
.then(setInfo)
|
|
175
|
+
.catch(() => { });
|
|
176
|
+
// Subscribe to all cast events (device found/lost, state changes, errors)
|
|
177
|
+
const unsub = SystemBar.onAppCastChange(setInfo);
|
|
178
|
+
return () => {
|
|
179
|
+
unsub();
|
|
180
|
+
// Auto-stop scan on unmount — avoids background battery drain
|
|
181
|
+
SystemBar.stopAppCastScan();
|
|
182
|
+
};
|
|
183
|
+
}, []);
|
|
184
|
+
const scan = (0, react_1.useCallback)(() => SystemBar.startAppCastScan(), []);
|
|
185
|
+
const stopScan = (0, react_1.useCallback)(() => SystemBar.stopAppCastScan(), []);
|
|
186
|
+
const connect = (0, react_1.useCallback)((id, pin) => SystemBar.connectAppCast(id, pin), []);
|
|
187
|
+
const disconnect = (0, react_1.useCallback)(() => SystemBar.disconnectAppCast(), []);
|
|
188
|
+
return Object.assign(Object.assign({}, info), { scan, stopScan, connect, disconnect });
|
|
189
|
+
};
|
|
190
|
+
exports.useAppCast = useAppCast;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ThemeMode, ThemeState } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Set the global theme mode from anywhere (outside a component).
|
|
4
|
+
* Triggers all `useTheme()` consumers.
|
|
5
|
+
*/
|
|
6
|
+
export declare function setGlobalThemeMode(mode: ThemeMode): void;
|
|
7
|
+
/**
|
|
8
|
+
* React hook — subscribe to theme state.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const { isDark, mode, setMode } = useTheme();
|
|
12
|
+
*
|
|
13
|
+
* // Auto nav bar colour based on theme
|
|
14
|
+
* useEffect(() => {
|
|
15
|
+
* setNavigationBarColor(isDark ? "#000000" : "#ffffff");
|
|
16
|
+
* setNavigationBarButtonStyle(isDark ? "light" : "dark");
|
|
17
|
+
* }, [isDark]);
|
|
18
|
+
*
|
|
19
|
+
* // Manual override
|
|
20
|
+
* <Button onPress={() => setMode("dark")} title="Force Dark" />
|
|
21
|
+
* <Button onPress={() => setMode("light")} title="Force Light" />
|
|
22
|
+
* <Button onPress={() => setMode("system")}title="Follow OS" />
|
|
23
|
+
*/
|
|
24
|
+
export declare function useTheme(): ThemeState;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ─────────────────────────────────────────────
|
|
3
|
+
// rn-system-bar · useTheme.ts
|
|
4
|
+
//
|
|
5
|
+
// Reads the OS colour scheme via React Native's
|
|
6
|
+
// `Appearance` API and lets the user override it
|
|
7
|
+
// with "dark" | "light" | "system".
|
|
8
|
+
//
|
|
9
|
+
// Usage:
|
|
10
|
+
// const { isDark, mode, setMode } = useTheme();
|
|
11
|
+
// const { isDark } = useTheme(); // read-only
|
|
12
|
+
// ─────────────────────────────────────────────
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.setGlobalThemeMode = setGlobalThemeMode;
|
|
15
|
+
exports.useTheme = useTheme;
|
|
16
|
+
const react_1 = require("react");
|
|
17
|
+
const react_native_1 = require("react-native");
|
|
18
|
+
// ── Module-level singleton so all consumers share state ──────────────────────
|
|
19
|
+
let _mode = "system";
|
|
20
|
+
let _systemIsDark = react_native_1.Appearance.getColorScheme() === "dark";
|
|
21
|
+
const _listeners = new Set();
|
|
22
|
+
function _notify() {
|
|
23
|
+
const isDark = resolveIsDark(_mode, _systemIsDark);
|
|
24
|
+
_listeners.forEach((fn) => fn({ isDark, mode: _mode }));
|
|
25
|
+
}
|
|
26
|
+
function resolveIsDark(mode, systemIsDark) {
|
|
27
|
+
if (mode === "dark")
|
|
28
|
+
return true;
|
|
29
|
+
if (mode === "light")
|
|
30
|
+
return false;
|
|
31
|
+
return systemIsDark; // "system"
|
|
32
|
+
}
|
|
33
|
+
// Keep in sync with OS changes
|
|
34
|
+
react_native_1.Appearance.addChangeListener(({ colorScheme }) => {
|
|
35
|
+
_systemIsDark = colorScheme === "dark";
|
|
36
|
+
_notify();
|
|
37
|
+
});
|
|
38
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
39
|
+
/**
|
|
40
|
+
* Set the global theme mode from anywhere (outside a component).
|
|
41
|
+
* Triggers all `useTheme()` consumers.
|
|
42
|
+
*/
|
|
43
|
+
function setGlobalThemeMode(mode) {
|
|
44
|
+
_mode = mode;
|
|
45
|
+
_notify();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* React hook — subscribe to theme state.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* const { isDark, mode, setMode } = useTheme();
|
|
52
|
+
*
|
|
53
|
+
* // Auto nav bar colour based on theme
|
|
54
|
+
* useEffect(() => {
|
|
55
|
+
* setNavigationBarColor(isDark ? "#000000" : "#ffffff");
|
|
56
|
+
* setNavigationBarButtonStyle(isDark ? "light" : "dark");
|
|
57
|
+
* }, [isDark]);
|
|
58
|
+
*
|
|
59
|
+
* // Manual override
|
|
60
|
+
* <Button onPress={() => setMode("dark")} title="Force Dark" />
|
|
61
|
+
* <Button onPress={() => setMode("light")} title="Force Light" />
|
|
62
|
+
* <Button onPress={() => setMode("system")}title="Follow OS" />
|
|
63
|
+
*/
|
|
64
|
+
function useTheme() {
|
|
65
|
+
const [state, setState] = (0, react_1.useState)(() => ({
|
|
66
|
+
isDark: resolveIsDark(_mode, _systemIsDark),
|
|
67
|
+
mode: _mode,
|
|
68
|
+
}));
|
|
69
|
+
(0, react_1.useEffect)(() => {
|
|
70
|
+
// Sync on mount in case singleton changed between renders
|
|
71
|
+
setState({ isDark: resolveIsDark(_mode, _systemIsDark), mode: _mode });
|
|
72
|
+
_listeners.add(setState);
|
|
73
|
+
return () => {
|
|
74
|
+
_listeners.delete(setState);
|
|
75
|
+
};
|
|
76
|
+
}, []);
|
|
77
|
+
const setMode = (0, react_1.useCallback)((newMode) => {
|
|
78
|
+
setGlobalThemeMode(newMode);
|
|
79
|
+
}, []);
|
|
80
|
+
return Object.assign(Object.assign({}, state), { setMode });
|
|
81
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rn-system-bar",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Control Android & iOS system bars, brightness, volume, orientation and screen flags from React Native.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"react-native": "lib/index.js",
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "rimraf lib && npx tsc",
|
|
39
39
|
"npm:prepublish": "npm publish --access public",
|
|
40
|
-
"installing": "cd .. && npm install rn-system-bar@latest && cd rn-system-bar"
|
|
40
|
+
"installing": "cd .. && npm install rn-system-bar@latest && cd rn-system-bar",
|
|
41
|
+
"dev": "npm run build && npm run npm:prepublish && npm run installing"
|
|
41
42
|
},
|
|
42
43
|
"expo": {
|
|
43
44
|
"plugins": [
|