@codeleap/styles 5.8.2 → 5.8.4
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/package.json +9 -15
- package/package.json.bak +8 -14
- package/src/classes/Cacher.ts +169 -0
- package/src/classes/StaleControl.ts +125 -0
- package/src/classes/StyleCache.ts +116 -0
- package/src/classes/StylePersistor.ts +62 -0
- package/src/{lib → classes}/StyleRegistry.ts +7 -12
- package/src/classes/index.ts +2 -0
- package/src/classes/tests/Cache.spec.ts +371 -0
- package/src/classes/tests/StaleControl.spec.ts +175 -0
- package/src/classes/tests/StyleCache.spec.ts +452 -0
- package/src/classes/tests/StylePersistor.spec.ts +231 -0
- package/src/{lib/constants.ts → constants.ts} +1 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/tests/useCompositionStyles.spec.ts +107 -0
- package/src/hooks/tests/useStyleObserver.spec.ts +89 -0
- package/src/hooks/useCompositionStyles.ts +33 -0
- package/src/hooks/useNestedStylesByKey.ts +13 -0
- package/src/hooks/useStyleObserver.ts +19 -0
- package/src/hooks/useTheme.ts +16 -0
- package/src/index.ts +9 -5
- package/src/lib/createStyles.ts +10 -1
- package/src/lib/createTheme.ts +22 -13
- package/src/lib/index.ts +1 -10
- package/src/lib/tests/createStyles.spec.ts +151 -0
- package/src/tests/colors/baseColors.ts +166 -0
- package/src/tests/colors/darkMode.ts +232 -0
- package/src/tests/colors/lightMode.ts +285 -0
- package/src/tests/measures.ts +31 -0
- package/src/tests/theme.ts +58 -0
- package/src/theme/generateColorScheme.ts +53 -0
- package/src/theme/index.ts +3 -0
- package/src/theme/tests/generateColorScheme.spec.ts +118 -0
- package/src/theme/tests/themeStore.spec.ts +698 -0
- package/src/theme/tests/validateTheme.spec.ts +173 -0
- package/src/{lib → theme}/themeStore.ts +68 -3
- package/src/{lib → theme}/validateTheme.ts +13 -0
- package/src/tools/colors.ts +83 -39
- package/src/tools/deepClone.ts +10 -0
- package/src/tools/deepmerge.ts +10 -0
- package/src/{lib → tools}/hashKey.ts +7 -0
- package/src/tools/index.ts +6 -1
- package/src/tools/minifier.ts +38 -0
- package/src/tools/tests/colors.spec.ts +233 -0
- package/src/tools/tests/deepClone.spec.ts +102 -0
- package/src/tools/tests/deepmerge.spec.ts +155 -0
- package/src/tools/tests/hashKey.spec.ts +69 -0
- package/src/tools/tests/minifier.spec.ts +173 -0
- package/src/types/store.ts +2 -2
- package/src/types/style.ts +3 -3
- package/src/types/theme.ts +4 -4
- package/src/{lib/utils.ts → utils.ts} +3 -3
- package/src/{lib → variants}/borderCreator.ts +2 -2
- package/src/{lib → variants}/createAppVariants.ts +1 -1
- package/src/{lib → variants}/dynamicVariants.ts +1 -1
- package/src/variants/index.ts +6 -0
- package/src/{lib → variants}/spacing.ts +37 -24
- package/src/variants/tests/borderCreator.spec.ts +180 -0
- package/src/variants/tests/dynamicVariants.spec.ts +194 -0
- package/src/variants/tests/spacing.spec.ts +177 -0
- package/src/lib/Cacher.ts +0 -112
- package/src/lib/StaleControl.ts +0 -73
- package/src/lib/StyleCache.ts +0 -81
- package/src/lib/StylePersistor.ts +0 -28
- package/src/lib/hooks.ts +0 -64
- package/src/lib/minifier.ts +0 -20
- /package/src/{lib → tools}/multiplierProperty.ts +0 -0
- /package/src/{lib → variants}/defaultVariants.ts +0 -0
- /package/src/{lib → variants}/mediaQuery.ts +0 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { validateTheme } from '../validateTheme'
|
|
3
|
+
|
|
4
|
+
describe('validateTheme', () => {
|
|
5
|
+
test('should merge baseColors into colors', () => {
|
|
6
|
+
const theme: any = {
|
|
7
|
+
baseColors: {
|
|
8
|
+
primary: '#000000',
|
|
9
|
+
secondary: '#ffffff',
|
|
10
|
+
},
|
|
11
|
+
colors: {
|
|
12
|
+
background: '#f0f0f0',
|
|
13
|
+
text: '#333333',
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const result = validateTheme(theme)
|
|
18
|
+
|
|
19
|
+
expect(result.colors).toEqual({
|
|
20
|
+
primary: '#000000',
|
|
21
|
+
secondary: '#ffffff',
|
|
22
|
+
background: '#f0f0f0',
|
|
23
|
+
text: '#333333',
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('should validate and merge alternate color schemes', () => {
|
|
28
|
+
const theme: any = {
|
|
29
|
+
baseColors: {
|
|
30
|
+
primary: '#000000',
|
|
31
|
+
secondary: '#ffffff',
|
|
32
|
+
},
|
|
33
|
+
colors: {
|
|
34
|
+
background: '#f0f0f0',
|
|
35
|
+
text: '#333333',
|
|
36
|
+
},
|
|
37
|
+
alternateColors: {
|
|
38
|
+
dark: {
|
|
39
|
+
background: '#1a1a1a',
|
|
40
|
+
text: '#ffffff',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const result = validateTheme(theme)
|
|
46
|
+
|
|
47
|
+
expect(result.alternateColors.dark).toEqual({
|
|
48
|
+
primary: '#000000',
|
|
49
|
+
secondary: '#ffffff',
|
|
50
|
+
background: '#1a1a1a',
|
|
51
|
+
text: '#ffffff',
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('should throw error when alternate scheme is missing required colors', () => {
|
|
56
|
+
const theme: any = {
|
|
57
|
+
baseColors: {
|
|
58
|
+
primary: '#000000',
|
|
59
|
+
},
|
|
60
|
+
colors: {
|
|
61
|
+
background: '#f0f0f0',
|
|
62
|
+
text: '#333333',
|
|
63
|
+
},
|
|
64
|
+
alternateColors: {
|
|
65
|
+
dark: {
|
|
66
|
+
background: '#1a1a1a',
|
|
67
|
+
// Missing 'text' color
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
expect(() => validateTheme(theme)).toThrow(
|
|
73
|
+
'Alternate color scheme dark is missing color text'
|
|
74
|
+
)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('should handle theme without alternateColors', () => {
|
|
78
|
+
const theme: any = {
|
|
79
|
+
baseColors: {
|
|
80
|
+
primary: '#000000',
|
|
81
|
+
},
|
|
82
|
+
colors: {
|
|
83
|
+
background: '#f0f0f0',
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const result = validateTheme(theme)
|
|
88
|
+
|
|
89
|
+
expect(result.colors).toEqual({
|
|
90
|
+
primary: '#000000',
|
|
91
|
+
background: '#f0f0f0',
|
|
92
|
+
})
|
|
93
|
+
expect(result.alternateColors).toEqual({})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('should handle empty baseColors', () => {
|
|
97
|
+
const theme: any = {
|
|
98
|
+
baseColors: {},
|
|
99
|
+
colors: {
|
|
100
|
+
background: '#f0f0f0',
|
|
101
|
+
text: '#333333',
|
|
102
|
+
},
|
|
103
|
+
alternateColors: {
|
|
104
|
+
dark: {
|
|
105
|
+
background: '#1a1a1a',
|
|
106
|
+
text: '#ffffff',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const result = validateTheme(theme)
|
|
112
|
+
|
|
113
|
+
expect(result.colors).toEqual({
|
|
114
|
+
background: '#f0f0f0',
|
|
115
|
+
text: '#333333',
|
|
116
|
+
})
|
|
117
|
+
expect(result.alternateColors.dark).toEqual({
|
|
118
|
+
background: '#1a1a1a',
|
|
119
|
+
text: '#ffffff',
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test('should handle multiple alternate color schemes', () => {
|
|
124
|
+
const theme: any = {
|
|
125
|
+
baseColors: {
|
|
126
|
+
primary: '#000000',
|
|
127
|
+
},
|
|
128
|
+
colors: {
|
|
129
|
+
background: '#f0f0f0',
|
|
130
|
+
text: '#333333',
|
|
131
|
+
},
|
|
132
|
+
alternateColors: {
|
|
133
|
+
dark: {
|
|
134
|
+
background: '#1a1a1a',
|
|
135
|
+
text: '#ffffff',
|
|
136
|
+
},
|
|
137
|
+
light: {
|
|
138
|
+
background: '#ffffff',
|
|
139
|
+
text: '#000000',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const result = validateTheme(theme)
|
|
145
|
+
|
|
146
|
+
expect(Object.keys(result.alternateColors)).toEqual(['dark', 'light'])
|
|
147
|
+
expect(result.alternateColors.dark).toEqual({
|
|
148
|
+
primary: '#000000',
|
|
149
|
+
background: '#1a1a1a',
|
|
150
|
+
text: '#ffffff',
|
|
151
|
+
})
|
|
152
|
+
expect(result.alternateColors.light).toEqual({
|
|
153
|
+
primary: '#000000',
|
|
154
|
+
background: '#ffffff',
|
|
155
|
+
text: '#000000',
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test('should preserve all original theme properties', () => {
|
|
160
|
+
const theme: any = {
|
|
161
|
+
baseColors: { primary: '#000000' },
|
|
162
|
+
colors: { background: '#f0f0f0' },
|
|
163
|
+
alternateColors: { dark: { background: '#1a1a1a' } },
|
|
164
|
+
name: 'test-theme',
|
|
165
|
+
fontSize: { sm: '12px', md: '16px' },
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const result = validateTheme(theme)
|
|
169
|
+
|
|
170
|
+
expect(result.name).toBe('test-theme')
|
|
171
|
+
expect(result.fontSize).toEqual({ sm: '12px', md: '16px' })
|
|
172
|
+
})
|
|
173
|
+
})
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { AppTheme, ColorMap, IAppVariants, ITheme, Theme } from '../types'
|
|
2
2
|
import { map, computed, atom } from 'nanostores'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Theme state interface containing theme and color scheme information.
|
|
6
|
+
*/
|
|
4
7
|
export type ThemeState = {
|
|
5
8
|
theme: AppTheme<Theme> | null
|
|
6
9
|
colorScheme: string | null
|
|
7
10
|
}
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Global theme store that manages application theme, color schemes, and variants.
|
|
14
|
+
* Uses nanostores for reactive state management.
|
|
15
|
+
*/
|
|
16
|
+
export class ThemeStore {
|
|
10
17
|
private readonly alternateColorsSchemeStore = map<{ [key: string]: ColorMap }>({})
|
|
11
18
|
|
|
12
19
|
public readonly colorSchemeStore = atom<string | null>(null)
|
|
@@ -15,44 +22,84 @@ class ThemeStore {
|
|
|
15
22
|
|
|
16
23
|
public readonly variantsStore = map<IAppVariants>({})
|
|
17
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Gets the current theme.
|
|
27
|
+
* @returns {ITheme | null} Current theme or null if not set
|
|
28
|
+
*/
|
|
18
29
|
get theme() {
|
|
19
30
|
return this.themeStore.get()
|
|
20
31
|
}
|
|
21
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Gets the current theme with typed interface.
|
|
35
|
+
* @returns {AppTheme<Theme>} Current theme cast to AppTheme type
|
|
36
|
+
*/
|
|
22
37
|
get themeTyped() {
|
|
23
38
|
return this.themeStore.get() as unknown as AppTheme<Theme>
|
|
24
39
|
}
|
|
25
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Gets the current color scheme name.
|
|
43
|
+
* @returns {string | null} Current color scheme name or null if not set
|
|
44
|
+
*/
|
|
26
45
|
get colorScheme() {
|
|
27
46
|
return this.colorSchemeStore.get()
|
|
28
47
|
}
|
|
29
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Gets the current variants configuration.
|
|
51
|
+
* @returns {IAppVariants} Current variants object
|
|
52
|
+
*/
|
|
30
53
|
get variants() {
|
|
31
54
|
return this.variantsStore.get()
|
|
32
55
|
}
|
|
33
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Gets all alternate color schemes.
|
|
59
|
+
* @returns {{ [key: string]: ColorMap }} Object containing all alternate color schemes
|
|
60
|
+
*/
|
|
34
61
|
get alternateColorsScheme() {
|
|
35
62
|
return this.alternateColorsSchemeStore.get() ?? {}
|
|
36
63
|
}
|
|
37
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Sets the variants configuration.
|
|
67
|
+
* @template T
|
|
68
|
+
* @param {T} variants - Variants configuration to set
|
|
69
|
+
*/
|
|
38
70
|
setVariants<T>(variants: T) {
|
|
39
71
|
this.variantsStore.set(variants as unknown as IAppVariants)
|
|
40
72
|
}
|
|
41
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Sets the current color scheme.
|
|
76
|
+
* @param {string} colorScheme - Color scheme name to set
|
|
77
|
+
*/
|
|
42
78
|
setColorScheme(colorScheme: string) {
|
|
43
79
|
this.colorSchemeStore.set(colorScheme)
|
|
44
80
|
}
|
|
45
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Sets the current theme.
|
|
84
|
+
* @param {ITheme} theme - Theme object to set
|
|
85
|
+
*/
|
|
46
86
|
setTheme(theme: ITheme) {
|
|
47
87
|
this.themeStore.set(theme)
|
|
48
88
|
}
|
|
49
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Sets all alternate color schemes.
|
|
92
|
+
* @param {{ [key: string]: ColorMap }} colors - Object containing color schemes
|
|
93
|
+
*/
|
|
50
94
|
setAlternateColorsScheme(colors: { [key: string]: ColorMap }) {
|
|
51
95
|
this.alternateColorsSchemeStore.set(colors)
|
|
52
96
|
}
|
|
53
97
|
|
|
54
|
-
|
|
55
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Gets the base color scheme colors (first available scheme).
|
|
100
|
+
* @private
|
|
101
|
+
* @returns {ColorMap} Base color scheme colors
|
|
102
|
+
*/
|
|
56
103
|
private getBaseSchemeColors(): ColorMap {
|
|
57
104
|
const alternateColors = this.alternateColorsScheme ?? {}
|
|
58
105
|
const colorSchemeKeys = Object.keys(alternateColors)
|
|
@@ -64,6 +111,12 @@ class ThemeStore {
|
|
|
64
111
|
return alternateColors[colorSchemeKeys[0]] ?? {}
|
|
65
112
|
}
|
|
66
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Injects a new color scheme, merging with base scheme colors.
|
|
116
|
+
* @param {string} name - Name of the color scheme
|
|
117
|
+
* @param {ColorMap} colors - Color map to inject
|
|
118
|
+
* @returns {{ [key: string]: ColorMap }} Updated alternate colors object
|
|
119
|
+
*/
|
|
67
120
|
injectColorScheme(name: string, colors: ColorMap) {
|
|
68
121
|
const baseSchemeColors = this.getBaseSchemeColors()
|
|
69
122
|
const currentAlternateColors = this.alternateColorsScheme ?? {}
|
|
@@ -82,6 +135,11 @@ class ThemeStore {
|
|
|
82
135
|
return alternateColors
|
|
83
136
|
}
|
|
84
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Removes a color scheme by name.
|
|
140
|
+
* @param {string} name - Name of the color scheme to remove
|
|
141
|
+
* @returns {{ [key: string]: ColorMap }} Updated alternate colors object
|
|
142
|
+
*/
|
|
85
143
|
ejectColorScheme(name:string) {
|
|
86
144
|
const currentAlternateColors = this.alternateColorsScheme ?? {}
|
|
87
145
|
|
|
@@ -95,8 +153,15 @@ class ThemeStore {
|
|
|
95
153
|
}
|
|
96
154
|
}
|
|
97
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Global theme store instance.
|
|
158
|
+
*/
|
|
98
159
|
export const themeStore = new ThemeStore()
|
|
99
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Computed store that combines theme and color scheme for reactive updates.
|
|
163
|
+
* @returns {ThemeState} Combined theme state with theme and colorScheme
|
|
164
|
+
*/
|
|
100
165
|
export const themeStoreComputed = computed([
|
|
101
166
|
themeStore.themeStore,
|
|
102
167
|
themeStore.colorSchemeStore,
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { Theme } from '../types/theme'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Validates and normalizes a theme object.
|
|
5
|
+
* Ensures all alternate color schemes include the same keys as `colors`,
|
|
6
|
+
* and merges `baseColors` into `colors` and `alternateColors`.
|
|
7
|
+
*
|
|
8
|
+
* @template T extends Theme
|
|
9
|
+
* @param {T} theme - The theme to validate.
|
|
10
|
+
* @throws {Error} If an alternate scheme is missing a required color.
|
|
11
|
+
* @returns {T & {
|
|
12
|
+
* alternateColors: Record<string, Record<string, string>>,
|
|
13
|
+
* colors: Record<string, string>
|
|
14
|
+
* }} The validated theme with merged colors.
|
|
15
|
+
*/
|
|
3
16
|
export function validateTheme<T extends Theme>(theme: T) {
|
|
4
17
|
const baseColors = theme.baseColors
|
|
5
18
|
|
package/src/tools/colors.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Converts a hex color to HSL format.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} hex - Hex color string (e.g., '#ff5733')
|
|
5
|
+
* @returns {object} HSL object with h (0-360), s (0-100), l (0-100)
|
|
6
|
+
*/
|
|
3
7
|
export function hexToHSL(hex: string) {
|
|
4
8
|
const r = parseInt(hex.slice(1, 3), 16) / 255
|
|
5
9
|
const g = parseInt(hex.slice(3, 5), 16) / 255
|
|
@@ -26,6 +30,14 @@ export function hexToHSL(hex: string) {
|
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Converts HSL values to hex color format.
|
|
35
|
+
*
|
|
36
|
+
* @param {number} h - Hue (0-360)
|
|
37
|
+
* @param {number} s - Saturation (0-100)
|
|
38
|
+
* @param {number} l - Lightness (0-100)
|
|
39
|
+
* @returns {string} Hex color string
|
|
40
|
+
*/
|
|
29
41
|
export function hslToHex(h: number, s: number, l: number): string {
|
|
30
42
|
s /= 100
|
|
31
43
|
l /= 100
|
|
@@ -37,6 +49,12 @@ export function hslToHex(h: number, s: number, l: number): string {
|
|
|
37
49
|
return `#${[f(0), f(8), f(4)].map(x => x.toString(16).padStart(2, '0')).join('')}`
|
|
38
50
|
}
|
|
39
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Converts a hex color to RGB format.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} hex - Hex color string (e.g., '#ff5733')
|
|
56
|
+
* @returns {object} RGB object with r, g, b values (0-255)
|
|
57
|
+
*/
|
|
40
58
|
export function hexToRGB(hex: string) {
|
|
41
59
|
const r = parseInt(hex.slice(1, 3), 16)
|
|
42
60
|
const g = parseInt(hex.slice(3, 5), 16)
|
|
@@ -44,6 +62,14 @@ export function hexToRGB(hex: string) {
|
|
|
44
62
|
return { r, g, b }
|
|
45
63
|
}
|
|
46
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Converts HSL values to RGB format.
|
|
67
|
+
*
|
|
68
|
+
* @param {number} h - Hue (0-360)
|
|
69
|
+
* @param {number} s - Saturation (0-100)
|
|
70
|
+
* @param {number} l - Lightness (0-100)
|
|
71
|
+
* @returns {object} RGB object with r, g, b values (0-255)
|
|
72
|
+
*/
|
|
47
73
|
export function hslToRGB(h: number, s: number, l: number) {
|
|
48
74
|
s /= 100
|
|
49
75
|
l /= 100
|
|
@@ -59,47 +85,56 @@ export function hslToRGB(h: number, s: number, l: number) {
|
|
|
59
85
|
}
|
|
60
86
|
}
|
|
61
87
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
export function generateColorScheme(
|
|
81
|
-
anchorHex: string,
|
|
82
|
-
prefix = 'primary',
|
|
83
|
-
lightnesses:typeof defaultLightnessMap = defaultLightnessMap,
|
|
84
|
-
alphas: typeof defaultAlphasMap = defaultAlphasMap,
|
|
85
|
-
): ColorMap {
|
|
86
|
-
const { h, s } = hexToHSL(anchorHex)
|
|
87
|
-
const baseRGB = hexToRGB(anchorHex)
|
|
88
|
-
|
|
89
|
-
const scheme: ColorMap = {}
|
|
90
|
-
|
|
91
|
-
Object.entries(lightnesses).forEach(([step, lightness]) => {
|
|
92
|
-
const rgb = hslToRGB(h, s, lightness)
|
|
93
|
-
scheme[`${prefix}Solid${step}`] = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 1.00)`
|
|
94
|
-
})
|
|
88
|
+
/**
|
|
89
|
+
* Converts RGB values to HSL format.
|
|
90
|
+
*
|
|
91
|
+
* @param {object} rgb - RGB object with r, g, b values (0-255)
|
|
92
|
+
* @returns {object} HSL object with h (0-360), s (0-100), l (0-100)
|
|
93
|
+
*/
|
|
94
|
+
export function rgbToHSL(rgb: { r: number; g: number; b: number }): { h: number; s: number; l: number } {
|
|
95
|
+
const r = rgb.r / 255
|
|
96
|
+
const g = rgb.g / 255
|
|
97
|
+
const b = rgb.b / 255
|
|
98
|
+
|
|
99
|
+
const max = Math.max(r, g, b)
|
|
100
|
+
const min = Math.min(r, g, b)
|
|
101
|
+
let h = 0
|
|
102
|
+
let s = 0
|
|
103
|
+
const l = (max + min) / 2
|
|
95
104
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
105
|
+
if (max !== min) {
|
|
106
|
+
const d = max - min
|
|
107
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
|
|
108
|
+
|
|
109
|
+
switch (max) {
|
|
110
|
+
case r:
|
|
111
|
+
h = (g - b) / d + (g < b ? 6 : 0)
|
|
112
|
+
break
|
|
113
|
+
case g:
|
|
114
|
+
h = (b - r) / d + 2
|
|
115
|
+
break
|
|
116
|
+
case b:
|
|
117
|
+
h = (r - g) / d + 4
|
|
118
|
+
break
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
h /= 6
|
|
122
|
+
}
|
|
99
123
|
|
|
100
|
-
return
|
|
124
|
+
return {
|
|
125
|
+
h: Math.round(h * 360),
|
|
126
|
+
s: Math.round(s * 100),
|
|
127
|
+
l: Math.round(l * 100)
|
|
128
|
+
}
|
|
101
129
|
}
|
|
102
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Calculates the relative luminance of an RGB color.
|
|
133
|
+
* Uses the WCAG formula for accessibility calculations.
|
|
134
|
+
*
|
|
135
|
+
* @param {object} rgb - RGB object with r, g, b values (0-255)
|
|
136
|
+
* @returns {number} Luminance value (0-1)
|
|
137
|
+
*/
|
|
103
138
|
export function getLuminance({ r, g, b }: { r: number; g: number; b: number }): number {
|
|
104
139
|
const [R, G, B] = [r, g, b].map(c => {
|
|
105
140
|
const channel = c / 255
|
|
@@ -111,6 +146,15 @@ export function getLuminance({ r, g, b }: { r: number; g: number; b: number }):
|
|
|
111
146
|
return 0.2126 * R + 0.7152 * G + 0.0722 * B
|
|
112
147
|
}
|
|
113
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Determines the best text color (dark or light) for a given background.
|
|
151
|
+
* Uses luminance calculation for optimal contrast.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} backgroundHex - Background hex color
|
|
154
|
+
* @param {string} darkColor - Dark text color option
|
|
155
|
+
* @param {string} lightColor - Light text color option
|
|
156
|
+
* @returns {string} Recommended text color
|
|
157
|
+
*/
|
|
114
158
|
export function getTextColor(backgroundHex: string, darkColor = 'black', lightColor = 'white'): string {
|
|
115
159
|
const rgb = hexToRGB(backgroundHex)
|
|
116
160
|
const luminance = getLuminance(rgb)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import rfdc from 'rfdc'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a deep clone of any JavaScript object or value.
|
|
5
|
+
* Uses rfdc (Really Fast Deep Clone) for optimal performance.
|
|
6
|
+
*
|
|
7
|
+
* @param {T} obj - The object or value to clone
|
|
8
|
+
* @returns {T} A deep copy of the input
|
|
9
|
+
*/
|
|
10
|
+
export const deepClone = rfdc()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep merge constructor from Fastify's optimized implementation.
|
|
3
|
+
* Returns a merge function that recursively merges objects with high performance.
|
|
4
|
+
* By default, arrays are concatenated and objects are deeply merged.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const merger = deepmerge()
|
|
8
|
+
* const result = merger(target, source)
|
|
9
|
+
*/
|
|
10
|
+
export { default as deepmerge } from '@fastify/deepmerge'
|
|
@@ -3,6 +3,13 @@ import { sha256 } from 'js-sha256'
|
|
|
3
3
|
const styleKey = '@styles-version'
|
|
4
4
|
const version = require('../../package.json')?.version
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Generates a SHA-256 hash from an array with automatic version injection.
|
|
8
|
+
* Appends package version to the array for cache invalidation purposes.
|
|
9
|
+
*
|
|
10
|
+
* @param {Array<any>} value - Array to be hashed
|
|
11
|
+
* @returns {string} SHA-256 hash string
|
|
12
|
+
*/
|
|
6
13
|
export const hashKey = (value: Array<any>): string => {
|
|
7
14
|
value.push({ [styleKey]: version })
|
|
8
15
|
|
package/src/tools/index.ts
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { compressToBase64, decompressFromBase64 } from 'lz-string'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Compresses any value to a Base64 string using LZ compression.
|
|
5
|
+
* Returns the original value if falsy.
|
|
6
|
+
*
|
|
7
|
+
* @param {any} value - Value to compress
|
|
8
|
+
* @returns {string|any} Compressed Base64 string or original falsy value
|
|
9
|
+
*/
|
|
10
|
+
export function compress(value: any): any {
|
|
11
|
+
if (!value) return value
|
|
12
|
+
|
|
13
|
+
return compressToBase64(JSON.stringify(value))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Decompresses a Base64 string back to its original value.
|
|
18
|
+
* Returns the original value if falsy.
|
|
19
|
+
*
|
|
20
|
+
* @param {any} value - Compressed Base64 string to decompress
|
|
21
|
+
* @returns {any} Original decompressed value or original falsy value
|
|
22
|
+
*/
|
|
23
|
+
export function decompress(value: any): any {
|
|
24
|
+
if (!value) return value
|
|
25
|
+
|
|
26
|
+
const decoded = decompressFromBase64(value)
|
|
27
|
+
|
|
28
|
+
return JSON.parse(decoded)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Utility object containing compress and decompress functions.
|
|
33
|
+
* Useful for data compression/decompression operations.
|
|
34
|
+
*/
|
|
35
|
+
export const minifier = {
|
|
36
|
+
compress,
|
|
37
|
+
decompress,
|
|
38
|
+
}
|