@react-navigation/elements 3.0.0-alpha.3 → 3.0.0-alpha.31

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 (177) hide show
  1. package/lib/module/ActivityView.js +90 -0
  2. package/lib/module/ActivityView.js.map +1 -0
  3. package/lib/module/ActivityView.native.js +67 -0
  4. package/lib/module/ActivityView.native.js.map +1 -0
  5. package/lib/module/Badge.js +5 -4
  6. package/lib/module/Badge.js.map +1 -1
  7. package/lib/module/Button.js +91 -13
  8. package/lib/module/Button.js.map +1 -1
  9. package/lib/module/Color.js +552 -4
  10. package/lib/module/Color.js.map +1 -1
  11. package/lib/module/Container.js +11 -1
  12. package/lib/module/Container.js.map +1 -1
  13. package/lib/module/Header/Header.js +26 -11
  14. package/lib/module/Header/Header.js.map +1 -1
  15. package/lib/module/Header/HeaderBackButton.js +27 -13
  16. package/lib/module/Header/HeaderBackButton.js.map +1 -1
  17. package/lib/module/Header/HeaderButton.js +4 -5
  18. package/lib/module/Header/HeaderButton.js.map +1 -1
  19. package/lib/module/Header/HeaderIcon.js +29 -10
  20. package/lib/module/Header/HeaderIcon.js.map +1 -1
  21. package/lib/module/Header/HeaderSearchBar.js +31 -24
  22. package/lib/module/Header/HeaderSearchBar.js.map +1 -1
  23. package/lib/module/Header/getHeaderTitle.js.map +1 -1
  24. package/lib/module/Header/useHeaderHeight.js +1 -1
  25. package/lib/module/Header/useHeaderHeight.js.map +1 -1
  26. package/lib/module/Label/getLabel.js.map +1 -1
  27. package/lib/module/PlatformColor.js +1 -0
  28. package/lib/module/PlatformColor.js.map +1 -1
  29. package/lib/module/PlatformColor.native.js +1 -1
  30. package/lib/module/PlatformColor.native.js.map +1 -1
  31. package/lib/module/PlatformPressable.js +14 -5
  32. package/lib/module/PlatformPressable.js.map +1 -1
  33. package/lib/module/SafeAreaProviderCompat.js +1 -1
  34. package/lib/module/SafeAreaProviderCompat.js.map +1 -1
  35. package/lib/module/Screen.js +31 -18
  36. package/lib/module/Screen.js.map +1 -1
  37. package/lib/module/index.js +1 -7
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/module/internal.js +1 -1
  40. package/lib/module/internal.js.map +1 -1
  41. package/lib/module/useFrameSize.js +1 -1
  42. package/lib/module/useFrameSize.js.map +1 -1
  43. package/lib/typescript/src/ActivityView.d.ts +31 -0
  44. package/lib/typescript/src/ActivityView.d.ts.map +1 -0
  45. package/lib/typescript/src/ActivityView.native.d.ts +3 -0
  46. package/lib/typescript/src/ActivityView.native.d.ts.map +1 -0
  47. package/lib/typescript/src/Badge.d.ts +3 -3
  48. package/lib/typescript/src/Badge.d.ts.map +1 -1
  49. package/lib/typescript/src/Button.d.ts +35 -4
  50. package/lib/typescript/src/Button.d.ts.map +1 -1
  51. package/lib/typescript/src/Color.d.ts +4 -4
  52. package/lib/typescript/src/Color.d.ts.map +1 -1
  53. package/lib/typescript/src/Container.d.ts +7 -4
  54. package/lib/typescript/src/Container.d.ts.map +1 -1
  55. package/lib/typescript/src/Header/Header.d.ts +2 -2
  56. package/lib/typescript/src/Header/Header.d.ts.map +1 -1
  57. package/lib/typescript/src/Header/HeaderBackButton.d.ts +1 -1
  58. package/lib/typescript/src/Header/HeaderBackButton.d.ts.map +1 -1
  59. package/lib/typescript/src/Header/HeaderBackground.d.ts +3 -3
  60. package/lib/typescript/src/Header/HeaderBackground.d.ts.map +1 -1
  61. package/lib/typescript/src/Header/HeaderButton.d.ts +6 -1
  62. package/lib/typescript/src/Header/HeaderButton.d.ts.map +1 -1
  63. package/lib/typescript/src/Header/HeaderIcon.d.ts +8 -4
  64. package/lib/typescript/src/Header/HeaderIcon.d.ts.map +1 -1
  65. package/lib/typescript/src/Header/HeaderSearchBar.d.ts +9 -6
  66. package/lib/typescript/src/Header/HeaderSearchBar.d.ts.map +1 -1
  67. package/lib/typescript/src/Header/HeaderTitle.d.ts +3 -3
  68. package/lib/typescript/src/Header/HeaderTitle.d.ts.map +1 -1
  69. package/lib/typescript/src/Header/getHeaderTitle.d.ts +1 -1
  70. package/lib/typescript/src/Header/getHeaderTitle.d.ts.map +1 -1
  71. package/lib/typescript/src/Label/getLabel.d.ts +2 -2
  72. package/lib/typescript/src/Label/getLabel.d.ts.map +1 -1
  73. package/lib/typescript/src/PlatformColor.d.ts +1 -0
  74. package/lib/typescript/src/PlatformColor.d.ts.map +1 -1
  75. package/lib/typescript/src/PlatformColor.native.d.ts +1 -1
  76. package/lib/typescript/src/PlatformColor.native.d.ts.map +1 -1
  77. package/lib/typescript/src/PlatformPressable.d.ts +12 -15
  78. package/lib/typescript/src/PlatformPressable.d.ts.map +1 -1
  79. package/lib/typescript/src/Screen.d.ts +6 -6
  80. package/lib/typescript/src/Screen.d.ts.map +1 -1
  81. package/lib/typescript/src/index.d.ts +1 -2
  82. package/lib/typescript/src/index.d.ts.map +1 -1
  83. package/lib/typescript/src/internal.d.ts +1 -1
  84. package/lib/typescript/src/internal.d.ts.map +1 -1
  85. package/lib/typescript/src/types.d.ts +119 -69
  86. package/lib/typescript/src/types.d.ts.map +1 -1
  87. package/package.json +20 -20
  88. package/src/ActivityView.native.tsx +90 -0
  89. package/src/ActivityView.tsx +129 -0
  90. package/src/Badge.tsx +9 -7
  91. package/src/Button.tsx +151 -14
  92. package/src/Color.tsx +726 -8
  93. package/src/Container.tsx +20 -4
  94. package/src/Header/Header.tsx +43 -20
  95. package/src/Header/HeaderBackButton.tsx +31 -17
  96. package/src/Header/HeaderBackground.tsx +3 -3
  97. package/src/Header/HeaderButton.tsx +16 -18
  98. package/src/Header/HeaderIcon.tsx +49 -9
  99. package/src/Header/HeaderSearchBar.tsx +50 -44
  100. package/src/Header/HeaderTitle.tsx +3 -3
  101. package/src/Header/getHeaderTitle.tsx +4 -1
  102. package/src/Header/useHeaderHeight.tsx +1 -1
  103. package/src/Label/getLabel.tsx +1 -1
  104. package/src/PlatformColor.native.tsx +1 -1
  105. package/src/PlatformColor.tsx +4 -0
  106. package/src/PlatformPressable.tsx +40 -29
  107. package/src/SafeAreaProviderCompat.tsx +1 -1
  108. package/src/Screen.tsx +40 -27
  109. package/src/index.tsx +1 -16
  110. package/src/internal.tsx +1 -1
  111. package/src/types.tsx +153 -76
  112. package/src/useFrameSize.tsx +1 -1
  113. package/lib/module/Lazy.js +0 -42
  114. package/lib/module/Lazy.js.map +0 -1
  115. package/lib/module/assets/back-icon.ios.svg +0 -4
  116. package/lib/module/assets/back-icon@1x.android.png +0 -0
  117. package/lib/module/assets/back-icon@1x.ios.png +0 -0
  118. package/lib/module/assets/back-icon@2x.android.png +0 -0
  119. package/lib/module/assets/back-icon@2x.ios.png +0 -0
  120. package/lib/module/assets/back-icon@3x.android.png +0 -0
  121. package/lib/module/assets/back-icon@3x.ios.png +0 -0
  122. package/lib/module/assets/back-icon@4x.android.png +0 -0
  123. package/lib/module/assets/back-icon@4x.ios.png +0 -0
  124. package/lib/module/assets/clear-icon@1x.png +0 -0
  125. package/lib/module/assets/clear-icon@2x.png +0 -0
  126. package/lib/module/assets/clear-icon@3x.png +0 -0
  127. package/lib/module/assets/clear-icon@4x.png +0 -0
  128. package/lib/module/assets/close-icon@1x.png +0 -0
  129. package/lib/module/assets/close-icon@2x.png +0 -0
  130. package/lib/module/assets/close-icon@3x.png +0 -0
  131. package/lib/module/assets/close-icon@4x.png +0 -0
  132. package/lib/module/assets/search-icon-legacy@1x.ios.png +0 -0
  133. package/lib/module/assets/search-icon-legacy@2x.ios.png +0 -0
  134. package/lib/module/assets/search-icon-legacy@3x.ios.png +0 -0
  135. package/lib/module/assets/search-icon-legacy@4x.ios.png +0 -0
  136. package/lib/module/assets/search-icon.ios.svg +0 -4
  137. package/lib/module/assets/search-icon@1x.android.png +0 -0
  138. package/lib/module/assets/search-icon@1x.ios.png +0 -0
  139. package/lib/module/assets/search-icon@2x.android.png +0 -0
  140. package/lib/module/assets/search-icon@2x.ios.png +0 -0
  141. package/lib/module/assets/search-icon@3x.android.png +0 -0
  142. package/lib/module/assets/search-icon@3x.ios.png +0 -0
  143. package/lib/module/assets/search-icon@4x.android.png +0 -0
  144. package/lib/module/assets/search-icon@4x.ios.png +0 -0
  145. package/lib/typescript/src/Lazy.d.ts +0 -31
  146. package/lib/typescript/src/Lazy.d.ts.map +0 -1
  147. package/src/Lazy.tsx +0 -59
  148. package/src/assets/back-icon.ios.svg +0 -4
  149. package/src/assets/back-icon@1x.android.png +0 -0
  150. package/src/assets/back-icon@1x.ios.png +0 -0
  151. package/src/assets/back-icon@2x.android.png +0 -0
  152. package/src/assets/back-icon@2x.ios.png +0 -0
  153. package/src/assets/back-icon@3x.android.png +0 -0
  154. package/src/assets/back-icon@3x.ios.png +0 -0
  155. package/src/assets/back-icon@4x.android.png +0 -0
  156. package/src/assets/back-icon@4x.ios.png +0 -0
  157. package/src/assets/clear-icon@1x.png +0 -0
  158. package/src/assets/clear-icon@2x.png +0 -0
  159. package/src/assets/clear-icon@3x.png +0 -0
  160. package/src/assets/clear-icon@4x.png +0 -0
  161. package/src/assets/close-icon@1x.png +0 -0
  162. package/src/assets/close-icon@2x.png +0 -0
  163. package/src/assets/close-icon@3x.png +0 -0
  164. package/src/assets/close-icon@4x.png +0 -0
  165. package/src/assets/search-icon-legacy@1x.ios.png +0 -0
  166. package/src/assets/search-icon-legacy@2x.ios.png +0 -0
  167. package/src/assets/search-icon-legacy@3x.ios.png +0 -0
  168. package/src/assets/search-icon-legacy@4x.ios.png +0 -0
  169. package/src/assets/search-icon.ios.svg +0 -4
  170. package/src/assets/search-icon@1x.android.png +0 -0
  171. package/src/assets/search-icon@1x.ios.png +0 -0
  172. package/src/assets/search-icon@2x.android.png +0 -0
  173. package/src/assets/search-icon@2x.ios.png +0 -0
  174. package/src/assets/search-icon@3x.android.png +0 -0
  175. package/src/assets/search-icon@3x.ios.png +0 -0
  176. package/src/assets/search-icon@4x.android.png +0 -0
  177. package/src/assets/search-icon@4x.ios.png +0 -0
