@darthrapid/react-native-color-picker 0.1.0 → 1.1.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.
Files changed (63) hide show
  1. package/README.md +151 -33
  2. package/lib/module/components/alpha-strip.js +213 -0
  3. package/lib/module/components/alpha-strip.js.map +1 -0
  4. package/lib/module/components/color-picker.js +148 -40
  5. package/lib/module/components/color-picker.js.map +1 -1
  6. package/lib/module/components/palettes-tab.js +366 -0
  7. package/lib/module/components/palettes-tab.js.map +1 -0
  8. package/lib/module/components/picker-panel.js +115 -43
  9. package/lib/module/components/picker-panel.js.map +1 -1
  10. package/lib/module/components/picker-tab.js +12 -1
  11. package/lib/module/components/picker-tab.js.map +1 -1
  12. package/lib/module/components/tab-bar.js +51 -22
  13. package/lib/module/components/tab-bar.js.map +1 -1
  14. package/lib/module/components/values-tab.js +72 -7
  15. package/lib/module/components/values-tab.js.map +1 -1
  16. package/lib/module/index.js +1 -0
  17. package/lib/module/index.js.map +1 -1
  18. package/lib/module/shared/const.js +4 -5
  19. package/lib/module/shared/const.js.map +1 -1
  20. package/lib/module/shared/palletes.js +297 -0
  21. package/lib/module/shared/palletes.js.map +1 -0
  22. package/lib/module/utils/colors.js +33 -4
  23. package/lib/module/utils/colors.js.map +1 -1
  24. package/lib/typescript/src/components/alpha-strip.d.ts +11 -0
  25. package/lib/typescript/src/components/alpha-strip.d.ts.map +1 -0
  26. package/lib/typescript/src/components/color-picker.d.ts +16 -7
  27. package/lib/typescript/src/components/color-picker.d.ts.map +1 -1
  28. package/lib/typescript/src/components/palettes-tab.d.ts +19 -0
  29. package/lib/typescript/src/components/palettes-tab.d.ts.map +1 -0
  30. package/lib/typescript/src/components/picker-panel.d.ts +9 -5
  31. package/lib/typescript/src/components/picker-panel.d.ts.map +1 -1
  32. package/lib/typescript/src/components/picker-tab.d.ts +4 -1
  33. package/lib/typescript/src/components/picker-tab.d.ts.map +1 -1
  34. package/lib/typescript/src/components/tab-bar.d.ts.map +1 -1
  35. package/lib/typescript/src/components/values-tab.d.ts +2 -1
  36. package/lib/typescript/src/components/values-tab.d.ts.map +1 -1
  37. package/lib/typescript/src/index.d.ts +3 -1
  38. package/lib/typescript/src/index.d.ts.map +1 -1
  39. package/lib/typescript/src/shared/const.d.ts.map +1 -1
  40. package/lib/typescript/src/shared/palletes.d.ts +580 -0
  41. package/lib/typescript/src/shared/palletes.d.ts.map +1 -0
  42. package/lib/typescript/src/types/misc.d.ts +5 -6
  43. package/lib/typescript/src/types/misc.d.ts.map +1 -1
  44. package/lib/typescript/src/utils/colors.d.ts +4 -1
  45. package/lib/typescript/src/utils/colors.d.ts.map +1 -1
  46. package/package.json +1 -1
  47. package/src/components/alpha-strip.tsx +212 -0
  48. package/src/components/color-picker.tsx +192 -56
  49. package/src/components/palettes-tab.tsx +358 -0
  50. package/src/components/picker-panel.tsx +104 -47
  51. package/src/components/picker-tab.tsx +17 -0
  52. package/src/components/tab-bar.tsx +60 -27
  53. package/src/components/values-tab.tsx +41 -6
  54. package/src/index.tsx +3 -1
  55. package/src/shared/const.ts +4 -5
  56. package/src/shared/palletes.ts +294 -0
  57. package/src/types/misc.ts +5 -6
  58. package/src/utils/colors.ts +34 -4
  59. package/lib/module/components/recent-tab.js +0 -89
  60. package/lib/module/components/recent-tab.js.map +0 -1
  61. package/lib/typescript/src/components/recent-tab.d.ts +0 -12
  62. package/lib/typescript/src/components/recent-tab.d.ts.map +0 -1
  63. package/src/components/recent-tab.tsx +0 -90
