@rific/auto-paper 0.13.1 → 0.13.2

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
@@ -272,7 +272,8 @@ export default function App() {
272
272
  | `onNavBarChange` | `(color: string, dark: boolean) => void` | Overrides the built-in nav bar sync: called on Android when the theme changes while a `BottomNavigation` is mounted, instead of the automatic `navigationBar`-driven icon-style sync |
273
273
  | `onReady` | `() => void` | Called once when the theme first resolves |
274
274
  | `reanimated` | `ReanimatedModule` | Injects `react-native-reanimated` (for example `import Reanimated from 'react-native-reanimated'`) so `<Dialog animatedStyle={...}>` can animate its card on the UI thread. Omit to keep `animatedStyle` as a no-op. |
275
- | `statusBarProps` | `StatusBarProps` | Spread over the auto-derived `StatusBar` defaults |
275
+ | `statusBar` | `boolean` | Renders React Native's `StatusBar`, styled from the theme (here and in `Appbar.Header`). Default `true`. Pass `false` when the app sets the status bar per screen instead (react-native-screens' `statusBarStyle`/`statusBarHidden` screen options, which need `UIViewControllerBasedStatusBarAppearance` on): there a React Native `StatusBar` logs an error. An app linked against the iOS 27 SDK can't set the status bar app-wide at all, which is the only way that component works on iOS, so per-screen options are the way to style it there. |
276
+ | `statusBarProps` | `StatusBarProps` | Spread over the auto-derived `StatusBar` defaults (ignored when `statusBar` is `false`) |
276
277
  | `style` | `StyleProp<ViewStyle>` | Applied to the wrapper `View` |
277
278
 
278
279
  `Provider` also renders its own `Portal.Host`, nested inside everything it injects, so anything rendered through `react-native-paper`'s `Portal` (this package's `Dialog`, Paper's `Menu` and `Tooltip`, or a `Modal`/`Snackbar`/anything else you wrap in `<Portal>` yourself) still sees `expoBlur`, `reanimated` and `defaults`. Paper's own host sits above all of those and its `Portal` only carries the theme across, so without this a portaled `Dialog` or `Menu` would silently render its solid fallback. Contexts your app provides *inside* `Provider` (a toast provider, say) are a separate matter: portal content can't see them unless you render another `<Portal.Host>` below them.
