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.
@@ -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 500ms on Android).
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
- const getScreencastInfo = () => {
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.getScreencastInfo();
311
+ return Native.getAppCastInfo();
174
312
  };
175
- exports.getScreencastInfo = getScreencastInfo;
176
- const onScreencastChange = (callback) => {
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
- Native.startScreencastListener();
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.onScreencastChange = onScreencastChange;
345
+ exports.onAppCastChange = onAppCastChange;
@@ -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
- export interface ScreencastInfo {
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?: string;
3
+ navigationBarColor?: NavigationBarColorValue;
4
4
  navigationBarVisibility?: NavigationBarVisibility;
5
5
  navigationBarButtonStyle?: NavigationBarButtonStyle;
6
6
  navigationBarStyle?: NavigationBarStyle;
7
7
  navigationBarBehavior?: NavigationBarBehavior;
8
- statusBarColor?: string;
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;
@@ -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
- // useScreencast
107
+ // Re-export useTheme + theme types
86
108
  // ─────────────────────────────────────────────
87
- const useScreencast = () => {
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.getScreencastInfo().then(setInfo).catch(() => { });
95
- const unsub = SystemBar.onScreencastChange(setInfo);
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.1.7",
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": [