@@ -0,0 +1,212 @@
1
+ import type React from "react";
2
+ import { useCallback, useRef, useState } from "react";
3
+ import { PanResponder, View } from "react-native";
4
+ import Svg, {
5
+ Defs,
6
+ LinearGradient,
7
+ Pattern,
8
+ Rect,
9
+ Stop,
10
+ } from "react-native-svg";
11
+
12
+ type AlphaStripProps = {
13
+ alpha: number;
14
+ color: string;
15
+ height: number;
16
+ disabled: boolean;
17
+ thumbBorder: string;
18
+ onChange: (alpha: number) => void;
19
+ };
20
+
21
+ export function AlphaStrip({
22
+ alpha,
23
+ color,
24
+ height,
25
+ disabled,
26
+ thumbBorder,
27
+ onChange,
28
+ }: AlphaStripProps) {
29
+ const containerRef = useRef<React.ComponentRef<typeof View>>(null);
30
+ const originXRef = useRef(0);
31
+ const widthRef = useRef(0);
32
+ const disabledRef = useRef(disabled);
33
+ const onChangeRef = useRef(onChange);
34
+ disabledRef.current = disabled;
35
+ onChangeRef.current = onChange;
36
+
37
+ const [layoutWidth, setLayoutWidth] = useState(0);
38
+
39
+ const calcAlpha = useCallback((pageX: number) => {
40
+ const w = widthRef.current;
41
+ if (w === 0) return 100;
42
+ const x = Math.max(0, Math.min(pageX - originXRef.current, w));
43
+ return Math.round((x / w) * 100);
44
+ }, []);
45
+
46
+ const panResponder = useRef(
47
+ PanResponder.create({
48
+ onStartShouldSetPanResponder: () => !disabledRef.current,
49
+ onStartShouldSetPanResponderCapture: () => !disabledRef.current,
50
+ onMoveShouldSetPanResponder: () => !disabledRef.current,
51
+ onMoveShouldSetPanResponderCapture: () => !disabledRef.current,
52
+ onPanResponderTerminationRequest: () => false,
53
+ onPanResponderGrant: (evt) => {
54
+ const { pageX } = evt.nativeEvent;
55
+ containerRef.current?.measure(
56
+ (_x: number, _y: number, w: number, _h: number, ox: number) => {
57
+ originXRef.current = ox;
58
+ widthRef.current = w;
59
+ onChangeRef.current(calcAlpha(pageX));
60
+ },
61
+ );
62
+ },
63
+ onPanResponderMove: (evt) => {
64
+ onChangeRef.current(calcAlpha(evt.nativeEvent.pageX));
65
+ },
66
+ }),
67
+ ).current;
68
+
69
+ const thumbLeft = layoutWidth > 0 ? (alpha / 100) * layoutWidth : 0;
70
+ const thumbSize = height + 6;
71
+ const checkerSize = 5;
72
+
73
+ return (
74
+ <View
75
+ ref={containerRef}
76
+ onLayout={(e) => {
77
+ const w = e.nativeEvent.layout.width;
78
+ setLayoutWidth(w);
79
+ widthRef.current = w;
80
+ }}
81
+ style={{ width: "100%", height, overflow: "visible" }}
82
+ {...panResponder.panHandlers}
83
+ >
84
+ {layoutWidth > 0 && (
85
+ <>
86
+ <Svg width={layoutWidth} height={height}>
87
+ <Defs>
88
+ <Pattern
89
+ id="alphaChecker"
90
+ x="0"
91
+ y="0"
92
+ width={checkerSize * 2}
93
+ height={checkerSize * 2}
94
+ patternUnits="userSpaceOnUse"
95
+ >
96
+ <Rect
97
+ x="0"
98
+ y="0"
99
+ width={checkerSize}
100
+ height={checkerSize}
101
+ fill="#CCCCCC"
102
+ />
103
+ <Rect
104
+ x={checkerSize}
105
+ y="0"
106
+ width={checkerSize}
107
+ height={checkerSize}
108
+ fill="#FFFFFF"
109
+ />
110
+ <Rect
111
+ x="0"
112
+ y={checkerSize}
113
+ width={checkerSize}
114
+ height={checkerSize}
115
+ fill="#FFFFFF"
116
+ />
117
+ <Rect
118
+ x={checkerSize}
119
+ y={checkerSize}
120
+ width={checkerSize}
121
+ height={checkerSize}
122
+ fill="#CCCCCC"
123
+ />
124
+ </Pattern>
125
+ <LinearGradient id="alphaGrad" x1="0" y1="0" x2="1" y2="0">
126
+ <Stop offset="0%" stopColor={color} stopOpacity={0} />
127
+ <Stop offset="100%" stopColor={color} stopOpacity={1} />
128
+ </LinearGradient>
129
+ </Defs>
130
+ {/* Checkerboard background */}
131
+ <Rect
132
+ x="0"
133
+ y="0"
134
+ width={layoutWidth}
135
+ height={height}
136
+ rx={height / 2}
137
+ fill="url(#alphaChecker)"
138
+ />
139
+ {/* Alpha gradient overlay */}
140
+ <Rect
141
+ x="0"
142
+ y="0"
143
+ width={layoutWidth}
144
+ height={height}
145
+ rx={height / 2}
146
+ fill="url(#alphaGrad)"
147
+ />
148
+ </Svg>
149
+ {/* Thumb with checkerboard background for alpha visualization */}
150
+ <View
151
+ style={{
152
+ position: "absolute",
153
+ left: thumbLeft - thumbSize / 2,
154
+ top: -3,
155
+ width: thumbSize,
156
+ height: thumbSize,
157
+ borderRadius: thumbSize / 2,
158
+ borderWidth: 3,
159
+ borderColor: thumbBorder,
160
+ overflow: "hidden",
161
+ shadowColor: "#000",
162
+ shadowOffset: { width: 0, height: 2 },
163
+ shadowOpacity: 0.3,
164
+ shadowRadius: 4,
165
+ elevation: 5,
166
+ }}
167
+ >
168
+ {/* Checkerboard background */}
169
+ <Svg
170
+ width={thumbSize}
171
+ height={thumbSize}
172
+ style={{ position: "absolute" }}
173
+ >
174
+ <Defs>
175
+ <Pattern
176
+ id="thumbChecker"
177
+ x="0"
178
+ y="0"
179
+ width={8}
180
+ height={8}
181
+ patternUnits="userSpaceOnUse"
182
+ >
183
+ <Rect x="0" y="0" width={4} height={4} fill="#CCCCCC" />
184
+ <Rect x={4} y="0" width={4} height={4} fill="#FFFFFF" />
185
+ <Rect x="0" y={4} width={4} height={4} fill="#FFFFFF" />
186
+ <Rect x={4} y={4} width={4} height={4} fill="#CCCCCC" />
187
+ </Pattern>
188
+ </Defs>
189
+ <Rect
190
+ x="0"
191
+ y="0"
192
+ width={thumbSize}
193
+ height={thumbSize}
194
+ fill="url(#thumbChecker)"
195
+ />
196
+ </Svg>
197
+ {/* Color overlay with actual alpha */}
198
+ <View
199
+ style={{
200
+ position: "absolute",
201
+ width: "100%",
202
+ height: "100%",
203
+ backgroundColor: color,
204
+ opacity: alpha / 100,
205
+ }}
206
+ />
207
+ </View>
208
+ </>
209
+ )}
210
+ </View>
211
+ );
212
+ }
@@ -7,24 +7,33 @@ import React, {
7
7
  import {
8
8
  Modal,
9
9
  Pressable,
10
+ View,
10
11
  type DimensionValue,
11
12
  type StyleProp,
12
- type ViewStyle
13
+ type ViewStyle,
13
14
  } from "react-native";
15
+ import Svg, { Defs, Pattern, Rect } from "react-native-svg";
14
16
  import { PickerPanel } from "./picker-panel";
15
17
  import type { ColorPickerLabels, ColorPickerRef, TabId } from "../types/misc";
16
18
  import { DEFAULT_LABELS, themes } from "../shared/const";
17
- import { getContrastColor, hexToHsb, hsbToHex, isValidHex } from "../utils/colors";
19
+ import {
20
+ appendAlphaToHex,
21
+ getContrastColor,
22
+ hexToHsb,
23
+ hexToRgba,
24
+ hsbToHex,
25
+ isValidHex,
26
+ parseAlphaFromHex,
27
+ } from "../utils/colors";
28
+ import type { ColorPalette } from "./palettes-tab";
18
29
 
19
30
  export type ColorPickerProps = {
20
- /** Current color value (hex string) */
31
+ /** Current color value (hex string, 6 or 8 digit) */
21
32
  value?: string;
22
- /** Called when color changes */
33
+ /** Called when color changes (returns #RRGGBB or #RRGGBBAA when showAlpha is true) */
23
34
  onChange?: (hex: string) => void;
24
- /** Tabs to show (default: ["picker", "values", "recent"]) */
35
+ /** Tabs to show (default: ["picker", "values", "palettes"]) */
25
36
  tabs?: TabId[];
26
- /** Max recent colors (default: 16) */
27
- maxRecentColors?: number;
28
37
  /** Panel width in modal mode (default: 320). Accepts number or "100%". Ignored in inline mode. */
29
38
  panelWidth?: DimensionValue;
30
39
  /** Hue strip height (default: 28) */
@@ -45,8 +54,18 @@ export type ColorPickerProps = {
45
54
  inline?: boolean;
46
55
  /** Override default labels for i18n */
47
56
  labels?: ColorPickerLabels;
48
- /** Style for modal wrapper */
49
- modalStyle?: StyleProp<ViewStyle>;
57
+ /** Style for content wrapper */
58
+ contentStyle?: StyleProp<ViewStyle>;
59
+ /** Show alpha channel strip (default: false) */
60
+ showAlpha?: boolean;
61
+ /** Color palettes for the palettes tab */
62
+ palettes?: ColorPalette[];
63
+ /** Saved colors (controlled) - if provided, component won't manage saved colors internally */
64
+ savedColors?: string[];
65
+ /** Called when user saves a color */
66
+ onSaveColor?: (hex: string) => void;
67
+ /** Called when user clears saved colors */
68
+ onClearSaved?: () => void;
50
69
  };
51
70
 
52
71
  export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
@@ -54,8 +73,7 @@ export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
54
73
  {
55
74
  value = "#007AFF",
56
75
  onChange,
57
- tabs = ["picker", "values", "recent"],
58
- maxRecentColors = 16,
76
+ tabs = ["picker", "values", "palettes"],
59
77
  panelWidth = "100%",
60
78
  hueStripHeight = 28,
61
79
  theme: themeName = "dark",
@@ -66,7 +84,12 @@ export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
66
84
  swatchStyle,
67
85
  inline = false,
68
86
  labels: userLabels,
69
- modalStyle,
87
+ contentStyle,
88
+ showAlpha = false,
89
+ palettes,
90
+ savedColors: savedColorsProp,
91
+ onSaveColor: onSaveColorProp,
92
+ onClearSaved: onClearSavedProp,
70
93
  },
71
94
  ref,
72
95
  ) => {
@@ -78,19 +101,29 @@ export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
78
101
  const [sat, setSat] = useState(initial.s);
79
102
  const [bright, setBright] = useState(initial.b);
80
103
  const [hexInput, setHexInput] = useState(value.toUpperCase());
81
- const [recentColors, setRecentColors] = useState<string[]>([]);
104
+ const [alpha, setAlpha] = useState(
105
+ showAlpha ? parseAlphaFromHex(value) : 100,
106
+ );
107
+ // Internal saved colors state (used when not controlled)
108
+ const [internalSavedColors, setInternalSavedColors] = useState<string[]>([]);
82
109
  const [modalVisible, setModalVisible] = useState(false);
83
110
 
111
+ // Use controlled or internal saved colors
112
+ const isControlled = savedColorsProp !== undefined;
113
+ const savedColors = isControlled ? savedColorsProp : internalSavedColors;
114
+
84
115
  const currentHex = hsbToHex(hue, sat, bright);
85
- const contrastColor = getContrastColor(currentHex);
116
+ const contrastColor = getContrastColor(currentHex, showAlpha ? alpha : 100);
86
117
 
87
118
  // Refs for stable handler closures
88
119
  const hueRef = useRef(hue);
89
120
  const satRef = useRef(sat);
90
121
  const brightRef = useRef(bright);
122
+ const alphaRef = useRef(alpha);
91
123
  hueRef.current = hue;
92
124
  satRef.current = sat;
93
125
  brightRef.current = bright;
126
+ alphaRef.current = alpha;
94
127
 
95
128
  // Sync hex input display — only update when NOT focused
96
129
  const hexInputFocusedRef = useRef(false);
@@ -113,27 +146,44 @@ export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
113
146
  setSat(hsb.s);
114
147
  setBright(hsb.b);
115
148
  }
149
+ if (showAlpha) {
150
+ const a = parseAlphaFromHex(value);
151
+ if (a !== alpha) setAlpha(a);
152
+ }
116
153
  }