package/src/Color.tsx CHANGED
@@ -1,21 +1,739 @@
1
- // eslint-disable-next-line no-restricted-imports
2
- import OriginalColor from 'color';
3
- import type { ColorValue } from 'react-native';
1
+ import { type ColorValue, Platform } from 'react-native';
2
+
3
+ import { DynamicColorIOS, PlatformColor } from './PlatformColor';
4
4
 
5
5
  type ColorType = {
6
- isLight(): boolean;
7
- isDark(): boolean;
8
6
  alpha(amount: number): ColorType;
9
7
  alpha(): number;
10
8
  fade(amount: number): ColorType;
11
- darken(amount: number): ColorType;
12
9
  string(): string;
13
10
  };
14
11
 
12
+ type ParsedColor = {
13
+ parts: number[];
14
+ type: (typeof COLOR_TYPES)[number];
15
+ };
16
+
15
17
  export function Color(value: ColorValue): ColorType | undefined {
16
- if (typeof value === 'string' && !value.startsWith('var(')) {
17
- return OriginalColor(value);
18
+ if (typeof value === 'string') {
19
+ const parsed = parse(value);
20
+
21
+ if (parsed) {
22
+ return build(parsed);
23
+ }
18
24
  }
19
25
 
20
26
  return undefined;
21
27
  }
28
+
29
+ Color.foreground = (color: ColorValue): ColorValue => {
30
+ const value = color as unknown;
31
+
32
+ if (typeof value === 'object' && value != null) {
33
+ // Special case for Android platform colors
34
+ // Available colors: https://developer.android.com/reference/android/R.color
35
+ if (
36
+ Platform.OS === 'android' &&
37
+ PlatformColor &&
38
+ 'resource_paths' in value &&
39
+ Array.isArray(value.resource_paths) &&
40
+ typeof value.resource_paths[0] === 'string'
41
+ ) {
42
+ const name = value.resource_paths[0].replace('@android:color/', '');
43
+
44
+ if (name in ANDROID_COLOR_MAP) {
45
+ return PlatformColor(`@android:color/${ANDROID_COLOR_MAP[name]}`);
46
+ }
47
+ }
48
+
49
+ // Special case for iOS platform colors
50
+ if (
51
+ Platform.OS === 'ios' &&
52
+ PlatformColor &&
53
+ 'semantic' in value &&
54
+ Array.isArray(value.semantic) &&
55
+ typeof value.semantic[0] === 'string'
56
+ ) {
57
+ const name = value.semantic[0];
58
+
59
+ if (name in IOS_COLOR_MAP) {
60
+ const foreground = IOS_COLOR_MAP[name];
61
+ return foreground === 'white' || foreground === 'black'
62
+ ? foreground
63
+ : PlatformColor(foreground);
64
+ }
65
+ }
66
+
67
+ // Special case for iOS dynamic colors
68
+ if (
69
+ Platform.OS === 'ios' &&
70
+ DynamicColorIOS &&
71
+ 'dynamic' in value &&
72
+ typeof value.dynamic === 'object' &&
73
+ value.dynamic != null &&
74
+ 'light' in value.dynamic &&
75
+ typeof value.dynamic.light === 'string' &&
76
+ 'dark' in value.dynamic &&
77
+ typeof value.dynamic.dark === 'string'
78
+ ) {
79
+ const lightForeground = Color.foreground(value.dynamic.light);
80
+ const darkForeground = Color.foreground(value.dynamic.dark);
81
+
82
+ if (lightForeground && darkForeground) {
83
+ return DynamicColorIOS({
84
+ light: lightForeground,
85
+ dark: darkForeground,
86
+ });
87
+ }
88
+ }
89
+ } else if (typeof color === 'string') {
90
+ const parsed = parse(color);
91
+
92
+ if (parsed && isLight(parsed)) {
93
+ return darken(parsed, 0.71).string();
94
+ }
95
+ }
96
+
97
+ return '#fff';
98
+ };
99
+
100
+ function build(parsed: ParsedColor): ColorType {
101
+ function alpha(amount: number): ColorType;
102
+ function alpha(): number;
103
+ function alpha(amount?: number): ColorType | number {
104
+ if (amount === undefined) {
105
+ return clamp(parsed.parts[3], 0, 1);
106
+ }
107
+
108
+ const newParts = parsed.parts.slice();
109
+
110
+ newParts[3] = amount;
111
+
112
+ return build({ type: parsed.type, parts: newParts });
113
+ }
114
+
115
+ function fade(amount: number): ColorType {
116
+ const newParts = parsed.parts.slice();
117
+
118
+ newParts[3] = parsed.parts[3] * (1 - amount);
119
+
120
+ return build({ type: parsed.type, parts: newParts });
121
+ }
122
+
123
+ function string(): string {
124
+ return stringify(parsed);
125
+ }
126
+
127
+ return {
128
+ alpha,
129
+ fade,
130
+ string,
131
+ };
132
+ }
133
+
134
+ function parse(color: string): ParsedColor | undefined {
135
+ if (color.toLowerCase() === 'transparent') {
136
+ return { type: 'rgb', parts: [0, 0, 0, 0] };
137
+ }
138
+
139
+ const named = NAMED_COLORS[color.toLowerCase()];
140
+
141
+ if (named) {
142
+ return { type: 'rgb', parts: [...named, 1] };
143
+ }
144
+
145
+ const hex = color.match(/^#([\da-f]+)$/i);
146
+
147
+ if (hex) {
148
+ // Normalize 3-digit (#rgb) and 4-digit (#rgba) hex to 6-digit (#rrggbb) and 8-digit (#rrggbbaa)
149
+ const digits =
150
+ hex[1].length === 3 || hex[1].length === 4
151
+ ? Array.from(hex[1], (d) => d + d).join('')
152
+ : hex[1];
153
+
154
+ if (digits.length !== 6 && digits.length !== 8) {
155
+ return undefined;
156
+ }
157
+
158
+ return {
159
+ type: 'rgb',
160
+ parts: [
161
+ parseInt(digits.slice(0, 2), 16),
162
+ parseInt(digits.slice(2, 4), 16),
163
+ parseInt(digits.slice(4, 6), 16),
164
+ digits.length === 8 ? parseInt(digits.slice(6, 8), 16) / 255 : 1,
165
+ ],
166
+ };
167
+ }
168
+
169
+ const matched = color.match(/^([a-z]+)\(([^)]*)\)$/i);
170
+
171
+ if (!matched) {
172
+ return undefined;
173
+ }
174
+
175
+ // Get the color function name (e.g., "rgb", "hsl", "lab", etc.) and the color values
176
+ const fnName = matched[1].toLowerCase();
177
+ const argStr = matched[2];
178
+
179
+ const type =
180
+ fnName === 'rgba'
181
+ ? 'rgb'
182
+ : fnName === 'hsla'
183
+ ? 'hsl'
184
+ : COLOR_TYPES.find((t) => t === fnName);
185
+
186
+ if (!type) {
187
+ return undefined;
188
+ }
189
+
190
+ // Get values and optional alpha value based on the color format
191
+ // e.g. CSS L4 (rgb(255 0 0 / 0.5) or rgb(255 0 0 / 50%)) or CSS L3 (rgba(255, 0, 0, 0.5))
192
+ const slashIdx = argStr.indexOf('/');
193
+ const valuesStr = slashIdx >= 0 ? argStr.slice(0, slashIdx) : argStr;
194
+
195
+ let alphaStr = slashIdx >= 0 ? argStr.slice(slashIdx + 1).trim() : undefined;
196
+
197
+ const tokens = valuesStr.split(/[,\s]+/).filter(Boolean);
198
+
199
+ if (alphaStr === undefined && tokens.length === 4) {
200
+ alphaStr = tokens.pop();
201
+ }
202
+
203
+ if (tokens.length !== 3) {
204
+ return undefined;
205
+ }
206
+
207
+ const percent = (max: number) => (value: string) => parsePercent(value, max);
208
+
209
+ const parsers = {
210
+ rgb: [percent(255), percent(255), percent(255)],
211
+ hsl: [parseAngle, percent(100), percent(100)],
212
+ hwb: [parseAngle, percent(100), percent(100)],
213
+ lab: [percent(100), percent(125), percent(125)],
214
+ lch: [percent(100), percent(150), parseAngle],
215
+ oklch: [percent(1), percent(0.4), parseAngle],
216
+ oklab: [percent(1), percent(0.4), percent(0.4)],
217
+ };
218
+
219
+ try {
220
+ const parts = parsers[type].map((parseChannel, index) =>
221
+ parseChannel(tokens[index])
222
+ );
223
+
224
+ const a = alphaStr === undefined ? 1 : parseAlpha(alphaStr);
225
+
226
+ parts.push(a);
227
+
228
+ return { type, parts };
229
+ } catch {
230
+ return undefined;
231
+ }
232
+ }
233
+
234
+ function stringify(color: ParsedColor): string {
235
+ const [p0, p1, p2, rawA] = color.parts;
236
+ const a = trim(clamp(rawA, 0, 1));
237
+
238
+ // Legacy CSS L3: name(v1, v2, v3) / namea(v1, v2, v3, alpha)
239
+ const legacy = (
240
+ name: string,
241
+ v1: string | number,
242
+ v2: string | number,
243
+ v3: string | number
244
+ ) =>
245
+ a < 1
246
+ ? `${name}a(${v1}, ${v2}, ${v3}, ${a})`
247
+ : `${name}(${v1}, ${v2}, ${v3})`;
248
+
249
+ // Modern CSS L4: name(v1 v2 v3) / name(v1 v2 v3 / alpha)
250
+ const modern = (
251
+ name: string,
252
+ v1: string | number,
253
+ v2: string | number,
254
+ v3: string | number
255
+ ) =>
256
+ a < 1 ? `${name}(${v1} ${v2} ${v3} / ${a})` : `${name}(${v1} ${v2} ${v3})`;
257
+
258
+ switch (color.type) {
259
+ case 'rgb':
260
+ return legacy(
261
+ 'rgb',
262
+ clamp(Math.round(p0), 0, 255),
263
+ clamp(Math.round(p1), 0, 255),
264
+ clamp(Math.round(p2), 0, 255)
265
+ );
266
+ case 'hsl':
267
+ return legacy(
268
+ 'hsl',
269
+ trim(p0),
270
+ `${trim(clamp(p1, 0, 100))}%`,
271
+ `${trim(clamp(p2, 0, 100))}%`
272
+ );
273
+ case 'hwb':
274
+ return modern(
275
+ 'hwb',
276
+ trim(p0),
277
+ `${trim(clamp(p1, 0, 100))}%`,
278
+ `${trim(clamp(p2, 0, 100))}%`
279
+ );
280
+ case 'lab':
281
+ return modern(
282
+ 'lab',
283
+ trim(clamp(p0, 0, 100)),
284
+ trim(clamp(p1, -125, 125)),
285
+ trim(clamp(p2, -125, 125))
286
+ );
287
+ case 'oklab':
288
+ return modern(
289
+ 'oklab',
290
+ trim(clamp(p0, 0, 1)),
291
+ trim(clamp(p1, -0.4, 0.4)),
292
+ trim(clamp(p2, -0.4, 0.4))
293
+ );
294
+ case 'lch':
295
+ return modern(
296
+ 'lch',
297
+ trim(clamp(p0, 0, 100)),
298
+ trim(clamp(p1, 0, 150)),
299
+ trim(p2)
300
+ );
301
+ case 'oklch':
302
+ return modern(
303
+ 'oklch',
304
+ trim(clamp(p0, 0, 1)),
305
+ trim(clamp(p1, 0, 0.4)),
306
+ trim(p2)
307
+ );
308
+ default: {
309
+ const _exhaustiveCheck: never = color.type;
310
+
311
+ return _exhaustiveCheck;
312
+ }
313
+ }
314
+ }
315
+
316
+ function darken(color: ParsedColor, amount: number): ColorType {
317
+ const factor = 1 - amount;
318
+ const parts = color.parts.slice();
319
+
320
+ switch (color.type) {
321
+ case 'lab':
322
+ case 'lch':
323
+ case 'oklab':
324
+ case 'oklch':
325
+ parts[0] *= factor;
326
+
327
+ return build({ type: color.type, parts });
328
+ case 'hsl':
329
+ parts[2] *= factor;
330
+
331
+ return build({ type: 'hsl', parts });
332
+ case 'rgb':
333
+ case 'hwb': {
334
+ const [r, g, b] =
335
+ color.type === 'hwb' ? hwbToRgb(parts[0], parts[1], parts[2]) : parts;
336
+
337
+ const [h, s, l] = rgbToHsl(r, g, b);
338
+
339
+ return build({ type: 'hsl', parts: [h, s, l * factor, parts[3]] });
340
+ }
341
+ default: {
342
+ const _exhaustiveCheck: never = color.type;
343
+
344
+ return _exhaustiveCheck;
345
+ }
346
+ }
347
+ }
348
+
349
+ // YIQ luma midpoint (0-255)
350
+ // https://en.wikipedia.org/wiki/YIQ
351
+ const YIQ_LIGHT_THRESHOLD = 128;
352
+
353
+ // Rec. 601 luma coefficients (sum to 1.0)
354
+ const YIQ_WEIGHT_R = 0.299;
355
+ const YIQ_WEIGHT_G = 0.587;
356
+ const YIQ_WEIGHT_B = 0.114;
357
+
358
+ function isLight(color: ParsedColor): boolean {
359
+ let rgb: [number, number, number];
360
+
361
+ switch (color.type) {
362
+ case 'lab':
363
+ case 'lch':
364
+ return color.parts[0] > 50;
365
+ case 'oklab':
366
+ case 'oklch':
367
+ return color.parts[0] > 0.5;
368
+ case 'rgb':
369
+ rgb = [color.parts[0], color.parts[1], color.parts[2]];
370
+ break;
371
+ case 'hsl':
372
+ rgb = hslToRgb(color.parts[0], color.parts[1], color.parts[2]);
373
+ break;
374
+ case 'hwb':
375
+ rgb = hwbToRgb(color.parts[0], color.parts[1], color.parts[2]);
376
+ break;
377
+ default: {
378
+ const _exhaustiveCheck: never = color.type;
379
+
380
+ return _exhaustiveCheck;
381
+ }
382
+ }
383
+
384
+ const yiq =
385
+ YIQ_WEIGHT_R * rgb[0] + YIQ_WEIGHT_G * rgb[1] + YIQ_WEIGHT_B * rgb[2];
386
+
387
+ return yiq > YIQ_LIGHT_THRESHOLD;
388
+ }
389
+
390
+ function trim(n: number): number {
391
+ return Number(n.toFixed(3));
392
+ }
393
+
394
+ function clamp(n: number, min: number, max: number): number {
395
+ return n < min ? min : n > max ? max : n;
396
+ }
397
+
398
+ function parseNumber(s: string): number {
399
+ const n = Number(s);
400
+
401
+ if (s === '' || Number.isNaN(n)) {
402
+ throw new Error(`Invalid number: ${s}`);
403
+ }
404
+
405
+ return n;
406
+ }
407
+
408
+ function parsePercent(s: string, max: number): number {
409
+ if (s.endsWith('%')) {
410
+ return (parseNumber(s.slice(0, -1)) / 100) * max;
411
+ }
412
+
413
+ return parseNumber(s);
414
+ }
415
+
416
+ function parseAngle(s: string): number {
417
+ let degrees: number;
418
+
419
+ if (s.endsWith('grad')) {
420
+ degrees = parseNumber(s.slice(0, -4)) * 0.9;
421
+ } else if (s.endsWith('turn')) {
422
+ degrees = parseNumber(s.slice(0, -4)) * 360;
423
+ } else if (s.endsWith('rad')) {
424
+ degrees = (parseNumber(s.slice(0, -3)) * 180) / Math.PI;
425
+ } else if (s.endsWith('deg')) {
426
+ degrees = parseNumber(s.slice(0, -3));
427
+ } else {
428
+ degrees = parseNumber(s);
429
+ }
430
+
431
+ return ((degrees % 360) + 360) % 360;
432
+ }
433
+
434
+ function parseAlpha(s: string): number {
435
+ return s.endsWith('%') ? parseNumber(s.slice(0, -1)) / 100 : parseNumber(s);
436
+ }
437
+
438
+ function rgbToHsl(r: number, g: number, b: number): [number, number, number] {
439
+ r /= 255;
440
+ g /= 255;
441
+ b /= 255;
442
+
443
+ const max = Math.max(r, g, b);
444
+ const min = Math.min(r, g, b);
445
+ const l = (max + min) / 2;
446
+
447
+ if (max === min) {
448
+ return [0, 0, l * 100];
449
+ }
450
+
451
+ const d = max - min;
452
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
453
+ let h: number;
454
+
455
+ if (max === r) {
456
+ h = (g - b) / d + (g < b ? 6 : 0);
457
+ } else if (max === g) {
458
+ h = (b - r) / d + 2;
459
+ } else {
460
+ h = (r - g) / d + 4;
461
+ }
462
+
463
+ return [h * 60, s * 100, l * 100];
464
+ }
465
+
466
+ function hslToRgb(h: number, s: number, l: number): [number, number, number] {
467
+ s /= 100;
468
+ l /= 100;
469
+
470
+ const chroma = (1 - Math.abs(2 * l - 1)) * s;
471
+ const huePrime = (((h % 360) + 360) % 360) / 60;
472
+ const secondary = chroma * (1 - Math.abs((huePrime % 2) - 1));
473
+ const lightnessOffset = l - chroma / 2;
474
+
475
+ let redPrime: number;
476
+ let greenPrime: number;
477
+ let bluePrime: number;
478
+
479
+ if (huePrime < 1) {
480
+ [redPrime, greenPrime, bluePrime] = [chroma, secondary, 0];
481
+ } else if (huePrime < 2) {
482
+ [redPrime, greenPrime, bluePrime] = [secondary, chroma, 0];
483
+ } else if (huePrime < 3) {
484
+ [redPrime, greenPrime, bluePrime] = [0, chroma, secondary];
485
+ } else if (huePrime < 4) {
486
+ [redPrime, greenPrime, bluePrime] = [0, secondary, chroma];
487
+ } else if (huePrime < 5) {
488
+ [redPrime, greenPrime, bluePrime] = [secondary, 0, chroma];
489
+ } else {
490
+ [redPrime, greenPrime, bluePrime] = [chroma, 0, secondary];
491
+ }
492
+
493
+ return [
494
+ (redPrime + lightnessOffset) * 255,
495
+ (greenPrime + lightnessOffset) * 255,
496
+ (bluePrime + lightnessOffset) * 255,
497
+ ];
498
+ }
499
+
500
+ function hwbToRgb(h: number, w: number, b: number): [number, number, number] {
501
+ const whiteRatio = w / 100;
502
+ const blackRatio = b / 100;
503
+
504
+ if (whiteRatio + blackRatio >= 1) {
505
+ const gray = (whiteRatio / (whiteRatio + blackRatio)) * 255;
506
+
507
+ return [gray, gray, gray];
508
+ }
509
+
510
+ const [pureRed, pureGreen, pureBlue] = hslToRgb(h, 100, 50);
511
+ const factor = 1 - whiteRatio - blackRatio;
512
+
513
+ return [
514
+ pureRed * factor + whiteRatio * 255,
515
+ pureGreen * factor + whiteRatio * 255,
516
+ pureBlue * factor + whiteRatio * 255,
517
+ ];
518
+ }
519
+
520
+ const COLOR_TYPES = [
521
+ 'rgb',
522
+ 'hsl',
523
+ 'hwb',
524
+ 'lab',
525
+ 'lch',
526
+ 'oklch',
527
+ 'oklab',
528
+ ] as const;
529
+
530
+ const NAMED_COLORS: Record<string, [number, number, number]> = {
531
+ aliceblue: [240, 248, 255],
532
+ antiquewhite: [250, 235, 215],
533
+ aqua: [0, 255, 255],
534
+ aquamarine: [127, 255, 212],
535
+ azure: [240, 255, 255],
536
+ beige: [245, 245, 220],
537
+ bisque: [255, 228, 196],
538
+ black: [0, 0, 0],
539
+ blanchedalmond: [255, 235, 205],
540
+ blue: [0, 0, 255],
541
+ blueviolet: [138, 43, 226],
542
+ brown: [165, 42, 42],
543
+ burntsienna: [234, 126, 93],
544
+ burlywood: [222, 184, 135],
545
+ cadetblue: [95, 158, 160],
546
+ chartreuse: [127, 255, 0],
547
+ chocolate: [210, 105, 30],
548
+ coral: [255, 127, 80],
549
+ cornflowerblue: [100, 149, 237],
550
+ cornsilk: [255, 248, 220],
551
+ crimson: [220, 20, 60],
552
+ cyan: [0, 255, 255],
553
+ darkblue: [0, 0, 139],
554
+ darkcyan: [0, 139, 139],
555
+ darkgoldenrod: [184, 134, 11],
556
+ darkgray: [169, 169, 169],
557
+ darkgreen: [0, 100, 0],
558
+ darkgrey: [169, 169, 169],
559
+ darkkhaki: [189, 183, 107],
560
+ darkmagenta: [139, 0, 139],
561
+ darkolivegreen: [85, 107, 47],
562
+ darkorange: [255, 140, 0],
563
+ darkorchid: [153, 50, 204],
564
+ darkred: [139, 0, 0],
565
+ darksalmon: [233, 150, 122],
566
+ darkseagreen: [143, 188, 143],
567
+ darkslateblue: [72, 61, 139],
568
+ darkslategray: [47, 79, 79],
569
+ darkslategrey: [47, 79, 79],
570
+ darkturquoise: [0, 206, 209],
571
+ darkviolet: [148, 0, 211],
572
+ deeppink: [255, 20, 147],
573
+ deepskyblue: [0, 191, 255],
574
+ dimgray: [105, 105, 105],
575
+ dimgrey: [105, 105, 105],
576
+ dodgerblue: [30, 144, 255],
577
+ firebrick: [178, 34, 34],
578
+ floralwhite: [255, 250, 240],
579
+ forestgreen: [34, 139, 34],
580
+ fuchsia: [255, 0, 255],
581
+ gainsboro: [220, 220, 220],
582
+ ghostwhite: [248, 248, 255],
583
+ gold: [255, 215, 0],
584
+ goldenrod: [218, 165, 32],
585
+ gray: [128, 128, 128],
586
+ green: [0, 128, 0],
587
+ greenyellow: [173, 255, 47],
588
+ grey: [128, 128, 128],
589
+ honeydew: [240, 255, 240],
590
+ hotpink: [255, 105, 180],
591
+ indianred: [205, 92, 92],
592
+ indigo: [75, 0, 130],
593
+ ivory: [255, 255, 240],
594
+ khaki: [240, 230, 140],
595
+ lavender: [230, 230, 250],
596
+ lavenderblush: [255, 240, 245],
597
+ lawngreen: [124, 252, 0],
598
+ lemonchiffon: [255, 250, 205],
599
+ lightblue: [173, 216, 230],
600
+ lightcoral: [240, 128, 128],
601
+ lightcyan: [224, 255, 255],
602
+ lightgoldenrodyellow: [250, 250, 210],
603
+ lightgray: [211, 211, 211],
604
+ lightgreen: [144, 238, 144],
605
+ lightgrey: [211, 211, 211],
606
+ lightpink: [255, 182, 193],
607
+ lightsalmon: [255, 160, 122],
608
+ lightseagreen: [32, 178, 170],
609
+ lightskyblue: [135, 206, 250],
610
+ lightslategray: [119, 136, 153],
611
+ lightslategrey: [119, 136, 153],
612
+ lightsteelblue: [176, 196, 222],
613
+ lightyellow: [255, 255, 224],
614
+ lime: [0, 255, 0],
615
+ limegreen: [50, 205, 50],
616
+ linen: [250, 240, 230],
617
+ magenta: [255, 0, 255],
618
+ maroon: [128, 0, 0],
619
+ mediumaquamarine: [102, 205, 170],
620
+ mediumblue: [0, 0, 205],
621
+ mediumorchid: [186, 85, 211],
622
+ mediumpurple: [147, 112, 219],
623
+ mediumseagreen: [60, 179, 113],
624
+ mediumslateblue: [123, 104, 238],
625
+ mediumspringgreen: [0, 250, 154],
626
+ mediumturquoise: [72, 209, 204],
627
+ mediumvioletred: [199, 21, 133],
628
+ midnightblue: [25, 25, 112],
629
+ mintcream: [245, 255, 250],
630
+ mistyrose: [255, 228, 225],
631
+ moccasin: [255, 228, 181],
632
+ navajowhite: [255, 222, 173],
633
+ navy: [0, 0, 128],
634
+ oldlace: [253, 245, 230],
635
+ olive: [128, 128, 0],
636
+ olivedrab: [107, 142, 35],
637
+ orange: [255, 165, 0],
638
+ orangered: [255, 69, 0],
639
+ orchid: [218, 112, 214],
640
+ palegoldenrod: [238, 232, 170],
641
+ palegreen: [152, 251, 152],
642
+ paleturquoise: [175, 238, 238],
643
+ palevioletred: [219, 112, 147],
644
+ papayawhip: [255, 239, 213],
645
+ peachpuff: [255, 218, 185],
646
+ peru: [205, 133, 63],
647
+ pink: [255, 192, 203],
648
+ plum: [221, 160, 221],
649
+ powderblue: [176, 224, 230],
650
+ purple: [128, 0, 128],
651
+ rebeccapurple: [102, 51, 153],
652
+ red: [255, 0, 0],
653
+ rosybrown: [188, 143, 143],
654
+ royalblue: [65, 105, 225],
655
+ saddlebrown: [139, 69, 19],
656
+ salmon: [250, 128, 114],
657
+ sandybrown: [244, 164, 96],
658
+ seagreen: [46, 139, 87],
659
+ seashell: [255, 245, 238],
660
+ sienna: [160, 82, 45],
661
+ silver: [192, 192, 192],
662
+ skyblue: [135, 206, 235],
663
+ slateblue: [106, 90, 205],
664
+ slategray: [112, 128, 144],
665
+ slategrey: [112, 128, 144],
666
+ snow: [255, 250, 250],
667
+ springgreen: [0, 255, 127],
668
+ steelblue: [70, 130, 180],
669
+ tan: [210, 180, 140],
670
+ teal: [0, 128, 128],
671
+ thistle: [216, 191, 216],
672
+ tomato: [255, 99, 71],
673
+ turquoise: [64, 224, 208],
674
+ violet: [238, 130, 238],
675
+ wheat: [245, 222, 179],
676
+ white: [255, 255, 255],
677
+ whitesmoke: [245, 245, 245],
678
+ yellow: [255, 255, 0],
679
+ yellowgreen: [154, 205, 50],
680
+ };
681
+
682
+ const ANDROID_COLOR_MAP: Record<string, string> = {
683
+ system_background_dark: 'system_on_background_dark',
684
+ system_background_light: 'system_on_background_light',
685
+ system_error_container_dark: 'system_on_error_container_dark',
686
+ system_error_container_light: 'system_on_error_container_light',
687
+ system_error_dark: 'system_on_error_dark',
688
+ system_error_light: 'system_on_error_light',
689
+ system_primary_container_dark: 'system_on_primary_container_dark',
690
+ system_primary_container_light: 'system_on_primary_container_light',
691
+ system_primary_dark: 'system_on_primary_dark',
692
+ system_primary_fixed: 'system_on_primary_fixed',
693
+ system_primary_light: 'system_on_primary_light',
694
+ system_secondary_container_dark: 'system_on_secondary_container_dark',
695
+ system_secondary_container_light: 'system_on_secondary_container_light',
696
+ system_secondary_dark: 'system_on_secondary_dark',
697
+ system_secondary_fixed: 'system_on_secondary_fixed',
698
+ system_secondary_light: 'system_on_secondary_light',
699
+ system_surface_dark: 'system_on_surface_dark',
700
+ system_surface_disabled: 'system_on_surface_disabled',
701
+ system_surface_light: 'system_on_surface_light',
702
+ system_surface_variant_dark: 'system_on_surface_variant_dark',
703
+ system_surface_variant_light: 'system_on_surface_variant_light',
704
+ };
705
+
706
+ const IOS_COLOR_MAP: Record<string, string> = {
707
+ systemBackground: 'label',
708
+ secondarySystemBackground: 'label',
709
+ tertiarySystemBackground: 'label',
710
+ systemGroupedBackground: 'label',
711
+ secondarySystemGroupedBackground: 'label',
712
+ tertiarySystemGroupedBackground: 'label',
713
+
714
+ systemFill: 'label',
715
+ secondarySystemFill: 'label',
716
+ tertiarySystemFill: 'label',
717
+ quaternarySystemFill: 'label',
718
+
719
+ systemRed: 'white',
720
+ systemGreen: 'white',
721
+ systemBlue: 'white',
722
+ systemIndigo: 'white',
723
+ systemPurple: 'white',
724
+ systemBrown: 'white',
725
+
726
+ systemOrange: 'black',
727
+ systemYellow: 'black',
728
+ systemMint: 'black',
729
+ systemTeal: 'black',
730
+ systemCyan: 'black',
731
+ systemPink: 'black',
732
+
733
+ systemGray: 'label',
734
+ systemGray2: 'label',
735
+ systemGray3: 'label',
736
+ systemGray4: 'label',
737
+ systemGray5: 'label',
738
+ systemGray6: 'label',
739
+ };