@pyreon/rocketstyle 0.11.6 → 0.11.7
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
CHANGED
|
@@ -175,10 +175,11 @@ const createLocalProvider = (WrappedComponent) => {
|
|
|
175
175
|
if (onBlur) onBlur(e);
|
|
176
176
|
}
|
|
177
177
|
};
|
|
178
|
+
const resolvedState = typeof $rocketstate === "function" ? $rocketstate() : $rocketstate;
|
|
178
179
|
const updatedState = {
|
|
179
|
-
|
|
180
|
+
...resolvedState,
|
|
180
181
|
pseudo: {
|
|
181
|
-
|
|
182
|
+
...resolvedState?.pseudo,
|
|
182
183
|
get hover() {
|
|
183
184
|
return hover();
|
|
184
185
|
},
|
|
@@ -497,14 +498,6 @@ const rocketComponent = (options) => {
|
|
|
497
498
|
const EnhancedComponent = (props) => {
|
|
498
499
|
const localCtx = useLocalContext(options.consumer);
|
|
499
500
|
const themeAttrs = useThemeAttrs(options);
|
|
500
|
-
const { pseudo, ...mergeProps } = {
|
|
501
|
-
...localCtx,
|
|
502
|
-
...props
|
|
503
|
-
};
|
|
504
|
-
const pseudoRocketstate = {
|
|
505
|
-
...pseudo,
|
|
506
|
-
...pick(props, [...PSEUDO_KEYS, ...PSEUDO_META_KEYS])
|
|
507
|
-
};
|
|
508
501
|
const theme = themeAttrs.theme;
|
|
509
502
|
const baseThemeHelper = ThemeManager$1.baseTheme;
|
|
510
503
|
if (!baseThemeHelper.has(theme)) baseThemeHelper.set(theme, getThemeFromChain(options.theme, theme));
|
|
@@ -517,16 +510,16 @@ const rocketComponent = (options) => {
|
|
|
517
510
|
useBooleans: options.useBooleans
|
|
518
511
|
});
|
|
519
512
|
const RESERVED_STYLING_PROPS_KEYS = Object.keys(reservedPropNames);
|
|
520
|
-
const rocketstate = _calculateStylingAttrs({
|
|
521
|
-
props: pickStyledAttrs(mergeProps, reservedPropNames),
|
|
522
|
-
dimensions
|
|
523
|
-
});
|
|
524
|
-
const finalRocketstate = {
|
|
525
|
-
...rocketstate,
|
|
526
|
-
pseudo: pseudoRocketstate
|
|
527
|
-
};
|
|
528
513
|
const $rocketstyleAccessor = () => {
|
|
514
|
+
const { pseudo, ...mergeProps } = {
|
|
515
|
+
...localCtx,
|
|
516
|
+
...props
|
|
517
|
+
};
|
|
529
518
|
const mode = themeAttrs.mode;
|
|
519
|
+
const rocketstate = _calculateStylingAttrs({
|
|
520
|
+
props: pickStyledAttrs(mergeProps, reservedPropNames),
|
|
521
|
+
dimensions
|
|
522
|
+
});
|
|
530
523
|
const modeBaseHelper = ThemeManager$1.modeBaseTheme[mode];
|
|
531
524
|
if (!modeBaseHelper.has(baseTheme)) modeBaseHelper.set(baseTheme, getThemeByMode(baseTheme, mode));
|
|
532
525
|
const currentModeBaseTheme = modeBaseHelper.get(baseTheme);
|
|
@@ -540,6 +533,27 @@ const rocketComponent = (options) => {
|
|
|
540
533
|
appTheme: theme
|
|
541
534
|
});
|
|
542
535
|
};
|
|
536
|
+
const $rocketstateAccessor = () => {
|
|
537
|
+
const { pseudo, ...mergeProps } = {
|
|
538
|
+
...localCtx,
|
|
539
|
+
...props
|
|
540
|
+
};
|
|
541
|
+
const pseudoRocketstate = {
|
|
542
|
+
...pseudo,
|
|
543
|
+
...pick(props, [...PSEUDO_KEYS, ...PSEUDO_META_KEYS])
|
|
544
|
+
};
|
|
545
|
+
return {
|
|
546
|
+
..._calculateStylingAttrs({
|
|
547
|
+
props: pickStyledAttrs(mergeProps, reservedPropNames),
|
|
548
|
+
dimensions
|
|
549
|
+
}),
|
|
550
|
+
pseudo: pseudoRocketstate
|
|
551
|
+
};
|
|
552
|
+
};
|
|
553
|
+
const { pseudo: _pseudo, ...mergeProps } = {
|
|
554
|
+
...localCtx,
|
|
555
|
+
...props
|
|
556
|
+
};
|
|
543
557
|
const finalProps = {
|
|
544
558
|
...omit(mergeProps, [
|
|
545
559
|
...RESERVED_STYLING_PROPS_KEYS,
|
|
@@ -549,14 +563,14 @@ const rocketComponent = (options) => {
|
|
|
549
563
|
...options.passProps ? pick(mergeProps, options.passProps) : {},
|
|
550
564
|
ref: props.ref,
|
|
551
565
|
$rocketstyle: $rocketstyleAccessor,
|
|
552
|
-
$rocketstate:
|
|
566
|
+
$rocketstate: $rocketstateAccessor
|
|
553
567
|
};
|
|
554
568
|
if (process.env.NODE_ENV !== "production") {
|
|
555
569
|
finalProps["data-rocketstyle"] = componentName;
|
|
556
570
|
if (options.DEBUG) {
|
|
557
571
|
const debugPayload = {
|
|
558
572
|
component: componentName,
|
|
559
|
-
rocketstate:
|
|
573
|
+
rocketstate: $rocketstateAccessor(),
|
|
560
574
|
rocketstyle: $rocketstyleAccessor(),
|
|
561
575
|
dimensions,
|
|
562
576
|
mode: themeAttrs.mode,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/rocketstyle",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.7",
|
|
4
4
|
"description": "Multi-dimensional style composition for Pyreon components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
"typecheck": "tsc --noEmit"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@pyreon/test-utils": "^0.11.
|
|
45
|
-
"@pyreon/typescript": "^0.11.
|
|
44
|
+
"@pyreon/test-utils": "^0.11.7",
|
|
45
|
+
"@pyreon/typescript": "^0.11.7",
|
|
46
46
|
"@vitus-labs/tools-rolldown": "^1.15.3"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@pyreon/core": "^0.11.
|
|
50
|
-
"@pyreon/reactivity": "^0.11.
|
|
51
|
-
"@pyreon/styler": "^0.11.
|
|
52
|
-
"@pyreon/ui-core": "^0.11.
|
|
49
|
+
"@pyreon/core": "^0.11.7",
|
|
50
|
+
"@pyreon/reactivity": "^0.11.7",
|
|
51
|
+
"@pyreon/styler": "^0.11.7",
|
|
52
|
+
"@pyreon/ui-core": "^0.11.7"
|
|
53
53
|
},
|
|
54
54
|
"engines": {
|
|
55
55
|
"node": ">= 22"
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Unlike the React version which tested CSS injection in the DOM,
|
|
8
8
|
* this Pyreon version tests the computed $rocketstyle output directly.
|
|
9
9
|
*/
|
|
10
|
-
import { ThemeCapture, getComputedTheme, initTestConfig } from '@pyreon/test-utils'
|
|
10
|
+
import { ThemeCapture, getComputedTheme, initTestConfig, withThemeContext } from '@pyreon/test-utils'
|
|
11
11
|
import rocketstyle from '../init'
|
|
12
12
|
|
|
13
13
|
let cleanup: () => void
|
|
@@ -234,3 +234,67 @@ describe('e2e: rocketstyle theme computation', () => {
|
|
|
234
234
|
expect(theme.sawStep).toBe('one')
|
|
235
235
|
})
|
|
236
236
|
})
|
|
237
|
+
|
|
238
|
+
// ── Reactive dimension props ──────────────────────────────────────────────────
|
|
239
|
+
|
|
240
|
+
describe('reactive $rocketstyle accessor', () => {
|
|
241
|
+
it('$rocketstyleAccessor resolves different themes for different dimension props', () => {
|
|
242
|
+
const Comp: any = rocketstyle()({
|
|
243
|
+
name: 'ReactiveComp',
|
|
244
|
+
component: ThemeCapture,
|
|
245
|
+
})
|
|
246
|
+
.theme({ color: 'black', bg: 'white' })
|
|
247
|
+
.states({
|
|
248
|
+
primary: { color: 'blue' },
|
|
249
|
+
secondary: { color: 'green' },
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
// First call with state=primary
|
|
253
|
+
const theme1 = getComputedTheme(Comp, { state: 'primary' })
|
|
254
|
+
expect(theme1.color).toBe('blue')
|
|
255
|
+
|
|
256
|
+
// Second call with state=secondary — should produce different theme
|
|
257
|
+
const theme2 = getComputedTheme(Comp, { state: 'secondary' })
|
|
258
|
+
expect(theme2.color).toBe('green')
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
it('$rocketstyleAccessor is a function, not a plain object', () => {
|
|
262
|
+
const Comp: any = rocketstyle()({
|
|
263
|
+
name: 'AccessorComp',
|
|
264
|
+
component: ThemeCapture,
|
|
265
|
+
}).theme({ color: 'red' })
|
|
266
|
+
|
|
267
|
+
const vnode = withThemeContext(() => Comp({}))
|
|
268
|
+
// ThemeCapture resolves the accessor — result should be the theme object
|
|
269
|
+
expect(vnode.$rocketstyle).toBeDefined()
|
|
270
|
+
expect(vnode.$rocketstyle.color).toBe('red')
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
it('$rocketstateAccessor resolves active dimensions', () => {
|
|
274
|
+
const Comp: any = rocketstyle()({
|
|
275
|
+
name: 'StateAccessorComp',
|
|
276
|
+
component: ThemeCapture,
|
|
277
|
+
}).states({
|
|
278
|
+
primary: { color: 'blue' },
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
const vnode = withThemeContext(() => Comp({ state: 'primary' }))
|
|
282
|
+
expect(vnode.$rocketstate).toBeDefined()
|
|
283
|
+
expect(vnode.$rocketstate.state).toBe('primary')
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('mode change produces different theme via accessor', () => {
|
|
287
|
+
const Comp: any = rocketstyle()({
|
|
288
|
+
name: 'ModeReactiveComp',
|
|
289
|
+
component: ThemeCapture,
|
|
290
|
+
}).theme((t: any, m: any) => ({
|
|
291
|
+
color: m('light-color', 'dark-color'),
|
|
292
|
+
}))
|
|
293
|
+
|
|
294
|
+
const lightTheme = getComputedTheme(Comp, {}, { mode: 'light' })
|
|
295
|
+
expect(lightTheme.color).toBe('light-color')
|
|
296
|
+
|
|
297
|
+
const darkTheme = getComputedTheme(Comp, {}, { mode: 'dark' })
|
|
298
|
+
expect(darkTheme.color).toBe('dark-color')
|
|
299
|
+
})
|
|
300
|
+
})
|
|
@@ -23,8 +23,8 @@ const BaseComponent: any = ({ children, $rocketstyle, $rocketstate, ...rest }: a
|
|
|
23
23
|
props: rest,
|
|
24
24
|
children,
|
|
25
25
|
key: null,
|
|
26
|
-
$rocketstyle,
|
|
27
|
-
$rocketstate,
|
|
26
|
+
$rocketstyle: typeof $rocketstyle === 'function' ? $rocketstyle() : $rocketstyle,
|
|
27
|
+
$rocketstate: typeof $rocketstate === 'function' ? $rocketstate() : $rocketstate,
|
|
28
28
|
})
|
|
29
29
|
BaseComponent.displayName = 'BaseComponent'
|
|
30
30
|
|
|
@@ -62,10 +62,13 @@ const createLocalProvider = (WrappedComponent: ComponentFn<any>) => {
|
|
|
62
62
|
// Without getters, hover()/focus()/pressed() reads here would register
|
|
63
63
|
// as dependencies of any parent effect, causing cascading re-renders
|
|
64
64
|
// on every mouse event.
|
|
65
|
+
// Resolve $rocketstate if it's a function accessor (from EnhancedComponent)
|
|
66
|
+
const resolvedState =
|
|
67
|
+
typeof $rocketstate === 'function' ? $rocketstate() : $rocketstate
|
|
65
68
|
const updatedState = {
|
|
66
|
-
|
|
69
|
+
...resolvedState,
|
|
67
70
|
pseudo: {
|
|
68
|
-
|
|
71
|
+
...resolvedState?.pseudo,
|
|
69
72
|
get hover() {
|
|
70
73
|
return hover()
|
|
71
74
|
},
|
package/src/rocketstyle.ts
CHANGED
|
@@ -110,41 +110,24 @@ const rocketComponent: RocketComponent = (options) => {
|
|
|
110
110
|
const themeAttrs = useTheme(options)
|
|
111
111
|
|
|
112
112
|
// --------------------------------------------------
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
...localCtx,
|
|
117
|
-
...props,
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const pseudoRocketstate = {
|
|
121
|
-
...pseudo,
|
|
122
|
-
...pick(props, [...PSEUDO_KEYS, ...PSEUDO_META_KEYS]),
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// --------------------------------------------------
|
|
126
|
-
// Static theme structure — computed once at mount, doesn't change with mode.
|
|
127
|
-
// Only the mode-dependent resolution is reactive (via $rocketstyle accessor).
|
|
113
|
+
// Theme structure — cached by theme object identity.
|
|
114
|
+
// Theme object itself doesn't change (enrichTheme produces a stable ref),
|
|
115
|
+
// only mode switches change which mode-variant is resolved.
|
|
128
116
|
// --------------------------------------------------
|
|
129
117
|
const theme = themeAttrs.theme
|
|
130
118
|
|
|
131
|
-
// BASE / DEFAULT THEME Object (cached by theme identity)
|
|
132
119
|
const baseThemeHelper = ThemeManager.baseTheme
|
|
133
120
|
if (!baseThemeHelper.has(theme)) {
|
|
134
121
|
baseThemeHelper.set(theme, getThemeFromChain(options.theme, theme))
|
|
135
122
|
}
|
|
136
123
|
const baseTheme = baseThemeHelper.get(theme)
|
|
137
124
|
|
|
138
|
-
// DIMENSION(S) THEMES Object (cached by theme identity)
|
|
139
125
|
const dimHelper = ThemeManager.dimensionsThemes
|
|
140
126
|
if (!dimHelper.has(theme)) {
|
|
141
127
|
dimHelper.set(theme, getDimensionThemes(theme, options))
|
|
142
128
|
}
|
|
143
129
|
const themes = dimHelper.get(theme)
|
|
144
130
|
|
|
145
|
-
// --------------------------------------------------
|
|
146
|
-
// dimension map & reserved prop names
|
|
147
|
-
// --------------------------------------------------
|
|
148
131
|
const { keysMap: dimensions, keywords: reservedPropNames } = getDimensionsMap({
|
|
149
132
|
themes,
|
|
150
133
|
useBooleans: options.useBooleans,
|
|
@@ -153,23 +136,28 @@ const rocketComponent: RocketComponent = (options) => {
|
|
|
153
136
|
const RESERVED_STYLING_PROPS_KEYS = Object.keys(reservedPropNames)
|
|
154
137
|
|
|
155
138
|
// --------------------------------------------------
|
|
156
|
-
//
|
|
139
|
+
// $rocketstyle as a FUNCTION ACCESSOR — fully reactive.
|
|
140
|
+
// Re-evaluates when mode OR dimension props change.
|
|
141
|
+
// Props are resolved fresh each call so reactive prop accessors
|
|
142
|
+
// (signals, getters) produce updated dimension values.
|
|
157
143
|
// --------------------------------------------------
|
|
158
|
-
const
|
|
159
|
-
props
|
|
160
|
-
|
|
161
|
-
|
|
144
|
+
const $rocketstyleAccessor = () => {
|
|
145
|
+
// Merge props fresh — if parent passes reactive accessors,
|
|
146
|
+
// spreading them here evaluates them in this reactive scope.
|
|
147
|
+
const { pseudo, ...mergeProps } = {
|
|
148
|
+
...localCtx,
|
|
149
|
+
...props,
|
|
150
|
+
}
|
|
162
151
|
|
|
163
|
-
|
|
152
|
+
const mode = themeAttrs.mode // reactive: tracks mode signal
|
|
164
153
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const $rocketstyleAccessor = () => {
|
|
171
|
-
const mode = themeAttrs.mode // reactive read via getter
|
|
154
|
+
// Resolve active dimensions from current prop values
|
|
155
|
+
const rocketstate = _calculateStylingAttrs({
|
|
156
|
+
props: pickStyledAttrs(mergeProps, reservedPropNames),
|
|
157
|
+
dimensions,
|
|
158
|
+
})
|
|
172
159
|
|
|
160
|
+
// Resolve mode-specific theme
|
|
173
161
|
const modeBaseHelper = ThemeManager.modeBaseTheme[mode]
|
|
174
162
|
if (!modeBaseHelper.has(baseTheme)) {
|
|
175
163
|
modeBaseHelper.set(baseTheme, getThemeByMode(baseTheme, mode))
|
|
@@ -191,6 +179,37 @@ const rocketComponent: RocketComponent = (options) => {
|
|
|
191
179
|
})
|
|
192
180
|
}
|
|
193
181
|
|
|
182
|
+
// --------------------------------------------------
|
|
183
|
+
// $rocketstate as a FUNCTION ACCESSOR — reactive on prop changes.
|
|
184
|
+
// Re-evaluates active dimensions + pseudo state from current props.
|
|
185
|
+
// --------------------------------------------------
|
|
186
|
+
const $rocketstateAccessor = () => {
|
|
187
|
+
const { pseudo, ...mergeProps } = {
|
|
188
|
+
...localCtx,
|
|
189
|
+
...props,
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const pseudoRocketstate = {
|
|
193
|
+
...pseudo,
|
|
194
|
+
...pick(props, [...PSEUDO_KEYS, ...PSEUDO_META_KEYS]),
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const rocketstate = _calculateStylingAttrs({
|
|
198
|
+
props: pickStyledAttrs(mergeProps, reservedPropNames),
|
|
199
|
+
dimensions,
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
return { ...rocketstate, pseudo: pseudoRocketstate }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// --------------------------------------------------
|
|
206
|
+
// Static mergeProps for final prop filtering (non-dimension props)
|
|
207
|
+
// --------------------------------------------------
|
|
208
|
+
const { pseudo: _pseudo, ...mergeProps } = {
|
|
209
|
+
...localCtx,
|
|
210
|
+
...props,
|
|
211
|
+
}
|
|
212
|
+
|
|
194
213
|
// --------------------------------------------------
|
|
195
214
|
// final props passed to WrappedComponent
|
|
196
215
|
// --------------------------------------------------
|
|
@@ -202,9 +221,9 @@ const rocketComponent: RocketComponent = (options) => {
|
|
|
202
221
|
]),
|
|
203
222
|
...(options.passProps ? pick(mergeProps, options.passProps) : {}),
|
|
204
223
|
ref: props.ref,
|
|
205
|
-
// FUNCTION
|
|
224
|
+
// FUNCTION accessors — styled component resolves them reactively
|
|
206
225
|
$rocketstyle: $rocketstyleAccessor,
|
|
207
|
-
$rocketstate:
|
|
226
|
+
$rocketstate: $rocketstateAccessor,
|
|
208
227
|
}
|
|
209
228
|
|
|
210
229
|
// development debugging
|
|
@@ -214,7 +233,7 @@ const rocketComponent: RocketComponent = (options) => {
|
|
|
214
233
|
if (options.DEBUG) {
|
|
215
234
|
const debugPayload = {
|
|
216
235
|
component: componentName,
|
|
217
|
-
rocketstate:
|
|
236
|
+
rocketstate: $rocketstateAccessor(),
|
|
218
237
|
rocketstyle: $rocketstyleAccessor(),
|
|
219
238
|
dimensions,
|
|
220
239
|
mode: themeAttrs.mode,
|