@@ -412,7 +413,7 @@ function SettingsScreen() {
412
413
 
413
414
  ### `Appbar`
414
415
 
415
- A thin wrapper around `react-native-paper`'s `Appbar` that automatically syncs `StatusBar` background color and bar style to the current theme surface color.
416
+ A thin wrapper around `react-native-paper`'s `Appbar` that automatically syncs `StatusBar` background color and bar style to the current theme surface color (no `StatusBar` under `<Provider statusBar={false}>`).
416
417
 
417
418
  ```tsx
418
419
  import { Appbar } from '@rific/auto-paper'
package/dist/index.d.mts CHANGED
@@ -257,19 +257,19 @@ declare const themeActions: {
257
257
  type: string;
258
258
  }) => action is _rific_core.SettingsAction<string | TriadicPalette>;
259
259
  };
260
- setAppearance: {
261
- (payload: ThemeAppearance): _rific_core.SettingsAction<ThemeAppearance>;
260
+ setBlur: {
261
+ (payload: boolean): _rific_core.SettingsAction<boolean>;
262
262
  type: string;
263
263
  match: (action: {
264
264
  type: string;
265
- }) => action is _rific_core.SettingsAction<ThemeAppearance>;
265
+ }) => action is _rific_core.SettingsAction<boolean>;
266
266
  };
267
- setBlur: {
268
- (payload: boolean): _rific_core.SettingsAction<boolean>;
267
+ setAppearance: {
268
+ (payload: ThemeAppearance): _rific_core.SettingsAction<ThemeAppearance>;
269
269
  type: string;
270
270
  match: (action: {
271
271
  type: string;
272
- }) => action is _rific_core.SettingsAction<boolean>;
272
+ }) => action is _rific_core.SettingsAction<ThemeAppearance>;
273
273
  };
274
274
  setHarmony: {
275
275
  (payload: ColorHarmony): _rific_core.SettingsAction<ColorHarmony>;
@@ -326,10 +326,15 @@ type ProviderProps = {
326
326
  onReady?: () => void;
327
327
  /** Injects react-native-reanimated so `<Dialog animatedStyle={...}>` (e.g. from `useAnimatedStyle()`) can animate its card on the UI thread. Pass `import Reanimated from 'react-native-reanimated'`; omit to keep Dialog's `animatedStyle` prop a no-op. */
328
328
  reanimated?: ReanimatedModule;
329
+ /** Renders React Native's `StatusBar`, styled from the theme (here and in `Appbar.Header`). Default true. Pass false
330
+ * for an app that sets the status bar per screen instead (react-native-screens' `statusBarStyle`/`statusBarHidden`
331
+ * screen options, with `UIViewControllerBasedStatusBarAppearance` on): there React Native's StatusBar logs an error,
332
+ * and an app linked against the iOS 27 SDK can't set the status bar app-wide at all, which is the only way it works on iOS. */
333
+ statusBar?: boolean;
329
334
  statusBarProps?: StatusBarProps;
330
335
  style?: StyleProp<ViewStyle>;
331
336
  };
332
- declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element;
337
+ declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style }: ProviderProps): react.JSX.Element;
333
338
 
334
339
  declare const useAutoPaperTheme: () => AutoPaperTheme;
335
340
 
package/dist/index.d.ts CHANGED
@@ -257,19 +257,19 @@ declare const themeActions: {
257
257
  type: string;
258
258
  }) => action is _rific_core.SettingsAction<string | TriadicPalette>;
259
259
  };
260
- setAppearance: {
261
- (payload: ThemeAppearance): _rific_core.SettingsAction<ThemeAppearance>;
260
+ setBlur: {
261
+ (payload: boolean): _rific_core.SettingsAction<boolean>;
262
262
  type: string;
263
263
  match: (action: {
264
264
  type: string;
265
- }) => action is _rific_core.SettingsAction<ThemeAppearance>;
265
+ }) => action is _rific_core.SettingsAction<boolean>;
266
266
  };
267
- setBlur: {
268
- (payload: boolean): _rific_core.SettingsAction<boolean>;
267
+ setAppearance: {
268
+ (payload: ThemeAppearance): _rific_core.SettingsAction<ThemeAppearance>;
269
269
  type: string;
270
270
  match: (action: {
271
271
  type: string;
272
- }) => action is _rific_core.SettingsAction<boolean>;
272
+ }) => action is _rific_core.SettingsAction<ThemeAppearance>;
273
273
  };
274
274
  setHarmony: {
275
275
  (payload: ColorHarmony): _rific_core.SettingsAction<ColorHarmony>;
@@ -326,10 +326,15 @@ type ProviderProps = {
326
326
  onReady?: () => void;
327
327
  /** Injects react-native-reanimated so `<Dialog animatedStyle={...}>` (e.g. from `useAnimatedStyle()`) can animate its card on the UI thread. Pass `import Reanimated from 'react-native-reanimated'`; omit to keep Dialog's `animatedStyle` prop a no-op. */
328
328
  reanimated?: ReanimatedModule;
329
+ /** Renders React Native's `StatusBar`, styled from the theme (here and in `Appbar.Header`). Default true. Pass false
330
+ * for an app that sets the status bar per screen instead (react-native-screens' `statusBarStyle`/`statusBarHidden`
331
+ * screen options, with `UIViewControllerBasedStatusBarAppearance` on): there React Native's StatusBar logs an error,
332
+ * and an app linked against the iOS 27 SDK can't set the status bar app-wide at all, which is the only way it works on iOS. */
333
+ statusBar?: boolean;
329
334
  statusBarProps?: StatusBarProps;
330
335
  style?: StyleProp<ViewStyle>;
331
336
  };
332
- declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element;
337
+ declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style }: ProviderProps): react.JSX.Element;
333
338
 
334
339
  declare const useAutoPaperTheme: () => AutoPaperTheme;
335
340
 
package/dist/index.js CHANGED
@@ -108,8 +108,8 @@ function useBlur(override) {
108
108
  }
109
109
 
110
110
  // src/components/Appbar.tsx
111
- var import_react_native = require("react-native");
112
- var import_react_native_paper = require("react-native-paper");
111
+ var import_react_native3 = require("react-native");
112
+ var import_react_native_paper3 = require("react-native-paper");
113
113
 
114
114
  // src/PaperDefaultsContext.tsx
115
115
  var import_react2 = require("react");
@@ -118,69 +118,15 @@ function usePaperDefaults() {
118
118
  return (0, import_react2.useContext)(PaperDefaultsContext);
119
119
  }
120
120
 
121
- // src/components/Appbar.tsx
122
- var import_jsx_runtime = require("react/jsx-runtime");
123
- function Header({ style, ...props }) {
124
- const defaults = usePaperDefaults();
125
- const theme = (0, import_react_native_paper.useTheme)();
126
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
127
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.StatusBar, { backgroundColor: theme.colors.surface, barStyle: theme.dark ? "light-content" : "dark-content" }),
128
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Appbar.Header, { ...defaults.AppbarHeader, ...props, style })
129
- ] });
130
- }
131
- var Appbar = {
132
- Header,
133
- Content: import_react_native_paper.Appbar.Content,
134
- Action: import_react_native_paper.Appbar.Action,
135
- BackAction: import_react_native_paper.Appbar.BackAction
136
- };
137
-
138
- // src/components/AppearancePicker.tsx
121
+ // src/ThemeProvider.tsx
122
+ var import_react4 = require("react");
123
+ var import_react_native2 = require("react-native");
139
124
  var import_react_native_paper2 = require("react-native-paper");
140
- var import_jsx_runtime2 = require("react/jsx-runtime");
141
- var APPEARANCES = [
142
- { label: "System", value: "system" },
143
- { label: "Light", value: "light" },
144
- { label: "Dark", value: "dark" }
145
- ];
146
- var DEFAULT_ICONS = {
147
- system: "monitor",
148
- light: "white-balance-sunny",
149
- dark: "weather-night"
150
- };
151
- var AppearancePicker = ({ value, onChange, showLabels = true, icons }) => {
152
- const resolvedIcons = { ...DEFAULT_ICONS, ...icons };
153
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.SegmentedButtons, { value, onValueChange: (v) => onChange(v), buttons: APPEARANCES.map((a) => ({ value: a.value, label: showLabels ? a.label : void 0, icon: resolvedIcons[a.value], accessibilityLabel: a.label })) });
154
- };
155
-
156
- // src/useThemeSettings.ts
157
- var useThemeSettings = useThemeSettingsInternal;
158
-
159
- // src/components/AutoAppearancePicker.tsx
160
- var import_jsx_runtime3 = require("react/jsx-runtime");
161
- var AutoAppearancePicker = (props) => {
162
- const { settings, set } = useThemeSettings();
163
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AppearancePicker, { ...props, value: settings.appearance, onChange: (appearance) => set({ appearance }) });
164
- };
165
-
166
- // src/utils/resolveSeedColor.ts
167
- var resolveSeedColor = (color) => typeof color === "string" ? color : color.primary;
168
-
169
- // src/components/PalettePicker.tsx
170
- var import_react9 = require("react");
171
- var import_react_native7 = require("react-native");
172
- var import_react_native_paper9 = require("react-native-paper");
173
125
 
174
- // src/useSwatchGrid.ts
126
+ // src/useComputedTheme.ts
175
127
  var import_react3 = require("react");
176
- var SWATCH_GRID_COLUMNS = 5;
177
- var SWATCH_GRID_GAP = 12;
178
- function useSwatchSize(fallback) {
179
- const [width, setWidth] = (0, import_react3.useState)(0);
180
- const onLayout = ({ nativeEvent }) => setWidth(nativeEvent.layout.width);
181
- const size = width > 0 ? (width - SWATCH_GRID_GAP * (SWATCH_GRID_COLUMNS - 1)) / SWATCH_GRID_COLUMNS : fallback;
182
- return { onLayout, size };
183
- }
128
+ var import_react_native = require("react-native");
129
+ var import_react_native_paper = require("react-native-paper");
184
130
 
185
131
  // src/utils/colorNames.ts
186
132
  var colorNames = {
@@ -380,25 +326,6 @@ var getRgb = (value) => {
380
326
  return null;
381
327
  };
382
328
 
383
- // src/utils/isDarkColor.ts
384
- var isDarkColor = (value) => {
385
- const rgb = getRgb(value);
386
- if (!rgb) return false;
387
- const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
388
- const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
389
- const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
390
- return luminance < 0.5;
391
- };
392
-
393
- // src/utils/getSwatchContrast.ts
394
- var RING_WIDTH = 1;
395
- var SELECTED_RING_WIDTH = 3;
396
- var getContrastColor = (color) => isDarkColor(color) ? "#ffffff" : "#000000";
397
- var getSwatchRing = (color, selected, outlineColor) => ({
398
- borderColor: selected ? getContrastColor(color) : outlineColor,
399
- borderWidth: selected ? SELECTED_RING_WIDTH : RING_WIDTH
400
- });
401
-
402
329
  // src/utils/getHex.ts
403
330
  var getHex = (value) => {
404
331
  const rgb = getRgb(value);
@@ -414,6 +341,92 @@ var getHex = (value) => {
414
341
  return hexString;
415
342
  };
416
343
 
344
+ // src/utils/getBlendedColor.ts
345
+ var getBlendedColor = (foreground, background, alpha) => {
346
+ const fg = getRgb(foreground);
347
+ const bg = getRgb(background);
348
+ if (!fg || !bg) return background;
349
+ const safeAlpha = Math.min(1, Math.max(0, alpha));
350
+ const r = Math.round(fg.r * safeAlpha + bg.r * (1 - safeAlpha));
351
+ const g = Math.round(fg.g * safeAlpha + bg.g * (1 - safeAlpha));
352
+ const b = Math.round(fg.b * safeAlpha + bg.b * (1 - safeAlpha));
353
+ return getHex(`rgb(${r}, ${g}, ${b})`) ?? background;
354
+ };
355
+
356
+ // src/utils/getTonalColor.ts
357
+ var getTonalColor = (color, lightness) => {
358
+ const rgb = getRgb(color);
359
+ if (!rgb) return color;
360
+ const r = rgb.r / 255;
361
+ const g = rgb.g / 255;
362
+ const b = rgb.b / 255;
363
+ const max = Math.max(r, g, b);
364
+ const min = Math.min(r, g, b);
365
+ const delta = max - min;
366
+ let h = 0;
367
+ if (delta !== 0) {
368
+ if (max === r) h = (g - b) / delta % 6;
369
+ else if (max === g) h = (b - r) / delta + 2;
370
+ else h = (r - g) / delta + 4;
371
+ h = (h * 60 + 360) % 360;
372
+ }
373
+ const l = (max + min) / 2;
374
+ const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
375
+ const targetL = Math.min(1, Math.max(0, lightness));
376
+ const c = (1 - Math.abs(2 * targetL - 1)) * s;
377
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
378
+ const m = targetL - c / 2;
379
+ let r2 = 0, g2 = 0, b2 = 0;
380
+ if (h < 60) {
381
+ r2 = c;
382
+ g2 = x;
383
+ b2 = 0;
384
+ } else if (h < 120) {
385
+ r2 = x;
386
+ g2 = c;
387
+ b2 = 0;
388
+ } else if (h < 180) {
389
+ r2 = 0;
390
+ g2 = c;
391
+ b2 = x;
392
+ } else if (h < 240) {
393
+ r2 = 0;
394
+ g2 = x;
395
+ b2 = c;
396
+ } else if (h < 300) {
397
+ r2 = x;
398
+ g2 = 0;
399
+ b2 = c;
400
+ } else {
401
+ r2 = c;
402
+ g2 = 0;
403
+ b2 = x;
404
+ }
405
+ const toInt = (n) => Math.round((n + m) * 255);
406
+ return getHex(`rgb(${toInt(r2)}, ${toInt(g2)}, ${toInt(b2)})`) ?? color;
407
+ };
408
+
409
+ // src/utils/isDarkColor.ts
410
+ var isDarkColor = (value) => {
411
+ const rgb = getRgb(value);
412
+ if (!rgb) return false;
413
+ const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
414
+ const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
415
+ const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
416
+ return luminance < 0.5;
417
+ };
418
+
419
+ // src/utils/getColorRoles.ts
420
+ var getColorRoles = (base, surface, containerAlpha = 0.15) => {
421
+ const container = getBlendedColor(base, surface, containerAlpha);
422
+ return {
423
+ color: base,
424
+ onColor: getTonalColor(base, isDarkColor(base) ? 0.95 : 0.08),
425
+ container,
426
+ onContainer: getTonalColor(base, isDarkColor(container) ? 0.92 : 0.12)
427
+ };
428
+ };
429
+
417
430
  // src/utils/getTriadicPalette.ts
418
431
  var HARMONY_OFFSETS = {
419
432
  analogous: [1 / 12, 2 / 12],
@@ -495,103 +508,6 @@ function hslToHex(h, s, l) {
495
508
  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
496
509
  }
497
510
 
498
- // src/components/ColorPicker.tsx
499
- var import_react8 = require("react");
500
- var import_react_native6 = require("react-native");
501
- var import_react_native_paper7 = require("react-native-paper");
502
-
503
- // src/components/Dialog.tsx
504
- var import_react7 = __toESM(require("react"));
505
- var import_react_native5 = require("react-native");
506
- var import_react_native_paper6 = require("react-native-paper");
507
- var import_react_native_safe_area_context = require("react-native-safe-area-context");
508
-
509
- // src/ThemeProvider.tsx
510
- var import_react5 = require("react");
511
- var import_react_native3 = require("react-native");
512
- var import_react_native_paper4 = require("react-native-paper");
513
-
514
- // src/useComputedTheme.ts
515
- var import_react4 = require("react");
516
- var import_react_native2 = require("react-native");
517
- var import_react_native_paper3 = require("react-native-paper");
518
-
519
- // src/utils/getBlendedColor.ts
520
- var getBlendedColor = (foreground, background, alpha) => {
521
- const fg = getRgb(foreground);
522
- const bg = getRgb(background);
523
- if (!fg || !bg) return background;
524
- const safeAlpha = Math.min(1, Math.max(0, alpha));
525
- const r = Math.round(fg.r * safeAlpha + bg.r * (1 - safeAlpha));
526
- const g = Math.round(fg.g * safeAlpha + bg.g * (1 - safeAlpha));
527
- const b = Math.round(fg.b * safeAlpha + bg.b * (1 - safeAlpha));
528
- return getHex(`rgb(${r}, ${g}, ${b})`) ?? background;
529
- };
530
-
531
- // src/utils/getTonalColor.ts
532
- var getTonalColor = (color, lightness) => {
533
- const rgb = getRgb(color);
534
- if (!rgb) return color;
535
- const r = rgb.r / 255;
536
- const g = rgb.g / 255;
537
- const b = rgb.b / 255;
538
- const max = Math.max(r, g, b);
539
- const min = Math.min(r, g, b);
540
- const delta = max - min;
541
- let h = 0;
542
- if (delta !== 0) {
543
- if (max === r) h = (g - b) / delta % 6;
544
- else if (max === g) h = (b - r) / delta + 2;
545
- else h = (r - g) / delta + 4;
546
- h = (h * 60 + 360) % 360;
547
- }
548
- const l = (max + min) / 2;
549
- const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
550
- const targetL = Math.min(1, Math.max(0, lightness));
551
- const c = (1 - Math.abs(2 * targetL - 1)) * s;
552
- const x = c * (1 - Math.abs(h / 60 % 2 - 1));
553
- const m = targetL - c / 2;
554
- let r2 = 0, g2 = 0, b2 = 0;
555
- if (h < 60) {
556
- r2 = c;
557
- g2 = x;
558
- b2 = 0;
559
- } else if (h < 120) {
560
- r2 = x;
561
- g2 = c;
562
- b2 = 0;
563
- } else if (h < 180) {
564
- r2 = 0;
565
- g2 = c;
566
- b2 = x;
567
- } else if (h < 240) {
568
- r2 = 0;
569
- g2 = x;
570
- b2 = c;
571
- } else if (h < 300) {
572
- r2 = x;
573
- g2 = 0;
574
- b2 = c;
575
- } else {
576
- r2 = c;
577
- g2 = 0;
578
- b2 = x;
579
- }
580
- const toInt = (n) => Math.round((n + m) * 255);
581
- return getHex(`rgb(${toInt(r2)}, ${toInt(g2)}, ${toInt(b2)})`) ?? color;
582
- };
583
-
584
- // src/utils/getColorRoles.ts
585
- var getColorRoles = (base, surface, containerAlpha = 0.15) => {
586
- const container = getBlendedColor(base, surface, containerAlpha);
587
- return {
588
- color: base,
589
- onColor: getTonalColor(base, isDarkColor(base) ? 0.95 : 0.08),
590
- container,
591
- onContainer: getTonalColor(base, isDarkColor(container) ? 0.92 : 0.12)
592
- };
593
- };
594
-
595
511
  // src/useComputedTheme.ts
596
512
  function normalizeExplicitColor(value) {
597
513
  if (!getRgb(value)) throw new Error(`Invalid color format: ${value}`);
@@ -603,15 +519,15 @@ var SEMANTIC_BASE_COLORS = {
603
519
  danger: "#B00020"
604
520
  };
605
521
  function useComputedTheme(appearance, color, harmony = "split-complementary") {
606
- const [theme, setTheme] = (0, import_react4.useState)(null);
522
+ const [theme, setTheme] = (0, import_react3.useState)(null);
607
523
  const primaryInput = typeof color === "string" ? color : color.primary;
608
524
  const secondaryInput = typeof color === "string" ? null : color.secondary;
609
525
  const tertiaryInput = typeof color === "string" ? null : color.tertiary;
610
- const computeTheme = (0, import_react4.useCallback)(() => {
526
+ const computeTheme = (0, import_react3.useCallback)(() => {
611
527
  let resolved = appearance;
612
- if (resolved === "system") resolved = import_react_native2.Appearance.getColorScheme();
528
+ if (resolved === "system") resolved = import_react_native.Appearance.getColorScheme();
613
529
  if (resolved === "no-preference" || !resolved) resolved = "light";
614
- const base = resolved === "dark" ? import_react_native_paper3.MD3DarkTheme : import_react_native_paper3.MD3LightTheme;
530
+ const base = resolved === "dark" ? import_react_native_paper.MD3DarkTheme : import_react_native_paper.MD3LightTheme;
615
531
  const surface = base.colors.surface;
616
532
  const successRoles = getColorRoles(SEMANTIC_BASE_COLORS.success, surface);
617
533
  const warningRoles = getColorRoles(SEMANTIC_BASE_COLORS.warning, surface);
@@ -669,55 +585,142 @@ function useComputedTheme(appearance, color, harmony = "split-complementary") {
669
585
  };
670
586
  setTheme(_theme);
671
587
  }, [appearance, primaryInput, secondaryInput, tertiaryInput, harmony]);
672
- (0, import_react4.useEffect)(() => {
588
+ (0, import_react3.useEffect)(() => {
673
589
  computeTheme();
674
590
  }, [computeTheme]);
675
- (0, import_react4.useEffect)(() => {
676
- const subscription = import_react_native2.Appearance.addChangeListener(computeTheme);
591
+ (0, import_react3.useEffect)(() => {
592
+ const subscription = import_react_native.Appearance.addChangeListener(computeTheme);
677
593
  return subscription.remove;
678
594
  }, [computeTheme]);
679
595
  return theme;
680
596
  }
681
597
 
682
598
  // src/ThemeProvider.tsx
683
- var import_jsx_runtime4 = require("react/jsx-runtime");
684
- var NavBarContext = (0, import_react5.createContext)({});
685
- var useNavBarContext = () => (0, import_react5.useContext)(NavBarContext);
686
- var BlurModuleContext = (0, import_react5.createContext)(void 0);
687
- var useBlurModule = () => (0, import_react5.useContext)(BlurModuleContext);
688
- var ReanimatedModuleContext = (0, import_react5.createContext)(void 0);
689
- var useReanimatedModule = () => (0, import_react5.useContext)(ReanimatedModuleContext);
690
- var MaskedViewModuleContext = (0, import_react5.createContext)(void 0);
691
- var useMaskedViewModule = () => (0, import_react5.useContext)(MaskedViewModuleContext);
692
- function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
693
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ThemeSettingsProvider, { initialValue, onChange, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ThemeProviderBody, { defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style, children }) });
599
+ var import_jsx_runtime = require("react/jsx-runtime");
600
+ var NavBarContext = (0, import_react4.createContext)({});
601
+ var useNavBarContext = () => (0, import_react4.useContext)(NavBarContext);
602
+ var BlurModuleContext = (0, import_react4.createContext)(void 0);
603
+ var useBlurModule = () => (0, import_react4.useContext)(BlurModuleContext);
604
+ var ReanimatedModuleContext = (0, import_react4.createContext)(void 0);
605
+ var useReanimatedModule = () => (0, import_react4.useContext)(ReanimatedModuleContext);
606
+ var MaskedViewModuleContext = (0, import_react4.createContext)(void 0);
607
+ var useMaskedViewModule = () => (0, import_react4.useContext)(MaskedViewModuleContext);
608
+ var StatusBarEnabledContext = (0, import_react4.createContext)(true);
609
+ var useStatusBarEnabled = () => (0, import_react4.useContext)(StatusBarEnabledContext);
610
+ function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style }) {
611
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeSettingsProvider, { initialValue, onChange, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeProviderBody, { defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style, children }) });
694
612
  }
695
613
  var ThemeSettingsProvider = themeSettingsProvider;
696
- function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
614
+ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBar = true, statusBarProps, style }) {
697
615
  const { settings } = useThemeSettingsInternal();
698
616
  const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony);
699
- const theme = (0, import_react5.useMemo)(() => {
617
+ const theme = (0, import_react4.useMemo)(() => {
700
618
  if (!computedTheme) return null;
701
619
  if (!fontFamily) return computedTheme;
702
- return { ...computedTheme, fonts: (0, import_react_native_paper4.configureFonts)({ config: { fontFamily } }) };
620
+ return { ...computedTheme, fonts: (0, import_react_native_paper2.configureFonts)({ config: { fontFamily } }) };
703
621
  }, [computedTheme, fontFamily]);
704
- const called = (0, import_react5.useRef)(false);
705
- (0, import_react5.useEffect)(() => {
622
+ const called = (0, import_react4.useRef)(false);
623
+ (0, import_react4.useEffect)(() => {
706
624
  if (theme && !called.current) {
707
625
  called.current = true;
708
626
  onReady?.();
709
627
  }
710
628
  }, [theme, onReady]);
711
629
  if (!theme) return null;
712
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native_paper4.Provider, { theme, children: [
713
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.StatusBar, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
714
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MaskedViewModuleContext.Provider, { value: maskedView, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Portal.Host, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) }) })
630
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native_paper2.Provider, { theme, children: [
631
+ statusBar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.StatusBar, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
632
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBarEnabledContext.Provider, { value: statusBar, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MaskedViewModuleContext.Provider, { value: maskedView, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper2.Portal.Host, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) }) }) })
715
633
  ] });
716
634
  }
717
- var styles = import_react_native3.StyleSheet.create({
635
+ var styles = import_react_native2.StyleSheet.create({
718
636
  flex: { flex: 1 }
719
637
  });
720
638
 
639
+ // src/components/Appbar.tsx
640
+ var import_jsx_runtime2 = require("react/jsx-runtime");
641
+ function Header({ style, ...props }) {
642
+ const defaults = usePaperDefaults();
643
+ const theme = (0, import_react_native_paper3.useTheme)();
644
+ const statusBar = useStatusBarEnabled();
645
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
646
+ statusBar && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.StatusBar, { backgroundColor: theme.colors.surface, barStyle: theme.dark ? "light-content" : "dark-content" }),
647
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper3.Appbar.Header, { ...defaults.AppbarHeader, ...props, style })
648
+ ] });
649
+ }
650
+ var Appbar = {
651
+ Header,
652
+ Content: import_react_native_paper3.Appbar.Content,
653
+ Action: import_react_native_paper3.Appbar.Action,
654
+ BackAction: import_react_native_paper3.Appbar.BackAction
655
+ };
656
+
657
+ // src/components/AppearancePicker.tsx
658
+ var import_react_native_paper4 = require("react-native-paper");
659
+ var import_jsx_runtime3 = require("react/jsx-runtime");
660
+ var APPEARANCES = [
661
+ { label: "System", value: "system" },
662
+ { label: "Light", value: "light" },
663
+ { label: "Dark", value: "dark" }
664
+ ];
665
+ var DEFAULT_ICONS = {
666
+ system: "monitor",
667
+ light: "white-balance-sunny",
668
+ dark: "weather-night"
669
+ };
670
+ var AppearancePicker = ({ value, onChange, showLabels = true, icons }) => {
671
+ const resolvedIcons = { ...DEFAULT_ICONS, ...icons };
672
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper4.SegmentedButtons, { value, onValueChange: (v) => onChange(v), buttons: APPEARANCES.map((a) => ({ value: a.value, label: showLabels ? a.label : void 0, icon: resolvedIcons[a.value], accessibilityLabel: a.label })) });
673
+ };
674
+
675
+ // src/useThemeSettings.ts
676
+ var useThemeSettings = useThemeSettingsInternal;
677
+
678
+ // src/components/AutoAppearancePicker.tsx
679
+ var import_jsx_runtime4 = require("react/jsx-runtime");
680
+ var AutoAppearancePicker = (props) => {
681
+ const { settings, set } = useThemeSettings();
682
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AppearancePicker, { ...props, value: settings.appearance, onChange: (appearance) => set({ appearance }) });
683
+ };
684
+
685
+ // src/utils/resolveSeedColor.ts
686
+ var resolveSeedColor = (color) => typeof color === "string" ? color : color.primary;
687
+
688
+ // src/components/PalettePicker.tsx
689
+ var import_react9 = require("react");
690
+ var import_react_native7 = require("react-native");
691
+ var import_react_native_paper9 = require("react-native-paper");
692
+
693
+ // src/useSwatchGrid.ts
694
+ var import_react5 = require("react");
695
+ var SWATCH_GRID_COLUMNS = 5;
696
+ var SWATCH_GRID_GAP = 12;
697
+ function useSwatchSize(fallback) {
698
+ const [width, setWidth] = (0, import_react5.useState)(0);
699
+ const onLayout = ({ nativeEvent }) => setWidth(nativeEvent.layout.width);
700
+ const size = width > 0 ? (width - SWATCH_GRID_GAP * (SWATCH_GRID_COLUMNS - 1)) / SWATCH_GRID_COLUMNS : fallback;
701
+ return { onLayout, size };
702
+ }
703
+
704
+ // src/utils/getSwatchContrast.ts
705
+ var RING_WIDTH = 1;
706
+ var SELECTED_RING_WIDTH = 3;
707
+ var getContrastColor = (color) => isDarkColor(color) ? "#ffffff" : "#000000";
708
+ var getSwatchRing = (color, selected, outlineColor) => ({
709
+ borderColor: selected ? getContrastColor(color) : outlineColor,
710
+ borderWidth: selected ? SELECTED_RING_WIDTH : RING_WIDTH
711
+ });
712
+
713
+ // src/components/ColorPicker.tsx
714
+ var import_react8 = require("react");
715
+ var import_react_native6 = require("react-native");
716
+ var import_react_native_paper7 = require("react-native-paper");
717
+
718
+ // src/components/Dialog.tsx
719
+ var import_react7 = __toESM(require("react"));
720
+ var import_react_native5 = require("react-native");
721
+ var import_react_native_paper6 = require("react-native-paper");
722
+ var import_react_native_safe_area_context = require("react-native-safe-area-context");
723
+
721
724
  // src/components/BlurView.tsx
722
725
  var import_react6 = require("react");
723
726
  var import_react_native4 = require("react-native");
package/dist/index.mjs CHANGED
@@ -22,7 +22,7 @@ function useBlur(override) {
22
22
  }
23
23
 
24
24
  // src/components/Appbar.tsx
25
- import { StatusBar } from "react-native";
25
+ import { StatusBar as StatusBar2 } from "react-native";
26
26
  import { Appbar as PaperAppbar, useTheme } from "react-native-paper";
27
27
 
28
28
  // src/PaperDefaultsContext.tsx
@@ -32,69 +32,15 @@ function usePaperDefaults() {
32
32
  return useContext2(PaperDefaultsContext);
33
33
  }
34
34
 
35
- // src/components/Appbar.tsx
36
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
37
- function Header({ style, ...props }) {
38
- const defaults = usePaperDefaults();
39
- const theme = useTheme();
40
- return /* @__PURE__ */ jsxs(Fragment, { children: [
41
- /* @__PURE__ */ jsx(StatusBar, { backgroundColor: theme.colors.surface, barStyle: theme.dark ? "light-content" : "dark-content" }),
42
- /* @__PURE__ */ jsx(PaperAppbar.Header, { ...defaults.AppbarHeader, ...props, style })
43
- ] });
44
- }
45
- var Appbar = {
46
- Header,
47
- Content: PaperAppbar.Content,
48
- Action: PaperAppbar.Action,
49
- BackAction: PaperAppbar.BackAction
50
- };
51
-
52
- // src/components/AppearancePicker.tsx
53
- import { SegmentedButtons } from "react-native-paper";
54
- import { jsx as jsx2 } from "react/jsx-runtime";
55
- var APPEARANCES = [
56
- { label: "System", value: "system" },
57
- { label: "Light", value: "light" },
58
- { label: "Dark", value: "dark" }
59
- ];
60
- var DEFAULT_ICONS = {
61
- system: "monitor",
62
- light: "white-balance-sunny",
63
- dark: "weather-night"
64
- };
65
- var AppearancePicker = ({ value, onChange, showLabels = true, icons }) => {
66
- const resolvedIcons = { ...DEFAULT_ICONS, ...icons };
67
- return /* @__PURE__ */ jsx2(SegmentedButtons, { value, onValueChange: (v) => onChange(v), buttons: APPEARANCES.map((a) => ({ value: a.value, label: showLabels ? a.label : void 0, icon: resolvedIcons[a.value], accessibilityLabel: a.label })) });
68
- };
69
-
70
- // src/useThemeSettings.ts
71
- var useThemeSettings = useThemeSettingsInternal;
72
-
73
- // src/components/AutoAppearancePicker.tsx
74
- import { jsx as jsx3 } from "react/jsx-runtime";
75
- var AutoAppearancePicker = (props) => {
76
- const { settings, set } = useThemeSettings();
77
- return /* @__PURE__ */ jsx3(AppearancePicker, { ...props, value: settings.appearance, onChange: (appearance) => set({ appearance }) });
78
- };
79
-
80
- // src/utils/resolveSeedColor.ts
81
- var resolveSeedColor = (color) => typeof color === "string" ? color : color.primary;
82
-
83
- // src/components/PalettePicker.tsx
84
- import { useState as useState5 } from "react";
85
- import { StyleSheet as StyleSheet5, TouchableOpacity as TouchableOpacity2, View as View5 } from "react-native";
86
- import { Icon as Icon2, TouchableRipple as TouchableRipple2, useTheme as useTheme5 } from "react-native-paper";
35
+ // src/ThemeProvider.tsx
36
+ import { createContext as createContext2, useContext as useContext3, useEffect as useEffect2, useMemo, useRef } from "react";
37
+ import { StatusBar, StyleSheet, View } from "react-native";
38
+ import { configureFonts, Portal, Provider as PaperProvider } from "react-native-paper";
87
39
 
88
- // src/useSwatchGrid.ts
89
- import { useState } from "react";
90
- var SWATCH_GRID_COLUMNS = 5;
91
- var SWATCH_GRID_GAP = 12;
92
- function useSwatchSize(fallback) {
93
- const [width, setWidth] = useState(0);
94
- const onLayout = ({ nativeEvent }) => setWidth(nativeEvent.layout.width);
95
- const size = width > 0 ? (width - SWATCH_GRID_GAP * (SWATCH_GRID_COLUMNS - 1)) / SWATCH_GRID_COLUMNS : fallback;
96
- return { onLayout, size };
97
- }
40
+ // src/useComputedTheme.ts
41
+ import { useCallback, useEffect, useState } from "react";
42
+ import { Appearance } from "react-native";
43
+ import { MD3DarkTheme, MD3LightTheme } from "react-native-paper";
98
44
 
99
45
  // src/utils/colorNames.ts
100
46
  var colorNames = {
@@ -294,25 +240,6 @@ var getRgb = (value) => {
294
240
  return null;
295
241
  };
296
242
 
297
- // src/utils/isDarkColor.ts
298
- var isDarkColor = (value) => {
299
- const rgb = getRgb(value);
300
- if (!rgb) return false;
301
- const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
302
- const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
303
- const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
304
- return luminance < 0.5;
305
- };
306
-
307
- // src/utils/getSwatchContrast.ts
308
- var RING_WIDTH = 1;
309
- var SELECTED_RING_WIDTH = 3;
310
- var getContrastColor = (color) => isDarkColor(color) ? "#ffffff" : "#000000";
311
- var getSwatchRing = (color, selected, outlineColor) => ({
312
- borderColor: selected ? getContrastColor(color) : outlineColor,
313
- borderWidth: selected ? SELECTED_RING_WIDTH : RING_WIDTH
314
- });
315
-
316
243
  // src/utils/getHex.ts
317
244
  var getHex = (value) => {
318
245
  const rgb = getRgb(value);
@@ -328,6 +255,92 @@ var getHex = (value) => {
328
255
  return hexString;
329
256
  };
330
257
 
258
+ // src/utils/getBlendedColor.ts
259
+ var getBlendedColor = (foreground, background, alpha) => {
260
+ const fg = getRgb(foreground);
261
+ const bg = getRgb(background);
262
+ if (!fg || !bg) return background;
263
+ const safeAlpha = Math.min(1, Math.max(0, alpha));
264
+ const r = Math.round(fg.r * safeAlpha + bg.r * (1 - safeAlpha));
265
+ const g = Math.round(fg.g * safeAlpha + bg.g * (1 - safeAlpha));
266
+ const b = Math.round(fg.b * safeAlpha + bg.b * (1 - safeAlpha));
267
+ return getHex(`rgb(${r}, ${g}, ${b})`) ?? background;
268
+ };
269
+
270
+ // src/utils/getTonalColor.ts
271
+ var getTonalColor = (color, lightness) => {
272
+ const rgb = getRgb(color);
273
+ if (!rgb) return color;
274
+ const r = rgb.r / 255;
275
+ const g = rgb.g / 255;
276
+ const b = rgb.b / 255;
277
+ const max = Math.max(r, g, b);
278
+ const min = Math.min(r, g, b);
279
+ const delta = max - min;
280
+ let h = 0;
281
+ if (delta !== 0) {
282
+ if (max === r) h = (g - b) / delta % 6;
283
+ else if (max === g) h = (b - r) / delta + 2;
284
+ else h = (r - g) / delta + 4;
285
+ h = (h * 60 + 360) % 360;
286
+ }
287
+ const l = (max + min) / 2;
288
+ const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
289
+ const targetL = Math.min(1, Math.max(0, lightness));
290
+ const c = (1 - Math.abs(2 * targetL - 1)) * s;
291
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
292
+ const m = targetL - c / 2;
293
+ let r2 = 0, g2 = 0, b2 = 0;
294
+ if (h < 60) {
295
+ r2 = c;
296
+ g2 = x;
297
+ b2 = 0;
298
+ } else if (h < 120) {
299
+ r2 = x;
300
+ g2 = c;
301
+ b2 = 0;
302
+ } else if (h < 180) {
303
+ r2 = 0;
304
+ g2 = c;
305
+ b2 = x;
306
+ } else if (h < 240) {
307
+ r2 = 0;
308
+ g2 = x;
309
+ b2 = c;
310
+ } else if (h < 300) {
311
+ r2 = x;
312
+ g2 = 0;
313
+ b2 = c;
314
+ } else {
315
+ r2 = c;
316
+ g2 = 0;
317
+ b2 = x;
318
+ }
319
+ const toInt = (n) => Math.round((n + m) * 255);
320
+ return getHex(`rgb(${toInt(r2)}, ${toInt(g2)}, ${toInt(b2)})`) ?? color;
321
+ };
322
+
323
+ // src/utils/isDarkColor.ts
324
+ var isDarkColor = (value) => {
325
+ const rgb = getRgb(value);
326
+ if (!rgb) return false;
327
+ const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
328
+ const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
329
+ const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
330
+ return luminance < 0.5;
331
+ };
332
+
333
+ // src/utils/getColorRoles.ts
334
+ var getColorRoles = (base, surface, containerAlpha = 0.15) => {
335
+ const container = getBlendedColor(base, surface, containerAlpha);
336
+ return {
337
+ color: base,
338
+ onColor: getTonalColor(base, isDarkColor(base) ? 0.95 : 0.08),
339
+ container,
340
+ onContainer: getTonalColor(base, isDarkColor(container) ? 0.92 : 0.12)
341
+ };
342
+ };
343
+
331
344
  // src/utils/getTriadicPalette.ts
332
345
  var HARMONY_OFFSETS = {
333
346
  analogous: [1 / 12, 2 / 12],
@@ -409,103 +422,6 @@ function hslToHex(h, s, l) {
409
422
  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
410
423
  }
411
424
 
412
- // src/components/ColorPicker.tsx
413
- import { useState as useState4 } from "react";
414
- import { StyleSheet as StyleSheet4, TouchableOpacity, View as View4 } from "react-native";
415
- import { Icon, TouchableRipple, useTheme as useTheme4 } from "react-native-paper";
416
-
417
- // src/components/Dialog.tsx
418
- import React, { useEffect as useEffect3, useState as useState3 } from "react";
419
- import { Animated, BackHandler, Easing, Platform as Platform2, Pressable, StyleSheet as StyleSheet3, View as View3 } from "react-native";
420
- import { Dialog as PaperDialog, Portal as Portal2, useTheme as useTheme3 } from "react-native-paper";
421
- import { useSafeAreaInsets } from "react-native-safe-area-context";
422
-
423
- // src/ThemeProvider.tsx
424
- import { createContext as createContext2, useContext as useContext3, useEffect as useEffect2, useMemo, useRef } from "react";
425
- import { StatusBar as StatusBar2, StyleSheet, View } from "react-native";
426
- import { configureFonts, Portal, Provider as PaperProvider } from "react-native-paper";
427
-
428
- // src/useComputedTheme.ts
429
- import { useCallback, useEffect, useState as useState2 } from "react";
430
- import { Appearance } from "react-native";
431
- import { MD3DarkTheme, MD3LightTheme } from "react-native-paper";
432
-
433
- // src/utils/getBlendedColor.ts
434
- var getBlendedColor = (foreground, background, alpha) => {
435
- const fg = getRgb(foreground);
436
- const bg = getRgb(background);
437
- if (!fg || !bg) return background;
438
- const safeAlpha = Math.min(1, Math.max(0, alpha));
439
- const r = Math.round(fg.r * safeAlpha + bg.r * (1 - safeAlpha));
440
- const g = Math.round(fg.g * safeAlpha + bg.g * (1 - safeAlpha));
441
- const b = Math.round(fg.b * safeAlpha + bg.b * (1 - safeAlpha));
442
- return getHex(`rgb(${r}, ${g}, ${b})`) ?? background;
443
- };
444
-
445
- // src/utils/getTonalColor.ts
446
- var getTonalColor = (color, lightness) => {
447
- const rgb = getRgb(color);
448
- if (!rgb) return color;
449
- const r = rgb.r / 255;
450
- const g = rgb.g / 255;
451
- const b = rgb.b / 255;
452
- const max = Math.max(r, g, b);
453
- const min = Math.min(r, g, b);
454
- const delta = max - min;
455
- let h = 0;
456
- if (delta !== 0) {
457
- if (max === r) h = (g - b) / delta % 6;
458
- else if (max === g) h = (b - r) / delta + 2;
459
- else h = (r - g) / delta + 4;
460
- h = (h * 60 + 360) % 360;
461
- }
462
- const l = (max + min) / 2;
463
- const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
464
- const targetL = Math.min(1, Math.max(0, lightness));
465
- const c = (1 - Math.abs(2 * targetL - 1)) * s;
466
- const x = c * (1 - Math.abs(h / 60 % 2 - 1));
467
- const m = targetL - c / 2;
468
- let r2 = 0, g2 = 0, b2 = 0;
469
- if (h < 60) {
470
- r2 = c;
471
- g2 = x;
472
- b2 = 0;
473
- } else if (h < 120) {
474
- r2 = x;
475
- g2 = c;
476
- b2 = 0;
477
- } else if (h < 180) {
478
- r2 = 0;
479
- g2 = c;
480
- b2 = x;
481
- } else if (h < 240) {
482
- r2 = 0;
483
- g2 = x;
484
- b2 = c;
485
- } else if (h < 300) {
486
- r2 = x;
487
- g2 = 0;
488
- b2 = c;
489
- } else {
490
- r2 = c;
491
- g2 = 0;
492
- b2 = x;
493
- }
494
- const toInt = (n) => Math.round((n + m) * 255);
495
- return getHex(`rgb(${toInt(r2)}, ${toInt(g2)}, ${toInt(b2)})`) ?? color;
496
- };
497
-
498
- // src/utils/getColorRoles.ts
499
- var getColorRoles = (base, surface, containerAlpha = 0.15) => {
500
- const container = getBlendedColor(base, surface, containerAlpha);
501
- return {
502
- color: base,
503
- onColor: getTonalColor(base, isDarkColor(base) ? 0.95 : 0.08),
504
- container,
505
- onContainer: getTonalColor(base, isDarkColor(container) ? 0.92 : 0.12)
506
- };
507
- };
508
-
509
425
  // src/useComputedTheme.ts
510
426
  function normalizeExplicitColor(value) {
511
427
  if (!getRgb(value)) throw new Error(`Invalid color format: ${value}`);
@@ -517,7 +433,7 @@ var SEMANTIC_BASE_COLORS = {
517
433
  danger: "#B00020"
518
434
  };
519
435
  function useComputedTheme(appearance, color, harmony = "split-complementary") {
520
- const [theme, setTheme] = useState2(null);
436
+ const [theme, setTheme] = useState(null);
521
437
  const primaryInput = typeof color === "string" ? color : color.primary;
522
438
  const secondaryInput = typeof color === "string" ? null : color.secondary;
523
439
  const tertiaryInput = typeof color === "string" ? null : color.tertiary;
@@ -594,7 +510,7 @@ function useComputedTheme(appearance, color, harmony = "split-complementary") {
594
510
  }
595
511
 
596
512
  // src/ThemeProvider.tsx
597
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
513
+ import { jsx, jsxs } from "react/jsx-runtime";
598
514
  var NavBarContext = createContext2({});
599
515
  var useNavBarContext = () => useContext3(NavBarContext);
600
516
  var BlurModuleContext = createContext2(void 0);
@@ -603,11 +519,13 @@ var ReanimatedModuleContext = createContext2(void 0);
603
519
  var useReanimatedModule = () => useContext3(ReanimatedModuleContext);
604
520
  var MaskedViewModuleContext = createContext2(void 0);
605
521
  var useMaskedViewModule = () => useContext3(MaskedViewModuleContext);
606
- function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
607
- return /* @__PURE__ */ jsx4(ThemeSettingsProvider, { initialValue, onChange, children: /* @__PURE__ */ jsx4(ThemeProviderBody, { defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style, children }) });
522
+ var StatusBarEnabledContext = createContext2(true);
523
+ var useStatusBarEnabled = () => useContext3(StatusBarEnabledContext);
524
+ function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style }) {
525
+ return /* @__PURE__ */ jsx(ThemeSettingsProvider, { initialValue, onChange, children: /* @__PURE__ */ jsx(ThemeProviderBody, { defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style, children }) });
608
526
  }
609
527
  var ThemeSettingsProvider = themeSettingsProvider;
610
- function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
528
+ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBar = true, statusBarProps, style }) {
611
529
  const { settings } = useThemeSettingsInternal();
612
530
  const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony);
613
531
  const theme = useMemo(() => {
@@ -623,15 +541,100 @@ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedVie
623
541
  }
624
542
  }, [theme, onReady]);
625
543
  if (!theme) return null;
626
- return /* @__PURE__ */ jsxs2(PaperProvider, { theme, children: [
627
- /* @__PURE__ */ jsx4(StatusBar2, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
628
- /* @__PURE__ */ jsx4(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ jsx4(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ jsx4(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ jsx4(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ jsx4(MaskedViewModuleContext.Provider, { value: maskedView, children: /* @__PURE__ */ jsx4(Portal.Host, { children: /* @__PURE__ */ jsx4(View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) }) })
544
+ return /* @__PURE__ */ jsxs(PaperProvider, { theme, children: [
545
+ statusBar && /* @__PURE__ */ jsx(StatusBar, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
546
+ /* @__PURE__ */ jsx(StatusBarEnabledContext.Provider, { value: statusBar, children: /* @__PURE__ */ jsx(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ jsx(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ jsx(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ jsx(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ jsx(MaskedViewModuleContext.Provider, { value: maskedView, children: /* @__PURE__ */ jsx(Portal.Host, { children: /* @__PURE__ */ jsx(View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) }) }) })
629
547
  ] });
630
548
  }
631
549
  var styles = StyleSheet.create({
632
550
  flex: { flex: 1 }
633
551
  });
634
552
 
553
+ // src/components/Appbar.tsx
554
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
555
+ function Header({ style, ...props }) {
556
+ const defaults = usePaperDefaults();
557
+ const theme = useTheme();
558
+ const statusBar = useStatusBarEnabled();
559
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
560
+ statusBar && /* @__PURE__ */ jsx2(StatusBar2, { backgroundColor: theme.colors.surface, barStyle: theme.dark ? "light-content" : "dark-content" }),
561
+ /* @__PURE__ */ jsx2(PaperAppbar.Header, { ...defaults.AppbarHeader, ...props, style })
562
+ ] });
563
+ }
564
+ var Appbar = {
565
+ Header,
566
+ Content: PaperAppbar.Content,
567
+ Action: PaperAppbar.Action,
568
+ BackAction: PaperAppbar.BackAction
569
+ };
570
+
571
+ // src/components/AppearancePicker.tsx
572
+ import { SegmentedButtons } from "react-native-paper";
573
+ import { jsx as jsx3 } from "react/jsx-runtime";
574
+ var APPEARANCES = [
575
+ { label: "System", value: "system" },
576
+ { label: "Light", value: "light" },
577
+ { label: "Dark", value: "dark" }
578
+ ];
579
+ var DEFAULT_ICONS = {
580
+ system: "monitor",
581
+ light: "white-balance-sunny",
582
+ dark: "weather-night"
583
+ };
584
+ var AppearancePicker = ({ value, onChange, showLabels = true, icons }) => {
585
+ const resolvedIcons = { ...DEFAULT_ICONS, ...icons };
586
+ return /* @__PURE__ */ jsx3(SegmentedButtons, { value, onValueChange: (v) => onChange(v), buttons: APPEARANCES.map((a) => ({ value: a.value, label: showLabels ? a.label : void 0, icon: resolvedIcons[a.value], accessibilityLabel: a.label })) });
587
+ };
588
+
589
+ // src/useThemeSettings.ts
590
+ var useThemeSettings = useThemeSettingsInternal;
591
+
592
+ // src/components/AutoAppearancePicker.tsx
593
+ import { jsx as jsx4 } from "react/jsx-runtime";
594
+ var AutoAppearancePicker = (props) => {
595
+ const { settings, set } = useThemeSettings();
596
+ return /* @__PURE__ */ jsx4(AppearancePicker, { ...props, value: settings.appearance, onChange: (appearance) => set({ appearance }) });
597
+ };
598
+
599
+ // src/utils/resolveSeedColor.ts
600
+ var resolveSeedColor = (color) => typeof color === "string" ? color : color.primary;
601
+
602
+ // src/components/PalettePicker.tsx
603
+ import { useState as useState5 } from "react";
604
+ import { StyleSheet as StyleSheet5, TouchableOpacity as TouchableOpacity2, View as View5 } from "react-native";
605
+ import { Icon as Icon2, TouchableRipple as TouchableRipple2, useTheme as useTheme5 } from "react-native-paper";
606
+
607
+ // src/useSwatchGrid.ts
608
+ import { useState as useState2 } from "react";
609
+ var SWATCH_GRID_COLUMNS = 5;
610
+ var SWATCH_GRID_GAP = 12;
611
+ function useSwatchSize(fallback) {
612
+ const [width, setWidth] = useState2(0);
613
+ const onLayout = ({ nativeEvent }) => setWidth(nativeEvent.layout.width);
614
+ const size = width > 0 ? (width - SWATCH_GRID_GAP * (SWATCH_GRID_COLUMNS - 1)) / SWATCH_GRID_COLUMNS : fallback;
615
+ return { onLayout, size };
616
+ }
617
+
618
+ // src/utils/getSwatchContrast.ts
619
+ var RING_WIDTH = 1;
620
+ var SELECTED_RING_WIDTH = 3;
621
+ var getContrastColor = (color) => isDarkColor(color) ? "#ffffff" : "#000000";
622
+ var getSwatchRing = (color, selected, outlineColor) => ({
623
+ borderColor: selected ? getContrastColor(color) : outlineColor,
624
+ borderWidth: selected ? SELECTED_RING_WIDTH : RING_WIDTH
625
+ });
626
+
627
+ // src/components/ColorPicker.tsx
628
+ import { useState as useState4 } from "react";
629
+ import { StyleSheet as StyleSheet4, TouchableOpacity, View as View4 } from "react-native";
630
+ import { Icon, TouchableRipple, useTheme as useTheme4 } from "react-native-paper";
631
+
632
+ // src/components/Dialog.tsx
633
+ import React, { useEffect as useEffect3, useState as useState3 } from "react";
634
+ import { Animated, BackHandler, Easing, Platform as Platform2, Pressable, StyleSheet as StyleSheet3, View as View3 } from "react-native";
635
+ import { Dialog as PaperDialog, Portal as Portal2, useTheme as useTheme3 } from "react-native-paper";
636
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
637
+
635
638
  // src/components/BlurView.tsx
636
639
  import { useContext as useContext4 } from "react";
637
640
  import { Platform, StyleSheet as StyleSheet2, View as View2 } from "react-native";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rific/auto-paper",
3
- "version": "0.13.1",
3
+ "version": "0.13.2",
4
4
  "description": "Adaptive react-native-paper theming: triadic palette from a seed color + system/light/dark appearance",
5
5
  "keywords": [
6
6
  "react-native",
@@ -81,14 +81,14 @@
81
81
  "eslint": "^9.39.4",
82
82
  "eslint-plugin-react-hooks": "^7.1.1",
83
83
  "eslint-plugin-react-native": "^5.0.0",
84
- "expo-blur": "^56.0.3",
85
- "expo-navigation-bar": "^56.0.3",
84
+ "expo-blur": "^57.0.3",
85
+ "expo-navigation-bar": "^57.0.3",
86
86
  "jest": "^30.0.0",
87
87
  "jest-environment-jsdom": "^30.0.0",
88
88
  "prettier": "^3.8.3",
89
89
  "react": "^19.0.0",
90
90
  "react-dom": "^19.0.0",
91
- "react-native": "^0.76.0",
91
+ "react-native": "^0.86.3",
92
92
  "react-native-paper": "^5.12.0",
93
93
  "react-native-safe-area-context": "^5.5.2",
94
94
  "tsup": "^8.0.0",
@@ -28,6 +28,10 @@ export const useReanimatedModule = () => useContext(ReanimatedModuleContext)
28
28
  const MaskedViewModuleContext = createContext<MaskedViewModule | undefined>(undefined)
29
29
  export const useMaskedViewModule = () => useContext(MaskedViewModuleContext)
30
30
 
31
+ // Whether this package's own components (Provider, Appbar.Header) render a React Native StatusBar - see `statusBar` below.
32
+ const StatusBarEnabledContext = createContext(true)
33
+ export const useStatusBarEnabled = () => useContext(StatusBarEnabledContext)
34
+
31
35
  export type ProviderProps = {
32
36
  children: ReactNode
33
37
  defaults?: PaperDefaults
@@ -49,6 +53,11 @@ export type ProviderProps = {
49
53
  onReady?: () => void
50
54
  /** Injects react-native-reanimated so `<Dialog animatedStyle={...}>` (e.g. from `useAnimatedStyle()`) can animate its card on the UI thread. Pass `import Reanimated from 'react-native-reanimated'`; omit to keep Dialog's `animatedStyle` prop a no-op. */
51
55
  reanimated?: ReanimatedModule
56
+ /** Renders React Native's `StatusBar`, styled from the theme (here and in `Appbar.Header`). Default true. Pass false
57
+ * for an app that sets the status bar per screen instead (react-native-screens' `statusBarStyle`/`statusBarHidden`
58
+ * screen options, with `UIViewControllerBasedStatusBarAppearance` on): there React Native's StatusBar logs an error,
59
+ * and an app linked against the iOS 27 SDK can't set the status bar app-wide at all, which is the only way it works on iOS. */
60
+ statusBar?: boolean
52
61
  statusBarProps?: StatusBarProps
53
62
  style?: StyleProp<ViewStyle>
54
63
  }
@@ -59,10 +68,10 @@ export type ProviderProps = {
59
68
  // wrapper for the settings half; ThemeProviderBody (an actual descendant, reached via
60
69
  // useThemeSettingsInternal()) does all the theme-computation/gating work the single combined
61
70
  // component used to do directly.
62
- export function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps) {
71
+ export function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style }: ProviderProps) {
63
72
  return (
64
73
  <ThemeSettingsProvider initialValue={initialValue} onChange={onChange}>
65
- <ThemeProviderBody defaults={defaults} expoBlur={expoBlur} fontFamily={fontFamily} maskedView={maskedView} navigationBar={navigationBar} onNavBarChange={onNavBarChange} onReady={onReady} reanimated={reanimated} statusBarProps={statusBarProps} style={style}>
74
+ <ThemeProviderBody defaults={defaults} expoBlur={expoBlur} fontFamily={fontFamily} maskedView={maskedView} navigationBar={navigationBar} onNavBarChange={onNavBarChange} onReady={onReady} reanimated={reanimated} statusBar={statusBar} statusBarProps={statusBarProps} style={style}>
66
75
  {children}
67
76
  </ThemeProviderBody>
68
77
  </ThemeSettingsProvider>
@@ -73,7 +82,7 @@ const ThemeSettingsProvider = themeSettingsProvider
73
82
 
74
83
  type ThemeProviderBodyProps = Omit<ProviderProps, 'initialValue' | 'onChange'>
75
84
 
76
- function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }: ThemeProviderBodyProps) {
85
+ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBar = true, statusBarProps, style }: ThemeProviderBodyProps) {
77
86
  const { settings } = useThemeSettingsInternal()
78
87
 
79
88
  const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony)
@@ -100,28 +109,30 @@ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedVie
100
109
 
101
110
  return (
102
111
  <PaperProvider theme={theme}>
103
- <StatusBar backgroundColor={theme.colors.background} barStyle={theme.dark ? 'light-content' : 'dark-content'} {...statusBarProps} />
104
- <PaperDefaultsContext.Provider value={defaults ?? {}}>
105
- <NavBarContext.Provider value={{ navigationBar, onNavBarChange }}>
106
- <BlurModuleContext.Provider value={expoBlur}>
107
- <ReanimatedModuleContext.Provider value={reanimated}>
108
- <MaskedViewModuleContext.Provider value={maskedView}>
109
- {/* PaperProvider's own PortalHost sits above every context provided here, and Paper's
112
+ {statusBar && <StatusBar backgroundColor={theme.colors.background} barStyle={theme.dark ? 'light-content' : 'dark-content'} {...statusBarProps} />}
113
+ <StatusBarEnabledContext.Provider value={statusBar}>
114
+ <PaperDefaultsContext.Provider value={defaults ?? {}}>
115
+ <NavBarContext.Provider value={{ navigationBar, onNavBarChange }}>
116
+ <BlurModuleContext.Provider value={expoBlur}>
117
+ <ReanimatedModuleContext.Provider value={reanimated}>
118
+ <MaskedViewModuleContext.Provider value={maskedView}>
119
+ {/* PaperProvider's own PortalHost sits above every context provided here, and Paper's
110
120
  Portal only carries its theme and settings across, so anything rendered through a
111
121
  Portal (this package's Dialog, Paper's Menu and Tooltip, or anything an app wraps
112
- in <Portal> itself) would otherwise lose expoBlur, reanimated, maskedView, defaults and the nav
122
+ in <Portal> itself) would otherwise lose expoBlur, reanimated, maskedView, defaults, statusBar and the nav
113
123
  bar hooks: Dialog/Menu's blur silently fell back to solid. This nested host is the
114
124
  nearest one for every Portal under Provider, so their content mounts inside all of
115
125
  these contexts instead. It wraps the styled View, not the other way round, so
116
126
  portals still cover the full area regardless of `style`. */}
117
- <Portal.Host>
118
- <View style={[styles.flex, { backgroundColor: theme.colors.background }, style]}>{children}</View>
119
- </Portal.Host>
120
- </MaskedViewModuleContext.Provider>
121
- </ReanimatedModuleContext.Provider>
122
- </BlurModuleContext.Provider>
123
- </NavBarContext.Provider>
124
- </PaperDefaultsContext.Provider>
127
+ <Portal.Host>
128
+ <View style={[styles.flex, { backgroundColor: theme.colors.background }, style]}>{children}</View>
129
+ </Portal.Host>
130
+ </MaskedViewModuleContext.Provider>
131
+ </ReanimatedModuleContext.Provider>
132
+ </BlurModuleContext.Provider>
133
+ </NavBarContext.Provider>
134
+ </PaperDefaultsContext.Provider>
135
+ </StatusBarEnabledContext.Provider>
125
136
  </PaperProvider>
126
137
  )
127
138
  }
@@ -2,13 +2,16 @@ import { StatusBar } from 'react-native'
2
2
  import { Appbar as PaperAppbar, type AppbarHeaderProps, useTheme } from 'react-native-paper'
3
3
 
4
4
  import { usePaperDefaults } from '../PaperDefaultsContext'
5
+ import { useStatusBarEnabled } from '../ThemeProvider'
5
6
 
6
7
  function Header({ style, ...props }: AppbarHeaderProps) {
7
8
  const defaults = usePaperDefaults()
8
9
  const theme = useTheme()
10
+ // Off under <Provider statusBar={false}>, an app that sets the status bar per screen.
11
+ const statusBar = useStatusBarEnabled()
9
12
  return (
10
13
  <>
11
- <StatusBar backgroundColor={theme.colors.surface} barStyle={theme.dark ? 'light-content' : 'dark-content'} />
14
+ {statusBar && <StatusBar backgroundColor={theme.colors.surface} barStyle={theme.dark ? 'light-content' : 'dark-content'} />}
12
15
  <PaperAppbar.Header {...defaults.AppbarHeader} {...props} style={style} />
13
16
  </>
14
17
  )
@@ -91,9 +91,10 @@ export function FadingScrollView({ style, fadeHeight = SCROLL_FADE_HEIGHT, onScr
91
91
  )
92
92
  }
93
93
 
94
- // React Native's native linear gradient (0.77+, the New Architecture; the fleet's apps are on 0.86), not
95
- // in this package's own 0.76 dev types, hence the cast. A mask's alpha is what shows: opaque keeps the
96
- // content, transparent hides it.
94
+ // React Native's native linear gradient (0.77+, the New Architecture; the fleet's apps are on 0.86). Cast
95
+ // because the peer floor is still 0.76, whose types don't have it, and an app resolves this source file
96
+ // against its own React Native types. A mask's alpha is what shows: opaque keeps the content, transparent
97
+ // hides it.
97
98
  const FADE_IN_BAND = { experimental_backgroundImage: 'linear-gradient(transparent, #000)' } as unknown as ViewStyle
98
99
  const FADE_OUT_BAND = { experimental_backgroundImage: 'linear-gradient(#000, transparent)' } as unknown as ViewStyle
99
100