react-native-ensys-camera-x 1.0.8 → 1.0.10

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 CHANGED
@@ -246,6 +246,13 @@ import {
246
246
  AudioTile,
247
247
  libraryPalette, // the shell's colours
248
248
  wp, hp, ms, fs, // and its screen-scaling helpers
249
+ // Theme management
250
+ useCameraTheme, // hook: get the current theme with subscriptions to changes
251
+ useDarkOnlyTheme, // hook: get the dark theme (independent of global theme)
252
+ createCameraTheme, // create a custom theme from overrides
253
+ setGlobalCameraTheme, // change the active theme for all screens
254
+ getCurrentCameraTheme, // get the current theme (without subscriptions)
255
+ CameraThemeProvider, // context provider for theme
249
256
  } from 'react-native-ensys-camera-x';
250
257
  ```
251
258
 
@@ -278,6 +285,114 @@ if (!result.cancelled) {
278
285
 
279
286
  ---
280
287
 
288
+ ## Theming
289
+
290
+ The plugin ships with four built-in theme presets: `default`, `dark`, `light`, and `minimal`. You
291
+ can set the theme globally during configuration or override it per-screen.
292
+
293
+ ### Set the theme globally
294
+
295
+ ```tsx
296
+ import { configureCameraPlugin } from 'react-native-ensys-camera-x';
297
+
298
+ configureCameraPlugin({
299
+ theme: 'default', // 'default' | 'dark' | 'light' | 'minimal'
300
+ });
301
+ ```
302
+
303
+ To switch themes at runtime:
304
+
305
+ ```tsx
306
+ import { setGlobalCameraTheme } from 'react-native-ensys-camera-x';
307
+
308
+ setGlobalCameraTheme('dark'); // changes all screens immediately
309
+ ```
310
+
311
+ ### Override theme per-screen
312
+
313
+ Pass `theme` as a prop to any screen:
314
+
315
+ ```tsx
316
+ <CameraMediaLibrary theme="dark" />
317
+ ```
318
+
319
+ ### Customize the theme
320
+
321
+ For deeper customization, pass a theme configuration object instead of a preset name:
322
+
323
+ ```tsx
324
+ import { createCameraTheme } from 'react-native-ensys-camera-x';
325
+
326
+ const myTheme = createCameraTheme({
327
+ colors: {
328
+ primary: '#FF6B6B',
329
+ text: '#FFFFFF',
330
+ background: '#1A1A1A',
331
+ // ... other colors
332
+ },
333
+ components: {
334
+ cameraPreview: {
335
+ controlButtonBackground: 'rgba(0, 0, 0, 0.7)',
336
+ // ... other component overrides
337
+ },
338
+ },
339
+ });
340
+
341
+ configureCameraPlugin({ theme: myTheme });
342
+ ```
343
+
344
+ ### Dark-only theme for specific UI elements
345
+
346
+ Some UI elements (recording indicators, camera controls, audio players, media previews) should
347
+ always appear in dark mode regardless of the global theme setting. Use the `useDarkOnlyTheme()` hook
348
+ for these components:
349
+
350
+ ```tsx
351
+ import { useDarkOnlyTheme } from 'react-native-ensys-camera-x';
352
+
353
+ export const MyCustomComponent = () => {
354
+ const theme = useDarkOnlyTheme(); // Always returns the dark theme
355
+
356
+ return (
357
+ <View style={{
358
+ backgroundColor: theme.colors.background,
359
+ color: theme.colors.text
360
+ }}>
361
+ {/* This component will always appear in dark mode */}
362
+ </View>
363
+ );
364
+ };
365
+ ```
366
+
367
+ **Why use dark-only theme?** Some UI elements—particularly system-like overlays, control panels, and
368
+ status indicators—need to maintain visual consistency and contrast regardless of the app's theme
369
+ setting. The `useDarkOnlyTheme()` hook ensures these elements are never affected by theme changes.
370
+
371
+ ### Access the current theme
372
+
373
+ To check the active theme in your components:
374
+
375
+ ```tsx
376
+ import { useCameraTheme, getCurrentCameraTheme } from 'react-native-ensys-camera-x';
377
+
378
+ // In a component (with subscriptions to theme changes):
379
+ const theme = useCameraTheme();
380
+
381
+ // Outside a component (static access):
382
+ const theme = getCurrentCameraTheme();
383
+ ```
384
+
385
+ ### Theme presets
386
+
387
+ | Preset | Description |
388
+ | --- | --- |
389
+ | `default` | Professional dark theme with blue accents (recommended) |
390
+ | `dark` | Pure dark theme optimized for low-light environments |
391
+ | `light` | Light theme with professional appearance |
392
+ | `minimal` | Minimalist monochrome dark theme using white accents |
393
+
394
+ ---
395
+
281
396
  ## Customizing icons
282
397
 
283
398
  All icon customization happens in the **`CUSTOM_ICONS`** block of your `App.tsx`. Nothing else to
@@ -275,8 +275,10 @@ class CameraTheme(
275
275
  val prefs = context.getSharedPreferences("CameraSettings", Context.MODE_PRIVATE)
276
276
  val jsonString = prefs.getString("cameraTheme", null)
277
277
 
278
- // Default palette (Dark)
279
- var colors = CameraThemeColors(
278
+ // Default palette (Dark). Kept in a named val so the Light-theme
279
+ // guard below can revert to it: native Android screens never adopt
280
+ // a Light theme (see the light-theme check after parsing).
281
+ val defaultDarkColors = CameraThemeColors(
280
282
  background = Color.parseColor("#000000"),
281
283
  surface = Color.parseColor("#111827"),
282
284
  surfaceMuted = Color.parseColor("#1F2937"),
@@ -295,6 +297,7 @@ class CameraTheme(
295
297
  error = Color.parseColor("#FF5B5B"),
296
298
  warning = Color.parseColor("#FBBF24")
297
299
  )
300
+ var colors = defaultDarkColors
298
301
 
299
302
  var typography = CameraThemeTypography("System", 18f, 16f, 13f, 14f)
300
303
  var spacing = CameraThemeSpacing(4, 8, 12, 16, 20, 24)
@@ -362,6 +365,18 @@ class CameraTheme(
362
365
  }
363
366
  }
364
367
 
368
+ // Native Android screens stay Dark regardless of the host's
369
+ // light/dark switch: only the React Native surfaces follow the host
370
+ // into Light. If the resolved/persisted theme reads as Light (this
371
+ // can only happen for a theme persisted by an older build, since
372
+ // `setCameraTheme` no longer stores Light themes), revert the color
373
+ // palette to the Dark defaults. Typography/spacing/radius are left
374
+ // as configured because they aren't light/dark specific, so branded
375
+ // fonts and metrics survive. Dark themes are untouched.
376
+ if (!colors.isDark) {
377
+ colors = defaultDarkColors
378
+ }
379
+
365
380
  // Flatten the theme's `components` section into per-component
366
381
  // key→string maps consumed via componentColor()/componentValue().
367
382
  val componentOverrides = mutableMapOf<String, Map<String, String>>()
@@ -1403,11 +1403,46 @@ class CustomCameraModule(
1403
1403
 
1404
1404
  @ReactMethod
1405
1405
  override fun setCameraTheme(theme: String, promise: Promise) {
1406
+ // Native Android screens must never switch to a Light theme: when the host
1407
+ // toggles Light, only the React Native surfaces follow. A Light theme push
1408
+ // is therefore NOT persisted natively, so the last Dark theme (or the Dark
1409
+ // default) is retained and native chrome stays Dark. Dark theme pushes —
1410
+ // including branded dark palettes — are saved as before, so Dark behavior is
1411
+ // unchanged. `CameraTheme.load()` applies the same guard defensively for any
1412
+ // Light theme persisted by an older build.
1413
+ if (isLightThemeJson(theme)) {
1414
+ // Re-read whatever Dark theme is already stored (no-op if unchanged) so a
1415
+ // subsequent load reflects the retained theme rather than the ignored push.
1416
+ CameraTheme.invalidate()
1417
+ promise.resolve(null)
1418
+ return
1419
+ }
1406
1420
  saveSetting("cameraTheme", theme)
1407
1421
  CameraTheme.invalidate()
1408
1422
  promise.resolve(null)
1409
1423
  }
1410
1424
 
1425
+ /**
1426
+ * Whether a `setCameraTheme` payload describes a Light theme, i.e. its
1427
+ * `colors.background` reads as light (relative luminance >= 0.5). Mirrors
1428
+ * [isColorDark] / the JS `isDarkTheme()` so the native/RN light-dark split
1429
+ * agrees. A payload with no parseable background is treated as NOT light, so
1430
+ * malformed themes fall through to the normal save path unchanged.
1431
+ */
1432
+ private fun isLightThemeJson(theme: String): Boolean {
1433
+ if (!theme.trimStart().startsWith("{")) return false
1434
+ return try {
1435
+ val background = org.json.JSONObject(theme)
1436
+ .optJSONObject("colors")
1437
+ ?.optString("background")
1438
+ ?.takeIf { it.isNotEmpty() }
1439
+ ?: return false
1440
+ !isColorDark(android.graphics.Color.parseColor(background))
1441
+ } catch (e: Exception) {
1442
+ false
1443
+ }
1444
+ }
1445
+
1411
1446
  @ReactMethod
1412
1447
  override fun setCameraConfig(configJson: String, promise: Promise) {
1413
1448
  saveSetting("cameraConfig", configJson)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-ensys-camera-x",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Full-screen native Android camera and media picker for React Native — photo/video capture, gallery multi-select, and an editor (crop, rotate, filters, stickers, emoji, text, drawing, trim) in one call. Fully white-labelled: every icon, colour and string is configurable.",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",