@pyreon/rocketstyle 0.11.1 → 0.11.2

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 (51) hide show
  1. package/package.json +8 -7
  2. package/src/__tests__/attrs.test.ts +190 -0
  3. package/src/__tests__/chaining.test.ts +86 -0
  4. package/src/__tests__/collection.test.ts +35 -0
  5. package/src/__tests__/compose.test.ts +36 -0
  6. package/src/__tests__/context.test.ts +200 -0
  7. package/src/__tests__/createLocalProvider.test.ts +248 -0
  8. package/src/__tests__/dimensions.test.ts +183 -0
  9. package/src/__tests__/e2e-styler.test.ts +291 -0
  10. package/src/__tests__/hooks.test.ts +207 -0
  11. package/src/__tests__/isRocketComponent.test.ts +48 -0
  12. package/src/__tests__/misc.test.ts +204 -0
  13. package/src/__tests__/providerConsumer.test.ts +248 -0
  14. package/src/__tests__/rocketstyleIntegration.test.ts +615 -0
  15. package/src/__tests__/themeUtils.test.ts +463 -0
  16. package/src/cache/LocalThemeManager.ts +14 -0
  17. package/src/cache/index.ts +3 -0
  18. package/src/constants/booleanTags.ts +32 -0
  19. package/src/constants/defaultDimensions.ts +23 -0
  20. package/src/constants/index.ts +44 -0
  21. package/src/context/context.ts +51 -0
  22. package/src/context/createLocalProvider.ts +84 -0
  23. package/src/context/localContext.ts +37 -0
  24. package/src/hoc/index.ts +3 -0
  25. package/src/hoc/rocketstyleAttrsHoc.ts +63 -0
  26. package/src/hooks/index.ts +4 -0
  27. package/src/hooks/usePseudoState.ts +79 -0
  28. package/src/hooks/useTheme.ts +36 -0
  29. package/src/index.ts +77 -0
  30. package/src/init.ts +93 -0
  31. package/src/isRocketComponent.ts +16 -0
  32. package/src/rocketstyle.ts +320 -0
  33. package/src/types/attrs.ts +13 -0
  34. package/src/types/config.ts +48 -0
  35. package/src/types/configuration.ts +69 -0
  36. package/src/types/dimensions.ts +106 -0
  37. package/src/types/hoc.ts +5 -0
  38. package/src/types/pseudo.ts +19 -0
  39. package/src/types/rocketComponent.ts +24 -0
  40. package/src/types/rocketstyle.ts +156 -0
  41. package/src/types/styles.ts +46 -0
  42. package/src/types/theme.ts +19 -0
  43. package/src/types/utils.ts +55 -0
  44. package/src/utils/attrs.ts +134 -0
  45. package/src/utils/chaining.ts +58 -0
  46. package/src/utils/collection.ts +9 -0
  47. package/src/utils/compose.ts +11 -0
  48. package/src/utils/dimensions.ts +126 -0
  49. package/src/utils/statics.ts +44 -0
  50. package/src/utils/styles.ts +18 -0
  51. package/src/utils/theme.ts +196 -0
