@rific/auto-paper 0.6.4 → 0.8.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.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -32,13 +42,17 @@ __export(index_exports, {
32
42
  HarmonyPicker: () => HarmonyPicker,
33
43
  IconButton: () => IconButton,
34
44
  Menu: () => Menu,
45
+ PalettePicker: () => PalettePicker,
35
46
  Provider: () => Provider,
47
+ SEMANTIC_BASE_COLORS: () => SEMANTIC_BASE_COLORS,
36
48
  TextInput: () => TextInput,
37
49
  ThemeSettingsContext: () => ThemeSettingsContext,
38
50
  createThemeReducer: () => createThemeReducer,
39
51
  defaultColors: () => defaultColors,
40
52
  defaultThemeSettings: () => defaultThemeSettings,
41
53
  getBlendedColor: () => getBlendedColor,
54
+ getColorRoles: () => getColorRoles,
55
+ getContrastColor: () => getContrastColor,
42
56
  getHex: () => getHex,
43
57
  getRgb: () => getRgb,
44
58
  getTintTextColor: () => getTintTextColor,
@@ -51,6 +65,7 @@ __export(index_exports, {
51
65
  selectThemeHarmony: () => selectThemeHarmony,
52
66
  themeActions: () => themeActions,
53
67
  themeReducer: () => themeReducer,
68
+ useAutoPaperTheme: () => useAutoPaperTheme,
54
69
  useBlur: () => useBlur,
55
70
  useComputedTheme: () => useComputedTheme,
56
71
  usePaperDefaults: () => usePaperDefaults,
@@ -114,11 +129,19 @@ var Appbar = {
114
129
  var import_react_native_paper2 = require("react-native-paper");
115
130
  var import_jsx_runtime2 = require("react/jsx-runtime");
116
131
  var APPEARANCES = [
117
- { label: "System", value: "system", icon: "brightness-auto" },
118
- { label: "Light", value: "light", icon: "white-balance-sunny" },
119
- { label: "Dark", value: "dark", icon: "weather-night" }
132
+ { label: "System", value: "system" },
133
+ { label: "Light", value: "light" },
134
+ { label: "Dark", value: "dark" }
120
135
  ];
121
- var AppearancePicker = ({ value, onChange }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.SegmentedButtons, { value, onValueChange: (v) => onChange(v), buttons: APPEARANCES.map((a) => ({ value: a.value, label: a.label, icon: a.icon })) });
136
+ var DEFAULT_ICONS = {
137
+ system: "monitor",
138
+ light: "white-balance-sunny",
139
+ dark: "weather-night"
140
+ };
141
+ var AppearancePicker = ({ value, onChange, showLabels = true, icons }) => {
142
+ const resolvedIcons = { ...DEFAULT_ICONS, ...icons };
143
+ 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 })) });
144
+ };
122
145
 
123
146
  // src/components/BlurView.tsx
124
147
  var import_react4 = require("react");
@@ -148,7 +171,7 @@ var BlurView = ({ blur = true, children, elevation, style, tintColor, tintOpacit
148
171
  tint,
149
172
  children
150
173
  ] });
