@codeleap/styles 5.5.0 → 5.5.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/styles",
3
- "version": "5.5.0",
3
+ "version": "5.5.3",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -9,7 +9,7 @@
9
9
  "directory": "packages/styles"
10
10
  },
11
11
  "devDependencies": {
12
- "@codeleap/config": "5.5.0",
12
+ "@codeleap/config": "5.5.3",
13
13
  "ts-node-dev": "^1.1.8"
14
14
  },
15
15
  "scripts": {
@@ -26,9 +26,11 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@fastify/deepmerge": "1.3.0",
29
+ "@nanostores/persistent": "^1.1.0",
30
+ "@nanostores/react": "^1.0.0",
29
31
  "js-sha256": "0.11.0",
30
32
  "lz-string": "^1.5.0",
31
- "rfdc": "^1.4.1",
32
- "zustand": "4.5.0"
33
+ "nanostores": "^1.0.1",
34
+ "rfdc": "^1.4.1"
33
35
  }
34
36
  }
package/package.json.bak CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/styles",
3
- "version": "5.5.0",
3
+ "version": "5.5.3",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -26,9 +26,11 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@fastify/deepmerge": "1.3.0",
29
+ "@nanostores/persistent": "^1.1.0",
30
+ "@nanostores/react": "^1.0.0",
29
31
  "js-sha256": "0.11.0",
30
32
  "lz-string": "^1.5.0",
31
- "rfdc": "^1.4.1",
32
- "zustand": "4.5.0"
33
+ "nanostores": "^1.0.1",
34
+ "rfdc": "^1.4.1"
33
35
  }
34
36
  }
package/src/index.ts CHANGED
@@ -3,5 +3,6 @@ import './lib/utils'
3
3
 
4
4
  export * from './lib'
5
5
  export * from './types'
6
+ export * from './tools'
6
7
 
7
8
  export { default as deepmerge } from '@fastify/deepmerge'
package/src/lib/Cacher.ts CHANGED
@@ -1,9 +1,7 @@
1
- import { create } from 'zustand'
2
- import { createJSONStorage, persist, StateStorage } from 'zustand/middleware'
3
- import { AnyRecord } from '../types'
4
1
  import { CacheType } from '../types/cache'
5
2
  import { StyleConstants } from './constants'
6
3
  import { hashKey } from './hashKey'
4
+ import { StateStorage } from '../types/store'
7
5
 
