@hanzogui/create-theme 2.0.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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/applyMask.cjs +58 -0
  3. package/dist/cjs/applyMask.native.js +67 -0
  4. package/dist/cjs/applyMask.native.js.map +1 -0
  5. package/dist/cjs/combineMasks.cjs +40 -0
  6. package/dist/cjs/combineMasks.native.js +61 -0
  7. package/dist/cjs/combineMasks.native.js.map +1 -0
  8. package/dist/cjs/createTheme.cjs +82 -0
  9. package/dist/cjs/createTheme.native.js +91 -0
  10. package/dist/cjs/createTheme.native.js.map +1 -0
  11. package/dist/cjs/createThemeTypes.cjs +16 -0
  12. package/dist/cjs/createThemeTypes.native.js +19 -0
  13. package/dist/cjs/createThemeTypes.native.js.map +1 -0
  14. package/dist/cjs/helpers.cjs +34 -0
  15. package/dist/cjs/helpers.native.js +39 -0
  16. package/dist/cjs/helpers.native.js.map +1 -0
  17. package/dist/cjs/index.cjs +23 -0
  18. package/dist/cjs/index.native.js +26 -0
  19. package/dist/cjs/index.native.js.map +1 -0
  20. package/dist/cjs/isMinusZero.cjs +28 -0
  21. package/dist/cjs/isMinusZero.native.js +31 -0
  22. package/dist/cjs/isMinusZero.native.js.map +1 -0
  23. package/dist/cjs/masks.cjs +122 -0
  24. package/dist/cjs/masks.native.js +172 -0
  25. package/dist/cjs/masks.native.js.map +1 -0
  26. package/dist/cjs/tests.cjs +22 -0
  27. package/dist/cjs/tests.native.js +25 -0
  28. package/dist/cjs/tests.native.js.map +1 -0
  29. package/dist/cjs/themeInfo.cjs +35 -0
  30. package/dist/cjs/themeInfo.native.js +40 -0
  31. package/dist/cjs/themeInfo.native.js.map +1 -0
  32. package/dist/esm/applyMask.mjs +34 -0
  33. package/dist/esm/applyMask.mjs.map +1 -0
  34. package/dist/esm/applyMask.native.js +40 -0
  35. package/dist/esm/applyMask.native.js.map +1 -0
  36. package/dist/esm/combineMasks.mjs +17 -0
  37. package/dist/esm/combineMasks.mjs.map +1 -0
  38. package/dist/esm/combineMasks.native.js +35 -0
  39. package/dist/esm/combineMasks.native.js.map +1 -0
  40. package/dist/esm/createTheme.mjs +57 -0
  41. package/dist/esm/createTheme.mjs.map +1 -0
  42. package/dist/esm/createTheme.native.js +63 -0
  43. package/dist/esm/createTheme.native.js.map +1 -0
  44. package/dist/esm/createThemeTypes.mjs +2 -0
  45. package/dist/esm/createThemeTypes.mjs.map +1 -0
  46. package/dist/esm/createThemeTypes.native.js +2 -0
  47. package/dist/esm/createThemeTypes.native.js.map +1 -0
  48. package/dist/esm/helpers.mjs +9 -0
  49. package/dist/esm/helpers.mjs.map +1 -0
  50. package/dist/esm/helpers.native.js +11 -0
  51. package/dist/esm/helpers.native.js.map +1 -0
  52. package/dist/esm/index.js +7 -0
  53. package/dist/esm/index.js.map +1 -0
  54. package/dist/esm/index.mjs +7 -0
  55. package/dist/esm/index.mjs.map +1 -0
  56. package/dist/esm/index.native.js +7 -0
  57. package/dist/esm/index.native.js.map +1 -0
  58. package/dist/esm/isMinusZero.mjs +5 -0
  59. package/dist/esm/isMinusZero.mjs.map +1 -0
  60. package/dist/esm/isMinusZero.native.js +5 -0
  61. package/dist/esm/isMinusZero.native.js.map +1 -0
  62. package/dist/esm/masks.mjs +92 -0
  63. package/dist/esm/masks.mjs.map +1 -0
  64. package/dist/esm/masks.native.js +139 -0
  65. package/dist/esm/masks.native.js.map +1 -0
  66. package/dist/esm/tests.mjs +23 -0
  67. package/dist/esm/tests.mjs.map +1 -0
  68. package/dist/esm/tests.native.js +23 -0
  69. package/dist/esm/tests.native.js.map +1 -0
  70. package/dist/esm/themeInfo.mjs +11 -0
  71. package/dist/esm/themeInfo.mjs.map +1 -0
  72. package/dist/esm/themeInfo.native.js +13 -0
  73. package/dist/esm/themeInfo.native.js.map +1 -0
  74. package/package.json +42 -0
  75. package/src/applyMask.tsx +69 -0
  76. package/src/combineMasks.tsx +29 -0
  77. package/src/createTheme.tsx +127 -0
  78. package/src/createThemeTypes.tsx +85 -0
  79. package/src/helpers.tsx +44 -0
  80. package/src/index.tsx +6 -0
  81. package/src/isMinusZero.tsx +3 -0
  82. package/src/masks.tsx +137 -0
  83. package/src/tests.tsx +28 -0
  84. package/src/themeInfo.tsx +36 -0
  85. package/types/applyMask.d.ts +8 -0
  86. package/types/applyMask.d.ts.map +11 -0
  87. package/types/combineMasks.d.ts +4 -0
  88. package/types/combineMasks.d.ts.map +11 -0
  89. package/types/createTheme.d.ts +28 -0
  90. package/types/createTheme.d.ts.map +11 -0
  91. package/types/createThemeTypes.d.ts +67 -0
  92. package/types/createThemeTypes.d.ts.map +11 -0
  93. package/types/helpers.d.ts +18 -0
  94. package/types/helpers.d.ts.map +11 -0
  95. package/types/index.d.ts +8 -0
  96. package/types/index.d.ts.map +11 -0
  97. package/types/isMinusZero.d.ts +3 -0
  98. package/types/isMinusZero.d.ts.map +11 -0
  99. package/types/masks.d.ts +15 -0
  100. package/types/masks.d.ts.map +11 -0
  101. package/types/tests.d.ts +3 -0
  102. package/types/tests.d.ts.map +11 -0
  103. package/types/themeInfo.d.ts +13 -0
  104. package/types/themeInfo.d.ts.map +11 -0
