@rific/auto-paper 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/BlurContext.tsx
9
2
  import { useContext } from "react";
10
3
 
@@ -61,67 +54,28 @@ var Appbar = {
61
54
  import { SegmentedButtons } from "react-native-paper";
62
55
  import { jsx as jsx2 } from "react/jsx-runtime";
63
56
  var APPEARANCES = [
64
- { label: "System", value: "system", icon: "brightness-auto" },
65
- { label: "Light", value: "light", icon: "white-balance-sunny" },
66
- { label: "Dark", value: "dark", icon: "weather-night" }
57
+ { label: "System", value: "system" },
58
+ { label: "Light", value: "light" },
59
+ { label: "Dark", value: "dark" }
67
60
  ];
68
- var AppearancePicker = ({ value, onChange }) => /* @__PURE__ */ jsx2(SegmentedButtons, { value, onValueChange: (v) => onChange(v), buttons: APPEARANCES.map((a) => ({ value: a.value, label: a.label, icon: a.icon })) });
61
+ var DEFAULT_ICONS = {
62
+ system: "monitor",
63
+ light: "white-balance-sunny",
64
+ dark: "weather-night"
65
+ };
66
+ var AppearancePicker = ({ value, onChange, showLabels = true, icons }) => {
67
+ const resolvedIcons = { ...DEFAULT_ICONS, ...icons };
68
+ 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 })) });
69
+ };
69
70
 
70
71
  // src/components/BlurView.tsx
71
- import { useContext as useContext3 } from "react";
72
- import { StyleSheet, View } from "react-native";
72
+ import { useContext as useContext4 } from "react";
73
+ import { StyleSheet as StyleSheet2, View as View2 } from "react-native";
73
74
  import { useTheme as useTheme2 } from "react-native-paper";
74
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
75
- var ExpoBlurView = (() => {
76
- try {
77
- return __require("expo-blur").BlurView;
78
- } catch {
79
- return null;
80
- }
81
- })();
82
- var ELEVATION_OPACITY = { 0: 0, 1: 0.05, 2: 0.08, 3: 0.11, 4: 0.12, 5: 0.14 };
83
- var VARIANT_TINT_OPACITY = { light: 0.12, dark: 0.18 };
84
- var BlurView = ({ blur = true, children, elevation, style, tintColor, tintOpacity, variant = "surface", ...props }) => {
85
- const { dark, colors } = useTheme2();
86
- const { settings } = useContext3(ThemeSettingsContext);
87
- const surfaceColor = variant === "surfaceVariant" ? colors.surfaceVariant : colors.surface;
88
- const variantColor = variant === "primary" ? colors.primary : variant === "secondary" ? colors.secondary : variant === "tertiary" ? colors.tertiary : variant === "error" ? colors.error : void 0;
89
- const resolvedTintColor = tintColor ?? variantColor ?? (elevation !== void 0 ? colors.primary : void 0);
90
- const resolvedTintOpacity = tintOpacity ?? (tintColor ? dark ? 0.24 : 0.16 : variantColor ? dark ? VARIANT_TINT_OPACITY.dark : VARIANT_TINT_OPACITY.light : elevation !== void 0 ? ELEVATION_OPACITY[elevation] : 0);
91
- const tint = resolvedTintColor && resolvedTintOpacity > 0 ? /* @__PURE__ */ jsx3(View, { style: [StyleSheet.absoluteFill, { backgroundColor: resolvedTintColor, opacity: resolvedTintOpacity }] }) : null;
92
- if (blur && ExpoBlurView)
93
- return /* @__PURE__ */ jsxs2(ExpoBlurView, { ...props, tint: dark ? "dark" : "light", style, children: [
94
- /* @__PURE__ */ jsx3(View, { style: [StyleSheet.absoluteFill, { backgroundColor: surfaceColor, opacity: settings.blurTint }] }),
95
- tint,
96
- children
97
- ] });
98
- return /* @__PURE__ */ jsxs2(View, { ...props, style: [{ backgroundColor: surfaceColor }, style], children: [
99
- tint,
100
- children
101
- ] });
102
- };
103
-
104
- // src/components/BottomNavigation.tsx
105
- import { useEffect as useEffect3 } from "react";
106
- import { Platform } from "react-native";
107
- import { BottomNavigation as PaperBottomNavigation, useTheme as useTheme3 } from "react-native-paper";
108
-
109
- // src/navigation-bar.ts
110
- var navigationBar = (() => {
111
- try {
112
- return __require("expo-navigation-bar");
113
- } catch {
114
- return null;
115
- }
116
- })();
117
- var setNavigationBarStyle = (dark) => {
118
- if (!navigationBar) return;
119
- navigationBar.NavigationBar.setStyle(dark ? "light" : "dark");
120
- };
121
75
 
122
76
  // src/ThemeProvider.tsx
123
- import { createContext as createContext3, useCallback as useCallback2, useContext as useContext4, useEffect as useEffect2, useRef, useState as useState2 } from "react";
124
- import { StatusBar as StatusBar2, StyleSheet as StyleSheet2, View as View2 } from "react-native";
77
+ import { createContext as createContext3, useCallback as useCallback2, useContext as useContext3, useEffect as useEffect2, useRef, useState as useState2 } from "react";
78
+ import { StatusBar as StatusBar2, StyleSheet, View } from "react-native";
125
79
  import { Provider as PaperProvider } from "react-native-paper";