8
6
  function getStaleTime() {
9
7
  const time = 7
@@ -15,23 +13,15 @@ function getStaleTime() {
15
13
  return currentTime
16
14
  }
17
15
 
18
- type StorePersistor = {
19
- cached: AnyRecord
20
- cacheFor: (key: string, value: any) => void
21
- staleTime: Date
22
- reset: () => void
23
- }
24
-
25
16
  export class Cache<T extends any = any> {
26
17
  cache: Record<string, T> = {}
27
18
 
28
- persistor = null
19
+ get persistKeyCache() {
20
+ return `@styles.caches.${this.registryName}.cache`
21
+ }
29
22
 
30
- store: StorePersistor = {
31
- cached: {},
32
- cacheFor: () => null,
33
- reset: () => null,
34
- staleTime: null
23
+ get persistKeyStaleTime() {
24
+ return `@styles.caches.${this.registryName}.staleTime`
35
25
  }
36
26
 
37
27
  constructor(
@@ -39,23 +29,21 @@ export class Cache<T extends any = any> {
39
29
  private storage: StateStorage = null,
40
30
  public persistCache: boolean = !!storage,
41
31
  ) {
42
- if (!persistCache) return
32
+ if (!persistCache || !StyleConstants.STORE_CACHE_ENABLED) return
43
33
 
44
- this.createPersistor(registryName)
45
-
46
- if (!StyleConstants.STORE_CACHE_ENABLED || !this.persistor) return
34
+ const { persistedCache, persistedStaleTime } = this.loadStorage()
47
35
 
48
36
  const currentTime = new Date()
49
- const staleTime = new Date(this.store.staleTime)
50
37
 
51
- const isStaled = currentTime > staleTime
38
+ const isStaled = currentTime > persistedStaleTime
52
39
 
53
40
  if (isStaled) {
54
- this.store.reset()
41
+ this.clearStorage()
55
42
  return
56
43
  }
57
44
 
58
- this.setCache(this.store.cached)
45
+ this.setCache(persistedCache)
46
+ this.storeStaleTime(persistedStaleTime)
59
47
  }
60
48
 
61
49
  keyFor(cacheBaseKey: string, data: Array<any> | any) {
@@ -72,60 +60,53 @@ export class Cache<T extends any = any> {
72
60
 
73
61
  cacheFor(key: string, cache: T) {
74
62
  this.cache[key] = cache
75
- if (this.persistCache) this.store.cacheFor(key, cache)
63
+ if (this.persistCache) this.storeCache()
76
64
  return cache
77
65
  }
78
66
 
67
+ // utils
68
+
79
69
  setCache(cache: Record<string, T>) {
80
70
  this.cache = cache
81
71
  }
82
72
 
83
73
  clear() {
84
74
  this.cache = {}
85
- if (this.persistCache) this.persistor.persist.clearStorage()
75
+ this.clearStorage()
86
76
  }
87
77
 
88
- createPersistor(registryName: string) {
89
- if (!this.persistCache) return null
90
-
91
- const persistor = create(persist<StorePersistor>(
92
- (set, get) => ({
93
- staleTime: getStaleTime(),
94
- cached: {},
95
- cacheFor: (key, value) => set(store => {
96
- const cached = store.cached
97
-
98
- cached[key] = value
99
-
100
- return {
101
- cached
102
- }
103
- }),
104
- reset: () => set({
105
- cached: {},
106
- staleTime: getStaleTime()
107
- })
108
- }),
109
- {
110
- name: `@styles.caches.${registryName}`,
111
- version: StyleConstants.STORES_PERSIST_VERSION,
112
- migrate: (persistedState: StorePersistor, version) => {
113
- if (version != StyleConstants.STORES_PERSIST_VERSION) {
114
- persistedState.staleTime = getStaleTime()
115
- persistedState.cached = {}
116
-
117
- return persistedState
118
- }
119
-
120
- return persistedState as StorePersistor
121
- },
122
- storage: createJSONStorage(() => this.storage),
123
- }
124
- ))
125
-
126
- this.persistor = persistor
127
- this.store = persistor.getState()
128
-
129
- return persistor
78
+ // storage
79
+
80
+ loadStorage() {
81
+ if (!this.persistCache) return
82
+
83
+ const persistedStaleTime = this.storage.getItem(this.persistKeyStaleTime)
84
+ const persistedCache = JSON.parse(this.storage.getItem(this.persistKeyCache))
85
+
86
+ return {
87
+ persistedStaleTime: !persistedStaleTime ? getStaleTime() : new Date(persistedStaleTime),
88
+ persistedCache,
89
+ }
90
+ }
91
+
92
+ clearStorage() {
93
+ if (!this.persistCache) return
94
+
95
+ this.storage.removeItem(this.persistKeyStaleTime)
96
+ this.storage.removeItem(this.persistKeyCache)
97
+ }
98
+
99
+ storeCache(cache: Record<string, T> = null) {
100
+ if (!this.persistCache) return
101
+
102
+ const value = JSON.stringify(cache ?? this.cache)
103
+ this.storage.setItem(this.persistKeyCache, value)
104
+ }
105
+
106
+ storeStaleTime(staleTime: Date) {
107
+ if (!this.persistCache) return
108
+
109
+ const value = staleTime.toISOString()
110
+ this.storage.setItem(this.persistKeyStaleTime, value)
130
111
  }
131
112
  }
@@ -3,7 +3,7 @@ import { hashKey } from './hashKey'
3
3
  import { StyleConstants } from './constants'
4
4
  import { CacheType } from '../types/cache'
5
5
  import { minifier } from './minifier'
6
- import { StateStorage } from 'zustand/middleware'
6
+ import { StateStorage } from '../types/store'
7
7
 
8
8
  export class StyleCache {
9
9
  baseKey: string
@@ -1,4 +1,4 @@
1
- import { StateStorage } from 'zustand/middleware'
1
+ import { StateStorage } from '../types/store'
2
2
  import { minifier } from './minifier'
3
3
 
4
4
  export type StoragePersistor = {
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable dot-notation */
2
2
  import { AnyRecord, AnyStyledComponent, ICSS, ITheme, StyleAggregator, StyleProp, VariantStyleSheet } from '../types'
3
- import { ThemeStore, themeStore } from './themeStore'
3
+ import { themeStore } from './themeStore'
4
4
  import deepmerge from '@fastify/deepmerge'
5
5
  import { MultiplierFunction } from './spacing'
6
6
  import { defaultVariants } from './defaultVariants'
@@ -8,8 +8,8 @@ import { dynamicVariants } from './dynamicVariants'
8
8
  import { capitalize, ignoredStyleKeys, isSpacingKey } from './utils'
9
9
  import { StyleCache } from './StyleCache'
10
10
  import { minifier } from './minifier'
11
- import { StateStorage } from 'zustand/middleware'
12
11
  import rfdc from 'rfdc'
12
+ import { StateStorage } from '../types/store'
13
13
 
14
14
  const deepClone = rfdc()
15
15
 
@@ -22,20 +22,16 @@ export class CodeleapStyleRegistry {
22
22
 
23
23
  components: Record<string, AnyStyledComponent> = {}
24
24
 
25
- private theme: ThemeStore
26
-
27
25
  private styleCache: StyleCache
28
26
 
29
27
  constructor(storage: StateStorage) {
30
28
  this.styleCache = new StyleCache(storage)
31
29
 
32
- this.theme = themeStore.getState()
33
-
34
30
  this.registerCommonVariants()
35
31
 
36
- const currentColorScheme = this.theme?.current?.['currentColorScheme']?.() ?? this.theme?.colorScheme ?? 'default'
32
+ const currentColorScheme = themeStore.theme?.['currentColorScheme']?.() ?? themeStore.colorScheme ?? 'default'
37
33
 
38
- this.styleCache.registerBaseKey([currentColorScheme, this.theme.current, this.commonVariants])
34
+ this.styleCache.registerBaseKey([currentColorScheme, themeStore.theme, this.commonVariants])
39
35
  }
40
36
 
41
37
  computeCommonVariantStyle(componentName: string, variant: string, component = null) {
@@ -47,7 +43,7 @@ export class CodeleapStyleRegistry {
47
43
  }
48
44
  }
49
45
 
50
- const theme = this.theme.current
46
+ const theme = themeStore.theme
51
47
 
52
48
  let mediaQuery = null
53
49
 
@@ -104,7 +100,7 @@ export class CodeleapStyleRegistry {
104
100
  return cache.value
105
101
  }
106
102
 
107
- const theme = this.theme.current
103
+ const theme = themeStore.theme
108
104
 
109
105
  const variantStyles = variants.map((variant) => {
110
106
  if (!!stylesheet[variant]) {
@@ -294,7 +290,7 @@ export class CodeleapStyleRegistry {
294
290
  const [breakpoint, query] = responsiveKey?.includes(':') ? responsiveKey?.split(':') : [responsiveKey, 'down']
295
291
 
296
292
  // @ts-expect-error - media not has type
297
- const mediaQuery = this.theme.current.media?.[query]?.(breakpoint)
293
+ const mediaQuery = themeStore.theme?.media?.[query]?.(breakpoint)
298
294
 
299
295
  return mediaQuery
300
296
  }
@@ -471,11 +467,11 @@ export class CodeleapStyleRegistry {
471
467
  }
472
468
 
473
469
  registerCommonVariants() {
474
- const spacingVariants = this.theme.current?.['spacing']
470
+ const spacingVariants = themeStore.theme?.['spacing']
475
471
 
476
- const insetVariants = this.theme.current?.['inset']
472
+ const insetVariants = themeStore.theme?.['inset']
477
473
 
478
- const appVariants = this.theme.variants
474
+ const appVariants = themeStore.variants
479
475
 
480
476
  const commonVariants = deepmerge({ all: true })(
481
477
  defaultVariants,
@@ -516,11 +512,9 @@ export class CodeleapStyleRegistry {
516
512
  }
517
513
 
518
514
  update() {
519
- this.theme = themeStore.getState()
520
-
521
- const currentColorScheme = this.theme?.current?.['currentColorScheme']?.() ?? this.theme?.colorScheme ?? 'default'
515
+ const currentColorScheme = themeStore.theme?.['currentColorScheme']?.() ?? themeStore.colorScheme ?? 'default'
522
516
 
523
- this.styleCache.registerBaseKey([currentColorScheme, this.theme.current, this.commonVariants])
517
+ this.styleCache.registerBaseKey([currentColorScheme, themeStore.theme, this.commonVariants])
524
518
  }
525
519
 
526
520
  private copyStyle(style: any) {
@@ -529,7 +523,7 @@ export class CodeleapStyleRegistry {
529
523
 
530
524
  createStyles<K extends string = string>(styles: Record<K, StyleProp<AnyRecord, ''>> | ((theme: ITheme) => Record<K, StyleProp<AnyRecord, ''>>)): Record<K, ICSS> {
531
525
  const compute = () => {
532
- const current = themeStore.getState().current
526
+ const current = themeStore.theme
533
527
 
534
528
  const stylesObj = typeof styles === 'function' ? styles(current) : styles
535
529
 
@@ -1,4 +1,4 @@
1
- import { AppTheme, IColors, ICSS, Theme } from '../types'
1
+ import { IColors, ICSS } from '../types'
2
2
  import { borderDirection } from './dynamicVariants'
3
3
  import { themeStore } from './themeStore'
4
4
  import { capitalize } from './utils'
@@ -21,7 +21,7 @@ export const borderCreator: BorderCreator = (args) => {
21
21
  directions = ['left', 'top', 'bottom', 'right'],
22
22
  } = args
23
23
 
24
- const theme = themeStore.getState().current as AppTheme<Theme>
24
+ const theme = themeStore.themeTyped
25
25
 
26
26
  const color = theme?.baseColors?.[colorKey] ?? colorKey
27
27
 
@@ -6,7 +6,7 @@ type AppVariantsMap = {
6
6
  }
7
7
 
8
8
  export function createAppVariants<T extends AppVariantsMap>(variants: T) {
9
- themeStore.setState({ variants })
9
+ themeStore.setVariants(variants)
10
10
 
11
11
  return variants
12
12
  }
@@ -9,7 +9,7 @@ export function createStyles<K extends string, V extends Value = {}>(
9
9
  styles: StylesShape<K, V> | ((theme: ITheme) => StylesShape<K, V>),
10
10
  ) {
11
11
  const compute = () => {
12
- const current = themeStore.getState().current
12
+ const current = themeStore.theme
13
13
 
14
14
  if (typeof styles === 'function') {
15
15
  return !current ? {} as StylesShape<K, V> : styles(current)
@@ -1,4 +1,4 @@
1
- import { AppTheme, ColorScheme, Theme } from '../types'
1
+ import { AppTheme, Theme } from '../types'
2
2
  import { borderCreator } from './borderCreator'
3
3
  import { createMediaQueries } from './mediaQuery'
4
4
  import { multiplierProperty } from './multiplierProperty'
@@ -6,12 +6,15 @@ import { defaultVariants } from './defaultVariants'
6
6
  import { spacingFactory } from './spacing'
7
7
  import { themeStore } from './themeStore'
8
8
 
9
- type ColorSchemaPersistor = {
10
- get: () => string
11
- set: (colorSchema: string) => void
9
+ type ThemePersistor = {
10
+ get: (name: string) => any
11
+ set: (name: string, value: any) => void
12
12
  }
13
13
 
14
- export const createTheme = <T extends Theme>(theme: T, colorSchemaPersistor: ColorSchemaPersistor): AppTheme<T> => {
14
+ const colorSchemeKey = '@styles.theme.colorScheme'
15
+ const alternateColorsKey = '@styles.theme.alternateColors'
16
+
17
+ export const createTheme = <T extends Theme>(theme: T, themePersistor: ThemePersistor): AppTheme<T> => {
15
18
  const {
16
19
  colors,
17
20
  breakpoints,
@@ -27,23 +30,34 @@ export const createTheme = <T extends Theme>(theme: T, colorSchemaPersistor: Col
27
30
  ...otherThemeValues
28
31
  } = theme
29
32
 
33
+ themeStore.setColorScheme(themePersistor.get(colorSchemeKey) ?? 'default')
34
+
35
+ const persistedAlternateColors = themePersistor.get(alternateColorsKey)
36
+
37
+ const currentAlternateColors = {
38
+ ...(persistedAlternateColors ?? {}),
39
+ ...otherThemeValues?.alternateColors,
40
+ }
41
+
42
+ themeStore.setAlternateColorsScheme(currentAlternateColors)
43
+
30
44
  const themeObj: AppTheme<T> = {
31
45
  ...otherThemeValues,
32
46
 
33
47
  baseColors,
34
48
 
35
49
  currentColorScheme() {
36
- return themeStore.getState().colorScheme
50
+ return themeStore.colorScheme
37
51
  },
38
52
 
39
53
  breakpoints: breakpoints ?? {},
40
54
 
41
55
  get colors() {
42
- const colorScheme = themeStore.getState().colorScheme
56
+ const colorScheme = themeStore.colorScheme ?? 'default'
43
57
 
44
58
  if (colorScheme === 'default') return colors
45
59
 
46
- const scheme = theme.alternateColors?.[colorScheme]
60
+ const scheme = themeStore.alternateColorsScheme?.[colorScheme]
47
61
 
48
62
  if (!scheme) {
49
63
  console.warn(`Color scheme ${colorScheme} not found in theme`)
@@ -52,12 +66,34 @@ export const createTheme = <T extends Theme>(theme: T, colorSchemaPersistor: Col
52
66
  return scheme ?? colors
53
67
  },
54
68
 
55
- setColorScheme(colorScheme: ColorScheme<Theme>) {
56
- themeStore.setState({ colorScheme: colorScheme as string })
57
- colorSchemaPersistor.set(colorScheme as string)
69
+ setColorScheme(colorScheme: string) {
70
+ const hasScheme = colorScheme === 'default' ? true : !!themeStore.alternateColorsScheme?.[colorScheme]
71
+
72
+ if (!hasScheme) {
73
+ console.warn(`Color scheme ${colorScheme} not found in theme`)
74
+ return
75
+ }
76
+
77
+ themeStore.setColorScheme(colorScheme)
78
+
79
+ themePersistor.set(colorSchemeKey, colorScheme)
80
+ },
81
+
82
+ injectColorScheme(name, colorMap) {
83
+ themeStore.injectColorScheme(name, colorMap)
84
+
85
+ const persistedAlternateColors = themePersistor.get(alternateColorsKey)
86
+
87
+ const unpersistedAlternateColors = {
88
+ ...(persistedAlternateColors ?? {}),
89
+ [name]: colorMap,
90
+ }
91
+
92
+ themePersistor.set(alternateColorsKey, unpersistedAlternateColors)
58
93
  },
59
94
 
60
95
  baseSpacing: theme.baseSpacing,
96
+
61
97
  value: (n = 1) => theme.baseSpacing * n,
62
98
 
63
99
  spacing: {
@@ -109,10 +145,7 @@ export const createTheme = <T extends Theme>(theme: T, colorSchemaPersistor: Col
109
145
  },
110
146
  }
111
147
 
112
- themeStore.setState({
113
- current: themeObj,
114
- colorScheme: colorSchemaPersistor.get() ?? 'default',
115
- })
148
+ themeStore.setTheme(themeObj)
116
149
 
117
150
  return themeObj
118
151
  }
package/src/lib/hooks.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { useMemo } from 'react'
2
2
  import { ICSS } from '../types'
3
3
  import { getNestedStylesByKey } from './utils'
4
- import { useShallow } from 'zustand/react/shallow'
5
- import { ThemeStore, themeStore } from './themeStore'
4
+ import { ThemeState, themeStoreComputed } from './themeStore'
5
+ import { useStore } from '@nanostores/react'
6
6
 
7
7
  export const useStyleObserver = (style) => {
8
8
  return useMemo(() => {
@@ -26,10 +26,18 @@ export function useNestedStylesByKey<T extends string>(match: string, componentS
26
26
  }, [styles])
27
27
  }
28
28
 
29
- type ThemeSelector<T = Record<string, any>> = (store: ThemeStore) => T
29
+ type ThemeSelector<T> = (state: ThemeState) => T
30
30
 
31
- export const useTheme = <T = Record<string, any>>(selector: ThemeSelector<T>): T => {
32
- return themeStore(useShallow(selector))
31
+ export const useTheme = <T = ThemeState>(
32
+ selector?: ThemeSelector<T>
33
+ ): T => {
34
+ const state = useStore(themeStoreComputed)
35
+
36
+ if (!selector) {
37
+ return state as T
38
+ }
39
+
40
+ return selector(state)
33
41
  }
34
42
 
35
43
  export function useCompositionStyles<T extends string, C extends string>(
@@ -1,14 +1,91 @@
1
- import { create } from 'zustand'
2
- import { IAppVariants, ITheme } from '../types'
1
+ import { AppTheme, ColorMap, IAppVariants, ITheme, Theme } from '../types'
2
+ import { map, computed } from 'nanostores'
3
3
 
4
- export type ThemeStore = {
5
- colorScheme: string | null
6
- current: ITheme | null
7
- variants: IAppVariants
4
+ export type ThemeState = {
5
+ theme: AppTheme<Theme> | null
8
6
  }
9
7
 
10
- export const themeStore = create<ThemeStore>(() => ({
11
- colorScheme: null,
12
- current: null,
13
- variants: {},
8
+ class ThemeStore {
9
+ private alternateColorsSchemeStore: { [key: string]: ColorMap } = {}
10
+
11
+ public colorSchemeStore: string = null
12
+
13
+ public themeStore = map<ITheme | null>(null)
14
+
15
+ public variantsStore: IAppVariants = {} as IAppVariants
16
+
17
+ get theme() {
18
+ return this.themeStore.get()
19
+ }
20
+
21
+ get themeTyped() {
22
+ return this.themeStore.get() as unknown as AppTheme<Theme>
23
+ }
24
+
25
+ get colorScheme() {
26
+ return this.colorSchemeStore
27
+ }
28
+
29
+ get variants() {
30
+ return this.variantsStore
31
+ }
32
+
33
+ get alternateColorsScheme() {
34
+ return this.alternateColorsSchemeStore ?? {}
35
+ }
36
+
37
+ setVariants<T>(variants: T) {
38
+ this.variantsStore = variants as unknown as IAppVariants
39
+ }
40
+
41
+ setColorScheme(colorScheme: string) {
42
+ this.colorSchemeStore = colorScheme
43
+ }
44
+
45
+ setTheme(theme: ITheme) {
46
+ this.themeStore.set(theme)
47
+ }
48
+
49
+ setAlternateColorsScheme(colors: { [key: string]: ColorMap }) {
50
+ this.alternateColorsSchemeStore = colors
51
+ }
52
+
53
+ // utils
54
+
55
+ private getBaseColorScheme(): ColorMap {
56
+ const alternateColors = this.alternateColorsScheme ?? {}
57
+ const colorSchemeKeys = Object.keys(alternateColors)
58
+
59
+ if (colorSchemeKeys.length === 0) {
60
+ return {}
61
+ }
62
+
63
+ return alternateColors[colorSchemeKeys[0]] ?? {}
64
+ }
65
+
66
+ injectColorScheme(name: string, colors: ColorMap) {
67
+ const baseColors = this.getBaseColorScheme()
68
+ const currentAlternateColors = this.alternateColorsScheme ?? {}
69
+
70
+ const alternateColors = {
71
+ ...currentAlternateColors,
72
+
73
+ [name]: {
74
+ ...baseColors,
75
+ ...colors,
76
+ }
77
+ }
78
+
79
+ this.setAlternateColorsScheme(alternateColors)
80
+
81
+ return alternateColors
82
+ }
83
+ }
84
+
85
+ export const themeStore = new ThemeStore()
86
+
87
+ export const themeStoreComputed = computed([
88
+ themeStore['themeStore'],
89
+ ], (theme) => ({
90
+ theme: theme as unknown as AppTheme<Theme>,
14
91
  }))
@@ -0,0 +1,106 @@
1
+ import { ColorMap } from '../types'
2
+
3
+ export function hexToHSL(hex: string) {
4
+ const r = parseInt(hex.slice(1, 3), 16) / 255
5
+ const g = parseInt(hex.slice(3, 5), 16) / 255
6
+ const b = parseInt(hex.slice(5, 7), 16) / 255
7
+
8
+ const max = Math.max(r, g, b), min = Math.min(r, g, b)
9
+ let h = 0, s = 0, l = (max + min) / 2
10
+
11
+ if (max !== min) {
12
+ const d = max - min
13
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
14
+ switch (max) {
15
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break
16
+ case g: h = (b - r) / d + 2; break
17
+ case b: h = (r - g) / d + 4; break
18
+ }
19
+ h /= 6
20
+ }
21
+
22
+ return {
23
+ h: Math.round(h * 360),
24
+ s: Math.round(s * 100),
25
+ l: Math.round(l * 100)
26
+ }
27
+ }
28
+
29
+ export function hslToHex(h: number, s: number, l: number): string {
30
+ s /= 100
31
+ l /= 100
32
+
33
+ const k = (n: number) => (n + h / 30) % 12
34
+ const a = s * Math.min(l, 1 - l)
35
+ const f = (n: number) =>
36
+ Math.round(255 * (l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)))))
37
+
38
+ return `#${[f(0), f(8), f(4)].map(x => x.toString(16).padStart(2, '0')).join('')}`
39
+ }
40
+
41
+ export function hexToRGB(hex: string) {
42
+ const r = parseInt(hex.slice(1, 3), 16)
43
+ const g = parseInt(hex.slice(3, 5), 16)
44
+ const b = parseInt(hex.slice(5, 7), 16)
45
+ return { r, g, b }
46
+ }
47
+
48
+ export function hslToRGB(h: number, s: number, l: number) {
49
+ s /= 100
50
+ l /= 100
51
+
52
+ const k = (n: number) => (n + h / 30) % 12
53
+ const a = s * Math.min(l, 1 - l)
54
+ const f = (n: number) =>
55
+ Math.round(255 * (l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)))))
56
+
57
+ return {
58
+ r: f(0),
59
+ g: f(8),
60
+ b: f(4)
61
+ }
62
+ }
63
+
64
+ export function generateColorScheme(
65
+ anchorHex: string,
66
+ prefix: string = 'primary'
67
+ ): ColorMap {
68
+ const { h, s } = hexToHSL(anchorHex)
69
+ const baseRGB = hexToRGB(anchorHex)
70
+
71
+ const scheme: ColorMap = {}
72
+
73
+ const lightnesses = [95, 85, 75, 60, 45, 30, 27, 21, 16, 10]
74
+
75
+ lightnesses.forEach((lightness, index) => {
76
+ const step = (index + 1) * 100
77
+ const rgb = hslToRGB(h, s, lightness)
78
+ scheme[`${prefix}Solid${step}`] = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 1.00)`
79
+ })
80
+
81
+ const alphas = [0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90]
82
+
83
+ alphas.forEach((alpha, index) => {
84
+ const step = (index + 1) * 100
85
+ scheme[`${prefix}Transparent${step}`] = `rgba(${baseRGB.r}, ${baseRGB.g}, ${baseRGB.b}, ${alpha.toFixed(2)})`
86
+ })
87
+
88
+ return scheme
89
+ }
90
+
91
+ export function getLuminance({ r, g, b }: { r: number; g: number; b: number }): number {
92
+ const [R, G, B] = [r, g, b].map(c => {
93
+ const channel = c / 255
94
+ return channel <= 0.03928
95
+ ? channel / 12.92
96
+ : Math.pow((channel + 0.055) / 1.055, 2.4)
97
+ })
98
+
99
+ return 0.2126 * R + 0.7152 * G + 0.0722 * B
100
+ }
101
+
102
+ export function getTextColor(backgroundHex: string, darkColor = 'black', lightColor = 'white'): string {
103
+ const rgb = hexToRGB(backgroundHex)
104
+ const luminance = getLuminance(rgb)
105
+ return luminance > 0.5 ? darkColor : lightColor
106
+ }
@@ -0,0 +1 @@
1
+ export * as colorTools from './colors'
@@ -0,0 +1,5 @@
1
+ export interface StateStorage {
2
+ getItem: (name: string) => string | null
3
+ setItem: (name: string, value: string) => void
4
+ removeItem: (name: string) => void
5
+ }
@@ -8,7 +8,7 @@ type AnyMap = {
8
8
  [key: string]: any
9
9
  }
10
10
 
11
- type ColorMap = {
11
+ export type ColorMap = {
12
12
  [key: string]: string
13
13
  }
14
14
 
@@ -75,8 +75,9 @@ type PredefinedThemeDerivedValues<T extends Theme> = {
75
75
  }
76
76
 
77
77
  type PredefinedAppTheme<T extends Theme> = PredefinedThemeDerivedValues<T> & {
78
- setColorScheme: (colorScheme: ColorScheme<T>) => void
78
+ setColorScheme: (colorScheme: string) => void
79
79
  currentColorScheme: () => ColorScheme<T>
80
+ injectColorScheme: (name: string, colorMap: ColorMap) => void
80
81
  spacing: SpacingMap
81
82
  media: MediaQueries
82
83
  border: BorderCreator