@@ -0,0 +1,196 @@
1
+ import { config, isEmpty, merge } from "@pyreon/ui-core"
2
+ import type { ThemeModeCallback } from "../types/theme"
3
+ import { removeNullableValues } from "./collection"
4
+ import { isMultiKey } from "./dimensions"
5
+
6
+ // --------------------------------------------------------
7
+ // Theme Mode Callback
8
+ // --------------------------------------------------------
9
+ const MODE_CALLBACK_BRAND = Symbol.for("pyreon.themeModeCallback")
10
+
11
+ /** Creates a mode-switching function that returns the light or dark value based on the active mode. */
12
+ export const themeModeCallback: ThemeModeCallback = (light, dark) => {
13
+ const fn = (mode: string) => {
14
+ if (!mode || mode === "light") return light
15
+ return dark
16
+ }
17
+ ;(fn as unknown as Record<string, unknown>).__brand = MODE_CALLBACK_BRAND
18
+ return fn
19
+ }
20
+
21
+ // --------------------------------------------------------
22
+ // Theme Mode Callback Check
23
+ // --------------------------------------------------------
24
+ /** Detects whether a value is a `themeModeCallback` function via Symbol brand. */
25
+ type IsModeCallback = (value: unknown) => boolean
26
+ const isModeCallback: IsModeCallback = (value: unknown) =>
27
+ typeof value === "function" &&
28
+ (value as unknown as Record<string, unknown>).__brand === MODE_CALLBACK_BRAND
29
+
30
+ // --------------------------------------------------------
31
+ // Get Theme From Chain
32
+ // --------------------------------------------------------
33
+ /** Reduces an array of chained `.theme()` callbacks into a single merged theme object. */
34
+ type OptionFunc = (...arg: any) => Record<string, unknown>
35
+ type GetThemeFromChain = (
36
+ options: OptionFunc[] | undefined | null,
37
+ theme: Record<string, any>,
38
+ ) => ReturnType<OptionFunc>
39
+
40
+ export const getThemeFromChain: GetThemeFromChain = (options, theme) => {
41
+ const result = {}
42
+ if (!options || isEmpty(options)) return result
43
+
44
+ return options.reduce(
45
+ (acc, item) => merge(acc, item(theme, themeModeCallback, config.css)),
46
+ result,
47
+ )
48
+ }
49
+
50
+ // --------------------------------------------------------
51
+ // calculate dimension themes
52
+ // --------------------------------------------------------
53
+ /**
54
+ * Computes the theme object for each dimension by evaluating its
55
+ * chained callbacks against the global theme, then strips nullable values.
56
+ */
57
+ type GetDimensionThemes = (
58
+ theme: Record<string, any>,
59
+ options: Record<string, any>,
60
+ ) => Record<string, any>
61
+
62
+ export const getDimensionThemes: GetDimensionThemes = (theme, options) => {
63
+ const result = {}
64
+
65
+ if (isEmpty(options.dimensions)) return result
66
+
67
+ return Object.entries(options.dimensions).reduce(
68
+ (acc, [key, value]) => {
69
+ const [, dimension] = isMultiKey(value as string | Record<string, unknown>)
70
+
71
+ const helper = options[key]
72
+
73
+ if (Array.isArray(helper) && helper.length > 0) {
74
+ const finalDimensionThemes = getThemeFromChain(helper, theme)
75
+
76
+ acc[dimension] = removeNullableValues(finalDimensionThemes)
77
+ }
78
+
79
+ return acc
80
+ },
81
+ result as Record<string, any>,
82
+ )
83
+ }
84
+
85
+ // --------------------------------------------------------
86
+ // combine values
87
+ // --------------------------------------------------------
88
+ /** Reduces an array of option callbacks by calling each with the given args and deep-merging results. */
89
+ type CalculateChainOptions = (
90
+ options: OptionFunc[] | undefined | null,
91
+ args: any[],
92
+ ) => Record<string, any>
93
+
94
+ export const calculateChainOptions: CalculateChainOptions = (options, args) => {
95
+ const result = {}
96
+ if (!options || isEmpty(options)) return result
97
+
98
+ return options.reduce((acc, item) => merge(acc, item(...args)), result)
99
+ }
100
+
101
+ // --------------------------------------------------------
102
+ // generate theme
103
+ // --------------------------------------------------------
104
+ /**
105
+ * Generates the final theme object by starting with the base theme
106
+ * and merging in dimension-specific theme slices based on the current
107
+ * rocketstate (active dimension values). Supports multi-key dimensions.
108
+ *
109
+ * Transform dimensions (marked with `transform: true`) are evaluated last.
110
+ * Their values are functions that receive the fully accumulated theme and
111
+ * return overrides — enabling derived styles like "outlined" or "inversed".
112
+ */
113
+ export type GetTheme = (params: {
114
+ rocketstate: Record<string, string | string[]>
115
+ themes: Record<string, Record<string, any>>
116
+ baseTheme: Record<string, any>
117
+ transformKeys?: Partial<Record<string, true>>
118
+ /** App theme from context — passed to transform dimension callbacks. */
119
+ appTheme?: Record<string, any>
120
+ }) => Record<string, unknown>
121
+
122
+ export const getTheme: GetTheme = ({ rocketstate, themes, baseTheme, transformKeys, appTheme }) => {
123
+ let finalTheme = { ...baseTheme }
124
+ const deferredTransforms: Array<
125
+ (
126
+ currentTheme: Record<string, any>,
127
+ currentAppTheme: Record<string, any>,
128
+ mode: typeof themeModeCallback,
129
+ cssFn: typeof config.css,
130
+ ) => Record<string, any>
131
+ > = []
132
+
133
+ Object.entries(rocketstate).forEach(([key, value]: [string, string | string[]]) => {
134
+ const keyTheme: Record<string, any> = themes[key] ?? {}
135
+ const isTransform = transformKeys?.[key]
136
+
137
+ const mergeValue = (item: string) => {
138
+ const val = keyTheme[item]
139
+ if (isTransform && typeof val === "function") {
140
+ deferredTransforms.push(val)
141
+ } else {
142
+ finalTheme = merge({}, finalTheme, val)
143
+ }
144
+ }
145
+
146
+ if (Array.isArray(value)) {
147
+ value.forEach(mergeValue)
148
+ } else {
149
+ mergeValue(value)
150
+ }
151
+ })
152
+
153
+ // Apply transform dimension values last with the fully accumulated theme
154
+ for (const transform of deferredTransforms) {
155
+ finalTheme = merge(
156
+ {},
157
+ finalTheme,
158
+ transform(finalTheme, appTheme ?? {}, themeModeCallback, config.css),
159
+ )
160
+ }
161
+
162
+ return finalTheme
163
+ }
164
+
165
+ // --------------------------------------------------------
166
+ // resolve theme by mode
167
+ // --------------------------------------------------------
168
+ /**
169
+ * Recursively traverses a theme object and resolves any `themeModeCallback`
170
+ * functions to their concrete light or dark values for the given mode.
171
+ */
172
+ export type GetThemeByMode = (
173
+ object: Record<string, any>,
174
+ mode: "light" | "dark",
175
+ ) => Partial<{
176
+ baseTheme: Record<string, unknown>
177
+ themes: Record<string, unknown>
178
+ }>
179
+
180
+ export const getThemeByMode: GetThemeByMode = (object, mode) =>
181
+ Object.keys(object).reduce(
182
+ (acc, key) => {
183
+ const value = object[key]
184
+
185
+ if (typeof value === "object" && value !== null) {
186
+ acc[key] = getThemeByMode(value, mode)
187
+ } else if (isModeCallback(value)) {
188
+ acc[key] = value(mode)
189
+ } else {
190
+ acc[key] = value
191
+ }
192
+
193
+ return acc
194
+ },
195
+ {} as Record<string, any>,
196
+ )