@@ -0,0 +1,69 @@
1
+ import { createTheme } from './createTheme'
2
+ import type { CreateMask, GenericTheme, MaskOptions, ThemeMask } from './createThemeTypes'
3
+ import type { ThemeInfo } from './themeInfo'
4
+ import { getThemeInfo, setThemeInfo } from './themeInfo'
5
+
6
+ export function applyMask<Theme extends GenericTheme | ThemeMask>(
7
+ theme: Theme,
8
+ mask: CreateMask,
9
+ options: MaskOptions = {},
10
+ parentName?: string,
11
+ nextName?: string
12
+ ): Theme {
13
+ const info = getThemeInfo(theme, parentName)
14
+ if (!info) {
15
+ throw new Error(
16
+ process.env.NODE_ENV !== 'production'
17
+ ? `No info found for theme, you must pass the theme created by createThemeFromPalette directly to extendTheme`
18
+ : `❌ Err2`
19
+ )
20
+ }
21
+
22
+ const next = applyMaskStateless(info, mask, options, parentName)
23
+
24
+ setThemeInfo(next.theme, {
25
+ definition: next.definition,
26
+ palette: info.palette,
27
+ name: nextName,
28
+ })
29
+
30
+ return next.theme as Theme
31
+ }
32
+
33
+ export function applyMaskStateless<Theme extends GenericTheme | ThemeMask>(
34
+ info: ThemeInfo,
35
+ mask: CreateMask,
36
+ options: MaskOptions = {},
37
+ parentName?: string
38
+ ): ThemeInfo & {
39
+ theme: Theme
40
+ } {
41
+ const skip = {
42
+ ...options.skip,
43
+ }
44
+
45
+ // skip nonInheritedValues from parent theme
46
+ if (info.options?.nonInheritedValues) {
47
+ for (const key in info.options.nonInheritedValues) {
48
+ skip[key] = 1
49
+ }
50
+ }
51
+
52
+ // convert theme back to template first
53
+ const maskOptions = {
54
+ parentName,
55
+ palette: info.palette,
56
+ ...options,
57
+ skip,
58
+ }
59
+
60
+ const template = mask.mask(info.definition, maskOptions)
61
+ const theme = createTheme(info.palette, template) as Theme
62
+
63
+ return {
64
+ ...info,
65
+ cache: new Map(),
66
+ definition: template,
67
+ theme,
68
+ }
69
+ }
@@ -0,0 +1,29 @@
1
+ import { applyMaskStateless } from './applyMask'
2
+ import type { CreateMask } from './createThemeTypes'
3
+ import { getThemeInfo } from './themeInfo'
4
+
5
+ export const combineMasks = (...masks: CreateMask[]): CreateMask => {
6
+ const mask: CreateMask = {
7
+ name: 'combine-mask',
8
+ mask: (template, opts) => {
9
+ let current = getThemeInfo(template, opts.parentName)
10
+ let theme: any
11
+ for (const mask of masks) {
12
+ if (!current) {
13
+ throw new Error(
14
+ `Nothing returned from mask: ${current}, for template: ${template} and mask: ${mask.toString()}, given opts ${JSON.stringify(
15
+ opts,
16
+ null,
17
+ 2
18
+ )}`
19
+ )
20
+ }
21
+ const next = applyMaskStateless(current, mask, opts)
22
+ current = next
23
+ theme = next.theme
24
+ }
25
+ return theme
26
+ },
27
+ }
28
+ return mask
29
+ }
@@ -0,0 +1,127 @@
1
+ import type {
2
+ CreateThemeOptions,
3
+ CreateThemePalette,
4
+ GenericTheme,
5
+ ThemeMask,
6
+ } from './createThemeTypes'
7
+ import { isMinusZero } from './isMinusZero'
8
+ import { setThemeInfo } from './themeInfo'
9
+
10
+ const identityCache = new Map()
11
+
12
+ export function createThemeWithPalettes<
13
+ Definition extends ThemeMask,
14
+ Extras extends GenericTheme = {},
15
+ >(
16
+ palettes: Record<string, CreateThemePalette>,
17
+ defaultPalette: string,
18
+ definition: Definition,
19
+ options?: CreateThemeOptions,
20
+ name?: string,
21
+ skipCache = false
22
+ ): {
23
+ [key in keyof Definition | keyof Extras]: string
24
+ } {
25
+ if (!palettes[defaultPalette]) {
26
+ throw new Error(`No palette: ${defaultPalette}`)
27
+ }
28
+ const newDef = { ...definition }
29
+ for (const key in definition) {
30
+ let val = definition[key]
31
+ if (typeof val === 'string' && val[0] === '$') {
32
+ const [altPaletteName$, altPaletteIndex] = val.split('.')
33
+ const altPaletteName = altPaletteName$.slice(1)
34
+ const parentName = defaultPalette.split('_')[0]
35
+ const altPalette =
36
+ palettes[altPaletteName] || palettes[`${parentName}_${altPaletteName}`]
37
+
38
+ if (altPalette) {
39
+ const next = getValue(altPalette, +altPaletteIndex)
40
+ if (typeof next !== 'undefined') {
41
+ newDef[key] = next as any
42
+ }
43
+ }
44
+ }
45
+ }
46
+
47
+ return createTheme(palettes[defaultPalette], newDef, options, name, skipCache)
48
+ }
49
+
50
+ export function createTheme<
51
+ Definition extends ThemeMask,
52
+ Extras extends GenericTheme = {},
53
+ >(
54
+ palette: CreateThemePalette,
55
+ definition: Definition,
56
+ options?: CreateThemeOptions,
57
+ name?: string,
58
+ skipCache = false
59
+ ): {
60
+ [key in keyof Definition | keyof Extras]: string
61
+ } {
62
+ const cacheKey = skipCache ? '' : JSON.stringify([name, palette, definition, options])
63
+ if (!skipCache) {
64
+ if (identityCache.has(cacheKey)) {
65
+ return identityCache.get(cacheKey)
66
+ }
67
+ }
68
+
69
+ const theme = {
70
+ ...(Object.fromEntries(
71
+ Object.entries(definition).map(([key, offset]) => {
72
+ return [key, getValue(palette, offset)]
73
+ })
74
+ ) as any),
75
+ ...options?.nonInheritedValues,
76
+ }
77
+
78
+ setThemeInfo(theme, { palette, definition, options, name })
79
+
80
+ if (cacheKey) {
81
+ identityCache.set(cacheKey, theme)
82
+ }
83
+
84
+ return theme
85
+ }
86
+
87
+ const getValue = (palette: CreateThemePalette, value: string | number) => {
88
+ if (!palette) {
89
+ throw new Error(`No palette!`)
90
+ }
91
+ if (typeof value === 'string') {
92
+ return value
93
+ }
94
+ const max = palette.length - 1
95
+ const isPositive = value === 0 ? !isMinusZero(value) : value >= 0
96
+ const next = isPositive ? value : max + value
97
+ const index = Math.min(Math.max(0, next), max)
98
+ return palette[index]
99
+ }
100
+
101
+ type SubThemeKeys<ParentKeys, ChildKeys> = `${ParentKeys extends string
102
+ ? ParentKeys
103
+ : never}_${ChildKeys extends string ? ChildKeys : never}`
104
+
105
+ type ChildGetter<Name extends string | number | symbol, Theme extends GenericTheme> = (
106
+ name: Name,
107
+ theme: Theme
108
+ ) => { [key: string]: Theme }
109
+
110
+ export function addChildren<
111
+ Themes extends { [key: string]: GenericTheme },
112
+ GetChildren extends ChildGetter<keyof Themes, Themes[keyof Themes]>,
113
+ >(
114
+ themes: Themes,
115
+ getChildren: GetChildren
116
+ ): Themes & {
117
+ [key in SubThemeKeys<keyof Themes, keyof ReturnType<GetChildren>>]: Themes[keyof Themes]
118
+ } {
119
+ const out = { ...themes }
120
+ for (const key in themes) {
121
+ const subThemes = getChildren(key, themes[key])
122
+ for (const sKey in subThemes) {
123
+ out[`${key}_${sKey}`] = subThemes[sKey] as any
124
+ }
125
+ }
126
+ return out as any
127
+ }
@@ -0,0 +1,85 @@
1
+ import type { Variable } from '@hanzogui/web'
2
+
3
+ export type CreateThemePalette = (string | Variable)[]
4
+
5
+ export type ThemeMask = Record<string, string | number>
6
+
7
+ export type MaskOptions = {
8
+ palette?: CreateThemePalette
9
+ override?: Partial<ThemeMask>
10
+ overrideStrategy?: 'shift' | 'swap'
11
+ overrideSwap?: Partial<ThemeMask>
12
+ overrideShift?: Partial<ThemeMask>
13
+ skip?: Partial<ThemeMask>
14
+ strength?: number
15
+ max?: number
16
+ min?: number
17
+ parentName?: string
18
+ }
19
+
20
+ export type GenericTheme = { [key: string]: string | Variable }
21
+
22
+ export type MaskFunction = <A extends ThemeMask>(template: A, options: MaskOptions) => A
23
+
24
+ export type CreateMask = {
25
+ name: string
26
+ mask: MaskFunction
27
+ }
28
+
29
+ export type CreateThemeOptions = {
30
+ nonInheritedValues?: GenericTheme
31
+ }
32
+
33
+ export type Palette = string[]
34
+
35
+ export type Template = {
36
+ [key: string]: number | string
37
+ }
38
+
39
+ export type ThemeUsingMask<Masks = string> = MaskOptions & {
40
+ mask: Masks
41
+ avoidNestingWithin?: string[]
42
+ childOptions?: MaskOptions & {
43
+ mask?: Masks
44
+ }
45
+ }
46
+
47
+ export type ThemeUsingTemplate = CreateThemeOptions & {
48
+ palette?: string
49
+ template: string
50
+ }
51
+
52
+ type ThemePreDefined = {
53
+ theme: { [key: string]: string }
54
+ }
55
+
56
+ export type Theme<Masks = string> =
57
+ | ThemePreDefined
58
+ | ThemeUsingTemplate
59
+ | ThemeUsingMask<Masks>
60
+
61
+ export type ThemeWithParent<Masks = string> = Theme<Masks> & {
62
+ parent: string
63
+ }
64
+
65
+ export type PaletteDefinitions = {
66
+ [key: string]: Palette
67
+ }
68
+
69
+ export type ThemeDefinition<Masks extends string = string> =
70
+ | Theme<Masks>
71
+ | ThemeWithParent<Masks>[]
72
+
73
+ type UnionableString = string & {}
74
+
75
+ export type ThemeDefinitions<Masks extends string = string> = {
76
+ [key: string]: ThemeDefinition<Masks | UnionableString>
77
+ }
78
+
79
+ export type TemplateDefinitions = {
80
+ [key: string]: Template
81
+ }
82
+
83
+ export type MaskDefinitions = {
84
+ [key: string]: CreateMask | CreateMask['mask']
85
+ }
@@ -0,0 +1,44 @@
1
+ type ObjectType = Record<PropertyKey, unknown>
2
+
3
+ type PickByValue<OBJ_T, VALUE_T> = // From https://stackoverflow.com/a/55153000
4
+ Pick<OBJ_T, { [K in keyof OBJ_T]: OBJ_T[K] extends VALUE_T ? K : never }[keyof OBJ_T]>
5
+
6
+ type ObjectEntries<OBJ_T> = // From https://stackoverflow.com/a/60142095
7
+ { [K in keyof OBJ_T]: [keyof PickByValue<OBJ_T, OBJ_T[K]>, OBJ_T[K]] }[keyof OBJ_T][]
8
+
9
+ export const objectKeys = <O extends object>(obj: O) => Object.keys(obj) as Array<keyof O>
10
+
11
+ export function objectEntries<OBJ_T extends ObjectType>(
12
+ obj: OBJ_T
13
+ ): ObjectEntries<OBJ_T> {
14
+ return Object.entries(obj) as ObjectEntries<OBJ_T>
15
+ }
16
+
17
+ type EntriesType =
18
+ | [PropertyKey, unknown][]
19
+ | ReadonlyArray<readonly [PropertyKey, unknown]>
20
+
21
+ // Existing Utils
22
+ type DeepWritable<OBJ_T> = { -readonly [P in keyof OBJ_T]: DeepWritable<OBJ_T[P]> }
23
+ type UnionToIntersection<UNION_T> = // From https://stackoverflow.com/a/50375286
24
+ (UNION_T extends any ? (k: UNION_T) => void : never) extends (k: infer I) => void
25
+ ? I
26
+ : never
27
+
28
+ // New Utils
29
+ type UnionObjectFromArrayOfPairs<ARR_T extends EntriesType> =
30
+ DeepWritable<ARR_T> extends (infer R)[]
31
+ ? R extends [infer key, infer val]
32
+ ? { [prop in key & PropertyKey]: val }
33
+ : never
34
+ : never
35
+ type MergeIntersectingObjects<ObjT> = { [key in keyof ObjT]: ObjT[key] }
36
+ type EntriesToObject<ARR_T extends EntriesType> = MergeIntersectingObjects<
37
+ UnionToIntersection<UnionObjectFromArrayOfPairs<ARR_T>>
38
+ >
39
+
40
+ export function objectFromEntries<ARR_T extends EntriesType>(
41
+ arr: ARR_T
42
+ ): EntriesToObject<ARR_T> {
43
+ return Object.fromEntries(arr) as EntriesToObject<ARR_T>
44
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,6 @@
1
+ export * from './createTheme'
2
+ export * from './masks'
3
+ export * from './combineMasks'
4
+ export * from './applyMask'
5
+ export * from './createThemeTypes'
6
+ export * from './helpers'
@@ -0,0 +1,3 @@
1
+ export function isMinusZero(value: number): boolean {
2
+ return 1 / value === Number.NEGATIVE_INFINITY
3
+ }
package/src/masks.tsx ADDED
@@ -0,0 +1,137 @@
1
+ import type { CreateMask, MaskFunction, MaskOptions } from './createThemeTypes'
2
+ import { objectEntries, objectFromEntries } from './helpers'
3
+ import { isMinusZero } from './isMinusZero'
4
+
5
+ export const createMask = <C extends CreateMask | MaskFunction>(
6
+ createMask: C
7
+ ): CreateMask =>
8
+ typeof createMask === 'function'
9
+ ? { name: createMask.name || 'unnamed', mask: createMask }
10
+ : createMask
11
+
12
+ export const skipMask: CreateMask = {
13
+ name: 'skip-mask',
14
+ mask: (template, opts) => {
15
+ const { skip } = opts
16
+ const result = Object.fromEntries(
17
+ Object.entries(template)
18
+ .filter(([k]) => !skip || !(k in skip))
19
+ .map(([k, v]) => [k, applyOverrides(k, v, opts)])
20
+ ) as typeof template
21
+
22
+ return result
23
+ },
24
+ }
25
+
26
+ function applyOverrides(key: string, value: number | string, opts: MaskOptions) {
27
+ let override: string | number | undefined
28
+ let strategy = opts.overrideStrategy
29
+
30
+ const overrideSwap = opts.overrideSwap?.[key]
31
+ if (typeof overrideSwap !== 'undefined') {
32
+ override = overrideSwap
33
+ strategy = 'swap'
34
+ } else {
35
+ const overrideShift = opts.overrideShift?.[key]
36
+ if (typeof overrideShift !== 'undefined') {
37
+ override = overrideShift
38
+ strategy = 'shift'
39
+ } else {
40
+ const overrideDefault = opts.override?.[key]
41
+ if (typeof overrideDefault !== 'undefined') {
42
+ override = overrideDefault
43
+ strategy = opts.overrideStrategy
44
+ }
45
+ }
46
+ }
47
+
48
+ if (typeof override === 'undefined') return value
49
+ if (typeof override === 'string') return value
50
+
51
+ if (strategy === 'swap') {
52
+ return override
53
+ }
54
+
55
+ return value
56
+ }
57
+
58
+ export const createIdentityMask = (): CreateMask => ({
59
+ name: 'identity-mask',
60
+ mask: (template, opts) => skipMask.mask(template, opts),
61
+ })
62
+
63
+ export const createInverseMask = (): CreateMask => {
64
+ const mask: CreateMask = {
65
+ name: 'inverse-mask',
66
+ mask: (template, opts) => {
67
+ const inversed = objectFromEntries(
68
+ objectEntries(template).map(([k, v]) => [k, -v])
69
+ ) as any
70
+ return skipMask.mask(inversed, opts)
71
+ },
72
+ }
73
+ return mask
74
+ }
75
+
76
+ type ShiftMaskOptions = { inverse?: boolean }
77
+
78
+ export const createShiftMask = (
79
+ { inverse }: ShiftMaskOptions = {},
80
+ defaultOptions?: MaskOptions
81
+ ): CreateMask => {
82
+ const mask: CreateMask = {
83
+ name: 'shift-mask',
84
+ mask: (template, opts) => {
85
+ const {
86
+ override,
87
+ overrideStrategy = 'shift',
88
+ max: maxIn,
89
+ palette,
90
+ min = 0,
91
+ strength = 1,
92
+ } = { ...defaultOptions, ...opts }
93
+ const values = Object.entries(template)
94
+ const max =
95
+ maxIn ?? (palette ? Object.values(palette).length - 1 : Number.POSITIVE_INFINITY)
96
+ const out = {}
97
+ for (const [key, value] of values) {
98
+ if (typeof value === 'string') continue
99
+ if (typeof override?.[key] === 'number') {
100
+ const overrideVal = override[key] as number
101
+ out[key] = overrideStrategy === 'shift' ? value + overrideVal : overrideVal
102
+ continue
103
+ }
104
+ if (typeof override?.[key] === 'string') {
105
+ out[key] = override[key]
106
+ continue
107
+ }
108
+ const isPositive = value === 0 ? !isMinusZero(value) : value >= 0
109
+ const direction = isPositive ? 1 : -1
110
+ const invert = inverse ? -1 : 1
111
+ const next = value + strength * direction * invert
112
+ const clamped = isPositive
113
+ ? Math.max(min, Math.min(max, next))
114
+ : Math.min(-min, Math.max(-max, next))
115
+
116
+ out[key] = clamped
117
+ }
118
+
119
+ const skipped = skipMask.mask(out, opts) as typeof template
120
+ return skipped
121
+ },
122
+ }
123
+ return mask
124
+ }
125
+
126
+ export const createWeakenMask = (defaultOptions?: MaskOptions): CreateMask => ({
127
+ name: 'soften-mask',
128
+ mask: createShiftMask({}, defaultOptions).mask,
129
+ })
130
+
131
+ export const createSoftenMask: (defaultOptions?: MaskOptions) => CreateMask =
132
+ createWeakenMask
133
+
134
+ export const createStrengthenMask = (defaultOptions?: MaskOptions): CreateMask => ({
135
+ name: 'strengthen-mask',
136
+ mask: createShiftMask({ inverse: true }, defaultOptions).mask,
137
+ })
package/src/tests.tsx ADDED
@@ -0,0 +1,28 @@
1
+ import { applyMask } from './applyMask'
2
+ import { createTheme } from './createTheme'
3
+ import { createStrengthenMask, createWeakenMask } from './masks'
4
+
5
+ // --- tests ---
6
+ if (process.env.NODE_ENV === 'development') {
7
+ const palette = ['0', '1', '2', '3', '-3', '-2', '-1', '-0']
8
+ const template = { bg: 1, fg: -1 }
9
+
10
+ const stongerMask = createStrengthenMask()
11
+ const weakerMask = createWeakenMask()
12
+
13
+ const theme = createTheme(palette, template)
14
+ if (theme.bg !== '1') throw `❌`
15
+ if (theme.fg !== '-1') throw `❌`
16
+
17
+ const str = applyMask(theme, stongerMask)
18
+ if (str.bg !== '0') throw `❌`
19
+ if (str.fg !== '-0') throw `❌`
20
+
21
+ const weak = applyMask(theme, weakerMask)
22
+ if (weak.bg !== '2') throw `❌`
23
+ if (weak.fg !== '-2') throw `❌`
24
+
25
+ const weak2 = applyMask(theme, weakerMask, { strength: 2 })
26
+ if (weak2.bg !== '3') throw `❌`
27
+ if (weak2.fg !== '-3') throw `❌`
28
+ }
@@ -0,0 +1,36 @@
1
+ import type {
2
+ CreateThemeOptions,
3
+ CreateThemePalette,
4
+ GenericTheme,
5
+ ThemeMask,
6
+ } from './createThemeTypes'
7
+
8
+ export type ThemeInfo = {
9
+ palette: CreateThemePalette
10
+ definition: ThemeMask
11
+ options?: CreateThemeOptions
12
+ cache: Map<any, any>
13
+ }
14
+
15
+ const THEME_INFO = new Map<string, ThemeInfo>()
16
+
17
+ export const getThemeInfo = (
18
+ theme: GenericTheme | ThemeMask,
19
+ name?: string
20
+ ): ThemeInfo | undefined => {
21
+ return THEME_INFO.get(name || JSON.stringify(theme))
22
+ }
23
+
24
+ export const setThemeInfo = (
25
+ theme: GenericTheme | ThemeMask,
26
+ info: Pick<ThemeInfo, 'palette' | 'definition' | 'options'> & {
27
+ name?: string
28
+ }
29
+ ): void => {
30
+ const next = {
31
+ ...info,
32
+ cache: new Map(),
33
+ }
34
+ THEME_INFO.set(info.name || JSON.stringify(theme), next)
35
+ THEME_INFO.set(JSON.stringify(info.definition), next)
36
+ }
@@ -0,0 +1,8 @@
1
+ import type { CreateMask, GenericTheme, MaskOptions, ThemeMask } from "./createThemeTypes";
2
+ import type { ThemeInfo } from "./themeInfo";
3
+ export declare function applyMask<Theme extends GenericTheme | ThemeMask>(theme: Theme, mask: CreateMask, options?: MaskOptions, parentName?: string, nextName?: string): Theme;
4
+ export declare function applyMaskStateless<Theme extends GenericTheme | ThemeMask>(info: ThemeInfo, mask: CreateMask, options?: MaskOptions, parentName?: string): ThemeInfo & {
5
+ theme: Theme;
6
+ };
7
+
8
+ //# sourceMappingURL=applyMask.d.ts.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "mappings": "AACA,cAAc,YAAY,cAAc,aAAa,iBAAiB;AACtE,cAAc,iBAAiB;AAG/B,OAAO,iBAAS,UAAU,cAAc,eAAe,WACrD,OAAO,OACP,MAAM,YACN,UAAS,aACT,qBACA,oBACC;AAqBH,OAAO,iBAAS,mBAAmB,cAAc,eAAe,WAC9D,MAAM,WACN,MAAM,YACN,UAAS,aACT,sBACC,YAAY;CACb,OAAO",
3
+ "names": [],
4
+ "sources": [
5
+ "src/applyMask.tsx"
6
+ ],
7
+ "version": 3,
8
+ "sourcesContent": [
9
+ "import { createTheme } from './createTheme'\nimport type { CreateMask, GenericTheme, MaskOptions, ThemeMask } from './createThemeTypes'\nimport type { ThemeInfo } from './themeInfo'\nimport { getThemeInfo, setThemeInfo } from './themeInfo'\n\nexport function applyMask<Theme extends GenericTheme | ThemeMask>(\n theme: Theme,\n mask: CreateMask,\n options: MaskOptions = {},\n parentName?: string,\n nextName?: string\n): Theme {\n const info = getThemeInfo(theme, parentName)\n if (!info) {\n throw new Error(\n process.env.NODE_ENV !== 'production'\n ? `No info found for theme, you must pass the theme created by createThemeFromPalette directly to extendTheme`\n : `❌ Err2`\n )\n }\n\n const next = applyMaskStateless(info, mask, options, parentName)\n\n setThemeInfo(next.theme, {\n definition: next.definition,\n palette: info.palette,\n name: nextName,\n })\n\n return next.theme as Theme\n}\n\nexport function applyMaskStateless<Theme extends GenericTheme | ThemeMask>(\n info: ThemeInfo,\n mask: CreateMask,\n options: MaskOptions = {},\n parentName?: string\n): ThemeInfo & {\n theme: Theme\n} {\n const skip = {\n ...options.skip,\n }\n\n // skip nonInheritedValues from parent theme\n if (info.options?.nonInheritedValues) {\n for (const key in info.options.nonInheritedValues) {\n skip[key] = 1\n }\n }\n\n // convert theme back to template first\n const maskOptions = {\n parentName,\n palette: info.palette,\n ...options,\n skip,\n }\n\n const template = mask.mask(info.definition, maskOptions)\n const theme = createTheme(info.palette, template) as Theme\n\n return {\n ...info,\n cache: new Map(),\n definition: template,\n theme,\n }\n}\n"
10
+ ]
11
+ }
@@ -0,0 +1,4 @@
1
+ import type { CreateMask } from "./createThemeTypes";
2
+ export declare const combineMasks: (...masks: CreateMask[]) => CreateMask;
3
+
4
+ //# sourceMappingURL=combineMasks.d.ts.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "mappings": "AACA,cAAc,kBAAkB;AAGhC,OAAO,cAAM,eAAgB,GAAG,OAAO,iBAAe",
3
+ "names": [],
4
+ "sources": [
5
+ "src/combineMasks.tsx"
6
+ ],
7
+ "version": 3,
8
+ "sourcesContent": [
9
+ "import { applyMaskStateless } from './applyMask'\nimport type { CreateMask } from './createThemeTypes'\nimport { getThemeInfo } from './themeInfo'\n\nexport const combineMasks = (...masks: CreateMask[]): CreateMask => {\n const mask: CreateMask = {\n name: 'combine-mask',\n mask: (template, opts) => {\n let current = getThemeInfo(template, opts.parentName)\n let theme: any\n for (const mask of masks) {\n if (!current) {\n throw new Error(\n `Nothing returned from mask: ${current}, for template: ${template} and mask: ${mask.toString()}, given opts ${JSON.stringify(\n opts,\n null,\n 2\n )}`\n )\n }\n const next = applyMaskStateless(current, mask, opts)\n current = next\n theme = next.theme\n }\n return theme\n },\n }\n return mask\n}\n"
10
+ ]
11
+ }
@@ -0,0 +1,28 @@
1
+ import type { CreateThemeOptions, CreateThemePalette, GenericTheme, ThemeMask } from "./createThemeTypes";
2
+ export declare function createThemeWithPalettes<
3
+ Definition extends ThemeMask,
4
+ Extras extends GenericTheme = {}
5
+ >(palettes: Record<string, CreateThemePalette>, defaultPalette: string, definition: Definition, options?: CreateThemeOptions, name?: string, skipCache?: boolean): { [key in keyof Definition | keyof Extras] : string };
6
+ export declare function createTheme<
7
+ Definition extends ThemeMask,
8
+ Extras extends GenericTheme = {}
9
+ >(palette: CreateThemePalette, definition: Definition, options?: CreateThemeOptions, name?: string, skipCache?: boolean): { [key in keyof Definition | keyof Extras] : string };
10
+ type SubThemeKeys<
11
+ ParentKeys,
12
+ ChildKeys
13
+ > = `${ParentKeys extends string ? ParentKeys : never}_${ChildKeys extends string ? ChildKeys : never}`;
14
+ type ChildGetter<
15
+ Name extends string | number | symbol,
16
+ Theme extends GenericTheme
17
+ > = (name: Name, theme: Theme) => {
18
+ [key: string]: Theme;
19
+ };
20
+ export declare function addChildren<
21
+ Themes extends {
22
+ [key: string]: GenericTheme;
23
+ },
24
+ GetChildren extends ChildGetter<keyof Themes, Themes[keyof Themes]>
25
+ >(themes: Themes, getChildren: GetChildren): Themes & { [key in SubThemeKeys<keyof Themes, keyof ReturnType<GetChildren>>] : Themes[keyof Themes] };
26
+ export {};
27
+
28
+ //# sourceMappingURL=createTheme.d.ts.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "mappings": "AAAA,cACE,oBACA,oBACA,cACA,iBACK;AAMP,OAAO,iBAAS;CACd,mBAAmB;CACnB,eAAe,eAAe;EAE9B,UAAU,eAAe,qBACzB,wBACA,YAAY,YACZ,UAAU,oBACV,eACA,yBAEC,aAAa,mBAAmB;AA2BnC,OAAO,iBAAS;CACd,mBAAmB;CACnB,eAAe,eAAe;EAE9B,SAAS,oBACT,YAAY,YACZ,UAAU,oBACV,eACA,yBAEC,aAAa,mBAAmB;KAyC9B;CAAa;CAAY;OAAgB,4BAC1C,sBACS,2BAA2B;KAEnC;CAAY;CAAuC,cAAc;KACpE,MAAM,MACN,OAAO,UACJ;gBAAiB;;AAEtB,OAAO,iBAAS;CACd,eAAe;iBAAiB;;CAChC,oBAAoB,kBAAkB,QAAQ,aAAa;EAE3D,QAAQ,QACR,aAAa,cACZ,YACA,OAAO,mBAAmB,cAAc,WAAW,iBAAgB,aAAa",
3
+ "names": [],
4
+ "sources": [
5
+ "src/createTheme.tsx"
6
+ ],
7
+ "version": 3,
8
+ "sourcesContent": [
9
+ "import type {\n CreateThemeOptions,\n CreateThemePalette,\n GenericTheme,\n ThemeMask,\n} from './createThemeTypes'\nimport { isMinusZero } from './isMinusZero'\nimport { setThemeInfo } from './themeInfo'\n\nconst identityCache = new Map()\n\nexport function createThemeWithPalettes<\n Definition extends ThemeMask,\n Extras extends GenericTheme = {},\n>(\n palettes: Record<string, CreateThemePalette>,\n defaultPalette: string,\n definition: Definition,\n options?: CreateThemeOptions,\n name?: string,\n skipCache = false\n): {\n [key in keyof Definition | keyof Extras]: string\n} {\n if (!palettes[defaultPalette]) {\n throw new Error(`No palette: ${defaultPalette}`)\n }\n const newDef = { ...definition }\n for (const key in definition) {\n let val = definition[key]\n if (typeof val === 'string' && val[0] === '$') {\n const [altPaletteName$, altPaletteIndex] = val.split('.')\n const altPaletteName = altPaletteName$.slice(1)\n const parentName = defaultPalette.split('_')[0]\n const altPalette =\n palettes[altPaletteName] || palettes[`${parentName}_${altPaletteName}`]\n\n if (altPalette) {\n const next = getValue(altPalette, +altPaletteIndex)\n if (typeof next !== 'undefined') {\n newDef[key] = next as any\n }\n }\n }\n }\n\n return createTheme(palettes[defaultPalette], newDef, options, name, skipCache)\n}\n\nexport function createTheme<\n Definition extends ThemeMask,\n Extras extends GenericTheme = {},\n>(\n palette: CreateThemePalette,\n definition: Definition,\n options?: CreateThemeOptions,\n name?: string,\n skipCache = false\n): {\n [key in keyof Definition | keyof Extras]: string\n} {\n const cacheKey = skipCache ? '' : JSON.stringify([name, palette, definition, options])\n if (!skipCache) {\n if (identityCache.has(cacheKey)) {\n return identityCache.get(cacheKey)\n }\n }\n\n const theme = {\n ...(Object.fromEntries(\n Object.entries(definition).map(([key, offset]) => {\n return [key, getValue(palette, offset)]\n })\n ) as any),\n ...options?.nonInheritedValues,\n }\n\n setThemeInfo(theme, { palette, definition, options, name })\n\n if (cacheKey) {\n identityCache.set(cacheKey, theme)\n }\n\n return theme\n}\n\nconst getValue = (palette: CreateThemePalette, value: string | number) => {\n if (!palette) {\n throw new Error(`No palette!`)\n }\n if (typeof value === 'string') {\n return value\n }\n const max = palette.length - 1\n const isPositive = value === 0 ? !isMinusZero(value) : value >= 0\n const next = isPositive ? value : max + value\n const index = Math.min(Math.max(0, next), max)\n return palette[index]\n}\n\ntype SubThemeKeys<ParentKeys, ChildKeys> = `${ParentKeys extends string\n ? ParentKeys\n : never}_${ChildKeys extends string ? ChildKeys : never}`\n\ntype ChildGetter<Name extends string | number | symbol, Theme extends GenericTheme> = (\n name: Name,\n theme: Theme\n) => { [key: string]: Theme }\n\nexport function addChildren<\n Themes extends { [key: string]: GenericTheme },\n GetChildren extends ChildGetter<keyof Themes, Themes[keyof Themes]>,\n>(\n themes: Themes,\n getChildren: GetChildren\n): Themes & {\n [key in SubThemeKeys<keyof Themes, keyof ReturnType<GetChildren>>]: Themes[keyof Themes]\n} {\n const out = { ...themes }\n for (const key in themes) {\n const subThemes = getChildren(key, themes[key])\n for (const sKey in subThemes) {\n out[`${key}_${sKey}`] = subThemes[sKey] as any\n }\n }\n return out as any\n}\n"
10
+ ]
11
+ }