@codeleap/styles 6.2.3 → 6.8.0
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/dist/classes/Cacher.d.ts +87 -0
- package/dist/classes/Cacher.d.ts.map +1 -0
- package/dist/classes/StaleControl.d.ts +65 -0
- package/dist/classes/StaleControl.d.ts.map +1 -0
- package/dist/classes/StyleCache.d.ts +63 -0
- package/dist/classes/StyleCache.d.ts.map +1 -0
- package/dist/classes/StylePersistor.d.ts +52 -0
- package/dist/classes/StylePersistor.d.ts.map +1 -0
- package/dist/classes/StyleRegistry.d.ts +108 -0
- package/dist/classes/StyleRegistry.d.ts.map +1 -0
- package/dist/classes/index.d.ts +3 -0
- package/dist/classes/index.d.ts.map +1 -0
- package/dist/constants.d.ts +22 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/useCompositionStyles.d.ts +12 -0
- package/dist/hooks/useCompositionStyles.d.ts.map +1 -0
- package/dist/hooks/useNestedStylesByKey.d.ts +11 -0
- package/dist/hooks/useNestedStylesByKey.d.ts.map +1 -0
- package/dist/hooks/useStyleObserver.d.ts +9 -0
- package/dist/hooks/useStyleObserver.d.ts.map +1 -0
- package/dist/hooks/useTheme.d.ts +19 -0
- package/dist/hooks/useTheme.d.ts.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/lib/calc.d.ts +27 -0
- package/dist/lib/calc.d.ts.map +1 -0
- package/dist/lib/createStyles.d.ts +30 -0
- package/dist/lib/createStyles.d.ts.map +1 -0
- package/dist/lib/createTheme.d.ts +28 -0
- package/dist/lib/createTheme.d.ts.map +1 -0
- package/dist/lib/cssVariables.d.ts +35 -0
- package/dist/lib/cssVariables.d.ts.map +1 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/theme/generateColorScheme.d.ts +22 -0
- package/dist/theme/generateColorScheme.d.ts.map +1 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/themeStore.d.ts +106 -0
- package/dist/theme/themeStore.d.ts.map +1 -0
- package/dist/theme/validateTheme.d.ts +19 -0
- package/dist/theme/validateTheme.d.ts.map +1 -0
- package/dist/tools/colors.d.ts +70 -0
- package/dist/tools/colors.d.ts.map +1 -0
- package/dist/tools/deepClone.d.ts +7 -0
- package/dist/tools/deepClone.d.ts.map +1 -0
- package/dist/tools/deepmerge.d.ts +13 -0
- package/dist/tools/deepmerge.d.ts.map +1 -0
- package/dist/tools/hashKey.d.ts +8 -0
- package/dist/tools/hashKey.d.ts.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/minifier.d.ts +24 -0
- package/dist/tools/minifier.d.ts.map +1 -0
- package/dist/tools/multiplierProperty.d.ts +4 -0
- package/dist/tools/multiplierProperty.d.ts.map +1 -0
- package/dist/types/cache.d.ts +12 -0
- package/dist/types/cache.d.ts.map +1 -0
- package/dist/types/component.d.ts +58 -0
- package/dist/types/component.d.ts.map +1 -0
- package/dist/types/core.d.ts +77 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/icon.d.ts +15 -0
- package/dist/types/icon.d.ts.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/spacing.d.ts +28 -0
- package/dist/types/spacing.d.ts.map +1 -0
- package/dist/types/store.d.ts +12 -0
- package/dist/types/store.d.ts.map +1 -0
- package/dist/types/style.d.ts +42 -0
- package/dist/types/style.d.ts.map +1 -0
- package/dist/types/theme.d.ts +109 -0
- package/dist/types/theme.d.ts.map +1 -0
- package/dist/utils.d.ts +40 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/variants/borderCreator.d.ts +22 -0
- package/dist/variants/borderCreator.d.ts.map +1 -0
- package/dist/variants/createAppVariants.d.ts +18 -0
- package/dist/variants/createAppVariants.d.ts.map +1 -0
- package/dist/variants/defaultVariants.d.ts +140 -0
- package/dist/variants/defaultVariants.d.ts.map +1 -0
- package/dist/variants/dynamicVariants.d.ts +43 -0
- package/dist/variants/dynamicVariants.d.ts.map +1 -0
- package/dist/variants/index.d.ts +7 -0
- package/dist/variants/index.d.ts.map +1 -0
- package/dist/variants/mediaQuery.d.ts +30 -0
- package/dist/variants/mediaQuery.d.ts.map +1 -0
- package/dist/variants/spacing.d.ts +26 -0
- package/dist/variants/spacing.d.ts.map +1 -0
- package/package.json +19 -5
- package/src/classes/Cacher.ts +9 -9
- package/src/classes/StaleControl.ts +1 -1
- package/src/classes/StyleCache.ts +9 -3
- package/src/classes/StylePersistor.ts +11 -0
- package/src/classes/StyleRegistry.ts +124 -43
- package/src/classes/tests/StyleRegistry.spec.ts +169 -0
- package/src/constants.ts +14 -0
- package/src/hooks/useCompositionStyles.ts +9 -7
- package/src/hooks/useNestedStylesByKey.ts +8 -0
- package/src/hooks/useStyleObserver.ts +6 -5
- package/src/hooks/useTheme.ts +14 -0
- package/src/lib/calc.ts +13 -0
- package/src/lib/createStyles.ts +35 -4
- package/src/lib/createTheme.ts +74 -25
- package/src/lib/cssVariables.ts +74 -0
- package/src/lib/index.ts +2 -1
- package/src/lib/tests/createStyles.spec.ts +80 -23
- package/src/lib/tests/createStylesWithContext.spec.ts +108 -0
- package/src/tests/theme.ts +6 -2
- package/src/theme/generateColorScheme.ts +13 -10
- package/src/theme/tests/themeStore.spec.ts +72 -71
- package/src/theme/themeStore.ts +10 -7
- package/src/theme/validateTheme.ts +1 -1
- package/src/tools/colors.ts +24 -36
- package/src/tools/deepClone.ts +3 -5
- package/src/tools/deepmerge.ts +8 -6
- package/src/tools/hashKey.ts +4 -5
- package/src/tools/minifier.ts +11 -12
- package/src/tools/tests/deepClone.spec.ts +2 -2
- package/src/types/cache.ts +10 -0
- package/src/types/component.ts +41 -5
- package/src/types/core.ts +66 -6
- package/src/types/icon.ts +11 -0
- package/src/types/spacing.ts +21 -0
- package/src/types/store.ts +6 -0
- package/src/types/style.ts +37 -10
- package/src/types/theme.ts +37 -1
- package/src/utils.ts +34 -4
- package/src/variants/borderCreator.ts +14 -5
- package/src/variants/createAppVariants.ts +11 -0
- package/src/variants/defaultVariants.ts +28 -8
- package/src/variants/dynamicVariants.ts +76 -18
- package/src/variants/mediaQuery.ts +18 -0
- package/src/variants/spacing.ts +15 -1
- package/package.json.bak +0 -30
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'bun:test'
|
|
2
|
+
import { createStyles, createStylesWithContext, CONTEXT_FACTORY_SYMBOL } from '../createStyles'
|
|
3
|
+
import { themeStore } from '../../theme'
|
|
4
|
+
import { mockTheme, MockedTheme } from '../../tests/theme'
|
|
5
|
+
|
|
6
|
+
describe('createStylesWithContext (web — isBrowser: true)', () => {
|
|
7
|
+
let currentTheme: MockedTheme = null as any
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
mockTheme({ isBrowser: true })
|
|
11
|
+
currentTheme = themeStore.theme as unknown as MockedTheme
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('exposes the factory function via CONTEXT_FACTORY_SYMBOL', () => {
|
|
15
|
+
const factory = (theme: any, context: { isActive: boolean }) => ({
|
|
16
|
+
wrapper: { color: context.isActive ? theme.colors.neutralSo : theme.colors.secondary },
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const styles = createStylesWithContext(factory as any)
|
|
20
|
+
|
|
21
|
+
expect((styles as any)[CONTEXT_FACTORY_SYMBOL]).toBe(factory)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('plain createStyles proxy returns undefined for CONTEXT_FACTORY_SYMBOL', () => {
|
|
25
|
+
const styles = createStyles((theme: any) => ({
|
|
26
|
+
wrapper: { color: theme.colors.neutralSo },
|
|
27
|
+
}))
|
|
28
|
+
|
|
29
|
+
expect((styles as any)[CONTEXT_FACTORY_SYMBOL]).toBeUndefined()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('returns expected styles when accessed with default (empty) context', () => {
|
|
33
|
+
const styles = createStylesWithContext((theme: any, context: { multiplier?: number }) => ({
|
|
34
|
+
wrapper: { padding: (context.multiplier ?? 1) * 8 },
|
|
35
|
+
}))
|
|
36
|
+
|
|
37
|
+
expect(styles.wrapper).toEqual({ padding: 8 })
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('re-evaluates on every property access (proxy behavior)', () => {
|
|
41
|
+
let callCount = 0
|
|
42
|
+
|
|
43
|
+
const styles = createStylesWithContext((theme: any, context: {}) => {
|
|
44
|
+
callCount++
|
|
45
|
+
return { wrapper: { color: theme.colors.neutralSo } }
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
callCount = 0
|
|
49
|
+
|
|
50
|
+
styles.wrapper
|
|
51
|
+
styles.wrapper
|
|
52
|
+
styles.wrapper
|
|
53
|
+
|
|
54
|
+
expect(callCount).toBe(3)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('reflects theme changes on subsequent accesses', () => {
|
|
58
|
+
const styles = createStylesWithContext((theme: any, _context: {}) => ({
|
|
59
|
+
wrapper: { color: theme.colors.buttonRegularPrimaryBgDefault },
|
|
60
|
+
}))
|
|
61
|
+
|
|
62
|
+
// CSS var reference is static — same regardless of active scheme.
|
|
63
|
+
// The browser CSS cascade handles the actual color switch.
|
|
64
|
+
expect(styles.wrapper).toEqual({ color: 'var(--cl-buttonRegularPrimaryBgDefault)' })
|
|
65
|
+
|
|
66
|
+
currentTheme.setColorScheme('dark')
|
|
67
|
+
|
|
68
|
+
expect(styles.wrapper).toEqual({ color: 'var(--cl-buttonRegularPrimaryBgDefault)' })
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('returns undefined for non-existent keys', () => {
|
|
72
|
+
const styles = createStylesWithContext((_theme: any, _context: {}) => ({
|
|
73
|
+
wrapper: { color: 'red' },
|
|
74
|
+
}))
|
|
75
|
+
|
|
76
|
+
expect((styles as any).nonExistent).toBeUndefined()
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
describe('createStylesWithContext (RN — isBrowser: false)', () => {
|
|
81
|
+
let currentTheme: MockedTheme = null as any
|
|
82
|
+
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
mockTheme({ isBrowser: false })
|
|
85
|
+
currentTheme = themeStore.theme as unknown as MockedTheme
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('returns raw RGBA values when isBrowser is false', () => {
|
|
89
|
+
const styles = createStylesWithContext((_theme: any, _context: {}) => ({
|
|
90
|
+
wrapper: { color: _theme.colors.neutralSolid500 },
|
|
91
|
+
}))
|
|
92
|
+
|
|
93
|
+
expect(styles.wrapper).toEqual({ color: 'rgba(136, 136, 136, 1.00)' })
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('currentSchemeColors reflects active scheme on RN', () => {
|
|
97
|
+
const styles = createStylesWithContext((_theme: any, _context: {}) => ({
|
|
98
|
+
wrapper: { color: _theme.currentSchemeColors.buttonRegularPrimaryBgDefault },
|
|
99
|
+
}))
|
|
100
|
+
|
|
101
|
+
expect(styles.wrapper).toEqual({ color: 'rgba(43, 105, 122, 1.00)' })
|
|
102
|
+
|
|
103
|
+
currentTheme.setColorScheme('dark')
|
|
104
|
+
|
|
105
|
+
const darkValue = currentTheme.currentSchemeColors.buttonRegularPrimaryBgDefault as string
|
|
106
|
+
expect(styles.wrapper).toEqual({ color: darkValue })
|
|
107
|
+
})
|
|
108
|
+
})
|
package/src/tests/theme.ts
CHANGED
|
@@ -6,7 +6,11 @@ import darkMode from './colors/darkMode'
|
|
|
6
6
|
import { createTheme } from '../lib'
|
|
7
7
|
import { validateTheme } from '../theme'
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
type MockThemeOptions = {
|
|
10
|
+
isBrowser?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const mockTheme = ({ isBrowser = true }: MockThemeOptions = {}) => {
|
|
10
14
|
return createTheme(
|
|
11
15
|
validateTheme({
|
|
12
16
|
baseColors,
|
|
@@ -46,7 +50,7 @@ export const mockTheme = () => {
|
|
|
46
50
|
icons: {},
|
|
47
51
|
presets: {},
|
|
48
52
|
values: {},
|
|
49
|
-
isBrowser
|
|
53
|
+
isBrowser,
|
|
50
54
|
}),
|
|
51
55
|
{
|
|
52
56
|
set: (name, colorSchema) => {},
|
|
@@ -20,25 +20,28 @@ const defaultAlphasMap = Object.fromEntries(
|
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
23
|
+
* Derives a 20-token color ramp from a single anchor hex color.
|
|
24
|
+
*
|
|
25
|
+
* Produces two groups of tokens:
|
|
26
|
+
* - **10 solid tokens** (`{prefix}Solid100` … `{prefix}Solid1000`) — vary the lightness
|
|
27
|
+
* of the anchor's hue/saturation from ~95 L (lightest) to ~10 L (darkest).
|
|
28
|
+
* - **10 transparent tokens** (`{prefix}Transparent100` … `{prefix}Transparent1000`) —
|
|
29
|
+
* keep the anchor's exact RGB channels but increase alpha from 0.05 to 0.90.
|
|
30
|
+
*
|
|
31
|
+
* Default lightness steps: `[95, 85, 75, 60, 45, 30, 27, 21, 16, 10]`.
|
|
32
|
+
* Default alpha steps: `[0.05, 0.10, …, 0.90]`.
|
|
33
|
+
* Both can be overridden by passing custom maps (step → value).
|
|
31
34
|
*/
|
|
32
35
|
export function generateColorScheme(
|
|
33
36
|
anchorHex: string,
|
|
34
37
|
prefix = 'primary',
|
|
35
38
|
lightnesses:typeof defaultLightnessMap = defaultLightnessMap,
|
|
36
39
|
alphas: typeof defaultAlphasMap = defaultAlphasMap,
|
|
37
|
-
):
|
|
40
|
+
): Record<string, string> {
|
|
38
41
|
const { h, s } = colorTools.hexToHSL(anchorHex)
|
|
39
42
|
const baseRGB = colorTools.hexToRGB(anchorHex)
|
|
40
43
|
|
|
41
|
-
const scheme:
|
|
44
|
+
const scheme: Record<string, string> = {}
|
|
42
45
|
|
|
43
46
|
Object.entries(lightnesses).forEach(([step, lightness]) => {
|
|
44
47
|
const rgb = colorTools.hslToRGB(h, s, lightness)
|
|
@@ -14,7 +14,7 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
14
14
|
it('should handle theme management', () => {
|
|
15
15
|
const mockTheme = {
|
|
16
16
|
colors: { primary: '#007bff' },
|
|
17
|
-
spacing: { small: '8px' }
|
|
17
|
+
spacing: { small: '8px' },
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
store.setTheme(mockTheme)
|
|
@@ -39,21 +39,21 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
39
39
|
secondary: `#${(i * 2).toString(16).padStart(6, '0')}`,
|
|
40
40
|
background: `#${(i * 3).toString(16).padStart(6, '0')}`,
|
|
41
41
|
surface: `#${(i * 4).toString(16).padStart(6, '0')}`,
|
|
42
|
-
text: `#${(i * 5).toString(16).padStart(6, '0')}
|
|
42
|
+
text: `#${(i * 5).toString(16).padStart(6, '0')}`,
|
|
43
43
|
},
|
|
44
44
|
spacing: {
|
|
45
45
|
xs: `${i}px`,
|
|
46
46
|
sm: `${i * 2}px`,
|
|
47
47
|
md: `${i * 4}px`,
|
|
48
48
|
lg: `${i * 8}px`,
|
|
49
|
-
xl: `${i * 16}px
|
|
49
|
+
xl: `${i * 16}px`,
|
|
50
50
|
},
|
|
51
51
|
typography: {
|
|
52
52
|
fontSize: `${i}px`,
|
|
53
53
|
fontWeight: i % 900,
|
|
54
|
-
lineHeight: i / 100
|
|
54
|
+
lineHeight: i / 100,
|
|
55
55
|
},
|
|
56
|
-
iteration: i
|
|
56
|
+
iteration: i,
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
store.setTheme(theme)
|
|
@@ -75,27 +75,27 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
75
75
|
})
|
|
76
76
|
|
|
77
77
|
it('should handle massive color schemes with deep nesting', () => {
|
|
78
|
-
const colorSchemes = {}
|
|
78
|
+
const colorSchemes: Record<string, any> = {}
|
|
79
79
|
const schemeCount = 1000
|
|
80
80
|
const colorsPerScheme = 100
|
|
81
81
|
|
|
82
82
|
// Generate massive color schemes
|
|
83
83
|
for (let scheme = 0; scheme < schemeCount; scheme++) {
|
|
84
|
-
const colors = {}
|
|
84
|
+
const colors: Record<string, any> = {}
|
|
85
85
|
for (let color = 0; color < colorsPerScheme; color++) {
|
|
86
86
|
colors[`color_${color}`] = {
|
|
87
87
|
primary: `#${(scheme * color).toString(16).padStart(6, '0')}`,
|
|
88
88
|
variants: {
|
|
89
89
|
light: `#${(scheme * color + 100000).toString(16).padStart(6, '0')}`,
|
|
90
90
|
dark: `#${(scheme * color + 200000).toString(16).padStart(6, '0')}`,
|
|
91
|
-
medium: `#${(scheme * color + 300000).toString(16).padStart(6, '0')}
|
|
91
|
+
medium: `#${(scheme * color + 300000).toString(16).padStart(6, '0')}`,
|
|
92
92
|
},
|
|
93
93
|
states: {
|
|
94
94
|
hover: `#${(scheme * color + 400000).toString(16).padStart(6, '0')}`,
|
|
95
95
|
active: `#${(scheme * color + 500000).toString(16).padStart(6, '0')}`,
|
|
96
96
|
disabled: `#${(scheme * color + 600000).toString(16).padStart(6, '0')}`,
|
|
97
|
-
focus: `#${(scheme * color + 700000).toString(16).padStart(6, '0')}
|
|
98
|
-
}
|
|
97
|
+
focus: `#${(scheme * color + 700000).toString(16).padStart(6, '0')}`,
|
|
98
|
+
},
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
colorSchemes[`scheme_${scheme}`] = colors
|
|
@@ -120,8 +120,8 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
120
120
|
base: {
|
|
121
121
|
primary: '#000000',
|
|
122
122
|
secondary: '#111111',
|
|
123
|
-
tertiary: '#222222'
|
|
124
|
-
}
|
|
123
|
+
tertiary: '#222222',
|
|
124
|
+
},
|
|
125
125
|
})
|
|
126
126
|
|
|
127
127
|
for (let i = 0; i < operations; i++) {
|
|
@@ -130,14 +130,15 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
130
130
|
primary: `#${i.toString(16).padStart(6, '0')}`,
|
|
131
131
|
accent: `#${(i * 2).toString(16).padStart(6, '0')}`,
|
|
132
132
|
background: `#${(i * 3).toString(16).padStart(6, '0')}`,
|
|
133
|
-
customColor: `#${(i * 4).toString(16).padStart(6, '0')}
|
|
133
|
+
customColor: `#${(i * 4).toString(16).padStart(6, '0')}`,
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
// Inject scheme
|
|
137
137
|
const injected = store.injectColorScheme(schemeName, colors)
|
|
138
|
-
|
|
139
|
-
expect(
|
|
140
|
-
expect(
|
|
138
|
+
const scheme = injected[schemeName] as Record<string, string>
|
|
139
|
+
expect(scheme).toBeDefined()
|
|
140
|
+
expect(scheme.primary).toBe(colors.primary)
|
|
141
|
+
expect(scheme.secondary).toBe('#111111') // inherited
|
|
141
142
|
|
|
142
143
|
// Every 10th operation, eject some schemes
|
|
143
144
|
if (i % 10 === 0 && i > 0) {
|
|
@@ -150,17 +151,17 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
150
151
|
const endTime = performance.now()
|
|
151
152
|
const executionTime = endTime - startTime
|
|
152
153
|
|
|
153
|
-
expect(executionTime).toBeLessThan(
|
|
154
|
+
expect(executionTime).toBeLessThan(3500) // Should be fast
|
|
154
155
|
console.log(`Color scheme operations: ${operations} inject/eject operations in ${executionTime.toFixed(2)}ms`)
|
|
155
156
|
})
|
|
156
157
|
|
|
157
158
|
it('should handle massive variants with complex structures', () => {
|
|
158
159
|
const componentCount = 500
|
|
159
160
|
const variantsPerComponent = 50
|
|
160
|
-
const variants = {}
|
|
161
|
+
const variants: Record<string, any> = {}
|
|
161
162
|
|
|
162
163
|
for (let comp = 0; comp < componentCount; comp++) {
|
|
163
|
-
const componentVariants = {}
|
|
164
|
+
const componentVariants: Record<string, any> = {}
|
|
164
165
|
for (let variant = 0; variant < variantsPerComponent; variant++) {
|
|
165
166
|
componentVariants[`variant_${variant}`] = {
|
|
166
167
|
className: `comp-${comp}-var-${variant}`,
|
|
@@ -172,14 +173,14 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
172
173
|
margin: `${comp * variant}px`,
|
|
173
174
|
borderRadius: `${variant / 2}px`,
|
|
174
175
|
transform: `rotate(${variant}deg) scale(${1 + comp / 100})`,
|
|
175
|
-
boxShadow: `${variant}px ${comp}px ${comp + variant}px rgba(${comp}, ${variant}, ${comp + variant}, 0.${variant})
|
|
176
|
+
boxShadow: `${variant}px ${comp}px ${comp + variant}px rgba(${comp}, ${variant}, ${comp + variant}, 0.${variant})`,
|
|
176
177
|
},
|
|
177
178
|
states: {
|
|
178
179
|
hover: { opacity: 0.8 + (variant / 100) },
|
|
179
180
|
active: { transform: `scale(${1 + variant / 1000})` },
|
|
180
181
|
disabled: { opacity: 0.5 },
|
|
181
|
-
focus: { outline: `${variant}px solid #${comp.toString(16).padStart(6, '0')}` }
|
|
182
|
-
}
|
|
182
|
+
focus: { outline: `${variant}px solid #${comp.toString(16).padStart(6, '0')}` },
|
|
183
|
+
},
|
|
183
184
|
}
|
|
184
185
|
}
|
|
185
186
|
variants[`component_${comp}`] = componentVariants
|
|
@@ -206,7 +207,7 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
206
207
|
// Simultaneous updates
|
|
207
208
|
store.setTheme({
|
|
208
209
|
colors: { primary: `#${i.toString(16).padStart(6, '0')}` },
|
|
209
|
-
iteration: i
|
|
210
|
+
iteration: i,
|
|
210
211
|
})
|
|
211
212
|
|
|
212
213
|
store.setColorScheme(`scheme_${i % 50}`)
|
|
@@ -215,21 +216,21 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
215
216
|
[`component_${i}`]: {
|
|
216
217
|
[`variant_${i}`]: {
|
|
217
218
|
className: `test-${i}`,
|
|
218
|
-
styles: { color: `#${i.toString(16).padStart(6, '0')}` }
|
|
219
|
-
}
|
|
220
|
-
}
|
|
219
|
+
styles: { color: `#${i.toString(16).padStart(6, '0')}` },
|
|
220
|
+
},
|
|
221
|
+
},
|
|
221
222
|
})
|
|
222
223
|
|
|
223
224
|
store.injectColorScheme(`dynamic_${i % 100}`, {
|
|
224
225
|
primary: `#${(i * 2).toString(16).padStart(6, '0')}`,
|
|
225
|
-
secondary: `#${(i * 3).toString(16).padStart(6, '0')}
|
|
226
|
+
secondary: `#${(i * 3).toString(16).padStart(6, '0')}`,
|
|
226
227
|
})
|
|
227
228
|
|
|
228
229
|
// Verify consistency every 200 iterations
|
|
229
230
|
if (i % 200 === 0) {
|
|
230
231
|
expect(getThemeStore()?.iteration).toBe(i)
|
|
231
232
|
expect(store.colorScheme).toBe(`scheme_${i % 50}`)
|
|
232
|
-
expect(store.variants[`component_${i}`]).toBeDefined()
|
|
233
|
+
expect((store.variants as any)[`component_${i}`]).toBeDefined()
|
|
233
234
|
}
|
|
234
235
|
}
|
|
235
236
|
|
|
@@ -246,12 +247,12 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
246
247
|
describe('function storage and execution stress tests', () => {
|
|
247
248
|
it('should handle thousands of stored functions with complex computations', () => {
|
|
248
249
|
const functionCount = 5000
|
|
249
|
-
const theme = {
|
|
250
|
+
const theme: Record<string, Record<string, any>> = {
|
|
250
251
|
colors: {},
|
|
251
252
|
spacing: {},
|
|
252
253
|
calculators: {},
|
|
253
254
|
transformers: {},
|
|
254
|
-
validators: {}
|
|
255
|
+
validators: {},
|
|
255
256
|
}
|
|
256
257
|
|
|
257
258
|
const startTime = performance.now()
|
|
@@ -259,23 +260,23 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
259
260
|
// Store various types of functions
|
|
260
261
|
for (let i = 0; i < functionCount; i++) {
|
|
261
262
|
// Simple calculators
|
|
262
|
-
theme.calculators[`calc_${i}`] = (value) => value * (i + 1)
|
|
263
|
+
theme.calculators[`calc_${i}`] = (value: any) => value * (i + 1)
|
|
263
264
|
|
|
264
265
|
// Complex transformers
|
|
265
|
-
theme.transformers[`transform_${i}`] = (input) => {
|
|
266
|
+
theme.transformers[`transform_${i}`] = (input: any) => {
|
|
266
267
|
return {
|
|
267
268
|
scaled: input * Math.pow(2, i % 10),
|
|
268
269
|
offset: input + (i * 3.14159),
|
|
269
270
|
computed: Math.sin(input + i) * 100,
|
|
270
271
|
conditional: input > i ? input * 2 : input / 2,
|
|
271
|
-
recursive: i > 0 ? theme.calculators[`calc_${i - 1}`]?.(input) || input : input
|
|
272
|
+
recursive: i > 0 ? theme.calculators[`calc_${i - 1}`]?.(input) || input : input,
|
|
272
273
|
}
|
|
273
274
|
}
|
|
274
275
|
|
|
275
276
|
// Spacing functions
|
|
276
277
|
theme.spacing[`space_${i}`] = (multiplier = 1) => `${i * 8 * multiplier}px`
|
|
277
278
|
|
|
278
|
-
// Color functions
|
|
279
|
+
// Color functions
|
|
279
280
|
theme.colors[`color_${i}`] = (opacity = 1) => {
|
|
280
281
|
const r = (i * 37) % 256
|
|
281
282
|
const g = (i * 73) % 256
|
|
@@ -284,7 +285,7 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
284
285
|
}
|
|
285
286
|
|
|
286
287
|
// Validators
|
|
287
|
-
theme.validators[`validate_${i}`] = (value) => {
|
|
288
|
+
theme.validators[`validate_${i}`] = (value: any) => {
|
|
288
289
|
return value !== null &&
|
|
289
290
|
value !== undefined &&
|
|
290
291
|
(typeof value === 'number' ? value >= i : value.length >= i % 10)
|
|
@@ -324,7 +325,7 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
324
325
|
// Test some complex chaining every 100 iterations
|
|
325
326
|
if (i % 100 === 0) {
|
|
326
327
|
const chainResult = getThemeStore()?.transformers[`transform_${i}`]?.(
|
|
327
|
-
getThemeStore()?.calculators[`calc_${i}`]?.(testValue) || 0
|
|
328
|
+
getThemeStore()?.calculators[`calc_${i}`]?.(testValue) || 0,
|
|
328
329
|
)
|
|
329
330
|
if (chainResult) executionResults++
|
|
330
331
|
}
|
|
@@ -347,15 +348,15 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
347
348
|
|
|
348
349
|
// Create deeply nested function chains
|
|
349
350
|
const theme = {
|
|
350
|
-
fibonacci: {},
|
|
351
|
-
factorial: {},
|
|
352
|
-
compose: {},
|
|
353
|
-
pipeline: {}
|
|
351
|
+
fibonacci: {} as Record<string, any>,
|
|
352
|
+
factorial: {} as Record<string, any>,
|
|
353
|
+
compose: {} as Record<string, any>,
|
|
354
|
+
pipeline: {} as Record<string, any>,
|
|
354
355
|
}
|
|
355
356
|
|
|
356
357
|
// Fibonacci functions
|
|
357
358
|
for (let i = 0; i < depth; i++) {
|
|
358
|
-
theme.fibonacci[`fib_${i}`] = (n) => {
|
|
359
|
+
theme.fibonacci[`fib_${i}`] = (n: any) => {
|
|
359
360
|
if (n <= 1) return n
|
|
360
361
|
if (i > 0 && theme.fibonacci[`fib_${i - 1}`]) {
|
|
361
362
|
return theme.fibonacci[`fib_${i - 1}`](n - 1) + theme.fibonacci[`fib_${i - 1}`](n - 2)
|
|
@@ -366,7 +367,7 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
366
367
|
|
|
367
368
|
// Factorial chains
|
|
368
369
|
for (let i = 0; i < depth; i++) {
|
|
369
|
-
theme.factorial[`fact_${i}`] = (n) => {
|
|
370
|
+
theme.factorial[`fact_${i}`] = (n: any) => {
|
|
370
371
|
if (n <= 1) return 1
|
|
371
372
|
const prevFact = theme.factorial[`fact_${Math.max(0, i - 1)}`]
|
|
372
373
|
return prevFact ? n * prevFact(n - 1) : n
|
|
@@ -375,7 +376,7 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
375
376
|
|
|
376
377
|
// Function composition
|
|
377
378
|
for (let i = 0; i < depth; i++) {
|
|
378
|
-
theme.compose[`comp_${i}`] = (value) => {
|
|
379
|
+
theme.compose[`comp_${i}`] = (value: any) => {
|
|
379
380
|
let result = value
|
|
380
381
|
for (let j = 0; j <= i && j < 10; j++) {
|
|
381
382
|
result = Math.sqrt(Math.abs(result * (j + 1)))
|
|
@@ -386,13 +387,13 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
386
387
|
|
|
387
388
|
// Pipeline functions
|
|
388
389
|
for (let i = 0; i < depth; i++) {
|
|
389
|
-
theme.pipeline[`pipe_${i}`] = (input) => {
|
|
390
|
+
theme.pipeline[`pipe_${i}`] = (input: any) => {
|
|
390
391
|
const steps = [
|
|
391
|
-
(x) => x * 2,
|
|
392
|
-
(x) => x + i,
|
|
393
|
-
(x) => Math.pow(x, 1.5),
|
|
394
|
-
(x) => x % 1000,
|
|
395
|
-
(x) => theme.compose[`comp_${Math.min(i, depth - 1)}`]?.(x) || x
|
|
392
|
+
(x: any) => x * 2,
|
|
393
|
+
(x: any) => x + i,
|
|
394
|
+
(x: any) => Math.pow(x, 1.5),
|
|
395
|
+
(x: any) => x % 1000,
|
|
396
|
+
(x: any) => theme.compose[`comp_${Math.min(i, depth - 1)}`]?.(x) || x,
|
|
396
397
|
]
|
|
397
398
|
return steps.reduce((acc, fn) => fn(acc), input)
|
|
398
399
|
}
|
|
@@ -427,7 +428,7 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
427
428
|
// Test chained execution
|
|
428
429
|
if (i % 50 === 0) {
|
|
429
430
|
const chainedResult = getThemeStore()?.pipeline[`pipe_${i % depth}`]?.(
|
|
430
|
-
getThemeStore()?.compose[`comp_${i % depth}`]?.(testValue) || 0
|
|
431
|
+
getThemeStore()?.compose[`comp_${i % depth}`]?.(testValue) || 0,
|
|
431
432
|
)
|
|
432
433
|
if (typeof chainedResult === 'number') successfulExecutions++
|
|
433
434
|
}
|
|
@@ -449,27 +450,27 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
449
450
|
const factoryCount = 1000
|
|
450
451
|
const generatedPerFactory = 10
|
|
451
452
|
|
|
452
|
-
const theme = {
|
|
453
|
+
const theme: Record<string, Record<string, any>> = {
|
|
453
454
|
factories: {},
|
|
454
455
|
generated: {},
|
|
455
|
-
dynamicComputed: {}
|
|
456
|
+
dynamicComputed: {},
|
|
456
457
|
}
|
|
457
458
|
|
|
458
459
|
const startTime = performance.now()
|
|
459
460
|
|
|
460
461
|
// Create function factories
|
|
461
462
|
for (let i = 0; i < factoryCount; i++) {
|
|
462
|
-
theme.factories[`factory_${i}`] = (config) => {
|
|
463
|
+
theme.factories[`factory_${i}`] = (config: any) => {
|
|
463
464
|
return {
|
|
464
|
-
calculator: (value) => value * (config.multiplier || 1) + (config.offset || 0),
|
|
465
|
-
validator: (value) => value >= (config.min || 0) && value <= (config.max || 1000),
|
|
466
|
-
transformer: (value) => ({
|
|
465
|
+
calculator: (value: any) => value * (config.multiplier || 1) + (config.offset || 0),
|
|
466
|
+
validator: (value: any) => value >= (config.min || 0) && value <= (config.max || 1000),
|
|
467
|
+
transformer: (value: any) => ({
|
|
467
468
|
original: value,
|
|
468
469
|
scaled: value * (config.scale || 1),
|
|
469
470
|
formatted: `${config.prefix || ''}${value}${config.suffix || ''}`,
|
|
470
|
-
computed: Math.pow(value, config.power || 1)
|
|
471
|
+
computed: Math.pow(value, config.power || 1),
|
|
471
472
|
}),
|
|
472
|
-
composer: (...fns) => (value) => fns.reduce((acc, fn) => fn(acc), value)
|
|
473
|
+
composer: (...fns: any[]) => (value: any) => fns.reduce((acc, fn) => fn(acc), value),
|
|
473
474
|
}
|
|
474
475
|
}
|
|
475
476
|
|
|
@@ -483,14 +484,14 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
483
484
|
max: i * 100,
|
|
484
485
|
prefix: `gen_${i}_`,
|
|
485
486
|
suffix: `_${j}`,
|
|
486
|
-
power: 1 + (j / 5)
|
|
487
|
+
power: 1 + (j / 5),
|
|
487
488
|
}
|
|
488
489
|
|
|
489
490
|
const generatedFunctions = theme.factories[`factory_${i}`](config)
|
|
490
491
|
theme.generated[`gen_${i}_${j}`] = generatedFunctions
|
|
491
492
|
|
|
492
493
|
// Create dynamic computed values
|
|
493
|
-
theme.dynamicComputed[`comp_${i}_${j}`] = (input) => {
|
|
494
|
+
theme.dynamicComputed[`comp_${i}_${j}`] = (input: any) => {
|
|
494
495
|
const calc = generatedFunctions.calculator(input)
|
|
495
496
|
const valid = generatedFunctions.validator(calc)
|
|
496
497
|
const transform = generatedFunctions.transformer(calc)
|
|
@@ -499,7 +500,7 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
499
500
|
calculated: calc,
|
|
500
501
|
isValid: valid,
|
|
501
502
|
transformed: transform,
|
|
502
|
-
final: valid ? transform.computed : 0
|
|
503
|
+
final: valid ? transform.computed : 0,
|
|
503
504
|
}
|
|
504
505
|
}
|
|
505
506
|
}
|
|
@@ -543,9 +544,9 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
543
544
|
const generated = getThemeStore()?.generated[`gen_${i}_${j}`]
|
|
544
545
|
if (generated?.composer) {
|
|
545
546
|
const composed = generated.composer(
|
|
546
|
-
(x) => x * 2,
|
|
547
|
-
(x) => x + 10,
|
|
548
|
-
generated.calculator
|
|
547
|
+
(x: any) => x * 2,
|
|
548
|
+
(x: any) => x + 10,
|
|
549
|
+
generated.calculator,
|
|
549
550
|
)(testValue)
|
|
550
551
|
if (typeof composed === 'number') executionCount++
|
|
551
552
|
}
|
|
@@ -577,11 +578,11 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
577
578
|
|
|
578
579
|
for (let set = 0; set < hugeSets; set++) {
|
|
579
580
|
// Create massive theme
|
|
580
|
-
const hugeTheme = {
|
|
581
|
+
const hugeTheme: Record<string, Record<string, any>> = {
|
|
581
582
|
colors: {},
|
|
582
583
|
spacing: {},
|
|
583
584
|
typography: {},
|
|
584
|
-
components: {}
|
|
585
|
+
components: {},
|
|
585
586
|
}
|
|
586
587
|
|
|
587
588
|
for (let i = 0; i < itemsPerSet; i++) {
|
|
@@ -590,16 +591,16 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
590
591
|
hugeTheme.typography[`font_${i}`] = `${i}px`
|
|
591
592
|
hugeTheme.components[`comp_${i}`] = {
|
|
592
593
|
styles: { margin: `${i}px` },
|
|
593
|
-
variants: Array.from({ length: 10 }, (_, j) => `variant_${j}`)
|
|
594
|
+
variants: Array.from({ length: 10 }, (_, j) => `variant_${j}`),
|
|
594
595
|
}
|
|
595
596
|
}
|
|
596
597
|
|
|
597
598
|
store.setTheme(hugeTheme)
|
|
598
599
|
|
|
599
600
|
// Create massive color schemes
|
|
600
|
-
const hugeColorSchemes = {}
|
|
601
|
+
const hugeColorSchemes: Record<string, any> = {}
|
|
601
602
|
for (let i = 0; i < itemsPerSet; i++) {
|
|
602
|
-
const scheme = {}
|
|
603
|
+
const scheme: Record<string, any> = {}
|
|
603
604
|
for (let j = 0; j < 50; j++) {
|
|
604
605
|
scheme[`color_${j}`] = `#${(i * j).toString(16).padStart(6, '0')}`
|
|
605
606
|
}
|
|
@@ -630,7 +631,7 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
630
631
|
for (let i = 0; i < updates; i++) {
|
|
631
632
|
themeStore.setTheme({
|
|
632
633
|
colors: { primary: `#${i.toString(16).padStart(6, '0')}` },
|
|
633
|
-
iteration: i
|
|
634
|
+
iteration: i,
|
|
634
635
|
})
|
|
635
636
|
themeStore.setColorScheme(`scheme_${i % 100}`)
|
|
636
637
|
|
|
@@ -681,7 +682,7 @@ describe('ThemeStore - Heavy Load Tests', () => {
|
|
|
681
682
|
for (let i = 0; i < updates; i++) {
|
|
682
683
|
themeStore.setTheme({
|
|
683
684
|
colors: { primary: `#${i.toString(16).padStart(6, '0')}` },
|
|
684
|
-
iteration: i
|
|
685
|
+
iteration: i,
|
|
685
686
|
})
|
|
686
687
|
}
|
|
687
688
|
|
package/src/theme/themeStore.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { map, computed, atom } from 'nanostores'
|
|
|
5
5
|
* Theme state interface containing theme and color scheme information.
|
|
6
6
|
*/
|
|
7
7
|
export type ThemeState = {
|
|
8
|
-
theme:
|
|
8
|
+
theme: ITheme | null
|
|
9
9
|
colorScheme: string | null
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -18,9 +18,9 @@ export class ThemeStore {
|
|
|
18
18
|
|
|
19
19
|
public readonly colorSchemeStore = atom<string | null>(null)
|
|
20
20
|
|
|
21
|
-
public readonly themeStore =
|
|
22
|
-
|
|
23
|
-
public readonly variantsStore = map<IAppVariants>({})
|
|
21
|
+
public readonly themeStore = atom<ITheme | null>(null)
|
|
22
|
+
|
|
23
|
+
public readonly variantsStore = map<IAppVariants>({} as IAppVariants)
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Gets the current theme.
|
|
@@ -35,7 +35,8 @@ export class ThemeStore {
|
|
|
35
35
|
* @returns {AppTheme<Theme>} Current theme cast to AppTheme type
|
|
36
36
|
*/
|
|
37
37
|
get themeTyped() {
|
|
38
|
-
|
|
38
|
+
const theme = this.themeStore.get()
|
|
39
|
+
return theme ? theme as unknown as AppTheme<Theme> : null
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
/**
|
|
@@ -159,8 +160,10 @@ export class ThemeStore {
|
|
|
159
160
|
export const themeStore = new ThemeStore()
|
|
160
161
|
|
|
161
162
|
/**
|
|
162
|
-
*
|
|
163
|
-
*
|
|
163
|
+
* Derived nanostores `computed` that combines `themeStore` and `colorSchemeStore`
|
|
164
|
+
* into a single `ThemeState` atom. Components subscribe to this via `useTheme`
|
|
165
|
+
* so they re-render only when either the theme object or the active color scheme
|
|
166
|
+
* changes — not on every `ThemeStore` method call.
|
|
164
167
|
*/
|
|
165
168
|
export const themeStoreComputed = computed([
|
|
166
169
|
themeStore.themeStore,
|
|
@@ -22,7 +22,7 @@ export function validateTheme<T extends Theme>(theme: T) {
|
|
|
22
22
|
|
|
23
23
|
const requiredColors = Object.keys(colors)
|
|
24
24
|
|
|
25
|
-
const mergedAlternateColors = {}
|
|
25
|
+
const mergedAlternateColors: Record<string, any> = {}
|
|
26
26
|
|
|
27
27
|
if (alternateColors) {
|
|
28
28
|
for (const [colorSchemeName, colorSchemeColors] of Object.entries(alternateColors)) {
|