@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.
@@ -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';
@@ -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
- );
@@ -1,9 +0,0 @@
1
- export type Position =
2
- | 'bottom-left'
3
- | 'bottom-right'
4
- | 'bottom'
5
- | 'top-left'
6
- | 'top-right'
7
- | 'top'
8
- | 'left'
9
- | 'right';
@@ -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
- }
@@ -1,6 +0,0 @@
1
- export type HintedString<KnownValues extends string> =
2
- | (string & {})
3
- | KnownValues;
4
-
5
- export type ArrayElement<ArrayType extends readonly unknown[]> =
6
- ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
@@ -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
- };
@@ -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
- }
@@ -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
- };
@@ -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;