@pyreon/rocketstyle 0.11.2 → 0.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +91 -74
- package/package.json +6 -6
- package/src/__tests__/e2e-styler.test.ts +4 -1
- package/src/__tests__/providerConsumer.test.ts +11 -4
- package/src/__tests__/rocketstyleIntegration.test.ts +10 -3
- package/src/hoc/rocketstyleAttrsHoc.ts +8 -4
- package/src/hooks/useTheme.ts +28 -13
- package/src/rocketstyle.ts +109 -91
package/lib/index.js
CHANGED
|
@@ -201,20 +201,33 @@ const createLocalProvider = (WrappedComponent) => {
|
|
|
201
201
|
//#region src/hooks/useTheme.ts
|
|
202
202
|
/**
|
|
203
203
|
* Retrieves the current theme object and resolved mode from context.
|
|
204
|
-
* Supports mode inversion so nested components can flip between
|
|
205
|
-
* light and dark without a new provider.
|
|
206
204
|
*
|
|
207
|
-
*
|
|
205
|
+
* Returns an object with getter properties so that mode/isDark/isLight
|
|
206
|
+
* are evaluated lazily on each access. This supports reactive mode
|
|
207
|
+
* switching via PyreonUI — when PyreonUI provides `get mode()` getters,
|
|
208
|
+
* rocketstyle picks up changes on every styled component re-evaluation.
|
|
209
|
+
*
|
|
210
|
+
* Without getters, destructuring would capture the mode value once at
|
|
211
|
+
* setup time, making theme switching permanently broken.
|
|
208
212
|
*/
|
|
209
213
|
const useThemeAttrs = ({ inversed }) => {
|
|
210
|
-
const
|
|
211
|
-
const mode = inversed ? THEME_MODES_INVERSED[ctxMode] : ctxMode;
|
|
212
|
-
const isDark = inversed ? !ctxDark : ctxDark;
|
|
214
|
+
const ctx = useContext(context) || {};
|
|
213
215
|
return {
|
|
214
|
-
theme
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
get theme() {
|
|
217
|
+
return ctx.theme ?? {};
|
|
218
|
+
},
|
|
219
|
+
get mode() {
|
|
220
|
+
const ctxMode = ctx.mode ?? "light";
|
|
221
|
+
return inversed ? THEME_MODES_INVERSED[ctxMode] : ctxMode;
|
|
222
|
+
},
|
|
223
|
+
get isDark() {
|
|
224
|
+
const ctxDark = ctx.isDark ?? false;
|
|
225
|
+
return inversed ? !ctxDark : ctxDark;
|
|
226
|
+
},
|
|
227
|
+
get isLight() {
|
|
228
|
+
const ctxDark = ctx.isDark ?? false;
|
|
229
|
+
return !(inversed ? !ctxDark : ctxDark);
|
|
230
|
+
}
|
|
218
231
|
};
|
|
219
232
|
};
|
|
220
233
|
|
|
@@ -282,12 +295,12 @@ const rocketStyleHOC = ({ inversed, attrs, priorityAttrs }) => {
|
|
|
282
295
|
const calculatePriorityAttrs = calculateChainOptions(priorityAttrs);
|
|
283
296
|
const Enhanced = (WrappedComponent) => {
|
|
284
297
|
const HOCComponent = (props) => {
|
|
285
|
-
const
|
|
286
|
-
const callbackParams = [theme, {
|
|
298
|
+
const themeAttrs = useThemeAttrs({ inversed });
|
|
299
|
+
const callbackParams = [themeAttrs.theme, {
|
|
287
300
|
render,
|
|
288
|
-
mode,
|
|
289
|
-
isDark,
|
|
290
|
-
isLight
|
|
301
|
+
mode: themeAttrs.mode,
|
|
302
|
+
isDark: themeAttrs.isDark,
|
|
303
|
+
isLight: themeAttrs.isLight
|
|
291
304
|
}];
|
|
292
305
|
const filteredProps = removeUndefinedProps(props);
|
|
293
306
|
const prioritizedAttrs = calculatePriorityAttrs([filteredProps, ...callbackParams]);
|
|
@@ -483,24 +496,7 @@ const rocketComponent = (options) => {
|
|
|
483
496
|
const hocsFuncs = [rocketStyleHOC(options), ...calculateHocsFuncs(options.compose)];
|
|
484
497
|
const EnhancedComponent = (props) => {
|
|
485
498
|
const localCtx = useLocalContext(options.consumer);
|
|
486
|
-
const
|
|
487
|
-
const baseThemeHelper = ThemeManager$1.baseTheme;
|
|
488
|
-
if (!baseThemeHelper.has(theme)) baseThemeHelper.set(theme, getThemeFromChain(options.theme, theme));
|
|
489
|
-
const baseTheme = baseThemeHelper.get(theme);
|
|
490
|
-
const dimHelper = ThemeManager$1.dimensionsThemes;
|
|
491
|
-
if (!dimHelper.has(theme)) dimHelper.set(theme, getDimensionThemes(theme, options));
|
|
492
|
-
const themes = dimHelper.get(theme);
|
|
493
|
-
const modeBaseHelper = ThemeManager$1.modeBaseTheme[mode];
|
|
494
|
-
if (!modeBaseHelper.has(baseTheme)) modeBaseHelper.set(baseTheme, getThemeByMode(baseTheme, mode));
|
|
495
|
-
const currentModeBaseTheme = modeBaseHelper.get(baseTheme);
|
|
496
|
-
const modeDimHelper = ThemeManager$1.modeDimensionTheme[mode];
|
|
497
|
-
if (!modeDimHelper.has(themes)) modeDimHelper.set(themes, getThemeByMode(themes, mode));
|
|
498
|
-
const currentModeThemes = modeDimHelper.get(themes);
|
|
499
|
-
const { keysMap: dimensions, keywords: reservedPropNames } = getDimensionsMap({
|
|
500
|
-
themes,
|
|
501
|
-
useBooleans: options.useBooleans
|
|
502
|
-
});
|
|
503
|
-
const RESERVED_STYLING_PROPS_KEYS = Object.keys(reservedPropNames);
|
|
499
|
+
const themeAttrs = useThemeAttrs(options);
|
|
504
500
|
const { pseudo, ...mergeProps } = {
|
|
505
501
|
...localCtx,
|
|
506
502
|
...props
|
|
@@ -509,48 +505,69 @@ const rocketComponent = (options) => {
|
|
|
509
505
|
...pseudo,
|
|
510
506
|
...pick(props, [...PSEUDO_KEYS, ...PSEUDO_META_KEYS])
|
|
511
507
|
};
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
508
|
+
return (() => {
|
|
509
|
+
const theme = themeAttrs.theme;
|
|
510
|
+
const mode = themeAttrs.mode;
|
|
511
|
+
const baseThemeHelper = ThemeManager$1.baseTheme;
|
|
512
|
+
if (!baseThemeHelper.has(theme)) baseThemeHelper.set(theme, getThemeFromChain(options.theme, theme));
|
|
513
|
+
const baseTheme = baseThemeHelper.get(theme);
|
|
514
|
+
const dimHelper = ThemeManager$1.dimensionsThemes;
|
|
515
|
+
if (!dimHelper.has(theme)) dimHelper.set(theme, getDimensionThemes(theme, options));
|
|
516
|
+
const themes = dimHelper.get(theme);
|
|
517
|
+
const modeBaseHelper = ThemeManager$1.modeBaseTheme[mode];
|
|
518
|
+
if (!modeBaseHelper.has(baseTheme)) modeBaseHelper.set(baseTheme, getThemeByMode(baseTheme, mode));
|
|
519
|
+
const currentModeBaseTheme = modeBaseHelper.get(baseTheme);
|
|
520
|
+
const modeDimHelper = ThemeManager$1.modeDimensionTheme[mode];
|
|
521
|
+
if (!modeDimHelper.has(themes)) modeDimHelper.set(themes, getThemeByMode(themes, mode));
|
|
522
|
+
const currentModeThemes = modeDimHelper.get(themes);
|
|
523
|
+
const { keysMap: dimensions, keywords: reservedPropNames } = getDimensionsMap({
|
|
524
|
+
themes,
|
|
525
|
+
useBooleans: options.useBooleans
|
|
526
|
+
});
|
|
527
|
+
const RESERVED_STYLING_PROPS_KEYS = Object.keys(reservedPropNames);
|
|
528
|
+
const rocketstate = _calculateStylingAttrs({
|
|
529
|
+
props: pickStyledAttrs(mergeProps, reservedPropNames),
|
|
530
|
+
dimensions
|
|
531
|
+
});
|
|
532
|
+
const finalRocketstate = {
|
|
533
|
+
...rocketstate,
|
|
534
|
+
pseudo: pseudoRocketstate
|
|
535
|
+
};
|
|
536
|
+
const computedRocketstyle = getTheme({
|
|
537
|
+
rocketstate,
|
|
538
|
+
themes: currentModeThemes,
|
|
539
|
+
baseTheme: currentModeBaseTheme,
|
|
540
|
+
transformKeys: options.transformKeys,
|
|
541
|
+
appTheme: theme
|
|
542
|
+
});
|
|
543
|
+
const finalProps = {
|
|
544
|
+
...omit(mergeProps, [
|
|
545
|
+
...RESERVED_STYLING_PROPS_KEYS,
|
|
546
|
+
...PSEUDO_KEYS,
|
|
547
|
+
...options.filterAttrs
|
|
548
|
+
]),
|
|
549
|
+
...options.passProps ? pick(mergeProps, options.passProps) : {},
|
|
550
|
+
ref: props.ref,
|
|
551
|
+
$rocketstyle: computedRocketstyle,
|
|
552
|
+
$rocketstate: finalRocketstate
|
|
553
|
+
};
|
|
554
|
+
if (process.env.NODE_ENV !== "production") {
|
|
555
|
+
finalProps["data-rocketstyle"] = componentName;
|
|
556
|
+
if (options.DEBUG) {
|
|
557
|
+
const debugPayload = {
|
|
558
|
+
component: componentName,
|
|
559
|
+
rocketstate: finalRocketstate,
|
|
560
|
+
rocketstyle: computedRocketstyle,
|
|
561
|
+
dimensions,
|
|
562
|
+
mode,
|
|
563
|
+
reservedPropNames: RESERVED_STYLING_PROPS_KEYS,
|
|
564
|
+
filteredAttrs: options.filterAttrs
|
|
565
|
+
};
|
|
566
|
+
console.debug(`[rocketstyle] ${componentName} render:`, debugPayload);
|
|
567
|
+
}
|
|
551
568
|
}
|
|
552
|
-
|
|
553
|
-
|
|
569
|
+
return RenderComponent(finalProps);
|
|
570
|
+
});
|
|
554
571
|
};
|
|
555
572
|
const FinalComponent = compose(...hocsFuncs)(EnhancedComponent);
|
|
556
573
|
FinalComponent.IS_ROCKETSTYLE = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/rocketstyle",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.4",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/pyreon/pyreon",
|
|
@@ -44,13 +44,13 @@
|
|
|
44
44
|
"typecheck": "tsc --noEmit"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"@pyreon/core": "^0.11.
|
|
48
|
-
"@pyreon/reactivity": "^0.11.
|
|
49
|
-
"@pyreon/ui-core": "^0.11.
|
|
50
|
-
"@pyreon/styler": "^0.11.
|
|
47
|
+
"@pyreon/core": "^0.11.4",
|
|
48
|
+
"@pyreon/reactivity": "^0.11.4",
|
|
49
|
+
"@pyreon/ui-core": "^0.11.4",
|
|
50
|
+
"@pyreon/styler": "^0.11.4"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@vitus-labs/tools-rolldown": "^1.15.3",
|
|
54
|
-
"@pyreon/typescript": "^0.11.
|
|
54
|
+
"@pyreon/typescript": "^0.11.4"
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -64,7 +64,10 @@ const getComputedTheme = (Component: any, props: Record<string, any> = {}) => {
|
|
|
64
64
|
]),
|
|
65
65
|
)
|
|
66
66
|
try {
|
|
67
|
-
|
|
67
|
+
let vnode = Component(props) as any
|
|
68
|
+
// EnhancedComponent returns a reactive accessor (function) for mode switching.
|
|
69
|
+
// In tests we evaluate it directly to get the VNode.
|
|
70
|
+
while (typeof vnode === "function") vnode = vnode()
|
|
68
71
|
return vnode.$rocketstyle
|
|
69
72
|
} finally {
|
|
70
73
|
popContext()
|
|
@@ -60,6 +60,13 @@ const ChildComponent: any = ({
|
|
|
60
60
|
})
|
|
61
61
|
ChildComponent.displayName = "ChildComponent"
|
|
62
62
|
|
|
63
|
+
/** Unwrap reactive accessors (EnhancedComponent returns a function for mode switching). */
|
|
64
|
+
const unwrap = (val: any): any => {
|
|
65
|
+
let result = val
|
|
66
|
+
while (typeof result === "function" && !result.IS_ROCKETSTYLE) result = result()
|
|
67
|
+
return result
|
|
68
|
+
}
|
|
69
|
+
|
|
63
70
|
/** Push a theme context and run fn, then pop */
|
|
64
71
|
const withThemeContext = (fn: () => any) => {
|
|
65
72
|
pushContext(
|
|
@@ -103,7 +110,7 @@ describe("Provider/Consumer integration", () => {
|
|
|
103
110
|
component: BaseComponent,
|
|
104
111
|
}).config({ provider: true })
|
|
105
112
|
|
|
106
|
-
const result = withThemeContext(() => ParentButton({ children: "Child" }))
|
|
113
|
+
const result = withThemeContext(() => unwrap(ParentButton({ children: "Child" })))
|
|
107
114
|
// Provider wraps with createLocalProvider which injects pseudo state
|
|
108
115
|
// Initial state should be false
|
|
109
116
|
expect(result.props["data-hover"]).toBe("false")
|
|
@@ -131,9 +138,9 @@ describe("Provider/Consumer integration", () => {
|
|
|
131
138
|
|
|
132
139
|
// Render parent, then render child within the same context
|
|
133
140
|
withThemeContext(() => {
|
|
134
|
-
const _parentResult = Parent({ children: null })
|
|
141
|
+
const _parentResult = unwrap(Parent({ children: null }))
|
|
135
142
|
// The parent pushes local context — child should see it
|
|
136
|
-
const childResult = Child({})
|
|
143
|
+
const childResult = unwrap(Child({}))
|
|
137
144
|
expect(childResult).toBeDefined()
|
|
138
145
|
const childProps = childResult?.props ?? childResult
|
|
139
146
|
expect(childProps["data-parent-hover"]).toBe("no")
|
|
@@ -151,7 +158,7 @@ describe("Provider/Consumer integration", () => {
|
|
|
151
158
|
})),
|
|
152
159
|
})
|
|
153
160
|
|
|
154
|
-
const result = withThemeContext(() => Child({}))
|
|
161
|
+
const result = withThemeContext(() => unwrap(Child({})))
|
|
155
162
|
const props = result?.props ?? result
|
|
156
163
|
expect(props["data-parent-hover"]).toBe("no")
|
|
157
164
|
})
|
|
@@ -66,10 +66,17 @@ const withThemeContext = (fn: () => any) => {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/** Unwrap reactive accessors (EnhancedComponent returns a function for mode switching). */
|
|
70
|
+
const unwrap = (val: any): any => {
|
|
71
|
+
let result = val
|
|
72
|
+
while (typeof result === "function" && !result.IS_ROCKETSTYLE) result = result()
|
|
73
|
+
return result
|
|
74
|
+
}
|
|
75
|
+
|
|
69
76
|
/** Helper: call the component and return its output for inspection. */
|
|
70
77
|
const renderProps = (Component: any, props: Record<string, any> = {}) => {
|
|
71
78
|
return withThemeContext(() => {
|
|
72
|
-
const vnode = Component(props)
|
|
79
|
+
const vnode = unwrap(Component(props))
|
|
73
80
|
return vnode?.props ?? vnode
|
|
74
81
|
})
|
|
75
82
|
}
|
|
@@ -584,7 +591,7 @@ describe("theme and state injection", () => {
|
|
|
584
591
|
primary: { color: "red" },
|
|
585
592
|
}))
|
|
586
593
|
|
|
587
|
-
const vnode = withThemeContext(() => Button({ state: "primary" }))
|
|
594
|
+
const vnode = withThemeContext(() => unwrap(Button({ state: "primary" })))
|
|
588
595
|
expect(vnode.$rocketstyle).toBeDefined()
|
|
589
596
|
expect(vnode.$rocketstyle.color).toBe("red")
|
|
590
597
|
expect(vnode.$rocketstyle.bg).toBe("white")
|
|
@@ -608,7 +615,7 @@ describe("theme and state injection", () => {
|
|
|
608
615
|
primary: { color: "blue" },
|
|
609
616
|
}))
|
|
610
617
|
|
|
611
|
-
const vnode = withThemeContext(() => Button({ state: "primary" }))
|
|
618
|
+
const vnode = withThemeContext(() => unwrap(Button({ state: "primary" })))
|
|
612
619
|
expect(vnode.$rocketstate).toBeDefined()
|
|
613
620
|
expect(vnode.$rocketstate.state).toBe("primary")
|
|
614
621
|
})
|
|
@@ -27,11 +27,15 @@ const rocketStyleHOC: RocketStyleHOC = ({ inversed, attrs, priorityAttrs }) => {
|
|
|
27
27
|
|
|
28
28
|
const Enhanced = (WrappedComponent: ComponentFn<any>) => {
|
|
29
29
|
const HOCComponent: ComponentFn<any> = (props) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
// IMPORTANT: Do NOT destructure — useTheme returns getter properties.
|
|
31
|
+
// Destructuring calls getters once and captures static values.
|
|
32
|
+
// Keep the object reference so properties re-evaluate lazily.
|
|
33
|
+
const themeAttrs = useTheme({ inversed })
|
|
33
34
|
|
|
34
|
-
const callbackParams = [
|
|
35
|
+
const callbackParams = [
|
|
36
|
+
themeAttrs.theme,
|
|
37
|
+
{ render, mode: themeAttrs.mode, isDark: themeAttrs.isDark, isLight: themeAttrs.isLight },
|
|
38
|
+
]
|
|
35
39
|
|
|
36
40
|
// Remove undefined props not to override potential default props
|
|
37
41
|
const filteredProps = removeUndefinedProps(props)
|
package/src/hooks/useTheme.ts
CHANGED
|
@@ -14,23 +14,38 @@ type UseThemeAttrs = ({ inversed }: { inversed?: boolean | undefined }) => Conte
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Retrieves the current theme object and resolved mode from context.
|
|
17
|
-
* Supports mode inversion so nested components can flip between
|
|
18
|
-
* light and dark without a new provider.
|
|
19
17
|
*
|
|
20
|
-
*
|
|
18
|
+
* Returns an object with getter properties so that mode/isDark/isLight
|
|
19
|
+
* are evaluated lazily on each access. This supports reactive mode
|
|
20
|
+
* switching via PyreonUI — when PyreonUI provides `get mode()` getters,
|
|
21
|
+
* rocketstyle picks up changes on every styled component re-evaluation.
|
|
22
|
+
*
|
|
23
|
+
* Without getters, destructuring would capture the mode value once at
|
|
24
|
+
* setup time, making theme switching permanently broken.
|
|
21
25
|
*/
|
|
22
26
|
const useThemeAttrs: UseThemeAttrs = ({ inversed }) => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
isDark: ctxDark,
|
|
27
|
-
} = useContext<Context>(context) || {}
|
|
28
|
-
|
|
29
|
-
const mode = inversed ? THEME_MODES_INVERSED[ctxMode] : ctxMode
|
|
30
|
-
const isDark = inversed ? !ctxDark : ctxDark
|
|
31
|
-
const isLight = !isDark
|
|
27
|
+
// Keep the context object reference — read its properties lazily via getters.
|
|
28
|
+
// PyreonUI provides { get mode() {...} } so each access re-evaluates.
|
|
29
|
+
const ctx = useContext<Context>(context) || ({} as Partial<Context>)
|
|
32
30
|
|
|
33
|
-
return {
|
|
31
|
+
return {
|
|
32
|
+
get theme() {
|
|
33
|
+
return ctx.theme ?? ({} as Record<string, unknown>)
|
|
34
|
+
},
|
|
35
|
+
get mode() {
|
|
36
|
+
const ctxMode = ctx.mode ?? "light"
|
|
37
|
+
return inversed ? THEME_MODES_INVERSED[ctxMode] : ctxMode
|
|
38
|
+
},
|
|
39
|
+
get isDark() {
|
|
40
|
+
const ctxDark = ctx.isDark ?? false
|
|
41
|
+
return inversed ? !ctxDark : ctxDark
|
|
42
|
+
},
|
|
43
|
+
get isLight() {
|
|
44
|
+
const ctxDark = ctx.isDark ?? false
|
|
45
|
+
const isDark = inversed ? !ctxDark : ctxDark
|
|
46
|
+
return !isDark
|
|
47
|
+
},
|
|
48
|
+
}
|
|
34
49
|
}
|
|
35
50
|
|
|
36
51
|
export default useThemeAttrs
|
package/src/rocketstyle.ts
CHANGED
|
@@ -104,120 +104,138 @@ const rocketComponent: RocketComponent = (options) => {
|
|
|
104
104
|
// --------------------------------------------------
|
|
105
105
|
// general theme and theme mode dark / light passed in context
|
|
106
106
|
// --------------------------------------------------
|
|
107
|
-
|
|
107
|
+
// IMPORTANT: Do NOT destructure — useTheme returns getter properties.
|
|
108
|
+
// Destructuring calls getters once and captures static values.
|
|
109
|
+
// Keep the object reference so mode/isDark/isLight re-evaluate lazily.
|
|
110
|
+
const themeAttrs = useTheme(options)
|
|
108
111
|
|
|
109
112
|
// --------------------------------------------------
|
|
110
|
-
//
|
|
111
|
-
// --------------------------------------------------
|
|
112
|
-
|
|
113
|
-
// BASE / DEFAULT THEME Object (cached by theme identity)
|
|
114
|
-
const baseThemeHelper = ThemeManager.baseTheme
|
|
115
|
-
if (!baseThemeHelper.has(theme)) {
|
|
116
|
-
baseThemeHelper.set(theme, getThemeFromChain(options.theme, theme))
|
|
117
|
-
}
|
|
118
|
-
const baseTheme = baseThemeHelper.get(theme)
|
|
119
|
-
|
|
120
|
-
// DIMENSION(S) THEMES Object (cached by theme identity)
|
|
121
|
-
const dimHelper = ThemeManager.dimensionsThemes
|
|
122
|
-
if (!dimHelper.has(theme)) {
|
|
123
|
-
dimHelper.set(theme, getDimensionThemes(theme, options))
|
|
124
|
-
}
|
|
125
|
-
const themes = dimHelper.get(theme)
|
|
126
|
-
|
|
127
|
-
// BASE / DEFAULT MODE THEME Object (cached by mode + baseTheme)
|
|
128
|
-
const modeBaseHelper = ThemeManager.modeBaseTheme[mode]
|
|
129
|
-
if (!modeBaseHelper.has(baseTheme)) {
|
|
130
|
-
modeBaseHelper.set(baseTheme, getThemeByMode(baseTheme, mode))
|
|
131
|
-
}
|
|
132
|
-
const currentModeBaseTheme = modeBaseHelper.get(baseTheme)
|
|
133
|
-
|
|
134
|
-
// DIMENSION(S) MODE THEMES Object (cached by mode + themes)
|
|
135
|
-
const modeDimHelper = ThemeManager.modeDimensionTheme[mode]
|
|
136
|
-
if (!modeDimHelper.has(themes)) {
|
|
137
|
-
modeDimHelper.set(themes, getThemeByMode(themes, mode))
|
|
138
|
-
}
|
|
139
|
-
const currentModeThemes = modeDimHelper.get(themes)
|
|
140
|
-
|
|
141
|
-
// --------------------------------------------------
|
|
142
|
-
// dimension map & reserved prop names
|
|
143
|
-
// --------------------------------------------------
|
|
144
|
-
const { keysMap: dimensions, keywords: reservedPropNames } = getDimensionsMap({
|
|
145
|
-
themes,
|
|
146
|
-
useBooleans: options.useBooleans,
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
const RESERVED_STYLING_PROPS_KEYS = Object.keys(reservedPropNames)
|
|
150
|
-
|
|
151
|
-
// --------------------------------------------------
|
|
152
|
-
// get final props: merged styling from context + attrs + direct props
|
|
113
|
+
// Static setup — runs once at component mount
|
|
153
114
|
// --------------------------------------------------
|
|
154
115
|
const { pseudo, ...mergeProps } = {
|
|
155
116
|
...localCtx,
|
|
156
117
|
...props,
|
|
157
118
|
}
|
|
158
119
|
|
|
159
|
-
// --------------------------------------------------
|
|
160
|
-
// pseudo rocket state
|
|
161
|
-
// --------------------------------------------------
|
|
162
120
|
const pseudoRocketstate = {
|
|
163
121
|
...pseudo,
|
|
164
122
|
...pick(props, [...PSEUDO_KEYS, ...PSEUDO_META_KEYS]),
|
|
165
123
|
}
|
|
166
124
|
|
|
167
125
|
// --------------------------------------------------
|
|
168
|
-
//
|
|
126
|
+
// Reactive accessor — re-evaluates when mode changes.
|
|
127
|
+
// When mounted, the runtime wraps this in an effect so
|
|
128
|
+
// reading themeAttrs.mode creates a reactive dependency.
|
|
129
|
+
// Mode switches are infrequent (user-initiated), so full
|
|
130
|
+
// re-render of the styled subtree is acceptable.
|
|
169
131
|
// --------------------------------------------------
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
132
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: theme resolution is inherently multi-step
|
|
133
|
+
return (() => {
|
|
134
|
+
// Read theme and mode lazily via getters — tracked by the effect
|
|
135
|
+
const theme = themeAttrs.theme
|
|
136
|
+
const mode = themeAttrs.mode
|
|
137
|
+
|
|
138
|
+
// --------------------------------------------------
|
|
139
|
+
// calculate themes for all defined styling dimensions
|
|
140
|
+
// --------------------------------------------------
|
|
141
|
+
|
|
142
|
+
// BASE / DEFAULT THEME Object (cached by theme identity)
|
|
143
|
+
const baseThemeHelper = ThemeManager.baseTheme
|
|
144
|
+
if (!baseThemeHelper.has(theme)) {
|
|
145
|
+
baseThemeHelper.set(theme, getThemeFromChain(options.theme, theme))
|
|
146
|
+
}
|
|
147
|
+
const baseTheme = baseThemeHelper.get(theme)
|
|
174
148
|
|
|
175
|
-
|
|
149
|
+
// DIMENSION(S) THEMES Object (cached by theme identity)
|
|
150
|
+
const dimHelper = ThemeManager.dimensionsThemes
|
|
151
|
+
if (!dimHelper.has(theme)) {
|
|
152
|
+
dimHelper.set(theme, getDimensionThemes(theme, options))
|
|
153
|
+
}
|
|
154
|
+
const themes = dimHelper.get(theme)
|
|
176
155
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
baseTheme: currentModeBaseTheme,
|
|
184
|
-
transformKeys: options.transformKeys,
|
|
185
|
-
appTheme: theme,
|
|
186
|
-
})
|
|
156
|
+
// BASE / DEFAULT MODE THEME Object (cached by mode + baseTheme)
|
|
157
|
+
const modeBaseHelper = ThemeManager.modeBaseTheme[mode]
|
|
158
|
+
if (!modeBaseHelper.has(baseTheme)) {
|
|
159
|
+
modeBaseHelper.set(baseTheme, getThemeByMode(baseTheme, mode))
|
|
160
|
+
}
|
|
161
|
+
const currentModeBaseTheme = modeBaseHelper.get(baseTheme)
|
|
187
162
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
// ref flows as a normal prop in Pyreon
|
|
195
|
-
ref: props.ref,
|
|
196
|
-
$rocketstyle: computedRocketstyle,
|
|
197
|
-
$rocketstate: finalRocketstate,
|
|
198
|
-
}
|
|
163
|
+
// DIMENSION(S) MODE THEMES Object (cached by mode + themes)
|
|
164
|
+
const modeDimHelper = ThemeManager.modeDimensionTheme[mode]
|
|
165
|
+
if (!modeDimHelper.has(themes)) {
|
|
166
|
+
modeDimHelper.set(themes, getThemeByMode(themes, mode))
|
|
167
|
+
}
|
|
168
|
+
const currentModeThemes = modeDimHelper.get(themes)
|
|
199
169
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
170
|
+
// --------------------------------------------------
|
|
171
|
+
// dimension map & reserved prop names
|
|
172
|
+
// --------------------------------------------------
|
|
173
|
+
const { keysMap: dimensions, keywords: reservedPropNames } = getDimensionsMap({
|
|
174
|
+
themes,
|
|
175
|
+
useBooleans: options.useBooleans,
|
|
176
|
+
})
|
|
203
177
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
178
|
+
const RESERVED_STYLING_PROPS_KEYS = Object.keys(reservedPropNames)
|
|
179
|
+
|
|
180
|
+
// --------------------------------------------------
|
|
181
|
+
// rocketstate — active dimension values
|
|
182
|
+
// --------------------------------------------------
|
|
183
|
+
const rocketstate = _calculateStylingAttrs({
|
|
184
|
+
props: pickStyledAttrs(mergeProps, reservedPropNames),
|
|
185
|
+
dimensions,
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
const finalRocketstate = { ...rocketstate, pseudo: pseudoRocketstate }
|
|
189
|
+
|
|
190
|
+
// --------------------------------------------------
|
|
191
|
+
// rocketstyle — computed theme based on active dimensions
|
|
192
|
+
// --------------------------------------------------
|
|
193
|
+
const computedRocketstyle = getTheme({
|
|
194
|
+
rocketstate,
|
|
195
|
+
themes: currentModeThemes,
|
|
196
|
+
baseTheme: currentModeBaseTheme,
|
|
197
|
+
transformKeys: options.transformKeys,
|
|
198
|
+
appTheme: theme,
|
|
199
|
+
})
|
|
214
200
|
|
|
215
|
-
|
|
216
|
-
|
|
201
|
+
// --------------------------------------------------
|
|
202
|
+
// final props passed to WrappedComponent
|
|
203
|
+
// --------------------------------------------------
|
|
204
|
+
const finalProps: Record<string, any> = {
|
|
205
|
+
...omit(mergeProps, [
|
|
206
|
+
...RESERVED_STYLING_PROPS_KEYS,
|
|
207
|
+
...PSEUDO_KEYS,
|
|
208
|
+
...options.filterAttrs,
|
|
209
|
+
]),
|
|
210
|
+
...(options.passProps ? pick(mergeProps, options.passProps) : {}),
|
|
211
|
+
// ref flows as a normal prop in Pyreon
|
|
212
|
+
ref: props.ref,
|
|
213
|
+
$rocketstyle: computedRocketstyle,
|
|
214
|
+
$rocketstate: finalRocketstate,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// development debugging
|
|
218
|
+
if (process.env.NODE_ENV !== "production") {
|
|
219
|
+
finalProps["data-rocketstyle"] = componentName
|
|
220
|
+
|
|
221
|
+
if (options.DEBUG) {
|
|
222
|
+
const debugPayload = {
|
|
223
|
+
component: componentName,
|
|
224
|
+
rocketstate: finalRocketstate,
|
|
225
|
+
rocketstyle: computedRocketstyle,
|
|
226
|
+
dimensions,
|
|
227
|
+
mode,
|
|
228
|
+
reservedPropNames: RESERVED_STYLING_PROPS_KEYS,
|
|
229
|
+
filteredAttrs: options.filterAttrs,
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// biome-ignore lint/suspicious/noConsole: debug logging controlled by DEBUG option
|
|
233
|
+
console.debug(`[rocketstyle] ${componentName} render:`, debugPayload)
|
|
234
|
+
}
|
|
217
235
|
}
|
|
218
|
-
}
|
|
219
236
|
|
|
220
|
-
|
|
237
|
+
return RenderComponent(finalProps)
|
|
238
|
+
}) as unknown as ReturnType<ComponentFn<InnerComponentProps>>
|
|
221
239
|
}
|
|
222
240
|
|
|
223
241
|
// ------------------------------------------------------
|