151
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { style: [{ backgroundColor: surfaceColor }, style], children: [
174
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native2.View, { ...props, style: [{ backgroundColor: surfaceColor }, style], children: [
152
175
  tint,
153
176
  children
154
177
  ] });
@@ -460,6 +483,27 @@ var getTonalColor = (color, lightness) => {
460
483
  return getHex(`rgb(${toInt(r2)}, ${toInt(g2)}, ${toInt(b2)})`) ?? color;
461
484
  };
462
485
 
486
+ // src/utils/isDarkColor.ts
487
+ var isDarkColor = (value) => {
488
+ const rgb = getRgb(value);
489
+ if (!rgb) return false;
490
+ const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
491
+ const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
492
+ const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
493
+ return luminance < 0.5;
494
+ };
495
+
496
+ // src/utils/getColorRoles.ts
497
+ var getColorRoles = (base, surface, containerAlpha = 0.15) => {
498
+ const container = getBlendedColor(base, surface, containerAlpha);
499
+ return {
500
+ color: base,
501
+ onColor: getTonalColor(base, isDarkColor(base) ? 0.95 : 0.08),
502
+ container,
503
+ onContainer: getTonalColor(base, isDarkColor(container) ? 0.92 : 0.12)
504
+ };
505
+ };
506
+
463
507
  // src/utils/getTriadicPalette.ts
464
508
  var HARMONY_OFFSETS = {
465
509
  analogous: [1 / 12, 2 / 12],
@@ -541,17 +585,12 @@ function hslToHex(h, s, l) {
541
585
  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
542
586
  }
543
587
 
544
- // src/utils/isDarkColor.ts
545
- var isDarkColor = (value) => {
546
- const rgb = getRgb(value);
547
- if (!rgb) return false;
548
- const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
549
- const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
550
- const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
551
- return luminance < 0.5;
552
- };
553
-
554
588
  // src/useComputedTheme.ts
589
+ var SEMANTIC_BASE_COLORS = {
590
+ success: "#2E7D32",
591
+ warning: "#A15C00",
592
+ danger: "#B00020"
593
+ };
555
594
  function useComputedTheme(appearance, color, harmony = "split-complementary") {
556
595
  const [theme, setTheme] = (0, import_react5.useState)(null);
557
596
  const computeTheme = (0, import_react5.useCallback)(() => {
@@ -559,27 +598,45 @@ function useComputedTheme(appearance, color, harmony = "split-complementary") {
559
598
  if (resolved === "system") resolved = import_react_native3.Appearance.getColorScheme();
560
599
  if (resolved === "no-preference" || !resolved) resolved = "light";
561
600
  const base = resolved === "dark" ? import_react_native_paper4.MD3DarkTheme : import_react_native_paper4.MD3LightTheme;
601
+ const surface = base.colors.surface;
602
+ const successRoles = getColorRoles(SEMANTIC_BASE_COLORS.success, surface);
603
+ const warningRoles = getColorRoles(SEMANTIC_BASE_COLORS.warning, surface);
604
+ const dangerRoles = getColorRoles(SEMANTIC_BASE_COLORS.danger, surface);
562
605
  const _theme = {
563
606
  ...base,
564
607
  colors: {
565
608
  ...base.colors,
566
- elevation: { ...base.colors.elevation }
609
+ elevation: { ...base.colors.elevation },
610
+ success: successRoles.color,
611
+ onSuccess: successRoles.onColor,
612
+ successContainer: successRoles.container,
613
+ onSuccessContainer: successRoles.onContainer,
614
+ warning: warningRoles.color,
615
+ onWarning: warningRoles.onColor,
616
+ warningContainer: warningRoles.container,
617
+ onWarningContainer: warningRoles.onContainer,
618
+ danger: dangerRoles.color,
619
+ onDanger: dangerRoles.onColor,
620
+ dangerContainer: dangerRoles.container,
621
+ onDangerContainer: dangerRoles.onContainer
567
622
  }
568
623
  };
569
624
  const palette = getTriadicPalette(color, harmony);
570
- const surface = base.colors.surface;
571
- _theme.colors.primary = palette.primary;
572
- _theme.colors.onPrimary = getTonalColor(palette.primary, isDarkColor(palette.primary) ? 0.95 : 0.08);
573
- _theme.colors.primaryContainer = getBlendedColor(palette.primary, surface, 0.15);
574
- _theme.colors.onPrimaryContainer = getTonalColor(palette.primary, isDarkColor(_theme.colors.primaryContainer) ? 0.92 : 0.12);
575
- _theme.colors.secondary = palette.secondary;
576
- _theme.colors.onSecondary = getTonalColor(palette.secondary, isDarkColor(palette.secondary) ? 0.95 : 0.08);
577
- _theme.colors.secondaryContainer = getBlendedColor(palette.secondary, surface, 0.15);
578
- _theme.colors.onSecondaryContainer = getTonalColor(palette.secondary, isDarkColor(_theme.colors.secondaryContainer) ? 0.92 : 0.12);
579
- _theme.colors.tertiary = palette.tertiary;
580
- _theme.colors.onTertiary = getTonalColor(palette.tertiary, isDarkColor(palette.tertiary) ? 0.95 : 0.08);
581
- _theme.colors.tertiaryContainer = getBlendedColor(palette.tertiary, surface, 0.15);
582
- _theme.colors.onTertiaryContainer = getTonalColor(palette.tertiary, isDarkColor(_theme.colors.tertiaryContainer) ? 0.92 : 0.12);
625
+ const primaryRoles = getColorRoles(palette.primary, surface);
626
+ _theme.colors.primary = primaryRoles.color;
627
+ _theme.colors.onPrimary = primaryRoles.onColor;
628
+ _theme.colors.primaryContainer = primaryRoles.container;
629
+ _theme.colors.onPrimaryContainer = primaryRoles.onContainer;
630
+ const secondaryRoles = getColorRoles(palette.secondary, surface);
631
+ _theme.colors.secondary = secondaryRoles.color;
632
+ _theme.colors.onSecondary = secondaryRoles.onColor;
633
+ _theme.colors.secondaryContainer = secondaryRoles.container;
634
+ _theme.colors.onSecondaryContainer = secondaryRoles.onContainer;
635
+ const tertiaryRoles = getColorRoles(palette.tertiary, surface);
636
+ _theme.colors.tertiary = tertiaryRoles.color;
637
+ _theme.colors.onTertiary = tertiaryRoles.onColor;
638
+ _theme.colors.tertiaryContainer = tertiaryRoles.container;
639
+ _theme.colors.onTertiaryContainer = tertiaryRoles.onContainer;
583
640
  const blended = getBlendedColor(color, palette.secondary, 0.5);
584
641
  const tintSurface = (alpha) => getBlendedColor(blended, surface, alpha);
585
642
  _theme.colors.background = base.dark ? "#0F0F0F" : "#F0F0F0";
@@ -688,35 +745,81 @@ function Chip({ variant, style, selectedColor, ...props }) {
688
745
  }
689
746
 
690
747
  // src/components/ColorPicker.tsx
691
- var import_react9 = require("react");
748
+ var import_react10 = require("react");
692
749
  var import_react_native7 = require("react-native");
693
750
  var import_react_native_paper10 = require("react-native-paper");
694
751
 
695
- // src/components/Dialog.tsx
752
+ // src/useSwatchGrid.ts
696
753
  var import_react8 = require("react");
754
+ var SWATCH_GRID_COLUMNS = 5;
755
+ var SWATCH_GRID_GAP = 12;
756
+ function useSwatchSize(fallback) {
757
+ const [width, setWidth] = (0, import_react8.useState)(0);
758
+ const onLayout = ({ nativeEvent }) => setWidth(nativeEvent.layout.width);
759
+ const size = width > 0 ? (width - SWATCH_GRID_GAP * (SWATCH_GRID_COLUMNS - 1)) / SWATCH_GRID_COLUMNS : fallback;
760
+ return { onLayout, size };
761
+ }
762
+
763
+ // src/utils/getSwatchContrast.ts
764
+ var RING_WIDTH = 1;
765
+ var SELECTED_RING_WIDTH = 3;
766
+ var getContrastColor = (color) => isDarkColor(color) ? "#ffffff" : "#000000";
767
+ var getSwatchRing = (color, selected, outlineColor) => ({
768
+ borderColor: selected ? getContrastColor(color) : outlineColor,
769
+ borderWidth: selected ? SELECTED_RING_WIDTH : RING_WIDTH
770
+ });
771
+
772
+ // src/components/Dialog.tsx
773
+ var import_react9 = __toESM(require("react"));
697
774
  var import_react_native6 = require("react-native");
698
775
  var import_react_native_paper9 = require("react-native-paper");
776
+ var import_react_native_safe_area_context = require("react-native-safe-area-context");
699
777
  var import_jsx_runtime8 = require("react/jsx-runtime");
700
- function DialogComponent({ blur: blurProp, visible, onDismiss, children, style, ...rest }) {
778
+ var ANIMATION_DURATION = 220;
779
+ var DIALOG_MARGIN = 32;
780
+ function DialogComponent({ blur: blurProp, dismissable = true, dismissableBackButton = dismissable, visible, onDismiss, children, style, testID = "dialog" }) {
701
781
  const blur = useBlur(blurProp);
702
- const { roundness, colors } = (0, import_react_native_paper9.useTheme)();
703
- const opacity = (0, import_react8.useRef)(new import_react_native6.Animated.Value(0)).current;
704
- const [mounted, setMounted] = (0, import_react8.useState)(visible);
705
- (0, import_react8.useEffect)(() => {
782
+ const theme = (0, import_react_native_paper9.useTheme)();
783
+ const { colors, isV3, roundness } = theme;
784
+ const { scale } = theme.animation;
785
+ const { bottom, left, right, top } = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
786
+ const opacity = (0, import_react9.useRef)(new import_react_native6.Animated.Value(visible ? 1 : 0)).current;
787
+ const [mounted, setMounted] = (0, import_react9.useState)(visible);
788
+ (0, import_react9.useEffect)(() => {
706
789
  if (visible) {
707
790
  setMounted(true);
708
- import_react_native6.Animated.timing(opacity, { toValue: 1, duration: 200, useNativeDriver: true }).start();
791
+ import_react_native6.Animated.timing(opacity, { duration: scale * ANIMATION_DURATION, easing: import_react_native6.Easing.out(import_react_native6.Easing.cubic), toValue: 1, useNativeDriver: true }).start();
709
792
  } else {
710
- import_react_native6.Animated.timing(opacity, { toValue: 0, duration: 150, useNativeDriver: true }).start(() => setMounted(false));
793
+ import_react_native6.Animated.timing(opacity, { duration: scale * ANIMATION_DURATION, easing: import_react_native6.Easing.out(import_react_native6.Easing.cubic), toValue: 0, useNativeDriver: true }).start(({ finished }) => {
794
+ if (finished) setMounted(false);
795
+ });
711
796
  }
712
797
  }, [visible]);
713
- if (!blur) {
714
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper9.Dialog, { visible, onDismiss, style: [styles2.sized, style], ...rest, children });
715
- }
798
+ (0, import_react9.useEffect)(() => {
799
+ if (!visible) return void 0;
800
+ const onHardwareBackPress = () => {
801
+ if (dismissable || dismissableBackButton) onDismiss?.();
802
+ return true;
803
+ };
804
+ const subscription = import_react_native6.BackHandler.addEventListener("hardwareBackPress", onHardwareBackPress);
805
+ return () => subscription.remove();
806
+ }, [dismissable, dismissableBackButton, onDismiss, visible]);
716
807
  if (!mounted) return null;
717
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper9.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native6.Animated.View, { style: [import_react_native6.StyleSheet.absoluteFill, { opacity }], pointerEvents: visible ? "box-none" : "none", children: [
718
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native6.TouchableWithoutFeedback, { onPress: onDismiss, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native6.View, { style: [import_react_native6.StyleSheet.absoluteFill, { backgroundColor: colors.backdrop }] }) }),
719
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native6.View, { style: styles2.wrapper, pointerEvents: "box-none", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(BlurView, { style: [styles2.card, { borderRadius: roundness * 7 }], children }) })
808
+ const content = import_react9.default.Children.toArray(children).filter((child) => child != null && typeof child !== "boolean").map((child, i) => {
809
+ if (isV3) {
810
+ if (i === 0 && import_react9.default.isValidElement(child)) {
811
+ return import_react9.default.cloneElement(child, { style: [{ marginTop: 24 }, child.props.style] });
812
+ }
813
+ }
814
+ if (i === 0 && import_react9.default.isValidElement(child) && child.type === import_react_native_paper9.Dialog.Content) {
815
+ return import_react9.default.cloneElement(child, { style: [{ paddingTop: 24 }, child.props.style] });
816
+ }
817
+ return child;
818
+ });
819
+ const borderRadius = (isV3 ? 7 : 1) * roundness;
820
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native_paper9.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native6.Animated.View, { accessibilityLiveRegion: "polite", accessibilityViewIsModal: true, pointerEvents: visible ? "box-none" : "none", style: [import_react_native6.StyleSheet.absoluteFill, { opacity }], testID, children: [
821
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native6.Pressable, { accessibilityLabel: "Close modal", accessibilityRole: "button", disabled: !dismissable, onPress: dismissable ? onDismiss : void 0, style: [import_react_native6.StyleSheet.absoluteFill, { backgroundColor: colors.backdrop }], testID: `${testID}-backdrop` }),
822
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native6.View, { pointerEvents: "box-none", style: [styles2.wrapper, { marginBottom: bottom, marginTop: top, paddingHorizontal: Math.max(left, right, DIALOG_MARGIN) }], testID: `${testID}-wrapper`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(BlurView, { blur, style: [styles2.card, { borderRadius }, style], testID: `${testID}-surface`, children: content }) })
720
823
  ] }) });
721
824
  }
722
825
  DialogComponent.Title = import_react_native_paper9.Dialog.Title;
@@ -727,12 +830,12 @@ var Dialog = DialogComponent;
727
830
  var styles2 = import_react_native6.StyleSheet.create({
728
831
  // Width cap keeps dialogs card-sized on wide viewports (web/tablet); no effect on phones.
729
832
  card: { alignSelf: "center", maxWidth: 400, overflow: "hidden", width: "100%" },
730
- sized: { alignSelf: "center", maxWidth: 400, width: "100%" },
731
- wrapper: { flex: 1, justifyContent: "center", paddingHorizontal: 26 }
833
+ wrapper: { flex: 1, justifyContent: "center", paddingVertical: DIALOG_MARGIN }
732
834
  });
733
835
 
734
836
  // src/components/ColorPicker.tsx
735
837
  var import_jsx_runtime9 = require("react/jsx-runtime");
838
+ var SIZE = 48;
736
839
  var defaultColors = [
737
840
  { label: "Red", value: "#f44336" },
738
841
  { label: "Coral", value: "#ff5722" },
@@ -743,6 +846,7 @@ var defaultColors = [
743
846
  { label: "Emerald", value: "#4caf50" },
744
847
  { label: "Teal", value: "#009688" },
745
848
  { label: "Cyan", value: "#00bcd4" },
849
+ { label: "Sky", value: "#03a9f4" },
746
850
  { label: "Blue", value: "#2196f3" },
747
851
  { label: "Indigo", value: "#3f51b5" },
748
852
  { label: "Deep Purple", value: "#673ab7" },
@@ -751,32 +855,34 @@ var defaultColors = [
751
855
  { label: "Pink", value: "#ec407a" },
752
856
  { label: "Rose", value: "#e91e63" },
753
857
  { label: "Brown", value: "#795548" },
858
+ { label: "Taupe", value: "#8d7b68" },
754
859
  { label: "Blue Grey", value: "#607d8b" }
755
860
  ];
756
861
  var ColorPicker = ({ value, onChange, colors = defaultColors, blur: blurProp }) => {
757
862
  const blur = useBlur(blurProp);
758
- const [open, setOpen] = (0, import_react9.useState)(false);
863
+ const { colors: themeColors } = (0, import_react_native_paper10.useTheme)();
864
+ const [open, setOpen] = (0, import_react10.useState)(false);
865
+ const { onLayout, size } = useSwatchSize(SIZE);
759
866
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
760
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper10.TouchableRipple, { onPress: () => setOpen(true), style: [styles3.trigger, { backgroundColor: value }], borderless: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper10.Icon, { source: "palette", size: 20, color: "white" }) }),
761
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Dialog, { blur, visible: open, onDismiss: () => setOpen(false), children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Dialog.Content, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native7.View, { style: styles3.swatches, children: colors.map(({ value: hex }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
867
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper10.TouchableRipple, { onPress: () => setOpen(true), style: [styles3.trigger, { backgroundColor: value }, getSwatchRing(value, false, themeColors.outlineVariant)], borderless: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper10.Icon, { source: "palette", size: 20, color: getContrastColor(value) }) }),
868
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Dialog, { blur, visible: open, onDismiss: () => setOpen(false), children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Dialog.Content, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native7.View, { style: styles3.swatches, onLayout, children: colors.map(({ value: hex }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
762
869
  import_react_native7.TouchableOpacity,
763
870
  {
764
871
  onPress: () => {
765
872
  onChange(hex);
766
873
  setOpen(false);
767
874
  },
768
- style: [styles3.swatch, { backgroundColor: hex }, value === hex && styles3.selected],
769
- children: value === hex && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper10.Icon, { source: "check", size: 20, color: "white" })
875
+ style: [styles3.swatch, { backgroundColor: hex, borderRadius: size / 2, height: size, width: size }, getSwatchRing(hex, value === hex, themeColors.outlineVariant)],
876
+ children: value === hex && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper10.Icon, { source: "check", size: 20, color: getContrastColor(hex) })
770
877
  },
771
878
  hex
772
879
  )) }) }) })
