@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,358 @@
1
+ import { memo, useCallback, useMemo, useState } from "react";
2
+ import { FlatList, Pressable, ScrollView, Text, View } from "react-native";
3
+ import Svg, { Defs, Pattern, Rect } from "react-native-svg";
4
+ import type { ColorPickerLabels, Theme } from "../types/misc";
5
+ import { getContrastColor, hexToRgba, parseAlphaFromHex } from "../utils/colors";
6
+
7
+ // Color can be a simple hex string or an object with shades
8
+ export type ColorValue = string | Record<string, string>;
9
+
10
+ export type ColorPalette = {
11
+ name: string;
12
+ colors: Record<string, ColorValue>;
13
+ };
14
+
15
+ type PalettesTabProps = {
16
+ palettes?: ColorPalette[];
17
+ savedColors?: string[];
18
+ showAlpha?: boolean;
19
+ disabled: boolean;
20
+ t: Theme;
21
+ labels: Required<ColorPickerLabels>;
22
+ onSelect: (hex: string) => void;
23
+ onClearSaved?: () => void;
24
+ };
25
+
26
+ const SWATCH_SIZE = 56;
27
+ const GAP = 6;
28
+
29
+ // Check if a color value has shades
30
+ function hasShades(value: ColorValue): value is Record<string, string> {
31
+ return typeof value === "object" && value !== null;
32
+ }
33
+
34
+ // Memoized swatch component
35
+ const ColorSwatch = memo(function ColorSwatch({
36
+ name,
37
+ hex,
38
+ showAlpha,
39
+ disabled,
40
+ borderColor,
41
+ onSelect,
42
+ }: {
43
+ name?: string;
44
+ hex: string;
45
+ showAlpha?: boolean;
46
+ disabled: boolean;
47
+ borderColor: string;
48
+ onSelect: (hex: string) => void;
49
+ }) {
50
+ const colorAlpha = showAlpha ? parseAlphaFromHex(hex) : 100;
51
+ const displayColor = showAlpha ? hexToRgba(hex, colorAlpha) : hex;
52
+ const contrast = getContrastColor(hex, colorAlpha);
53
+
54
+ return (
55
+ <Pressable
56
+ onPress={() => onSelect(hex)}
57
+ disabled={disabled}
58
+ style={({ pressed }) => ({
59
+ width: SWATCH_SIZE,
60
+ height: SWATCH_SIZE,
61
+ borderRadius: 8,
62
+ overflow: "hidden",
63
+ borderWidth: 1,
64
+ borderColor,
65
+ opacity: pressed ? 0.7 : 1,
66
+ justifyContent: "center",
67
+ alignItems: "center",
68
+ })}
69
+ >
70
+ {showAlpha && (
71
+ <Svg width={SWATCH_SIZE} height={SWATCH_SIZE} style={{ position: "absolute" }}>
72
+ <Defs>
73
+ <Pattern
74
+ id={`checker-${hex.replace("#", "")}`}
75
+ x="0"
76
+ y="0"
77
+ width={10}
78
+ height={10}
79
+ patternUnits="userSpaceOnUse"
80
+ >
81
+ <Rect x="0" y="0" width={5} height={5} fill="#CCCCCC" />
82
+ <Rect x={5} y="0" width={5} height={5} fill="#FFFFFF" />
83
+ <Rect x="0" y={5} width={5} height={5} fill="#FFFFFF" />
84
+ <Rect x={5} y={5} width={5} height={5} fill="#CCCCCC" />
85
+ </Pattern>
86
+ </Defs>
87
+ <Rect x="0" y="0" width={SWATCH_SIZE} height={SWATCH_SIZE} fill={`url(#checker-${hex.replace("#", "")})`} />
88
+ </Svg>
89
+ )}
90
+ <View
91
+ style={{
92
+ position: "absolute",
93
+ width: "100%",
94
+ height: "100%",
95
+ backgroundColor: displayColor,
96
+ justifyContent: "center",
97
+ alignItems: "center",
98
+ }}
99
+ >
100
+ {name && (
101
+ <Text
102
+ style={{
103
+ color: contrast,
104
+ fontSize: 9,
105
+ fontWeight: "600",
106
+ textAlign: "center",
107
+ }}
108
+ numberOfLines={1}
109
+ >
110
+ {name}
111
+ </Text>
112
+ )}
113
+ </View>
114
+ </Pressable>
115
+ );
116
+ });
117
+
118
+ // Row item type for FlatList
119
+ type RowItem =
120
+ | { type: "saved-header" }
121
+ | { type: "saved-colors"; colors: string[] }
122
+ | { type: "saved-empty" }
123
+ | { type: "simple"; colors: [string, string][] }
124
+ | { type: "group"; name: string; shades: [string, string][] };
125
+
126
+ export function PalettesTab({
127
+ palettes = [],
128
+ savedColors = [],
129
+ showAlpha,
130
+ disabled,
131
+ t,
132
+ labels,
133
+ onSelect,
134
+ onClearSaved,
135
+ }: PalettesTabProps) {
136
+ const [activePaletteIndex, setActivePaletteIndex] = useState(0);
137
+
138
+ // User palettes first, then "Saved" at the end
139
+ const allPalettes = useMemo(() => {
140
+ return [...palettes.map((p) => ({ ...p, isSaved: false })), { name: labels.saved, isSaved: true }];
141
+ }, [palettes, labels.saved]);
142
+
143
+ const activePalette = allPalettes[activePaletteIndex];
144
+
145
+ // Memoize row data
146
+ const rowData = useMemo(() => {
147
+ if (!activePalette) return [];
148
+
149
+ // Saved colors tab
150
+ if (activePalette.isSaved) {
151
+ if (savedColors.length === 0) {
152
+ return [{ type: "saved-empty" as const }];
153
+ }
154
+ return [{ type: "saved-colors" as const, colors: savedColors }];
155
+ }
156
+
157
+ // Regular palette
158
+ const palette = activePalette as ColorPalette & { isSaved: false };
159
+ const simpleColors: [string, string][] = [];
160
+ const colorGroups: [string, Record<string, string>][] = [];
161
+
162
+ for (const [name, value] of Object.entries(palette.colors)) {
163
+ if (hasShades(value)) {
164
+ colorGroups.push([name, value]);
165
+ } else {
166
+ simpleColors.push([name, value]);
167
+ }
168
+ }
169
+
170
+ const rows: RowItem[] = [];
171
+
172
+ if (simpleColors.length > 0) {
173
+ rows.push({ type: "simple", colors: simpleColors });
174
+ }
175
+
176
+ for (const [name, shades] of colorGroups) {
177
+ rows.push({
178
+ type: "group",
179
+ name,
180
+ shades: Object.entries(shades),
181
+ });
182
+ }
183
+
184
+ return rows;
185
+ }, [activePalette, savedColors]);
186
+
187
+ const handleSelect = useCallback(
188
+ (hex: string) => {
189
+ onSelect(hex);
190
+ },
191
+ [onSelect]
192
+ );
193
+
194
+ const renderRow = useCallback(
195
+ ({ item }: { item: RowItem }) => {
196
+ if (item.type === "saved-empty") {
197
+ return (
198
+ <View style={{ padding: 20, alignItems: "center", justifyContent: "center" }}>
199
+ <Text style={{ color: t.textDim, fontSize: 14 }}>{labels.noSavedColors}</Text>
200
+ </View>
201
+ );
202
+ }
203
+
204
+ if (item.type === "saved-colors") {
205
+ return (
206
+ <View style={{ gap: 12 }}>
207
+ <View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
208
+ <Text style={{ color: t.textMuted, fontSize: 11, fontWeight: "600", textTransform: "uppercase" }}>
209
+ {labels.saved}
210
+ </Text>
211
+ {onClearSaved && item.colors.length > 0 && (
212
+ <Pressable onPress={onClearSaved}>
213
+ <Text style={{ color: t.textDim, fontSize: 12, fontWeight: "600" }}>
214
+ {labels.clearSaved}
215
+ </Text>
216
+ </Pressable>
217
+ )}
218
+ </View>
219
+ <View style={{ flexDirection: "row", flexWrap: "wrap", gap: GAP }}>
220
+ {item.colors.map((hex, i) => (
221
+ <ColorSwatch
222
+ key={`${hex}-${i}`}
223
+ name={showAlpha && hex.length === 9 ? hex.slice(1).toUpperCase() : hex.slice(1, 7).toUpperCase()}
224
+ hex={hex}
225
+ showAlpha={showAlpha}
226
+ disabled={disabled}
227
+ borderColor={t.border}
228
+ onSelect={handleSelect}
229
+ />
230
+ ))}
231
+ </View>
232
+ </View>
233
+ );
234
+ }
235
+
236
+ if (item.type === "simple") {
237
+ return (
238
+ <View style={{ flexDirection: "row", flexWrap: "wrap", gap: GAP }}>
239
+ {item.colors.map(([name, hex]) => (
240
+ <ColorSwatch
241
+ key={name}
242
+ name={name}
243
+ hex={hex}
244
+ disabled={disabled}
245
+ borderColor={t.border}
246
+ onSelect={handleSelect}
247
+ />
248
+ ))}
249
+ </View>
250
+ );
251
+ }
252
+
253
+ if (item.type === "group") {
254
+ return (
255
+ <View style={{ gap: 6 }}>
256
+ <Text
257
+ style={{
258
+ color: t.textMuted,
259
+ fontSize: 11,
260
+ fontWeight: "600",
261
+ textTransform: "capitalize",
262
+ }}
263
+ >
264
+ {item.name}
265
+ </Text>
266
+ <ScrollView
267
+ horizontal
268
+ showsHorizontalScrollIndicator={false}
269
+ contentContainerStyle={{ gap: GAP }}
270
+ >
271
+ {item.shades.map(([shade, hex]) => (
272
+ <ColorSwatch
273
+ key={shade}
274
+ name={shade}
275
+ hex={hex}
276
+ disabled={disabled}
277
+ borderColor={t.border}
278
+ onSelect={handleSelect}
279
+ />
280
+ ))}
281
+ </ScrollView>
282
+ </View>
283
+ );
284
+ }
285
+
286
+ return null;
287
+ },
288
+ [disabled, showAlpha, t, labels, handleSelect, onClearSaved]
289
+ );
290
+
291
+ const keyExtractor = useCallback(
292
+ (item: RowItem, index: number) => {
293
+ if (item.type === "saved-header") return "saved-header";
294
+ if (item.type === "saved-colors") return "saved-colors";
295
+ if (item.type === "saved-empty") return "saved-empty";
296
+ if (item.type === "simple") return "simple";
297
+ if (item.type === "group") return `group-${item.name}-${index}`;
298
+ return `row-${index}`;
299
+ },
300
+ []
301
+ );
302
+
303
+ return (
304
+ <View>
305
+ {/* Palette selector */}
306
+ {allPalettes.length > 1 && (
307
+ <View style={{ borderBottomWidth: 1, borderBottomColor: t.border }}>
308
+ <ScrollView
309
+ horizontal
310
+ showsHorizontalScrollIndicator={false}
311
+ contentContainerStyle={{ paddingHorizontal: 12, paddingVertical: 8, gap: 8 }}
312
+ >
313
+ {allPalettes.map((palette, index) => {
314
+ const isActive = index === activePaletteIndex;
315
+ return (
316
+ <Pressable
317
+ key={palette.name}
318
+ onPress={() => setActivePaletteIndex(index)}
319
+ style={{
320
+ paddingHorizontal: 12,
321
+ paddingVertical: 6,
322
+ borderRadius: 6,
323
+ backgroundColor: isActive ? t.tabIndicator : t.surface,
324
+ }}
325
+ >
326
+ <Text
327
+ style={{
328
+ fontSize: 12,
329
+ fontWeight: "600",
330
+ color: isActive ? (t.tabIndicator === "#FFFFFF" ? "#000000" : "#FFFFFF") : t.textMuted,
331
+ }}
332
+ >
333
+ {palette.name}
334
+ </Text>
335
+ </Pressable>
336
+ );
337
+ })}
338
+ </ScrollView>
339
+ </View>
340
+ )}
341
+
342
+ {/* Colors grid - virtualized */}
343
+ <View style={{ height: 280 }}>
344
+ <FlatList
345
+ data={rowData}
346
+ renderItem={renderRow}
347
+ keyExtractor={keyExtractor}
348
+ contentContainerStyle={{ padding: 12, gap: 12 }}
349
+ showsVerticalScrollIndicator={false}
350
+ initialNumToRender={5}
351
+ maxToRenderPerBatch={5}
352
+ windowSize={5}
353
+ removeClippedSubviews
354
+ />
355
+ </View>
356
+ </View>
357
+ );
358
+ }
@@ -6,9 +6,11 @@ import {
6
6
  type StyleProp,
7
7
  type ViewStyle
8
8
  } from "react-native";