126
80
 
127
81
  // src/useComputedTheme.ts
@@ -407,6 +361,27 @@ var getTonalColor = (color, lightness) => {
407
361
  return getHex(`rgb(${toInt(r2)}, ${toInt(g2)}, ${toInt(b2)})`) ?? color;
408
362
  };
409
363
 
364
+ // src/utils/isDarkColor.ts
365
+ var isDarkColor = (value) => {
366
+ const rgb = getRgb(value);
367
+ if (!rgb) return false;
368
+ const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
369
+ const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
370
+ const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
371
+ return luminance < 0.5;
372
+ };
373
+
374
+ // src/utils/getColorRoles.ts
375
+ var getColorRoles = (base, surface, containerAlpha = 0.15) => {
376
+ const container = getBlendedColor(base, surface, containerAlpha);
377
+ return {
378
+ color: base,
379
+ onColor: getTonalColor(base, isDarkColor(base) ? 0.95 : 0.08),
380
+ container,
381
+ onContainer: getTonalColor(base, isDarkColor(container) ? 0.92 : 0.12)
382
+ };
383
+ };
384
+
410
385
  // src/utils/getTriadicPalette.ts
411
386
  var HARMONY_OFFSETS = {
412
387
  analogous: [1 / 12, 2 / 12],
@@ -488,51 +463,71 @@ function hslToHex(h, s, l) {
488
463
  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
489
464
  }
490
465
 
491
- // src/utils/isDarkColor.ts
492
- var isDarkColor = (value) => {
493
- const rgb = getRgb(value);
494
- if (!rgb) return false;
495
- const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
496
- const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
497
- const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
498
- return luminance < 0.5;
499
- };
500
-
501
466
  // src/useComputedTheme.ts
467
+ function normalizeExplicitColor(value) {
468
+ if (!getRgb(value)) throw new Error(`Invalid color format: ${value}`);
469
+ return getHex(value) ?? value;
470
+ }
471
+ var SEMANTIC_BASE_COLORS = {
472
+ success: "#2E7D32",
473
+ warning: "#A15C00",
474
+ danger: "#B00020"
475
+ };
502
476
  function useComputedTheme(appearance, color, harmony = "split-complementary") {
503
477
  const [theme, setTheme] = useState(null);
478
+ const primaryInput = typeof color === "string" ? color : color.primary;
479
+ const secondaryInput = typeof color === "string" ? null : color.secondary;
480
+ const tertiaryInput = typeof color === "string" ? null : color.tertiary;
504
481
  const computeTheme = useCallback(() => {
505
482
  let resolved = appearance;
506
483
  if (resolved === "system") resolved = Appearance.getColorScheme();
507
484
  if (resolved === "no-preference" || !resolved) resolved = "light";
508
485
  const base = resolved === "dark" ? MD3DarkTheme : MD3LightTheme;
486
+ const surface = base.colors.surface;
487
+ const successRoles = getColorRoles(SEMANTIC_BASE_COLORS.success, surface);
488
+ const warningRoles = getColorRoles(SEMANTIC_BASE_COLORS.warning, surface);
489
+ const dangerRoles = getColorRoles(SEMANTIC_BASE_COLORS.danger, surface);
509
490
  const _theme = {
510
491
  ...base,
511
492
  colors: {
512
493
  ...base.colors,
513
- elevation: { ...base.colors.elevation }
494
+ elevation: { ...base.colors.elevation },
495
+ success: successRoles.color,
496
+ onSuccess: successRoles.onColor,
497
+ successContainer: successRoles.container,
498
+ onSuccessContainer: successRoles.onContainer,
499
+ warning: warningRoles.color,
500
+ onWarning: warningRoles.onColor,
501
+ warningContainer: warningRoles.container,
502
+ onWarningContainer: warningRoles.onContainer,
503
+ danger: dangerRoles.color,
504
+ onDanger: dangerRoles.onColor,
505
+ dangerContainer: dangerRoles.container,
506
+ onDangerContainer: dangerRoles.onContainer
514
507
  }
515
508
  };
516
- const palette = getTriadicPalette(color, harmony);
517
- const surface = base.colors.surface;
518
- _theme.colors.primary = palette.primary;
519
- _theme.colors.onPrimary = getTonalColor(palette.primary, isDarkColor(palette.primary) ? 0.95 : 0.08);
520
- _theme.colors.primaryContainer = getBlendedColor(palette.primary, surface, 0.15);
521
- _theme.colors.onPrimaryContainer = getTonalColor(palette.primary, isDarkColor(_theme.colors.primaryContainer) ? 0.92 : 0.12);
522
- _theme.colors.secondary = palette.secondary;
523
- _theme.colors.onSecondary = getTonalColor(palette.secondary, isDarkColor(palette.secondary) ? 0.95 : 0.08);
524
- _theme.colors.secondaryContainer = getBlendedColor(palette.secondary, surface, 0.15);
525
- _theme.colors.onSecondaryContainer = getTonalColor(palette.secondary, isDarkColor(_theme.colors.secondaryContainer) ? 0.92 : 0.12);
526
- _theme.colors.tertiary = palette.tertiary;
527
- _theme.colors.onTertiary = getTonalColor(palette.tertiary, isDarkColor(palette.tertiary) ? 0.95 : 0.08);
528
- _theme.colors.tertiaryContainer = getBlendedColor(palette.tertiary, surface, 0.15);
529
- _theme.colors.onTertiaryContainer = getTonalColor(palette.tertiary, isDarkColor(_theme.colors.tertiaryContainer) ? 0.92 : 0.12);
530
- const blended = getBlendedColor(color, palette.secondary, 0.5);
509
+ const palette = secondaryInput !== null && tertiaryInput !== null ? { primary: normalizeExplicitColor(primaryInput), secondary: normalizeExplicitColor(secondaryInput), tertiary: normalizeExplicitColor(tertiaryInput) } : getTriadicPalette(primaryInput, harmony);
510
+ const primaryRoles = getColorRoles(palette.primary, surface);
511
+ _theme.colors.primary = primaryRoles.color;
512
+ _theme.colors.onPrimary = primaryRoles.onColor;
513
+ _theme.colors.primaryContainer = primaryRoles.container;
514
+ _theme.colors.onPrimaryContainer = primaryRoles.onContainer;
515
+ const secondaryRoles = getColorRoles(palette.secondary, surface);
516
+ _theme.colors.secondary = secondaryRoles.color;
517
+ _theme.colors.onSecondary = secondaryRoles.onColor;
518
+ _theme.colors.secondaryContainer = secondaryRoles.container;
519
+ _theme.colors.onSecondaryContainer = secondaryRoles.onContainer;
520
+ const tertiaryRoles = getColorRoles(palette.tertiary, surface);
521
+ _theme.colors.tertiary = tertiaryRoles.color;
522
+ _theme.colors.onTertiary = tertiaryRoles.onColor;
523
+ _theme.colors.tertiaryContainer = tertiaryRoles.container;
524
+ _theme.colors.onTertiaryContainer = tertiaryRoles.onContainer;
525
+ const blended = getBlendedColor(palette.primary, palette.secondary, 0.5);
531
526
  const tintSurface = (alpha) => getBlendedColor(blended, surface, alpha);
532
527
  _theme.colors.background = base.dark ? "#0F0F0F" : "#F0F0F0";
533
528
  _theme.colors.surface = surface;
534
529
  _theme.colors.surfaceVariant = tintSurface(0.2);
535
- _theme.colors.outline = getBlendedColor(color, base.colors.outline, 0.45);
530
+ _theme.colors.outline = getBlendedColor(palette.primary, base.colors.outline, 0.45);
536
531
  const grey = base.dark ? "#424242" : "#c2c2c2";
537
532
  const blendedGrey = getBlendedColor(blended, grey, 0.05);
538
533
  _theme.colors.elevation = {
@@ -544,7 +539,7 @@ function useComputedTheme(appearance, color, harmony = "split-complementary") {
544
539
  level5: getBlendedColor(blendedGrey, _theme.colors.surface, 0.7)
545
540
  };
546
541
  setTheme(_theme);
547
- }, [appearance, color, harmony]);
542
+ }, [appearance, primaryInput, secondaryInput, tertiaryInput, harmony]);
548
543
  useEffect(() => {
549
544
  computeTheme();
550
545
  }, [computeTheme]);
@@ -556,10 +551,12 @@ function useComputedTheme(appearance, color, harmony = "split-complementary") {
556
551
  }
557
552
 
558
553
  // src/ThemeProvider.tsx
559
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
554
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
560
555
  var NavBarContext = createContext3({});
561
- var useNavBarContext = () => useContext4(NavBarContext);
562
- function Provider({ initialValue, onChange, children, defaults, onNavBarChange, onReady, statusBarProps, style }) {
556
+ var useNavBarContext = () => useContext3(NavBarContext);
557
+ var BlurModuleContext = createContext3(void 0);
558
+ var useBlurModule = () => useContext3(BlurModuleContext);
559
+ function Provider({ children, defaults, expoBlur, initialValue, navigationBar, onChange, onNavBarChange, onReady, statusBarProps, style }) {
563
560
  const [settings, setSettings] = useState2(() => ({ ...defaultThemeSettings, ...initialValue }));
564
561
  const set = useCallback2((patch) => {
565
562
  setSettings((prev) => ({ ...prev, ...patch }));
@@ -582,25 +579,61 @@ function Provider({ initialValue, onChange, children, defaults, onNavBarChange,
582
579
  }
583
580
  }, [theme, onReady]);
584
581
  if (!theme) return null;
585
- return /* @__PURE__ */ jsx4(ThemeSettingsContext.Provider, { value: { settings, set }, children: /* @__PURE__ */ jsxs3(PaperProvider, { theme, children: [
586
- /* @__PURE__ */ jsx4(StatusBar2, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
587
- /* @__PURE__ */ jsx4(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ jsx4(NavBarContext.Provider, { value: { onNavBarChange }, children: /* @__PURE__ */ jsx4(View2, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) })
582
+ return /* @__PURE__ */ jsx3(ThemeSettingsContext.Provider, { value: { settings, set }, children: /* @__PURE__ */ jsxs2(PaperProvider, { theme, children: [
583
+ /* @__PURE__ */ jsx3(StatusBar2, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
584
+ /* @__PURE__ */ jsx3(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ jsx3(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ jsx3(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ jsx3(View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) })
588
585
  ] }) });
589
586
  }
590
- var styles = StyleSheet2.create({
587
+ var styles = StyleSheet.create({
591
588
  flex: { flex: 1 }
592
589
  });
593
590
 
591
+ // src/components/BlurView.tsx
592
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
593
+ var ELEVATION_OPACITY = { 0: 0, 1: 0.05, 2: 0.08, 3: 0.11, 4: 0.12, 5: 0.14 };
594
+ var VARIANT_TINT_OPACITY = { light: 0.12, dark: 0.18 };
595
+ var BlurView = ({ blur = true, children, elevation, style, tintColor, tintOpacity, variant = "surface", ...props }) => {
596
+ const { dark, colors } = useTheme2();
597
+ const { settings } = useContext4(ThemeSettingsContext);
598
+ const expoBlur = useBlurModule();
599
+ const surfaceColor = variant === "surfaceVariant" ? colors.surfaceVariant : colors.surface;
600
+ const variantColor = variant === "primary" ? colors.primary : variant === "secondary" ? colors.secondary : variant === "tertiary" ? colors.tertiary : variant === "error" ? colors.error : void 0;
601
+ const resolvedTintColor = tintColor ?? variantColor ?? (elevation !== void 0 ? colors.primary : void 0);
602
+ const resolvedTintOpacity = tintOpacity ?? (tintColor ? dark ? 0.24 : 0.16 : variantColor ? dark ? VARIANT_TINT_OPACITY.dark : VARIANT_TINT_OPACITY.light : elevation !== void 0 ? ELEVATION_OPACITY[elevation] : 0);
603
+ const tint = resolvedTintColor && resolvedTintOpacity > 0 ? /* @__PURE__ */ jsx4(View2, { style: [StyleSheet2.absoluteFill, { backgroundColor: resolvedTintColor, opacity: resolvedTintOpacity }] }) : null;
604
+ if (blur && expoBlur)
605
+ return /* @__PURE__ */ jsxs3(expoBlur.BlurView, { ...props, tint: dark ? "dark" : "light", style, children: [
606
+ /* @__PURE__ */ jsx4(View2, { style: [StyleSheet2.absoluteFill, { backgroundColor: surfaceColor, opacity: settings.blurTint }] }),
607
+ tint,
608
+ children
609
+ ] });
610
+ return /* @__PURE__ */ jsxs3(View2, { ...props, style: [{ backgroundColor: surfaceColor }, style], children: [
611
+ tint,
612
+ children
613
+ ] });
614
+ };
615
+
616
+ // src/components/BottomNavigation.tsx
617
+ import { useEffect as useEffect3 } from "react";
618
+ import { Platform } from "react-native";
619
+ import { BottomNavigation as PaperBottomNavigation, useTheme as useTheme3 } from "react-native-paper";
620
+
621
+ // src/navigation-bar.ts
622
+ var setNavigationBarStyle = (navigationBar, dark) => {
623
+ if (!navigationBar) return;
624
+ navigationBar.NavigationBar.setStyle(dark ? "light" : "dark");
625
+ };
626
+
594
627
  // src/components/BottomNavigation.tsx
595
628
  import { jsx as jsx5 } from "react/jsx-runtime";
596
629
  function useNavigationBarSync() {
597
630
  const theme = useTheme3();
598
- const { onNavBarChange } = useNavBarContext();
631
+ const { navigationBar, onNavBarChange } = useNavBarContext();
599
632
  useEffect3(() => {
600
633
  if (Platform.OS !== "android") return;
601
634
  if (onNavBarChange) onNavBarChange(theme.colors.surface, theme.dark);
602
- else if (navigationBar) setNavigationBarStyle(theme.dark);
603
- }, [theme, onNavBarChange]);
635
+ else if (navigationBar) setNavigationBarStyle(navigationBar, theme.dark);
636
+ }, [theme, onNavBarChange, navigationBar]);
604
637
  }
605
638
  function BottomNavigationComponent(props) {
606
639
  const defaults = usePaperDefaults();
@@ -637,7 +670,7 @@ function Chip({ variant, style, selectedColor, ...props }) {
637
670
  // src/components/ColorPicker.tsx
638
671
  import { useState as useState5 } from "react";
639
672
  import { StyleSheet as StyleSheet4, TouchableOpacity, View as View4 } from "react-native";
640
- import { Icon, TouchableRipple } from "react-native-paper";
673
+ import { Icon, TouchableRipple, useTheme as useTheme6 } from "react-native-paper";
641
674
 
642
675
  // src/useSwatchGrid.ts
643
676
  import { useState as useState3 } from "react";
@@ -650,6 +683,15 @@ function useSwatchSize(fallback) {
650
683
  return { onLayout, size };
651
684
  }
652
685
 
686
+ // src/utils/getSwatchContrast.ts
687
+ var RING_WIDTH = 1;
688
+ var SELECTED_RING_WIDTH = 3;
689
+ var getContrastColor = (color) => isDarkColor(color) ? "#ffffff" : "#000000";
690
+ var getSwatchRing = (color, selected, outlineColor) => ({
691
+ borderColor: selected ? getContrastColor(color) : outlineColor,
692
+ borderWidth: selected ? SELECTED_RING_WIDTH : RING_WIDTH
693
+ });
694
+
653
695
  // src/components/Dialog.tsx
654
696
  import React2, { useEffect as useEffect4, useRef as useRef2, useState as useState4 } from "react";
655
697
  import { Animated, BackHandler, Easing, Pressable, StyleSheet as StyleSheet3, View as View3 } from "react-native";
@@ -741,10 +783,11 @@ var defaultColors = [
741
783
  ];
742
784
  var ColorPicker = ({ value, onChange, colors = defaultColors, blur: blurProp }) => {
743
785
  const blur = useBlur(blurProp);
786
+ const { colors: themeColors } = useTheme6();
744
787
  const [open, setOpen] = useState5(false);
745
788
  const { onLayout, size } = useSwatchSize(SIZE);
746
789
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
747
- /* @__PURE__ */ jsx9(TouchableRipple, { onPress: () => setOpen(true), style: [styles3.trigger, { backgroundColor: value }], borderless: true, children: /* @__PURE__ */ jsx9(Icon, { source: "palette", size: 20, color: "white" }) }),
790
+ /* @__PURE__ */ jsx9(TouchableRipple, { onPress: () => setOpen(true), style: [styles3.trigger, { backgroundColor: value }, getSwatchRing(value, false, themeColors.outlineVariant)], borderless: true, children: /* @__PURE__ */ jsx9(Icon, { source: "palette", size: 20, color: getContrastColor(value) }) }),
748
791
  /* @__PURE__ */ jsx9(Dialog, { blur, visible: open, onDismiss: () => setOpen(false), children: /* @__PURE__ */ jsx9(Dialog.Content, { children: /* @__PURE__ */ jsx9(View4, { style: styles3.swatches, onLayout, children: colors.map(({ value: hex }) => /* @__PURE__ */ jsx9(
749
792
  TouchableOpacity,
750
793
  {
@@ -752,15 +795,14 @@ var ColorPicker = ({ value, onChange, colors = defaultColors, blur: blurProp })
752
795
  onChange(hex);
753
796
  setOpen(false);
754
797
  },
755
- style: [styles3.swatch, { backgroundColor: hex, borderRadius: size / 2, height: size, width: size }, value === hex && styles3.selected],
756
- children: value === hex && /* @__PURE__ */ jsx9(Icon, { source: "check", size: 20, color: "white" })
798
+ style: [styles3.swatch, { backgroundColor: hex, borderRadius: size / 2, height: size, width: size }, getSwatchRing(hex, value === hex, themeColors.outlineVariant)],
799
+ children: value === hex && /* @__PURE__ */ jsx9(Icon, { source: "check", size: 20, color: getContrastColor(hex) })
757
800
  },
758
801
  hex
759
802
  )) }) }) })
760
803
  ] });
761
804
  };
762
805
  var styles3 = StyleSheet4.create({
763
- selected: { borderColor: "white", borderWidth: 3 },
764
806
  swatch: { alignItems: "center", justifyContent: "center" },
765
807
  swatches: { flexDirection: "row", flexWrap: "wrap", gap: SWATCH_GRID_GAP, paddingTop: 12 },
766
808
  trigger: { alignItems: "center", alignSelf: "flex-start", borderRadius: SIZE / 2, height: SIZE, justifyContent: "center", width: SIZE }
@@ -831,11 +873,11 @@ var HarmonyPicker = ({ value, onChange, blur: blurProp }) => {
831
873
  };
832
874
 
833
875
  // src/components/IconButton.tsx
834
- import { IconButton as PaperIconButton, useTheme as useTheme6 } from "react-native-paper";
876
+ import { IconButton as PaperIconButton, useTheme as useTheme7 } from "react-native-paper";
835
877
  import { jsx as jsx13 } from "react/jsx-runtime";
836
878
  function IconButton({ variant, iconColor, containerColor, ...props }) {
837
879
  const defaults = usePaperDefaults();
838
- const { colors } = useTheme6();
880
+ const { colors } = useTheme7();
839
881
  const variantContainerColor = variant === "primary" ? colors.primaryContainer : variant === "secondary" ? colors.secondaryContainer : variant === "tertiary" ? colors.tertiaryContainer : variant === "surface" ? colors.surface : void 0;
840
882
  const variantIconColor = variant === "primary" ? colors.onPrimaryContainer : variant === "secondary" ? colors.onSecondaryContainer : variant === "tertiary" ? colors.onTertiaryContainer : variant === "surface" ? colors.onSurface : void 0;
841
883
  return /* @__PURE__ */ jsx13(PaperIconButton, { ...defaults.IconButton, containerColor: containerColor ?? variantContainerColor, iconColor: iconColor ?? variantIconColor, ...props });
@@ -844,7 +886,7 @@ function IconButton({ variant, iconColor, containerColor, ...props }) {
844
886
  // src/components/PalettePicker.tsx
845
887
  import { useState as useState7 } from "react";
846
888
  import { StyleSheet as StyleSheet6, TouchableOpacity as TouchableOpacity2, View as View5 } from "react-native";
847
- import { Icon as Icon2, TouchableRipple as TouchableRipple2 } from "react-native-paper";
889
+ import { Icon as Icon2, TouchableRipple as TouchableRipple2, useTheme as useTheme8 } from "react-native-paper";
848
890
  import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
849
891
  var SIZE2 = 48;
850
892
  var defaultWeights = { primary: 2, secondary: 1, tertiary: 1 };
@@ -862,12 +904,13 @@ var buildWedges = (weights) => {
862
904
  var Pie = ({ palette, weights = defaultWeights, size = SIZE2 }) => /* @__PURE__ */ jsx14(View5, { style: [styles5.pie, { borderRadius: size / 2, height: size, width: size }], children: buildWedges(weights).map(({ key, rotate, share }) => /* @__PURE__ */ jsx14(View5, { style: [styles5.rotator, { transform: [{ rotate: `${rotate}deg` }] }], children: /* @__PURE__ */ jsx14(View5, { style: styles5.halfMask, children: /* @__PURE__ */ jsx14(View5, { style: [styles5.innerRotator, { transform: [{ rotate: `${share - 180}deg` }] }], children: /* @__PURE__ */ jsx14(View5, { style: [styles5.wedgeFill, { backgroundColor: palette[key] }] }) }) }) }, key)) });
863
905
  var PalettePicker = ({ value, onChange, harmony = "split-complementary", weights, colors = defaultColors, blur: blurProp }) => {
864
906
  const blur = useBlur(blurProp);
907
+ const { colors: themeColors } = useTheme8();
865
908
  const [open, setOpen] = useState7(false);
866
909
  const { onLayout, size } = useSwatchSize(SIZE2);
867
910
  return /* @__PURE__ */ jsxs6(Fragment3, { children: [
868
911
  /* @__PURE__ */ jsx14(TouchableRipple2, { onPress: () => setOpen(true), style: styles5.trigger, borderless: true, children: /* @__PURE__ */ jsxs6(View5, { style: styles5.triggerContent, children: [
869
912
  /* @__PURE__ */ jsx14(Pie, { palette: getTriadicPalette(value, harmony), weights }),
870
- /* @__PURE__ */ jsx14(View5, { pointerEvents: "none", style: styles5.triggerIcon, children: /* @__PURE__ */ jsx14(Icon2, { source: "palette", size: 20, color: "white" }) })
913
+ /* @__PURE__ */ jsx14(View5, { pointerEvents: "none", style: [styles5.triggerIcon, { borderRadius: SIZE2 / 2 }, getSwatchRing(value, false, themeColors.outlineVariant)], children: /* @__PURE__ */ jsx14(Icon2, { source: "palette", size: 20, color: getContrastColor(value) }) })
871
914
  ] }) }),
872
915
  /* @__PURE__ */ jsx14(Dialog, { blur, visible: open, onDismiss: () => setOpen(false), children: /* @__PURE__ */ jsx14(Dialog.Content, { children: /* @__PURE__ */ jsx14(View5, { style: styles5.swatches, onLayout, children: colors.map(({ value: hex }) => /* @__PURE__ */ jsxs6(
873
916
  TouchableOpacity2,
@@ -879,7 +922,7 @@ var PalettePicker = ({ value, onChange, harmony = "split-complementary", weights
879
922
  style: [styles5.swatch, { height: size, width: size }],
880
923
  children: [
881
924
  /* @__PURE__ */ jsx14(Pie, { palette: getTriadicPalette(hex, harmony), weights, size }),
882
- value === hex && /* @__PURE__ */ jsx14(View5, { pointerEvents: "none", style: [styles5.selectedRing, { borderRadius: size / 2 }], children: /* @__PURE__ */ jsx14(Icon2, { source: "check", size: 20, color: "white" }) })
925
+ /* @__PURE__ */ jsx14(View5, { pointerEvents: "none", style: [styles5.ring, { borderRadius: size / 2 }, getSwatchRing(hex, value === hex, themeColors.outlineVariant)], children: value === hex && /* @__PURE__ */ jsx14(Icon2, { source: "check", size: 20, color: getContrastColor(hex) }) })
883
926
  ]
884
927
  },
885
928
  hex
@@ -891,7 +934,7 @@ var styles5 = StyleSheet6.create({
891
934
  innerRotator: { height: "100%", left: "-100%", overflow: "hidden", position: "absolute", top: 0, width: "200%" },
892
935
  pie: { overflow: "hidden" },
893
936
  rotator: { height: "100%", left: 0, overflow: "hidden", position: "absolute", top: 0, width: "100%" },
894
- selectedRing: { alignItems: "center", borderColor: "white", borderWidth: 3, bottom: 0, justifyContent: "center", left: 0, position: "absolute", right: 0, top: 0 },
937
+ ring: { alignItems: "center", bottom: 0, justifyContent: "center", left: 0, position: "absolute", right: 0, top: 0 },
895
938
  swatch: { alignItems: "center", justifyContent: "center" },
896
939
  swatches: { flexDirection: "row", flexWrap: "wrap", gap: SWATCH_GRID_GAP, paddingTop: 12 },
897
940
  trigger: { alignItems: "center", alignSelf: "flex-start", borderRadius: SIZE2 / 2, height: SIZE2, justifyContent: "center", width: SIZE2 },
@@ -946,10 +989,90 @@ var selectThemeBlur = (state) => state.blur;
946
989
  var selectThemeColor = (state) => state.color;
947
990
  var selectThemeHarmony = (state) => state.harmony;
948
991
 
992
+ // src/useTheme.ts
993
+ import { useTheme as usePaperTheme } from "react-native-paper";
994
+ var useAutoPaperTheme = () => usePaperTheme();
995
+
949
996
  // src/useThemeSettings.ts
950
997
  import { useContext as useContext5 } from "react";
951
998
  var useThemeSettings = () => useContext5(ThemeSettingsContext);
952
999
 
1000
+ // src/utils/getThirdColor.ts
1001
+ function rgbToHsl(r, g, b) {
1002
+ r /= 255;
1003
+ g /= 255;
1004
+ b /= 255;
1005
+ const max = Math.max(r, g, b);
1006
+ const min = Math.min(r, g, b);
1007
+ const delta = max - min;
1008
+ let h = 0;
1009
+ if (delta !== 0) {
1010
+ if (max === r) h = (g - b) / delta % 6;
1011
+ else if (max === g) h = (b - r) / delta + 2;
1012
+ else h = (r - g) / delta + 4;
1013
+ h = (h * 60 + 360) % 360;
1014
+ }
1015
+ const l = (max + min) / 2;
1016
+ const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
1017
+ return { h, s, l };
1018
+ }
1019
+ function hslToHex2(h, s, l) {
1020
+ const c = (1 - Math.abs(2 * l - 1)) * s;
1021
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
1022
+ const m = l - c / 2;
1023
+ let r = 0;
1024
+ let g = 0;
1025
+ let b = 0;
1026
+ if (h < 60) {
1027
+ r = c;
1028
+ g = x;
1029
+ b = 0;
1030
+ } else if (h < 120) {
1031
+ r = x;
1032
+ g = c;
1033
+ b = 0;
1034
+ } else if (h < 180) {
1035
+ r = 0;
1036
+ g = c;
1037
+ b = x;
1038
+ } else if (h < 240) {
1039
+ r = 0;
1040
+ g = x;
1041
+ b = c;
1042
+ } else if (h < 300) {
1043
+ r = x;
1044
+ g = 0;
1045
+ b = c;
1046
+ } else {
1047
+ r = c;
1048
+ g = 0;
1049
+ b = x;
1050
+ }
1051
+ const toInt = (value) => Math.round((value + m) * 255);
1052
+ return getHex(`rgb(${toInt(r)}, ${toInt(g)}, ${toInt(b)})`) ?? "#000000";
1053
+ }
1054
+ var toRad = (deg) => deg * Math.PI / 180;
1055
+ var toDeg = (rad) => rad * 180 / Math.PI;
1056
+ function thirdHue(hA, hB) {
1057
+ const x = Math.cos(toRad(hA)) + Math.cos(toRad(hB));
1058
+ const y = Math.sin(toRad(hA)) + Math.sin(toRad(hB));
1059
+ if (Math.abs(x) < 1e-9 && Math.abs(y) < 1e-9) return (hA + 90) % 360;
1060
+ const meanHue = (toDeg(Math.atan2(y, x)) + 360) % 360;
1061
+ return (meanHue + 180) % 360;
1062
+ }
1063
+ function getThirdColor(colorA, colorB) {
1064
+ const rgbA = getRgb(colorA);
1065
+ const rgbB = getRgb(colorB);
1066
+ if (!rgbA) throw new Error(`Invalid color format: ${colorA}`);
1067
+ if (!rgbB) throw new Error(`Invalid color format: ${colorB}`);
1068
+ const hslA = rgbToHsl(rgbA.r, rgbA.g, rgbA.b);
1069
+ const hslB = rgbToHsl(rgbB.r, rgbB.g, rgbB.b);
1070
+ const h = thirdHue(hslA.h, hslB.h);
1071
+ const s = (hslA.s + hslB.s) / 2;
1072
+ const l = (hslA.l + hslB.l) / 2;
1073
+ return hslToHex2(h, s, l);
1074
+ }
1075
+
953
1076
  // src/utils/getTintTextColor.ts
954
1077
  var getTintTextColor = (tintColor, backdropColor, tintOpacity) => {
955
1078
  const blended = getBlendedColor(tintColor, backdropColor, tintOpacity);
@@ -970,14 +1093,18 @@ export {
970
1093
  Menu,
971
1094
  PalettePicker,
972
1095
  Provider,
1096
+ SEMANTIC_BASE_COLORS,
973
1097
  TextInput,
974
1098
  ThemeSettingsContext,
975
1099
  createThemeReducer,
976
1100
  defaultColors,
977
1101
  defaultThemeSettings,
978
1102
  getBlendedColor,
1103
+ getColorRoles,
1104
+ getContrastColor,
979
1105
  getHex,
980
1106
  getRgb,
1107
+ getThirdColor,
981
1108
  getTintTextColor,
982
1109
  getTonalColor,
983
1110
  getTriadicPalette,
@@ -988,8 +1115,11 @@ export {
988
1115
  selectThemeHarmony,
989
1116
  themeActions,
990
1117
  themeReducer,
1118
+ useAutoPaperTheme,
991
1119
  useBlur,
1120
+ useBlurModule,
992
1121
  useComputedTheme,
1122
+ useNavBarContext,
993
1123
  usePaperDefaults,
994
1124
  useThemeSettings
995
1125
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rific/auto-paper",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
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",
@@ -31,14 +31,15 @@
31
31
  "type": "commonjs",
32
32
  "exports": {
33
33
  ".": {
34
- "react-native": "./dist/index.js",
34
+ "react-native": "./dist/index.mjs",
35
35
  "types": "./dist/index.d.ts",
36
- "import": "./dist/index.js",
36
+ "import": "./dist/index.mjs",
37
37
  "require": "./dist/index.js"
38
38
  }
39
39
  },
40
40
  "main": "dist/index.js",
41
- "module": "dist/index.js",
41
+ "module": "dist/index.mjs",
42
+ "react-native": "dist/index.mjs",
42
43
  "types": "dist/index.d.ts",
43
44
  "files": [
44
45
  "dist",
@@ -2,26 +2,40 @@ import { createContext, type ReactNode, useCallback, useContext, useEffect, useR
2
2
  import { StatusBar, type StatusBarProps, type StyleProp, StyleSheet, View, type ViewStyle } from 'react-native'
3
3
  import { Provider as PaperProvider } from 'react-native-paper'
4
4
 
5
+ import type { ExpoBlurModule } from './components/BlurView'
6
+ import type { ExpoNavigationBarModule } from './navigation-bar'
5
7
  import { type PaperDefaults, PaperDefaultsContext } from './PaperDefaultsContext'
6
8
  import { defaultThemeSettings, type ThemeSettings, ThemeSettingsContext } from './ThemeSettingsContext'
7
9
  import { useComputedTheme } from './useComputedTheme'
8
10
 
9
- type NavBarContextType = { onNavBarChange?: (color: string, dark: boolean) => void }
11
+ // expo-navigation-bar/expo-blur are no longer auto-detected via require() - Metro doesn't
12
+ // rewrite a require()-in-try/catch call into its module graph inside an ESM (.mjs) build, so
13
+ // the module-level detection this package used to do silently broke as soon as consumers'
14
+ // bundlers resolved this package's ESM entry point. <Provider navigationBar={...} expoBlur={...}>
15
+ // receives the already-imported modules directly instead.
16
+ type NavBarContextType = { navigationBar?: ExpoNavigationBarModule; onNavBarChange?: (color: string, dark: boolean) => void }
10
17
  const NavBarContext = createContext<NavBarContextType>({})
11
18
  export const useNavBarContext = () => useContext(NavBarContext)
12
19
 
20
+ const BlurModuleContext = createContext<ExpoBlurModule | undefined>(undefined)
21
+ export const useBlurModule = () => useContext(BlurModuleContext)
22
+
13
23
  export type ProviderProps = {
14
- initialValue?: Partial<ThemeSettings>
15
- onChange?: (settings: ThemeSettings) => void
16
24
  children: ReactNode
17
25
  defaults?: PaperDefaults
26
+ /** Injects expo-blur for `<BlurView>`. Pass `import * as ExpoBlur from 'expo-blur'`; omit to always render BlurView's solid fallback. */
27
+ expoBlur?: ExpoBlurModule
28
+ initialValue?: Partial<ThemeSettings>
29
+ /** Injects expo-navigation-bar so the Android nav bar icon style auto-syncs with the theme. Pass `import * as ExpoNavigationBar from 'expo-navigation-bar'`; omit (and don't pass onNavBarChange) to skip nav bar syncing entirely. */
30
+ navigationBar?: ExpoNavigationBarModule
31
+ onChange?: (settings: ThemeSettings) => void
18
32
  onNavBarChange?: (color: string, dark: boolean) => void
19
33
  onReady?: () => void
20
34
  statusBarProps?: StatusBarProps
21
35
  style?: StyleProp<ViewStyle>
22
36
  }
23
37
 
24
- export function Provider({ initialValue, onChange, children, defaults, onNavBarChange, onReady, statusBarProps, style }: ProviderProps) {
38
+ export function Provider({ children, defaults, expoBlur, initialValue, navigationBar, onChange, onNavBarChange, onReady, statusBarProps, style }: ProviderProps) {
25
39
  const [settings, setSettings] = useState<ThemeSettings>(() => ({ ...defaultThemeSettings, ...initialValue }))
26
40
 
27
41
  const set = useCallback((patch: Partial<ThemeSettings>) => {
@@ -55,8 +69,10 @@ export function Provider({ initialValue, onChange, children, defaults, onNavBarC
55
69
  <PaperProvider theme={theme}>
56
70
  <StatusBar backgroundColor={theme.colors.background} barStyle={theme.dark ? 'light-content' : 'dark-content'} {...statusBarProps} />
57
71
  <PaperDefaultsContext.Provider value={defaults ?? {}}>
58
- <NavBarContext.Provider value={{ onNavBarChange }}>
59
- <View style={[styles.flex, { backgroundColor: theme.colors.background }, style]}>{children}</View>
72
+ <NavBarContext.Provider value={{ navigationBar, onNavBarChange }}>
73
+ <BlurModuleContext.Provider value={expoBlur}>
74
+ <View style={[styles.flex, { backgroundColor: theme.colors.background }, style]}>{children}</View>
75
+ </BlurModuleContext.Provider>
60
76
  </NavBarContext.Provider>
61
77
  </PaperDefaultsContext.Provider>
62
78
  </PaperProvider>
@@ -1,13 +1,13 @@
1
1
  import { createContext } from 'react'
2
2
 
3
3
  import type { ThemeAppearance } from './useComputedTheme'
4
- import type { ColorHarmony } from './utils/getTriadicPalette'
4
+ import type { ColorHarmony, TriadicPalette } from './utils/getTriadicPalette'
5
5
 
6
6
  export type ThemeSettings = {
7
7
  appearance: ThemeAppearance
8
8
  blur: boolean
9
9
  blurTint: number
10
- color: string
10
+ color: string | TriadicPalette
11
11
  harmony: ColorHarmony
12
12
  }
13
13