773
880
  ] });
774
881
  };
775
882
  var styles3 = import_react_native7.StyleSheet.create({
776
- selected: { borderColor: "white", borderWidth: 3 },
777
- swatch: { alignItems: "center", borderRadius: 24, height: 48, justifyContent: "center", width: 48 },
778
- swatches: { flexDirection: "row", flexWrap: "wrap", gap: 12, paddingTop: 12 },
779
- trigger: { alignItems: "center", alignSelf: "flex-start", borderRadius: 24, height: 48, justifyContent: "center", width: 48 }
883
+ swatch: { alignItems: "center", justifyContent: "center" },
884
+ swatches: { flexDirection: "row", flexWrap: "wrap", gap: SWATCH_GRID_GAP, paddingTop: 12 },
885
+ trigger: { alignItems: "center", alignSelf: "flex-start", borderRadius: SIZE / 2, height: SIZE, justifyContent: "center", width: SIZE }
780
886
  });
781
887
 
782
888
  // src/components/FAB.tsx
@@ -788,7 +894,7 @@ function FAB(props) {
788
894
  }
789
895
 
790
896
  // src/components/HarmonyPicker.tsx
791
- var import_react10 = require("react");
897
+ var import_react11 = require("react");
792
898
  var import_react_native_paper13 = require("react-native-paper");