117
154
  }
118
155
 
119
156
  const notifyChange = useCallback(
120
- (h: number, s: number, b: number) => {
157
+ (h: number, s: number, b: number, a?: number) => {
121
158
  const hex = hsbToHex(h, s, b);
122
- lastExternalValue.current = hex; // prevent sync-back
123
- onChange?.(hex);
159
+ const result = showAlpha
160
+ ? appendAlphaToHex(hex, a ?? alphaRef.current)
161
+ : hex;
162
+ lastExternalValue.current = result; // prevent sync-back
163
+ onChange?.(result);
124
164
  },
125
- [onChange],
165
+ [onChange, showAlpha],
126
166
  );
127
167
 
128
- const addToRecent = useCallback(
129
- (hex: string) => {
130
- setRecentColors((prev) => {
131
- const filtered = prev.filter((c) => c !== hex);
132
- return [hex, ...filtered].slice(0, maxRecentColors);
168
+ const handleSaveColor = useCallback(() => {
169
+ const hex = showAlpha ? appendAlphaToHex(currentHex, alpha) : currentHex;
170
+ if (isControlled) {
171
+ onSaveColorProp?.(hex);
172
+ } else {
173
+ setInternalSavedColors((prev) => {
174
+ if (prev.includes(hex)) return prev;
175
+ return [hex, ...prev];
133
176
  });
134
- },
135
- [maxRecentColors],
136
- );
177
+ }
178
+ }, [currentHex, alpha, showAlpha, isControlled, onSaveColorProp]);
179
+
180
+ const handleClearSaved = useCallback(() => {
181
+ if (isControlled) {
182
+ onClearSavedProp?.();
183
+ } else {
184
+ setInternalSavedColors([]);
185
+ }
186
+ }, [isControlled, onClearSavedProp]);
137
187
 
138
188
  const handleHueChange = useCallback(
139
189
  (h: number) => {
@@ -152,6 +202,14 @@ export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
152
202
  [notifyChange],
153
203
  );
154
204
 
205
+ const handleAlphaChange = useCallback(
206
+ (a: number) => {
207
+ setAlpha(a);
208
+ notifyChange(hueRef.current, satRef.current, brightRef.current, a);
209
+ },
210
+ [notifyChange],
211
+ );
212
+
155
213
  const handleHexSubmit = useCallback(() => {
156
214
  const clean = hexInput.startsWith("#") ? hexInput : `#${hexInput}`;
157
215
  if (isValidHex(clean)) {
@@ -160,21 +218,26 @@ export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
160
218
  setSat(hsb.s);
161
219
  setBright(hsb.b);
162
220
  notifyChange(hsb.h, hsb.s, hsb.b);
163
- addToRecent(clean);
164
221
  } else {
165
222
  setHexInput(currentHex.toUpperCase());
166
223
  }
167
- }, [hexInput, currentHex, notifyChange, addToRecent]);
224
+ }, [hexInput, currentHex, notifyChange]);
168
225
 
169
- const handleRecentSelect = useCallback(
226
+ const handleSelectColor = useCallback(
170
227
  (hex: string) => {
171
228
  const hsb = hexToHsb(hex);
172
229
  setHue(hsb.h);
173
230
  setSat(hsb.s);
174
231
  setBright(hsb.b);
175
- notifyChange(hsb.h, hsb.s, hsb.b);
232
+ if (showAlpha) {
233
+ const a = parseAlphaFromHex(hex);
234
+ setAlpha(a);
235
+ notifyChange(hsb.h, hsb.s, hsb.b, a);
236
+ } else {
237
+ notifyChange(hsb.h, hsb.s, hsb.b);
238
+ }
176
239
  },
177
- [notifyChange],
240
+ [notifyChange, showAlpha],
178
241
  );
179
242
 
180
243
  useImperativeHandle(
@@ -190,21 +253,22 @@ export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
190
253
  lastExternalValue.current = hex;
191
254
  notifyChange(hsb.h, hsb.s, hsb.b);
192
255
  },
193
- clearRecent: () => setRecentColors([]),
256
+ clearSaved: () => handleClearSaved(),
194
257
  open: () => setModalVisible(true),
195
258
  close: () => setModalVisible(false),
196
259
  }),
197
- [currentHex, notifyChange],
260
+ [currentHex, notifyChange, handleClearSaved],
198
261
  );