9
+ import Svg, { Defs, Pattern, Rect } from "react-native-svg";
9
10
  import type { ColorPickerLabels, TabId, Theme } from "../types/misc";
11
+ import { appendAlphaToHex, hexToRgba } from "../utils/colors";
12
+ import { type ColorPalette, PalettesTab } from "./palettes-tab";
10
13
  import { PickerTab } from "./picker-tab";
11
- import { RecentTab } from "./recent-tab";
12
14
  import { TabBar } from "./tab-bar";
13
15
  import { ValuesTab } from "./values-tab";
14
16
 
@@ -16,10 +18,11 @@ type PickerPanelProps = {
16
18
  hue: number;
17
19
  sat: number;
18
20
  bright: number;
21
+ alpha?: number;
19
22
  currentHex: string;
20
23
  contrastColor: string;
21
24
  hexInput: string;
22
- recentColors: string[];
25
+ savedColors: string[];
23
26
  tabs: TabId[];
24
27
  hueStripHeight: number;
25
28
  disabled: boolean;
@@ -27,13 +30,15 @@ type PickerPanelProps = {
27
30
  labels: Required<ColorPickerLabels>;
28
31
  onHueChange: (h: number) => void;
29
32
  onSatBrightChange: (vals: { s: number; b: number }) => void;
33
+ onAlphaChange?: (a: number) => void;
30
34
  onHexInputChange: (text: string) => void;
31
35
  onHexSubmit: () => void;
32
36
  onHexInputFocus: () => void;
33
37
  onHexInputBlur: () => void;
34
- onSaveRecent: () => void;
35
- onRecentSelect: (hex: string) => void;
36
- onClearRecent: () => void;
38
+ onSaveColor: () => void;
39
+ onSelectColor: (hex: string) => void;
40
+ onClearSaved?: () => void;
41
+ palettes?: ColorPalette[];
37
42
  style?: StyleProp<ViewStyle>;
38
43
  }
39
44
 
@@ -41,27 +46,34 @@ export function PickerPanel({
41
46
  hue,
42
47
  sat,
43
48
  bright,
49
+ alpha,
44
50
  currentHex,
45
51
  contrastColor,
46
52
  hexInput,
47
- recentColors,
53
+ savedColors,
48
54
  tabs,
49
55
  hueStripHeight,
50
56
  disabled,
51
57
  t,
52
58
  onHueChange,
53
59
  onSatBrightChange,
60
+ onAlphaChange,
54
61
  onHexInputChange,
55
62
  onHexSubmit,
56
63
  onHexInputFocus,
57
64
  onHexInputBlur,
58
- onSaveRecent,
59
- onRecentSelect,
60
- onClearRecent,
65
+ onSaveColor,
66
+ onSelectColor,
67
+ onClearSaved,
68
+ palettes,
61
69
  labels,
62
70
  style,
63
71
  }: PickerPanelProps) {
64
72
  const [activeTab, setActiveTab] = useState<TabId>(tabs[0]!);
73
+ const showAlpha = alpha != null;
74
+ const displayHex = showAlpha ? appendAlphaToHex(currentHex, alpha) : currentHex;
75
+ const headerBgColor = showAlpha ? hexToRgba(currentHex, alpha) : currentHex;
76
+ const checkerSize = 8;
65
77
 
66
78
  return (
67
79
  <View
@@ -74,51 +86,90 @@ export function PickerPanel({
74
86
  <View
75
87
  style={{
76
88
  height: 64,
77
- backgroundColor: currentHex,
78
- justifyContent: "flex-end",
79
- paddingHorizontal: 16,
80
- paddingBottom: 12,
89
+ overflow: "hidden",
81
90
  }}
82
91
  >
92
+ {/* Checkerboard background for alpha */}
93
+ {showAlpha && (
94
+ <Svg
95
+ width="100%"
96
+ height={64}
97
+ style={{ position: "absolute" }}
98
+ >
99
+ <Defs>
100
+ <Pattern
101
+ id="headerChecker"
102
+ x="0"
103
+ y="0"
104
+ width={checkerSize * 2}
105
+ height={checkerSize * 2}
106
+ patternUnits="userSpaceOnUse"
107
+ >
108
+ <Rect x="0" y="0" width={checkerSize} height={checkerSize} fill="#CCCCCC" />
109
+ <Rect x={checkerSize} y="0" width={checkerSize} height={checkerSize} fill="#FFFFFF" />
110
+ <Rect x="0" y={checkerSize} width={checkerSize} height={checkerSize} fill="#FFFFFF" />
111
+ <Rect x={checkerSize} y={checkerSize} width={checkerSize} height={checkerSize} fill="#CCCCCC" />
112
+ </Pattern>
113
+ </Defs>
114
+ <Rect x="0" y="0" width="100%" height={64} fill="url(#headerChecker)" />
115
+ </Svg>
116
+ )}
117
+ {/* Color overlay */}
83
118
  <View
84
119
  style={{
85
- flexDirection: "row",
86
- alignItems: "center",
87
- justifyContent: "space-between",
120
+ position: "absolute",
121
+ width: "100%",
122
+ height: "100%",
123
+ backgroundColor: headerBgColor,
124
+ justifyContent: "flex-end",
125
+ paddingHorizontal: 16,
126
+ paddingBottom: 12,
88
127
  }}
89
128
  >
90
- <Text
129
+ <View
91
130
  style={{
92
- fontSize: 18,
93
- fontWeight: "700",
94
- color: contrastColor,
95
- fontVariant: ["tabular-nums"],
96
- letterSpacing: 0.5,
131
+ flexDirection: "row",
132
+ alignItems: "center",
133
+ justifyContent: "space-between",
97
134
  }}
98
- >
99
- {currentHex.toUpperCase()}
100
- </Text>
101
- <Pressable
102
- onPress={onSaveRecent}
103
- style={({ pressed }) => ({
104
- backgroundColor: pressed
105
- ? contrastColor === "#FFFFFF"
106
- ? "rgba(255,255,255,0.5)"
107
- : "rgba(0,0,0,0.3)"
108
- : contrastColor === "#FFFFFF"
109
- ? "rgba(255,255,255,0.2)"
110
- : "rgba(0,0,0,0.1)",
111
- paddingHorizontal: 12,
112
- paddingVertical: 5,
113
- borderRadius: 8,
114
- })}
115
135
  >
116
136
  <Text
117
- style={{ color: contrastColor, fontSize: 12, fontWeight: "600" }}
137
+ style={{
138
+ fontSize: 18,
139
+ fontWeight: "700",
140
+ color: contrastColor,
141
+ fontVariant: ["tabular-nums"],
142
+ letterSpacing: 0.5,
143
+ }}
118
144
  >
119
- {labels.save}
145
+ {displayHex.toUpperCase()}
120
146
  </Text>
121
- </Pressable>
147
+ {tabs.includes("palettes") && (
148
+ <Pressable
149
+ onPress={onSaveColor}
150
+ style={({ pressed }) => ({
151
+ backgroundColor: pressed
152
+ ? contrastColor === "#FFFFFF"
153
+ ? "rgba(255,255,255,0.6)"
154
+ : "rgba(0,0,0,0.5)"
155
+ : contrastColor === "#FFFFFF"
156
+ ? "rgba(255,255,255,0.35)"
157
+ : "rgba(0,0,0,0.35)",
158
+ paddingHorizontal: 12,
159
+ paddingVertical: 5,
160
+ borderRadius: 8,
161
+ borderColor: contrastColor,
162
+ borderWidth: 1,
163
+ })}
164
+ >
165
+ <Text
166
+ style={{ color: contrastColor, fontSize: 12, fontWeight: "600" }}
167
+ >
168
+ {labels.save}
169
+ </Text>
170
+ </Pressable>
171
+ )}
172
+ </View>
122
173
  </View>
123
174
  </View>
124
175
 
@@ -139,11 +190,14 @@ export function PickerPanel({
139
190
  hue={hue}
140
191
  sat={sat}
141
192
  bright={bright}
193
+ alpha={alpha}
194
+ currentHex={currentHex}
142
195
  hueStripHeight={hueStripHeight}
143
196
  disabled={disabled}
144
197
  t={t}
145
198
  onHueChange={onHueChange}
146
199
  onSatBrightChange={onSatBrightChange}
200
+ onAlphaChange={onAlphaChange}
147
201
  />
148
202
  )}
149
203
  {activeTab === "values" && (
@@ -151,6 +205,7 @@ export function PickerPanel({
151
205
  hue={hue}
152
206
  sat={sat}
153
207
  bright={bright}
208
+ alpha={alpha}
154
209
  currentHex={currentHex}
155
210
  hexInput={hexInput}
156
211
  disabled={disabled}
@@ -161,14 +216,16 @@ export function PickerPanel({
161
216
  onHexInputBlur={onHexInputBlur}
162
217
  />
163
218
  )}
164
- {activeTab === "recent" && (
165
- <RecentTab
166
- recentColors={recentColors}
219
+ {activeTab === "palettes" && (
220
+ <PalettesTab
221
+ palettes={palettes}
222
+ savedColors={savedColors}
223
+ showAlpha={showAlpha}
167
224
  disabled={disabled}
168
225
  t={t}
169
226
  labels={labels}
170
- onSelect={onRecentSelect}
171
- onClear={onClearRecent}
227
+ onSelect={onSelectColor}
228
+ onClearSaved={onClearSaved}
172
229
  />
173
230
  )}
174
231
  </View>
@@ -2,6 +2,7 @@ import {
2
2
  View
3
3
  } from "react-native";
4
4
  import type { Theme } from "../types/misc";
5
+ import { AlphaStrip } from "./alpha-strip";
5
6
  import { HueStrip } from "./hue-strip";
6
7
  import { SatBrightPad } from "./sat-bright-pad";
7
8
 
@@ -9,22 +10,28 @@ type PickerTabProps = {
9
10
  hue: number;
10
11
  sat: number;
11
12
  bright: number;
13
+ alpha?: number;
14
+ currentHex: string;
12
15
  hueStripHeight: number;
13
16
  disabled: boolean;
14
17
  t: Theme;
15
18
  onHueChange: (h: number) => void;
16
19
  onSatBrightChange: (vals: { s: number; b: number }) => void;
20
+ onAlphaChange?: (a: number) => void;
17
21
  }
18
22
 
19
23
  export function PickerTab({
20
24
  hue,
21
25
  sat,
22
26
  bright,
27
+ alpha,
28
+ currentHex,
23
29
  hueStripHeight,
24
30
  disabled,
25
31
  t,
26
32
  onHueChange,
27
33
  onSatBrightChange,
34
+ onAlphaChange,
28
35
  }: PickerTabProps) {
29
36
  return (
30
37
  <View style={{ gap: 16, padding: 20 }}>
@@ -43,6 +50,16 @@ export function PickerTab({
43
50
  thumbBorder={t.thumbBorder}
44
51
  onChange={onHueChange}
45
52
  />
53
+ {alpha != null && onAlphaChange && (
54
+ <AlphaStrip
55
+ alpha={alpha}
56
+ color={currentHex}
57
+ height={hueStripHeight}
58
+ disabled={disabled}
59
+ thumbBorder={t.thumbBorder}
60
+ onChange={onAlphaChange}
61
+ />
62
+ )}
46
63
  </View>
47
64
  );
48
65
  }