793
899
 
794
900
  // src/components/Menu.tsx
@@ -818,7 +924,7 @@ var HARMONIES = [
818
924
  ];
819
925
  var HarmonyPicker = ({ value, onChange, blur: blurProp }) => {
820
926
  const blur = useBlur(blurProp);
821
- const [open, setOpen] = (0, import_react10.useState)(false);
927
+ const [open, setOpen] = (0, import_react11.useState)(false);
822
928
  const selected = HARMONIES.find((h) => h.value === value);
823
929
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
824
930
  Menu,
@@ -854,13 +960,73 @@ function IconButton({ variant, iconColor, containerColor, ...props }) {
854
960
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper14.IconButton, { ...defaults.IconButton, containerColor: containerColor ?? variantContainerColor, iconColor: iconColor ?? variantIconColor, ...props });
855
961
  }
856
962
 
857
- // src/components/TextInput.tsx
858
- var import_react11 = require("react");
963
+ // src/components/PalettePicker.tsx
964
+ var import_react12 = require("react");
965
+ var import_react_native9 = require("react-native");
859
966
  var import_react_native_paper15 = require("react-native-paper");
860
967
  var import_jsx_runtime14 = require("react/jsx-runtime");
861
- var TextInput = (0, import_react11.forwardRef)(function TextInput2(props, ref) {
968
+ var SIZE2 = 48;
969
+ var defaultWeights = { primary: 2, secondary: 1, tertiary: 1 };
970
+ var buildWedges = (weights) => {
971
+ const total = weights.primary + weights.secondary + weights.tertiary;
972
+ const primary = weights.primary / total * 360;
973
+ const secondary = weights.secondary / total * 360;
974
+ const tertiary = weights.tertiary / total * 360;
975
+ return [
976
+ { key: "primary", rotate: -primary / 2, share: primary },
977
+ { key: "tertiary", rotate: primary / 2, share: tertiary },
978
+ { key: "secondary", rotate: primary / 2 + tertiary, share: secondary }
979
+ ];
980
+ };
981
+ var Pie = ({ palette, weights = defaultWeights, size = SIZE2 }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: [styles5.pie, { borderRadius: size / 2, height: size, width: size }], children: buildWedges(weights).map(({ key, rotate, share }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: [styles5.rotator, { transform: [{ rotate: `${rotate}deg` }] }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: styles5.halfMask, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: [styles5.innerRotator, { transform: [{ rotate: `${share - 180}deg` }] }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: [styles5.wedgeFill, { backgroundColor: palette[key] }] }) }) }) }, key)) });
982
+ var PalettePicker = ({ value, onChange, harmony = "split-complementary", weights, colors = defaultColors, blur: blurProp }) => {
983
+ const blur = useBlur(blurProp);
984
+ const { colors: themeColors } = (0, import_react_native_paper15.useTheme)();
985
+ const [open, setOpen] = (0, import_react12.useState)(false);
986
+ const { onLayout, size } = useSwatchSize(SIZE2);
987
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
988
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper15.TouchableRipple, { onPress: () => setOpen(true), style: styles5.trigger, borderless: true, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native9.View, { style: styles5.triggerContent, children: [
989
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Pie, { palette: getTriadicPalette(value, harmony), weights }),
990
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { pointerEvents: "none", style: [styles5.triggerIcon, { borderRadius: SIZE2 / 2 }, getSwatchRing(value, false, themeColors.outlineVariant)], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper15.Icon, { source: "palette", size: 20, color: getContrastColor(value) }) })
991
+ ] }) }),
992
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Dialog, { blur, visible: open, onDismiss: () => setOpen(false), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Dialog.Content, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: styles5.swatches, onLayout, children: colors.map(({ value: hex }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
993
+ import_react_native9.TouchableOpacity,
994
+ {
995
+ onPress: () => {
996
+ onChange(hex);
997
+ setOpen(false);
998
+ },
999
+ style: [styles5.swatch, { height: size, width: size }],
1000
+ children: [
1001
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Pie, { palette: getTriadicPalette(hex, harmony), weights, size }),
1002
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { pointerEvents: "none", style: [styles5.ring, { borderRadius: size / 2 }, getSwatchRing(hex, value === hex, themeColors.outlineVariant)], children: value === hex && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper15.Icon, { source: "check", size: 20, color: getContrastColor(hex) }) })
1003
+ ]
1004
+ },
1005
+ hex
1006
+ )) }) }) })
1007
+ ] });
1008
+ };
1009
+ var styles5 = import_react_native9.StyleSheet.create({
1010
+ halfMask: { height: "100%", left: "50%", overflow: "hidden", position: "absolute", top: 0, width: "50%" },
1011
+ innerRotator: { height: "100%", left: "-100%", overflow: "hidden", position: "absolute", top: 0, width: "200%" },
1012
+ pie: { overflow: "hidden" },
1013
+ rotator: { height: "100%", left: 0, overflow: "hidden", position: "absolute", top: 0, width: "100%" },
1014
+ ring: { alignItems: "center", bottom: 0, justifyContent: "center", left: 0, position: "absolute", right: 0, top: 0 },
1015
+ swatch: { alignItems: "center", justifyContent: "center" },
1016
+ swatches: { flexDirection: "row", flexWrap: "wrap", gap: SWATCH_GRID_GAP, paddingTop: 12 },
1017
+ trigger: { alignItems: "center", alignSelf: "flex-start", borderRadius: SIZE2 / 2, height: SIZE2, justifyContent: "center", width: SIZE2 },
1018
+ triggerContent: { height: SIZE2, width: SIZE2 },
1019
+ triggerIcon: { alignItems: "center", bottom: 0, justifyContent: "center", left: 0, position: "absolute", right: 0, top: 0 },
1020
+ wedgeFill: { height: "100%", left: "50%", position: "absolute", top: 0, width: "50%" }
1021
+ });
1022
+
1023
+ // src/components/TextInput.tsx
1024
+ var import_react13 = require("react");
1025
+ var import_react_native_paper16 = require("react-native-paper");
1026
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1027
+ var TextInput = (0, import_react13.forwardRef)(function TextInput2(props, ref) {
862
1028
  const defaults = usePaperDefaults();
863
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper15.TextInput, { ref, ...defaults.TextInput, ...props });
1029
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper16.TextInput, { ref, ...defaults.TextInput, ...props });
864
1030
  });