199
262
 
200
263
  const panelProps = {
201
264
  hue,
202
265
  sat,
203
266
  bright,
267
+ ...(showAlpha ? { alpha, onAlphaChange: handleAlphaChange } : {}),
204
268
  currentHex,
205
269
  contrastColor,
206
270
  hexInput,
207
- recentColors,
271
+ savedColors,
208
272
  tabs,
209
273
  hueStripHeight,
210
274
  disabled,
@@ -220,15 +284,19 @@ export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
220
284
  onHexInputBlur: () => {
221
285
  hexInputFocusedRef.current = false;
222
286
  },
223
- onSaveRecent: () => addToRecent(currentHex),
224
- onRecentSelect: handleRecentSelect,
225
- onClearRecent: () => setRecentColors([]),
287
+ palettes,
288
+ onSaveColor: handleSaveColor,
289
+ onSelectColor: handleSelectColor,
290
+ onClearSaved: isControlled ? onClearSavedProp : handleClearSaved,
226
291
  };
227
292
 
228
293
  if (inline) {
229
294
  return <PickerPanel {...panelProps} style={style} />;
230
295
  }
231
296
 
297
+ const swatchColor = showAlpha ? hexToRgba(currentHex, alpha) : currentHex;
298
+ const checkerSize = 6;
299
+
232
300
  return (
233
301
  <>
234
302
  <Pressable
@@ -239,7 +307,7 @@ export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
239
307
  width: swatchSize,
240
308
  height: swatchSize,
241
309
  borderRadius: swatchBorderRadius,
242
- backgroundColor: currentHex,
310
+ overflow: "hidden",
243
311
  borderWidth: 2,
244
312
  borderColor: t.border,
245
313
  shadowColor: "#000",
@@ -250,7 +318,70 @@ export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
250
318
  },
