@hero-design/rn 8.108.0 → 8.108.2
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/.turbo/turbo-build.log +3 -3
- package/CHANGELOG.md +14 -0
- package/es/index.js +187 -120
- package/lib/index.js +185 -118
- package/package.json +1 -1
- package/src/components/FAB/ActionGroup/__tests__/__snapshots__/index.spec.tsx.snap +0 -1
- package/src/components/FAB/ActionGroup/index.tsx +5 -12
- package/src/components/MapPin/__tests__/__snapshots__/Focussed.spec.tsx.snap +1 -1
- package/src/components/Slider/RangeSlider.tsx +14 -8
- package/src/components/Slider/SingleSlider.tsx +6 -4
- package/src/components/Slider/StyledRangeSlider.tsx +2 -2
- package/src/components/Slider/__tests__/RangeSlider.spec.tsx +6 -4
- package/src/components/Slider/__tests__/__snapshots__/RangeSlider.spec.tsx.snap +6 -4
- package/src/components/Slider/__tests__/__snapshots__/SingleSlider.spec.tsx.snap +3 -3
- package/src/components/Slider/constants.ts +3 -0
- package/src/theme/__tests__/__snapshots__/index.spec.ts.snap +6 -6
- package/src/theme/components/slider.ts +25 -6
- package/src/utils/__tests__/helpers.spec.ts +213 -0
- package/src/utils/helpers.ts +113 -5
- package/stats/8.108.0/rn-stats.html +3 -1
- package/stats/8.108.1/rn-stats.html +4844 -0
- package/stats/8.108.2/rn-stats.html +4842 -0
- package/types/components/Slider/constants.d.ts +3 -0
- package/types/components/TextInput/index.d.ts +1 -1
- package/types/theme/components/slider.d.ts +6 -6
- package/types/utils/helpers.d.ts +1 -0
|
@@ -1,13 +1,32 @@
|
|
|
1
|
+
import { UNSELECTED_TRACK_OPACITY } from '../../components/Slider/constants';
|
|
2
|
+
import { dimHex, hexToRgba } from '../../utils/helpers';
|
|
1
3
|
import type { GlobalTheme } from '../global';
|
|
2
4
|
|
|
3
5
|
const getSliderTheme = (theme: GlobalTheme) => {
|
|
4
6
|
const colors = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
// Unselected track colors
|
|
8
|
+
unselectedTrack: hexToRgba(
|
|
9
|
+
theme.colors.overlayGlobalSurface,
|
|
10
|
+
UNSELECTED_TRACK_OPACITY
|
|
11
|
+
),
|
|
12
|
+
disabledUnselectedTrack: dimHex(
|
|
13
|
+
theme.colors.overlayGlobalSurface,
|
|
14
|
+
theme.colors.defaultGlobalSurface
|
|
15
|
+
),
|
|
16
|
+
|
|
17
|
+
// Selected track colors
|
|
18
|
+
selectedTrack: theme.colors.primary,
|
|
19
|
+
disabledSelectedTrack: dimHex(
|
|
20
|
+
theme.colors.primary,
|
|
21
|
+
theme.colors.defaultGlobalSurface
|
|
22
|
+
),
|
|
23
|
+
|
|
24
|
+
// Thumb/marker colors
|
|
25
|
+
thumb: theme.colors.primary,
|
|
26
|
+
disabledThumb: dimHex(
|
|
27
|
+
theme.colors.primary,
|
|
28
|
+
theme.colors.defaultGlobalSurface
|
|
29
|
+
),
|
|
11
30
|
};
|
|
12
31
|
|
|
13
32
|
const shadows = {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deepCompareValue,
|
|
3
|
+
dimHex,
|
|
4
|
+
hexToRgba,
|
|
3
5
|
omit,
|
|
4
6
|
pick,
|
|
5
7
|
transformKebabCaseToCamelCase,
|
|
@@ -114,3 +116,214 @@ describe('transformKebabCaseToCamelCase', () => {
|
|
|
114
116
|
}
|
|
115
117
|
);
|
|
116
118
|
});
|
|
119
|
+
|
|
120
|
+
describe('hexToRgba', () => {
|
|
121
|
+
it.each`
|
|
122
|
+
hex | alpha | expected
|
|
123
|
+
${'#FF0000'} | ${1} | ${'rgba(255,0,0,1)'}
|
|
124
|
+
${'#00FF00'} | ${0.5} | ${'rgba(0,255,0,0.5)'}
|
|
125
|
+
${'#0000FF'} | ${0} | ${'rgba(0,0,255,0)'}
|
|
126
|
+
${'#FFFFFF'} | ${0.8} | ${'rgba(255,255,255,0.8)'}
|
|
127
|
+
${'#000000'} | ${0.3} | ${'rgba(0,0,0,0.3)'}
|
|
128
|
+
${'#123456'} | ${0.7} | ${'rgba(18,52,86,0.7)'}
|
|
129
|
+
${'#ABCDEF'} | ${0.2} | ${'rgba(171,205,239,0.2)'}
|
|
130
|
+
${'#FF0000'} | ${0.25} | ${'rgba(255,0,0,0.25)'}
|
|
131
|
+
`(
|
|
132
|
+
'should convert hex "$hex" with alpha $alpha to "$expected"',
|
|
133
|
+
({ hex, alpha, expected }) => {
|
|
134
|
+
expect(hexToRgba(hex, alpha)).toBe(expected);
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
it.each`
|
|
139
|
+
hex | alpha | expected
|
|
140
|
+
${'FF0000'} | ${1} | ${'rgba(255,0,0,1)'}
|
|
141
|
+
${'00FF00'} | ${0.5} | ${'rgba(0,255,0,0.5)'}
|
|
142
|
+
${'0000FF'} | ${0} | ${'rgba(0,0,255,0)'}
|
|
143
|
+
${'FFFFFF'} | ${0.8} | ${'rgba(255,255,255,0.8)'}
|
|
144
|
+
${'000000'} | ${0.3} | ${'rgba(0,0,0,0.3)'}
|
|
145
|
+
${'123456'} | ${0.7} | ${'rgba(18,52,86,0.7)'}
|
|
146
|
+
${'ABCDEF'} | ${0.2} | ${'rgba(171,205,239,0.2)'}
|
|
147
|
+
`(
|
|
148
|
+
'should convert hex without # "$hex" with alpha $alpha to "$expected"',
|
|
149
|
+
({ hex, alpha, expected }) => {
|
|
150
|
+
expect(hexToRgba(hex, alpha)).toBe(expected);
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
it('should handle lowercase hex values', () => {
|
|
155
|
+
expect(hexToRgba('#ff0000', 1)).toBe('rgba(255,0,0,1)');
|
|
156
|
+
expect(hexToRgba('#abcdef', 0.5)).toBe('rgba(171,205,239,0.5)');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should handle mixed case hex values', () => {
|
|
160
|
+
expect(hexToRgba('#Ff0000', 1)).toBe('rgba(255,0,0,1)');
|
|
161
|
+
expect(hexToRgba('#AbCdEf', 0.5)).toBe('rgba(171,205,239,0.5)');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should handle edge case alpha values', () => {
|
|
165
|
+
expect(hexToRgba('#000000', 0)).toBe('rgba(0,0,0,0)');
|
|
166
|
+
expect(hexToRgba('#FFFFFF', 1)).toBe('rgba(255,255,255,1)');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should handle decimal alpha values', () => {
|
|
170
|
+
expect(hexToRgba('#FF0000', 0.123)).toBe('rgba(255,0,0,0.123)');
|
|
171
|
+
expect(hexToRgba('#00FF00', 0.999)).toBe('rgba(0,255,0,0.999)');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('3-character hex codes', () => {
|
|
175
|
+
it.each`
|
|
176
|
+
hex | alpha | expected
|
|
177
|
+
${'#fff'} | ${1} | ${'rgba(255,255,255,1)'}
|
|
178
|
+
${'#000'} | ${0.5} | ${'rgba(0,0,0,0.5)'}
|
|
179
|
+
${'#f00'} | ${0.8} | ${'rgba(255,0,0,0.8)'}
|
|
180
|
+
${'#0f0'} | ${0.3} | ${'rgba(0,255,0,0.3)'}
|
|
181
|
+
${'#00f'} | ${0.7} | ${'rgba(0,0,255,0.7)'}
|
|
182
|
+
${'#abc'} | ${0.2} | ${'rgba(170,187,204,0.2)'}
|
|
183
|
+
${'#123'} | ${0.25} | ${'rgba(17,34,51,0.25)'}
|
|
184
|
+
`(
|
|
185
|
+
'should convert 3-char hex "$hex" with alpha $alpha to "$expected"',
|
|
186
|
+
({ hex, alpha, expected }) => {
|
|
187
|
+
expect(hexToRgba(hex, alpha)).toBe(expected);
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
it.each`
|
|
192
|
+
hex | alpha | expected
|
|
193
|
+
${'fff'} | ${1} | ${'rgba(255,255,255,1)'}
|
|
194
|
+
${'000'} | ${0.5} | ${'rgba(0,0,0,0.5)'}
|
|
195
|
+
${'f00'} | ${0.8} | ${'rgba(255,0,0,0.8)'}
|
|
196
|
+
${'0f0'} | ${0.3} | ${'rgba(0,255,0,0.3)'}
|
|
197
|
+
${'00f'} | ${0.7} | ${'rgba(0,0,255,0.7)'}
|
|
198
|
+
${'abc'} | ${0.2} | ${'rgba(170,187,204,0.2)'}
|
|
199
|
+
`(
|
|
200
|
+
'should convert 3-char hex without # "$hex" with alpha $alpha to "$expected"',
|
|
201
|
+
({ hex, alpha, expected }) => {
|
|
202
|
+
expect(hexToRgba(hex, alpha)).toBe(expected);
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
it('should handle lowercase 3-char hex values', () => {
|
|
207
|
+
expect(hexToRgba('#fff', 1)).toBe('rgba(255,255,255,1)');
|
|
208
|
+
expect(hexToRgba('#abc', 0.5)).toBe('rgba(170,187,204,0.5)');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should handle mixed case 3-char hex values', () => {
|
|
212
|
+
expect(hexToRgba('#Ff0', 1)).toBe('rgba(255,255,0,1)');
|
|
213
|
+
expect(hexToRgba('#AbC', 0.5)).toBe('rgba(170,187,204,0.5)');
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe('validation', () => {
|
|
218
|
+
it('should throw error for invalid input types', () => {
|
|
219
|
+
expect(() => hexToRgba(123 as any, 0.5)).toThrow(
|
|
220
|
+
'hexToRgba: hex must be a string and alpha must be a number'
|
|
221
|
+
);
|
|
222
|
+
expect(() => hexToRgba('#fff', '0.5' as any)).toThrow(
|
|
223
|
+
'hexToRgba: hex must be a string and alpha must be a number'
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should throw error for alpha values outside 0-1 range', () => {
|
|
228
|
+
expect(() => hexToRgba('#fff', -0.1)).toThrow(
|
|
229
|
+
'hexToRgba: alpha must be between 0 and 1'
|
|
230
|
+
);
|
|
231
|
+
expect(() => hexToRgba('#fff', 1.1)).toThrow(
|
|
232
|
+
'hexToRgba: alpha must be between 0 and 1'
|
|
233
|
+
);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should throw error for invalid hex formats', () => {
|
|
237
|
+
expect(() => hexToRgba('invalid', 0.5)).toThrow(
|
|
238
|
+
'hexToRgba: hex must be a valid 3 or 6 character hex color'
|
|
239
|
+
);
|
|
240
|
+
expect(() => hexToRgba('#gggggg', 0.5)).toThrow(
|
|
241
|
+
'hexToRgba: hex must be a valid 3 or 6 character hex color'
|
|
242
|
+
);
|
|
243
|
+
expect(() => hexToRgba('#ff', 0.5)).toThrow(
|
|
244
|
+
'hexToRgba: hex must be a valid 3 or 6 character hex color'
|
|
245
|
+
);
|
|
246
|
+
expect(() => hexToRgba('#ffffff00', 0.5)).toThrow(
|
|
247
|
+
'hexToRgba: hex must be a valid 3 or 6 character hex color'
|
|
248
|
+
);
|
|
249
|
+
expect(() => hexToRgba('', 0.5)).toThrow(
|
|
250
|
+
'hexToRgba: hex must be a valid 3 or 6 character hex color'
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('should accept valid alpha boundary values', () => {
|
|
255
|
+
expect(() => hexToRgba('#fff', 0)).not.toThrow();
|
|
256
|
+
expect(() => hexToRgba('#fff', 1)).not.toThrow();
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('dimHex', () => {
|
|
262
|
+
it.each`
|
|
263
|
+
hex | surface | amount | expected
|
|
264
|
+
${'#FF0000'} | ${'#FFFFFF'} | ${0.5} | ${'#ff8080'}
|
|
265
|
+
${'#000000'} | ${'#FFFFFF'} | ${0.5} | ${'#808080'}
|
|
266
|
+
${'#FF0000'} | ${'#FFFFFF'} | ${0} | ${'#ff0000'}
|
|
267
|
+
${'#FF0000'} | ${'#FFFFFF'} | ${1} | ${'#ffffff'}
|
|
268
|
+
${'FF0000'} | ${'FFFFFF'} | ${0.5} | ${'#ff8080'}
|
|
269
|
+
${'#fff'} | ${'#000'} | ${0.5} | ${'#808080'}
|
|
270
|
+
${'fff'} | ${'000'} | ${0.5} | ${'#808080'}
|
|
271
|
+
`(
|
|
272
|
+
'should dim hex "$hex" with surface "$surface" by amount $amount to "$expected"',
|
|
273
|
+
({ hex, surface, amount, expected }) => {
|
|
274
|
+
expect(dimHex(hex, surface, amount)).toBe(expected);
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
it('should use default parameters', () => {
|
|
279
|
+
expect(dimHex('#000000', '#ffffff')).toBe('#999999');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should throw error for invalid input types', () => {
|
|
283
|
+
// @ts-expect-error: Invalid param type
|
|
284
|
+
expect(() => dimHex(123, '#fff', 0.5)).toThrow(
|
|
285
|
+
'dimHex: hex and surface must be strings and amount must be a number'
|
|
286
|
+
);
|
|
287
|
+
// @ts-expect-error: Invalid param type
|
|
288
|
+
expect(() => dimHex('#fff', 123, 0.5)).toThrow(
|
|
289
|
+
'dimHex: hex and surface must be strings and amount must be a number'
|
|
290
|
+
);
|
|
291
|
+
// @ts-expect-error: Invalid param type
|
|
292
|
+
expect(() => dimHex('#fff', '#fff', '0.5')).toThrow(
|
|
293
|
+
'dimHex: hex and surface must be strings and amount must be a number'
|
|
294
|
+
);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should throw error for amount values outside 0-1 range', () => {
|
|
298
|
+
expect(() => dimHex('#fff', '#fff', -0.1)).toThrow(
|
|
299
|
+
'dimHex: amount must be between 0 and 1'
|
|
300
|
+
);
|
|
301
|
+
expect(() => dimHex('#fff', '#fff', 1.1)).toThrow(
|
|
302
|
+
'dimHex: amount must be between 0 and 1'
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('should throw error for invalid hex formats', () => {
|
|
307
|
+
expect(() => dimHex('invalid', '#fff', 0.5)).toThrow(
|
|
308
|
+
'dimHex: hex must be a valid 3 or 6 character hex color'
|
|
309
|
+
);
|
|
310
|
+
expect(() => dimHex('#gggggg', '#fff', 0.5)).toThrow(
|
|
311
|
+
'dimHex: hex must be a valid 3 or 6 character hex color'
|
|
312
|
+
);
|
|
313
|
+
expect(() => dimHex('#ff', '#fff', 0.5)).toThrow(
|
|
314
|
+
'dimHex: hex must be a valid 3 or 6 character hex color'
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should throw error for invalid surface formats', () => {
|
|
319
|
+
expect(() => dimHex('#fff', 'invalid', 0.5)).toThrow(
|
|
320
|
+
'dimHex: surface must be a valid 3 or 6 character hex color'
|
|
321
|
+
);
|
|
322
|
+
expect(() => dimHex('#fff', '#gggggg', 0.5)).toThrow(
|
|
323
|
+
'dimHex: surface must be a valid 3 or 6 character hex color'
|
|
324
|
+
);
|
|
325
|
+
expect(() => dimHex('#fff', '#ff', 0.5)).toThrow(
|
|
326
|
+
'dimHex: surface must be a valid 3 or 6 character hex color'
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
});
|
package/src/utils/helpers.ts
CHANGED
|
@@ -31,14 +31,122 @@ export function omit<O, T extends keyof O>(keys: T[], obj: O): Omit<O, T> {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function hexToRgba(hex: string, a: number) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
// Validate inputs
|
|
35
|
+
if (typeof hex !== 'string' || typeof a !== 'number') {
|
|
36
|
+
throw new Error(
|
|
37
|
+
'hexToRgba: hex must be a string and alpha must be a number'
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (a < 0 || a > 1) {
|
|
42
|
+
throw new Error('hexToRgba: alpha must be between 0 and 1');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Remove # if present and validate hex format
|
|
46
|
+
const cleanHex = hex.replace(/^#/, '');
|
|
47
|
+
|
|
48
|
+
if (!/^[0-9A-Fa-f]{3}$|^[0-9A-Fa-f]{6}$/.test(cleanHex)) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
'hexToRgba: hex must be a valid 3 or 6 character hex color'
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Handle 3-character hex codes (e.g., #fff -> #ffffff)
|
|
55
|
+
const normalizedHex =
|
|
56
|
+
cleanHex.length === 3
|
|
57
|
+
? cleanHex
|
|
58
|
+
.split('')
|
|
59
|
+
.map((char) => char + char)
|
|
60
|
+
.join('')
|
|
61
|
+
: cleanHex;
|
|
62
|
+
|
|
63
|
+
// Parse hex values
|
|
64
|
+
const r = parseInt(normalizedHex.substring(0, 2), 16);
|
|
65
|
+
const g = parseInt(normalizedHex.substring(2, 4), 16);
|
|
66
|
+
const b = parseInt(normalizedHex.substring(4, 6), 16);
|
|
67
|
+
|
|
39
68
|
return `rgba(${r},${g},${b},${a})`;
|
|
40
69
|
}
|
|
41
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Dim a hex color by blending it with a surface color
|
|
73
|
+
* @param hex - The hex color to dim (3 or 6 characters, with or without #)
|
|
74
|
+
* @param surface - The surface color to blend with.
|
|
75
|
+
* @param amount - The amount of dimming (0 = original color, 1 = fully surface color)
|
|
76
|
+
* @returns The dimmed hex color
|
|
77
|
+
*/
|
|
78
|
+
const DEFAULT_DIM_AMOUNT = 0.6;
|
|
79
|
+
export function dimHex(
|
|
80
|
+
hex: string,
|
|
81
|
+
surface: string,
|
|
82
|
+
amount = DEFAULT_DIM_AMOUNT
|
|
83
|
+
): string {
|
|
84
|
+
// Validate inputs
|
|
85
|
+
if (
|
|
86
|
+
typeof hex !== 'string' ||
|
|
87
|
+
typeof surface !== 'string' ||
|
|
88
|
+
typeof amount !== 'number'
|
|
89
|
+
) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
'dimHex: hex and surface must be strings and amount must be a number'
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (amount < 0 || amount > 1) {
|
|
96
|
+
throw new Error('dimHex: amount must be between 0 and 1');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Remove # if present and validate hex format
|
|
100
|
+
const cleanHex = hex.replace(/^#/, '');
|
|
101
|
+
const cleanSurface = surface.replace(/^#/, '');
|
|
102
|
+
|
|
103
|
+
if (!/^[0-9A-Fa-f]{3}$|^[0-9A-Fa-f]{6}$/.test(cleanHex)) {
|
|
104
|
+
throw new Error('dimHex: hex must be a valid 3 or 6 character hex color');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!/^[0-9A-Fa-f]{3}$|^[0-9A-Fa-f]{6}$/.test(cleanSurface)) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
'dimHex: surface must be a valid 3 or 6 character hex color'
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Handle 3-character hex codes (e.g., #fff -> #ffffff)
|
|
114
|
+
const normalizedHex =
|
|
115
|
+
cleanHex.length === 3
|
|
116
|
+
? cleanHex
|
|
117
|
+
.split('')
|
|
118
|
+
.map((char) => char + char)
|
|
119
|
+
.join('')
|
|
120
|
+
: cleanHex;
|
|
121
|
+
|
|
122
|
+
const normalizedSurface =
|
|
123
|
+
cleanSurface.length === 3
|
|
124
|
+
? cleanSurface
|
|
125
|
+
.split('')
|
|
126
|
+
.map((char) => char + char)
|
|
127
|
+
.join('')
|
|
128
|
+
: cleanSurface;
|
|
129
|
+
|
|
130
|
+
// Parse hex values
|
|
131
|
+
const r = parseInt(normalizedHex.substring(0, 2), 16);
|
|
132
|
+
const g = parseInt(normalizedHex.substring(2, 4), 16);
|
|
133
|
+
const b = parseInt(normalizedHex.substring(4, 6), 16);
|
|
134
|
+
|
|
135
|
+
const sr = parseInt(normalizedSurface.substring(0, 2), 16);
|
|
136
|
+
const sg = parseInt(normalizedSurface.substring(2, 4), 16);
|
|
137
|
+
const sb = parseInt(normalizedSurface.substring(4, 6), 16);
|
|
138
|
+
|
|
139
|
+
// Linear interpolation between colors
|
|
140
|
+
const lerp = (c: number, s: number): number =>
|
|
141
|
+
Math.round(c * (1 - amount) + s * amount);
|
|
142
|
+
|
|
143
|
+
const rr = lerp(r, sr).toString(16).padStart(2, '0');
|
|
144
|
+
const gg = lerp(g, sg).toString(16).padStart(2, '0');
|
|
145
|
+
const bb = lerp(b, sb).toString(16).padStart(2, '0');
|
|
146
|
+
|
|
147
|
+
return `#${rr}${gg}${bb}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
42
150
|
export const deepCompareValue = <V>(a: V, b: V): boolean => {
|
|
43
151
|
// Handle strict equality first (handles primitives, null, undefined)
|
|
44
152
|
if (a === b) return true;
|