865
1031
 
866
1032
  // src/redux/themeSlice.ts
@@ -900,9 +1066,13 @@ var selectThemeBlur = (state) => state.blur;
900
1066
  var selectThemeColor = (state) => state.color;
901
1067
  var selectThemeHarmony = (state) => state.harmony;
902
1068
 
1069
+ // src/useTheme.ts
1070
+ var import_react_native_paper17 = require("react-native-paper");
1071
+ var useAutoPaperTheme = () => (0, import_react_native_paper17.useTheme)();
1072
+
903
1073
  // src/useThemeSettings.ts
904
- var import_react12 = require("react");
905
- var useThemeSettings = () => (0, import_react12.useContext)(ThemeSettingsContext);
1074
+ var import_react14 = require("react");
1075
+ var useThemeSettings = () => (0, import_react14.useContext)(ThemeSettingsContext);
906
1076
 
907
1077
  // src/utils/getTintTextColor.ts
908
1078
  var getTintTextColor = (tintColor, backdropColor, tintOpacity) => {
@@ -923,13 +1093,17 @@ var getTintTextColor = (tintColor, backdropColor, tintOpacity) => {
923
1093
  HarmonyPicker,
924
1094
  IconButton,
925
1095
  Menu,
1096
+ PalettePicker,
926
1097
  Provider,
1098
+ SEMANTIC_BASE_COLORS,
927
1099
  TextInput,
928
1100
  ThemeSettingsContext,
929
1101
  createThemeReducer,
930
1102
  defaultColors,
931
1103
  defaultThemeSettings,
932
1104
  getBlendedColor,
1105
+ getColorRoles,
1106
+ getContrastColor,
933
1107
  getHex,
934
1108
  getRgb,
935
1109
  getTintTextColor,
@@ -942,6 +1116,7 @@ var getTintTextColor = (tintColor, backdropColor, tintOpacity) => {
942
1116
  selectThemeHarmony,
943
1117
  themeActions,
944
1118
  themeReducer,
1119
+ useAutoPaperTheme,
945
1120
  useBlur,
946
1121
  useComputedTheme,
947
1122
  usePaperDefaults,