@cleen/ui-core 0.1.0 → 0.1.1
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/README.md +40 -0
- package/dist/index.d.ts +258 -0
- package/dist/index.js +758 -0
- package/dist/styles.css +4560 -0
- package/package.json +42 -42
- package/tailwind-entry.css +81 -81
- package/tailwind.config.js +10 -10
- package/tailwind.preset.js +30 -30
- package/src/hooks/useAnimateNumber.ts +0 -56
- package/src/hooks/useControlled.ts +0 -40
- package/src/hooks/useDebounce.ts +0 -17
- package/src/hooks/useDisclosure.ts +0 -33
- package/src/hooks/useForm.ts +0 -38
- package/src/hooks/useOutsideClick.ts +0 -42
- package/src/hooks/usePaginationState.ts +0 -39
- package/src/hooks/usePositionClose.ts +0 -69
- package/src/hooks/useValidation.ts +0 -33
- package/src/hooks/useWatchResize.ts +0 -52
- package/src/index.ts +0 -21
- package/src/store/colors.ts +0 -98
- package/src/types/position.ts +0 -9
- package/src/types/styles.ts +0 -24
- package/src/types/utils.ts +0 -6
- package/src/utils/audio.ts +0 -69
- package/src/utils/cn.ts +0 -13
- package/src/utils/colors.ts +0 -159
- package/src/utils/images.ts +0 -42
- package/src/utils/object.ts +0 -86
- package/src/utils/position.ts +0 -140
- package/src/utils/string.ts +0 -27
- package/styles/react-day-styles.css +0 -457
- package/tsconfig.json +0 -27
- package/tsup.config.ts +0 -10
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { useCallback, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
export const useValidation = <T>(defaultValue: Partial<T>) => {
|
|
4
|
-
const [errors, setErrors] = useState<Partial<T>>(defaultValue);
|
|
5
|
-
|
|
6
|
-
const setError = useCallback((field: keyof T, value: Partial<T>[keyof T]) => {
|
|
7
|
-
setErrors(prev => {
|
|
8
|
-
prev[field] = value;
|
|
9
|
-
|
|
10
|
-
return { ...prev };
|
|
11
|
-
});
|
|
12
|
-
}, []);
|
|
13
|
-
|
|
14
|
-
const clearErrors = useCallback(() => {
|
|
15
|
-
setErrors(defaultValue);
|
|
16
|
-
}, [defaultValue]);
|
|
17
|
-
|
|
18
|
-
const clearError = useCallback((field: keyof T) => {
|
|
19
|
-
setErrors(prev => {
|
|
20
|
-
delete prev[field];
|
|
21
|
-
|
|
22
|
-
return { ...prev };
|
|
23
|
-
});
|
|
24
|
-
}, []);
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
errors,
|
|
28
|
-
setError,
|
|
29
|
-
setErrors,
|
|
30
|
-
clearError,
|
|
31
|
-
clearErrors,
|
|
32
|
-
};
|
|
33
|
-
};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { useEffect, type RefObject } from 'react';
|
|
2
|
-
|
|
3
|
-
interface UseWidthDynamicResizeParams<T> {
|
|
4
|
-
ref: RefObject<T>;
|
|
5
|
-
skip?: boolean;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export const useWidthDynamicResize = <T extends HTMLElement | null>(
|
|
9
|
-
{ ref, skip }: UseWidthDynamicResizeParams<T>,
|
|
10
|
-
deps: unknown[]
|
|
11
|
-
) => {
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
const node = ref.current;
|
|
14
|
-
|
|
15
|
-
if (!node || skip) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Capture current width before content changes
|
|
20
|
-
const currentWidth = node.offsetWidth;
|
|
21
|
-
|
|
22
|
-
// Set explicit width to enable smooth transitions
|
|
23
|
-
node.style.width = `${currentWidth}px`;
|
|
24
|
-
|
|
25
|
-
// Use requestAnimationFrame to allow DOM to update with new content
|
|
26
|
-
requestAnimationFrame(() => {
|
|
27
|
-
// Measure the natural width with new content
|
|
28
|
-
node.style.width = 'auto';
|
|
29
|
-
const newWidth = node.offsetWidth;
|
|
30
|
-
|
|
31
|
-
// Set back to old width momentarily
|
|
32
|
-
node.style.width = `${currentWidth}px`;
|
|
33
|
-
|
|
34
|
-
// Force reflow
|
|
35
|
-
void node.offsetHeight;
|
|
36
|
-
|
|
37
|
-
// Transition to new width
|
|
38
|
-
node.style.width = `${newWidth}px`;
|
|
39
|
-
|
|
40
|
-
// After transition, set to auto for flexibility
|
|
41
|
-
const handleTransitionEnd = () => {
|
|
42
|
-
if (node) {
|
|
43
|
-
node.style.width = 'auto';
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
node?.removeEventListener('transitionend', handleTransitionEnd);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
node.addEventListener('transitionend', handleTransitionEnd);
|
|
50
|
-
});
|
|
51
|
-
}, deps);
|
|
52
|
-
};
|
package/src/index.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export * from './hooks/useAnimateNumber';
|
|
2
|
-
export * from './hooks/useControlled';
|
|
3
|
-
export * from './hooks/useDebounce';
|
|
4
|
-
export * from './hooks/useDisclosure';
|
|
5
|
-
export * from './hooks/useForm';
|
|
6
|
-
export * from './hooks/useOutsideClick';
|
|
7
|
-
export * from './hooks/usePaginationState';
|
|
8
|
-
export * from './hooks/usePositionClose';
|
|
9
|
-
export * from './hooks/useValidation';
|
|
10
|
-
export * from './hooks/useWatchResize';
|
|
11
|
-
export * from './store/colors';
|
|
12
|
-
export * from './types/position';
|
|
13
|
-
export * from './types/styles';
|
|
14
|
-
export * from './types/utils';
|
|
15
|
-
export * from './utils/audio';
|
|
16
|
-
export * from './utils/cn';
|
|
17
|
-
export * from './utils/colors';
|
|
18
|
-
export * from './utils/images';
|
|
19
|
-
export * from './utils/object';
|
|
20
|
-
export * from './utils/position';
|
|
21
|
-
export * from './utils/string';
|
package/src/store/colors.ts
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { create } from 'zustand';
|
|
2
|
-
import { persist } from 'zustand/middleware';
|
|
3
|
-
|
|
4
|
-
const PREFIX = '--cleen-';
|
|
5
|
-
|
|
6
|
-
const colorVars = [
|
|
7
|
-
'white',
|
|
8
|
-
'black',
|
|
9
|
-
'gray',
|
|
10
|
-
'pink',
|
|
11
|
-
'purple',
|
|
12
|
-
'indigo',
|
|
13
|
-
'blue',
|
|
14
|
-
'primary',
|
|
15
|
-
'success',
|
|
16
|
-
'warning',
|
|
17
|
-
'error',
|
|
18
|
-
'brand',
|
|
19
|
-
'sidebar',
|
|
20
|
-
'background',
|
|
21
|
-
] as const;
|
|
22
|
-
|
|
23
|
-
export type ColorVar = (typeof colorVars)[number];
|
|
24
|
-
|
|
25
|
-
interface UseCleenColorsStore {
|
|
26
|
-
colors: Partial<Record<ColorVar, string>>;
|
|
27
|
-
getColors: () => Partial<Record<ColorVar, string>>;
|
|
28
|
-
getColor: (color: ColorVar) => string;
|
|
29
|
-
setColor: (colorVar: ColorVar, value: string) => void;
|
|
30
|
-
setColors: (colors: Partial<Record<ColorVar, string>>) => void;
|
|
31
|
-
resetColors: () => void;
|
|
32
|
-
resetColor: (color: ColorVar) => void;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export const useCleenColors = create<UseCleenColorsStore>()(
|
|
36
|
-
persist(
|
|
37
|
-
(set, get) => ({
|
|
38
|
-
colors: {},
|
|
39
|
-
getColors: () => {
|
|
40
|
-
const colors = get().colors;
|
|
41
|
-
|
|
42
|
-
return colorVars.reduce<Partial<Record<ColorVar, string>>>(
|
|
43
|
-
(prev, colorVar) => {
|
|
44
|
-
if (colors[colorVar]) {
|
|
45
|
-
prev[colorVar] = colors[colorVar];
|
|
46
|
-
} else {
|
|
47
|
-
prev[colorVar] = getComputedStyle(
|
|
48
|
-
document.documentElement
|
|
49
|
-
).getPropertyValue(`${PREFIX}${colorVar}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return prev;
|
|
53
|
-
},
|
|
54
|
-
{}
|
|
55
|
-
);
|
|
56
|
-
},
|
|
57
|
-
getColor: (color: ColorVar) => {
|
|
58
|
-
const colors = get().colors;
|
|
59
|
-
|
|
60
|
-
if (colors[color]) {
|
|
61
|
-
return colors[color];
|
|
62
|
-
} else {
|
|
63
|
-
return getComputedStyle(document.documentElement).getPropertyValue(
|
|
64
|
-
`${PREFIX}${color}`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
setColors: (colors: Partial<Record<ColorVar, string>>) =>
|
|
69
|
-
Object.entries(colors).map(([colorVar, value]) =>
|
|
70
|
-
set(state => ({
|
|
71
|
-
colors: {
|
|
72
|
-
...state.colors,
|
|
73
|
-
[colorVar]: value,
|
|
74
|
-
},
|
|
75
|
-
}))
|
|
76
|
-
),
|
|
77
|
-
setColor: (colorVar: ColorVar, value: string) => {
|
|
78
|
-
set(state => ({
|
|
79
|
-
colors: {
|
|
80
|
-
...state.colors,
|
|
81
|
-
[colorVar]: value,
|
|
82
|
-
},
|
|
83
|
-
}));
|
|
84
|
-
},
|
|
85
|
-
resetColors: () => {
|
|
86
|
-
set({ colors: {} });
|
|
87
|
-
},
|
|
88
|
-
resetColor: (colorVar: ColorVar) => {
|
|
89
|
-
const colors = get().colors;
|
|
90
|
-
delete colors[colorVar];
|
|
91
|
-
set({ colors });
|
|
92
|
-
},
|
|
93
|
-
}),
|
|
94
|
-
{
|
|
95
|
-
name: 'cleen-colors',
|
|
96
|
-
}
|
|
97
|
-
)
|
|
98
|
-
);
|
package/src/types/position.ts
DELETED
package/src/types/styles.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { CSSProperties } from 'react';
|
|
2
|
-
|
|
3
|
-
// Helper interfaces to extract types of the classNames or styles props and pass in additional reserved class/style for className/style prop
|
|
4
|
-
export interface ComponentClassnames<
|
|
5
|
-
T extends {
|
|
6
|
-
classNames?: {
|
|
7
|
-
[key: string]: string | object;
|
|
8
|
-
};
|
|
9
|
-
},
|
|
10
|
-
> {
|
|
11
|
-
className?: string;
|
|
12
|
-
classNames?: T['classNames'];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface ComponentStyles<
|
|
16
|
-
T extends {
|
|
17
|
-
styles?: {
|
|
18
|
-
[key: string]: CSSProperties | object;
|
|
19
|
-
};
|
|
20
|
-
},
|
|
21
|
-
> {
|
|
22
|
-
style?: CSSProperties;
|
|
23
|
-
styles?: T['styles'];
|
|
24
|
-
}
|
package/src/types/utils.ts
DELETED
package/src/utils/audio.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/** Minimal synchronous PCM WAV encoder – no extra deps needed =w= */
|
|
2
|
-
export const encodeWav = (buffer: AudioBuffer): Blob => {
|
|
3
|
-
const { numberOfChannels: channels, sampleRate, length } = buffer;
|
|
4
|
-
const bitsPerSample = 16;
|
|
5
|
-
const blockAlign = (channels * bitsPerSample) / 8;
|
|
6
|
-
const byteRate = sampleRate * blockAlign;
|
|
7
|
-
const dataSize = length * blockAlign;
|
|
8
|
-
const wavBuffer = new ArrayBuffer(44 + dataSize);
|
|
9
|
-
const v = new DataView(wavBuffer);
|
|
10
|
-
|
|
11
|
-
const str = (offset: number, s: string) => {
|
|
12
|
-
for (let i = 0; i < s.length; i++) v.setUint8(offset + i, s.charCodeAt(i));
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
str(0, 'RIFF');
|
|
16
|
-
v.setUint32(4, 36 + dataSize, true);
|
|
17
|
-
str(8, 'WAVE');
|
|
18
|
-
str(12, 'fmt ');
|
|
19
|
-
v.setUint32(16, 16, true);
|
|
20
|
-
v.setUint16(20, 1, true); // PCM
|
|
21
|
-
v.setUint16(22, channels, true);
|
|
22
|
-
v.setUint32(24, sampleRate, true);
|
|
23
|
-
v.setUint32(28, byteRate, true);
|
|
24
|
-
v.setUint16(32, blockAlign, true);
|
|
25
|
-
v.setUint16(34, bitsPerSample, true);
|
|
26
|
-
str(36, 'data');
|
|
27
|
-
v.setUint32(40, dataSize, true);
|
|
28
|
-
|
|
29
|
-
let offset = 44;
|
|
30
|
-
for (let i = 0; i < length; i++) {
|
|
31
|
-
for (let ch = 0; ch < channels; ch++) {
|
|
32
|
-
const s = Math.max(-1, Math.min(1, buffer.getChannelData(ch)[i]));
|
|
33
|
-
v.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
|
|
34
|
-
offset += 2;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return new Blob([wavBuffer], { type: 'audio/wav' });
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export const trimBlob = async (
|
|
42
|
-
blob: Blob,
|
|
43
|
-
startSec: number,
|
|
44
|
-
endSec: number
|
|
45
|
-
): Promise<Blob> => {
|
|
46
|
-
const ctx = new AudioContext();
|
|
47
|
-
const decoded = await ctx.decodeAudioData(await blob.arrayBuffer());
|
|
48
|
-
await ctx.close();
|
|
49
|
-
|
|
50
|
-
const { sampleRate, numberOfChannels } = decoded;
|
|
51
|
-
const startSample = Math.round(startSec * sampleRate);
|
|
52
|
-
const endSample = Math.round(endSec * sampleRate);
|
|
53
|
-
const trimLength = endSample - startSample;
|
|
54
|
-
|
|
55
|
-
// OfflineAudioContext would add latency; just slice the buffers directly
|
|
56
|
-
const trimmedBuffer = new AudioContext().createBuffer(
|
|
57
|
-
numberOfChannels,
|
|
58
|
-
trimLength,
|
|
59
|
-
sampleRate
|
|
60
|
-
);
|
|
61
|
-
for (let ch = 0; ch < numberOfChannels; ch++) {
|
|
62
|
-
trimmedBuffer.copyToChannel(
|
|
63
|
-
decoded.getChannelData(ch).subarray(startSample, endSample),
|
|
64
|
-
ch
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return encodeWav(trimmedBuffer);
|
|
69
|
-
};
|
package/src/utils/cn.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import clsx, { type ClassValue } from 'clsx';
|
|
2
|
-
import { createTailwindMerge, getDefaultConfig } from 'tailwind-merge';
|
|
3
|
-
|
|
4
|
-
const twConfig = () => ({
|
|
5
|
-
...getDefaultConfig(),
|
|
6
|
-
prefix: 'cleen-',
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
const twMerge = createTailwindMerge(twConfig);
|
|
10
|
-
|
|
11
|
-
export const cn = (...inputs: ClassValue[]) => {
|
|
12
|
-
return twMerge(clsx(inputs));
|
|
13
|
-
};
|
package/src/utils/colors.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
darken,
|
|
3
|
-
getLuminance,
|
|
4
|
-
lighten,
|
|
5
|
-
parseToRgba,
|
|
6
|
-
toHex,
|
|
7
|
-
toRgba,
|
|
8
|
-
} from 'color2k';
|
|
9
|
-
|
|
10
|
-
type RGBA = [number, number, number, number];
|
|
11
|
-
|
|
12
|
-
export class ColorHelpers {
|
|
13
|
-
/**
|
|
14
|
-
* Calculates the Delta E (CIE94) between two RGB colors.
|
|
15
|
-
*
|
|
16
|
-
* Delta E is a metric for color difference. Based on the value, we can describe how different the colors are:
|
|
17
|
-
*
|
|
18
|
-
* Perception Levels:
|
|
19
|
-
* - Delta E <= 1.0: Not perceptible by human eyes.
|
|
20
|
-
* - Delta E between 1-2: Perceptible through close observation.
|
|
21
|
-
* - Delta E between 2-10: Perceptible at a glance.
|
|
22
|
-
* - Delta E between 11-49: Colors are more similar than opposite.
|
|
23
|
-
* - Delta E = 100: Colors are exact opposites.
|
|
24
|
-
*
|
|
25
|
-
* @param {Array<number>} rgb1 - The first RGB color as an array [R, G, B].
|
|
26
|
-
* @param {Array<number>} rgb2 - The second RGB color as an array [R, G, B].
|
|
27
|
-
* @returns {number} The Delta E value representing the difference between the two colors.
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* deltaE([128, 0, 255], [128, 0, 255]); // 0
|
|
31
|
-
* deltaE([128, 0, 255], [128, 0, 230]); // 3.175
|
|
32
|
-
* deltaE([128, 0, 255], [255, 0, 0]); // 61.24
|
|
33
|
-
*/
|
|
34
|
-
static deltaE(rgbA: RGBA, rgbB: RGBA) {
|
|
35
|
-
const labA = ColorHelpers.rgb2lab(rgbA);
|
|
36
|
-
const labB = ColorHelpers.rgb2lab(rgbB);
|
|
37
|
-
|
|
38
|
-
const deltaL = labA[0] - labB[0];
|
|
39
|
-
const deltaA = labA[1] - labB[1];
|
|
40
|
-
const deltaB = labA[2] - labB[2];
|
|
41
|
-
const c1 = Math.sqrt(labA[1] * labA[1] + labA[2] * labA[2]);
|
|
42
|
-
const c2 = Math.sqrt(labB[1] * labB[1] + labB[2] * labB[2]);
|
|
43
|
-
const deltaC = c1 - c2;
|
|
44
|
-
let deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC;
|
|
45
|
-
deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH);
|
|
46
|
-
const sc = 1.0 + 0.045 * c1;
|
|
47
|
-
const sh = 1.0 + 0.015 * c1;
|
|
48
|
-
const deltaLKlsl = deltaL / 1.0;
|
|
49
|
-
const deltaCkcsc = deltaC / sc;
|
|
50
|
-
const deltaHkhsh = deltaH / sh;
|
|
51
|
-
const i =
|
|
52
|
-
deltaLKlsl * deltaLKlsl +
|
|
53
|
-
deltaCkcsc * deltaCkcsc +
|
|
54
|
-
deltaHkhsh * deltaHkhsh;
|
|
55
|
-
return i < 0 ? 0 : Math.sqrt(i);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
static rgb2lab(rgb: RGBA) {
|
|
59
|
-
let r = rgb[0] / 255,
|
|
60
|
-
g = rgb[1] / 255,
|
|
61
|
-
b = rgb[2] / 255,
|
|
62
|
-
x,
|
|
63
|
-
y,
|
|
64
|
-
z;
|
|
65
|
-
r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
|
|
66
|
-
g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
|
|
67
|
-
b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
|
|
68
|
-
x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
|
|
69
|
-
y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1.0;
|
|
70
|
-
z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
|
|
71
|
-
x = x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787 * x + 16 / 116;
|
|
72
|
-
y = y > 0.008856 ? Math.pow(y, 1 / 3) : 7.787 * y + 16 / 116;
|
|
73
|
-
z = z > 0.008856 ? Math.pow(z, 1 / 3) : 7.787 * z + 16 / 116;
|
|
74
|
-
return [116 * y - 16, 500 * (x - y), 200 * (y - z)];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
static isSimilar(color1: string, color2: string, defaultDeltaE = 24) {
|
|
78
|
-
const e = ColorHelpers.deltaE(parseToRgba(color1), parseToRgba(color2));
|
|
79
|
-
return e < defaultDeltaE;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
static adjustColorForContrast(hexColor: string, amount = 0.25) {
|
|
83
|
-
// Always convert to hex for polished
|
|
84
|
-
let _colorAsHex = toHex(hexColor);
|
|
85
|
-
let result = hexColor;
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
// If color is almost white, force to pure white to avoid hue bias
|
|
89
|
-
if (
|
|
90
|
-
_colorAsHex.match(/^#f{2,}e{0,2}$/i) ||
|
|
91
|
-
getLuminance(_colorAsHex) > 0.98
|
|
92
|
-
) {
|
|
93
|
-
_colorAsHex = '#ffffff';
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (getLuminance(_colorAsHex) > 0.6) {
|
|
97
|
-
const darkenedColorHSLA = darken(_colorAsHex, amount);
|
|
98
|
-
const darkenedColorRGBA = toRgba(darkenedColorHSLA);
|
|
99
|
-
result = darkenedColorRGBA;
|
|
100
|
-
} else {
|
|
101
|
-
const lightenedColorHSLA = lighten(_colorAsHex, amount);
|
|
102
|
-
const lightenedColorRGBA = toRgba(lightenedColorHSLA);
|
|
103
|
-
result = lightenedColorRGBA;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return result;
|
|
107
|
-
} catch {
|
|
108
|
-
return 'rgba(var(--cleen-white)';
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Util functions to get color based on pass string (CSS variables can be parsed as well)
|
|
113
|
-
static getComputedColor(color?: string) {
|
|
114
|
-
if (!color?.includes('var(')) {
|
|
115
|
-
return color;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Extract variable name from var(--variable-name)
|
|
119
|
-
const varName = color.match(/var\((--[^,)]+)/)?.[1];
|
|
120
|
-
|
|
121
|
-
if (varName && typeof window !== 'undefined') {
|
|
122
|
-
// Get the computed style value from the document
|
|
123
|
-
const color = getComputedStyle(document.documentElement)
|
|
124
|
-
.getPropertyValue(varName)
|
|
125
|
-
.trim();
|
|
126
|
-
|
|
127
|
-
return color ? `rgb(${color})` : undefined;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return undefined;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
static getComputedRgb(color?: string) {
|
|
134
|
-
if (!color) {
|
|
135
|
-
return undefined;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (!color?.includes('var(')) {
|
|
139
|
-
const [r, g, b] = parseToRgba(color);
|
|
140
|
-
return `${r}, ${g}, ${b}`;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Extract variable name from var(--variable-name)
|
|
144
|
-
const varName = color.match(/var\((--[^,)]+)/)?.[1];
|
|
145
|
-
|
|
146
|
-
if (varName && typeof window !== 'undefined') {
|
|
147
|
-
// Get the computed style value from the document
|
|
148
|
-
return getComputedStyle(document.documentElement)
|
|
149
|
-
.getPropertyValue(varName)
|
|
150
|
-
.trim();
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return undefined;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
static trasparentize(color?: string, value = 0) {
|
|
157
|
-
return `color-mix(in srgb, ${color} ${(1 - value) * 100}%, transparent)`;
|
|
158
|
-
}
|
|
159
|
-
}
|
package/src/utils/images.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const PICSUM_BASE_URL = 'https://picsum.photos';
|
|
2
|
-
|
|
3
|
-
interface PicsumOptions {
|
|
4
|
-
width: number;
|
|
5
|
-
height?: number;
|
|
6
|
-
grayscale?: boolean;
|
|
7
|
-
blur?: number; // 1 to 10
|
|
8
|
-
seed?: string; // for consistent images
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const getRandomImageUrl = (options: PicsumOptions): string => {
|
|
12
|
-
const { width, height, grayscale, blur, seed } = options;
|
|
13
|
-
|
|
14
|
-
let url = PICSUM_BASE_URL;
|
|
15
|
-
|
|
16
|
-
if (seed) {
|
|
17
|
-
url += `/seed/${encodeURIComponent(seed)}`;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
url += `/${width}/${height || width}`;
|
|
21
|
-
|
|
22
|
-
const params = new URLSearchParams();
|
|
23
|
-
|
|
24
|
-
if (grayscale) {
|
|
25
|
-
params.append('grayscale', '');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (blur) {
|
|
29
|
-
params.append('blur', blur.toString());
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (seed) {
|
|
33
|
-
params.append('seed', seed);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const queryString = params.toString();
|
|
37
|
-
if (queryString) {
|
|
38
|
-
url += `?${queryString}`;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return url;
|
|
42
|
-
};
|
package/src/utils/object.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deep partial type — makes all properties optional recursively.
|
|
3
|
-
*/
|
|
4
|
-
export type PartialDeep<T> = T extends (...args: unknown[]) => unknown
|
|
5
|
-
? T
|
|
6
|
-
: T extends Array<infer U>
|
|
7
|
-
? Array<PartialDeep<U>>
|
|
8
|
-
: T extends object
|
|
9
|
-
? { [K in keyof T]?: PartialDeep<T[K]> }
|
|
10
|
-
: T | undefined;
|
|
11
|
-
|
|
12
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
13
|
-
return (
|
|
14
|
-
typeof value === 'object' &&
|
|
15
|
-
value !== null &&
|
|
16
|
-
!Array.isArray(value) &&
|
|
17
|
-
Object.prototype.toString.call(value) === '[object Object]'
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Recursively apply values on top of default values.
|
|
23
|
-
*
|
|
24
|
-
* Rules / assumptions:
|
|
25
|
-
* - Plain objects are merged recursively.
|
|
26
|
-
* - Arrays are replaced entirely by provided values (not concatenated).
|
|
27
|
-
* - If a provided value is `undefined`, the default is kept.
|
|
28
|
-
* - If a provided value is `null`, it overrides the default.
|
|
29
|
-
* - Non-plain objects (Date, RegExp, Map, Set, class instances) are replaced by the provided value when present.
|
|
30
|
-
*
|
|
31
|
-
* Example:
|
|
32
|
-
* const defaults = { a: 1, b: { c: 2, d: [1] } };
|
|
33
|
-
* const vals = { b: { c: 3 } };
|
|
34
|
-
* applyDefaults(defaults, vals) // => { a: 1, b: { c: 3, d: [1] } }
|
|
35
|
-
*/
|
|
36
|
-
export function applyDefaults<T>(defaults: T, values?: PartialDeep<T>): T {
|
|
37
|
-
// Internal recursive merge
|
|
38
|
-
const merge = (d: unknown, v: unknown): unknown => {
|
|
39
|
-
// If v is explicitly undefined, keep default
|
|
40
|
-
if (v === undefined) {
|
|
41
|
-
return cloneValue(d);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// If both are plain objects, merge recursively
|
|
45
|
-
if (isPlainObject(d) && isPlainObject(v)) {
|
|
46
|
-
const result: Record<string, unknown> = {};
|
|
47
|
-
const keys = new Set<string>([...Object.keys(d), ...Object.keys(v)]);
|
|
48
|
-
keys.forEach(key => {
|
|
49
|
-
const dv = (d as Record<string, unknown>)[key];
|
|
50
|
-
const vv = (v as Record<string, unknown>)[key];
|
|
51
|
-
result[key] = merge(dv, vv);
|
|
52
|
-
});
|
|
53
|
-
return result;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Arrays: prefer provided value (replace), or clone default if v is undefined handled above
|
|
57
|
-
if (Array.isArray(d) || Array.isArray(v)) {
|
|
58
|
-
if (v === undefined) return cloneValue(d);
|
|
59
|
-
// If provided a value for array, return clone of that array
|
|
60
|
-
return cloneValue(v);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// For all other cases, if v is provided (including null), use it; otherwise clone default
|
|
64
|
-
return v !== undefined ? cloneValue(v) : cloneValue(d);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// Shallow/deep clone helper for primitives, arrays and plain objects
|
|
68
|
-
const cloneValue = (x: unknown): unknown => {
|
|
69
|
-
if (x === undefined) return undefined;
|
|
70
|
-
if (x === null) return null;
|
|
71
|
-
if (Array.isArray(x)) return (x as unknown[]).map(item => cloneValue(item));
|
|
72
|
-
if (isPlainObject(x)) {
|
|
73
|
-
const o: Record<string, unknown> = {};
|
|
74
|
-
Object.keys(x as Record<string, unknown>).forEach(k => {
|
|
75
|
-
o[k] = cloneValue((x as Record<string, unknown>)[k]);
|
|
76
|
-
});
|
|
77
|
-
return o;
|
|
78
|
-
}
|
|
79
|
-
// For non-plain objects (Date, RegExp, Map, Set, etc.) and primitives, return as is
|
|
80
|
-
return x;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
return merge(defaults, values) as T;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export default applyDefaults;
|