251
319
  swatchStyle,
252
320
  ]}
253
- />
321
+ >
322
+ {showAlpha && (
323
+ <Svg
324
+ width={swatchSize}
325
+ height={swatchSize}
326
+ style={{ position: "absolute" }}
327
+ >
328
+ <Defs>
329
+ <Pattern
330
+ id="swatchChecker"
331
+ x="0"
332
+ y="0"
333
+ width={checkerSize * 2}
334
+ height={checkerSize * 2}
335
+ patternUnits="userSpaceOnUse"
336
+ >
337
+ <Rect
338
+ x="0"
339
+ y="0"
340
+ width={checkerSize}
341
+ height={checkerSize}
342
+ fill="#CCCCCC"
343
+ />
344
+ <Rect
345
+ x={checkerSize}
346
+ y="0"
347
+ width={checkerSize}
348
+ height={checkerSize}
349
+ fill="#FFFFFF"
350
+ />
351
+ <Rect
352
+ x="0"
353
+ y={checkerSize}
354
+ width={checkerSize}
355
+ height={checkerSize}
356
+ fill="#FFFFFF"
357
+ />
358
+ <Rect
359
+ x={checkerSize}
360
+ y={checkerSize}
361
+ width={checkerSize}
362
+ height={checkerSize}
363
+ fill="#CCCCCC"
364
+ />
365
+ </Pattern>
366
+ </Defs>
367
+ <Rect
368
+ x="0"
369
+ y="0"
370
+ width={swatchSize}
371
+ height={swatchSize}
372
+ fill="url(#swatchChecker)"
373
+ />
374
+ </Svg>
375
+ )}
376
+ <View
377
+ style={{
378
+ position: "absolute",
379
+ width: "100%",
380
+ height: "100%",
381
+ backgroundColor: swatchColor,
382
+ }}
383
+ />
384
+ </Pressable>
254
385
 
255
386
  <Modal
256
387
  visible={modalVisible}
@@ -258,29 +389,34 @@ export const ColorPicker = React.forwardRef<ColorPickerRef, ColorPickerProps>(
258
389
  animationType="fade"
259
390
  onRequestClose={() => setModalVisible(false)}
260
391
  >
261
- <Pressable
262
- onPress={() => setModalVisible(false)}
263
- style={[
264
- {
265
- flex: 1,
266
- backgroundColor: t.overlay,
267
- justifyContent: "center",
268
- alignItems: "center",
269
- padding: 20,
270
- },
271
- modalStyle,
272
- ]}
392
+ <View
393
+ style={{
394
+ flex: 1,
395
+ backgroundColor: t.overlay,
396
+ justifyContent: "center",
397
+ alignItems: "center",
398
+ padding: 20,
399
+ }}
273
400
  >
401
+ {/* Overlay pressable - absolute positioned behind content */}
274
402
  <Pressable
275
- onPress={() => {}}
276
- style={{ width: "100%", alignItems: "center" }}
277
- >
403
+ onPress={() => setModalVisible(false)}
404
+ style={{
405
+ position: "absolute",
406
+ top: 0,
407
+ left: 0,
408
+ right: 0,
409
+ bottom: 0,
410
+ }}
411
+ />
412
+ {/* Content - not wrapped in Pressable */}
413
+ <View style={[{ width: "100%", alignItems: "center" }, contentStyle]}>
278
414
  <PickerPanel
279
415
  {...panelProps}
280
416
  style={[{ width: panelWidth }, style]}
281
417
  />
282
- </Pressable>
283
- </Pressable>
418
+ </View>
419
+ </View>
284
420
  </Modal>
285
421
  </>
286
422
  );