@vitus-labs/rocketstyle 2.6.2 → 2.7.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.
- package/lib/index.js +149 -110
- package/package.json +11 -6
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Provider as Provider$1, compose, config, context, get, hoistNonReactStatics, isEmpty, merge, omit, pick, render, set, useStableValue } from "@vitus-labs/core";
|
|
2
|
-
import { createContext,
|
|
2
|
+
import { createContext, memo, useContext, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/constants/index.ts
|
|
@@ -125,36 +125,59 @@ var ThemeManager = class {
|
|
|
125
125
|
* Tracks hover, focus, and pressed pseudo-states via mouse and focus
|
|
126
126
|
* event handlers. Returns the current state flags and wrapped event
|
|
127
127
|
* callbacks that preserve any user-provided handlers.
|
|
128
|
+
*
|
|
129
|
+
* Consumer handlers are captured in a ref so the wrapped event callbacks
|
|
130
|
+
* keep stable identity across re-renders — otherwise inline arrow
|
|
131
|
+
* handlers (`onClick={() => …}`) would re-create the wrappers every
|
|
132
|
+
* render and defeat downstream memoization.
|
|
128
133
|
*/
|
|
129
134
|
const usePseudoState = ({ onBlur, onFocus, onMouseDown, onMouseEnter, onMouseLeave, onMouseUp }) => {
|
|
130
135
|
const [hover, setHover] = useState(false);
|
|
131
136
|
const [focus, setFocus] = useState(false);
|
|
132
137
|
const [pressed, setPressed] = useState(false);
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
138
|
+
const latest = useRef({
|
|
139
|
+
onBlur,
|
|
140
|
+
onFocus,
|
|
141
|
+
onMouseDown,
|
|
142
|
+
onMouseEnter,
|
|
143
|
+
onMouseLeave,
|
|
144
|
+
onMouseUp
|
|
145
|
+
});
|
|
146
|
+
latest.current = {
|
|
147
|
+
onBlur,
|
|
148
|
+
onFocus,
|
|
149
|
+
onMouseDown,
|
|
150
|
+
onMouseEnter,
|
|
151
|
+
onMouseLeave,
|
|
152
|
+
onMouseUp
|
|
153
|
+
};
|
|
154
|
+
const events = useMemo(() => ({
|
|
155
|
+
onMouseEnter: (e) => {
|
|
156
|
+
setHover(true);
|
|
157
|
+
latest.current.onMouseEnter?.(e);
|
|
158
|
+
},
|
|
159
|
+
onMouseLeave: (e) => {
|
|
160
|
+
setHover(false);
|
|
161
|
+
setPressed(false);
|
|
162
|
+
latest.current.onMouseLeave?.(e);
|
|
163
|
+
},
|
|
164
|
+
onMouseDown: (e) => {
|
|
165
|
+
setPressed(true);
|
|
166
|
+
latest.current.onMouseDown?.(e);
|
|
167
|
+
},
|
|
168
|
+
onMouseUp: (e) => {
|
|
169
|
+
setPressed(false);
|
|
170
|
+
latest.current.onMouseUp?.(e);
|
|
171
|
+
},
|
|
172
|
+
onFocus: (e) => {
|
|
173
|
+
setFocus(true);
|
|
174
|
+
latest.current.onFocus?.(e);
|
|
175
|
+
},
|
|
176
|
+
onBlur: (e) => {
|
|
177
|
+
setFocus(false);
|
|
178
|
+
latest.current.onBlur?.(e);
|
|
179
|
+
}
|
|
180
|
+
}), []);
|
|
158
181
|
return {
|
|
159
182
|
state: useMemo(() => ({
|
|
160
183
|
hover,
|
|
@@ -165,21 +188,7 @@ const usePseudoState = ({ onBlur, onFocus, onMouseDown, onMouseEnter, onMouseLea
|
|
|
165
188
|
focus,
|
|
166
189
|
pressed
|
|
167
190
|
]),
|
|
168
|
-
events
|
|
169
|
-
onMouseEnter: handleOnMouseEnter,
|
|
170
|
-
onMouseLeave: handleOnMouseLeave,
|
|
171
|
-
onMouseDown: handleOnMouseDown,
|
|
172
|
-
onMouseUp: handleOnMouseUp,
|
|
173
|
-
onFocus: handleOnFocus,
|
|
174
|
-
onBlur: handleOnBlur
|
|
175
|
-
}), [
|
|
176
|
-
handleOnMouseEnter,
|
|
177
|
-
handleOnMouseLeave,
|
|
178
|
-
handleOnMouseDown,
|
|
179
|
-
handleOnMouseUp,
|
|
180
|
-
handleOnFocus,
|
|
181
|
-
handleOnBlur
|
|
182
|
-
])
|
|
191
|
+
events
|
|
183
192
|
};
|
|
184
193
|
};
|
|
185
194
|
|
|
@@ -192,8 +201,8 @@ const usePseudoState = ({ onBlur, onFocus, onMouseDown, onMouseEnter, onMouseLea
|
|
|
192
201
|
*/
|
|
193
202
|
const useRocketstyleRef = ({ $rocketstyleRef, ref }) => {
|
|
194
203
|
const internalRef = useRef(null);
|
|
195
|
-
useImperativeHandle($rocketstyleRef, () => internalRef.current);
|
|
196
|
-
useImperativeHandle(ref, () => internalRef.current);
|
|
204
|
+
useImperativeHandle($rocketstyleRef, () => internalRef.current, []);
|
|
205
|
+
useImperativeHandle(ref, () => internalRef.current, []);
|
|
197
206
|
return internalRef;
|
|
198
207
|
};
|
|
199
208
|
|
|
@@ -208,18 +217,12 @@ const useThemeAttrs = ({ inversed }) => {
|
|
|
208
217
|
const { theme = {}, mode: ctxMode = "light", isDark: ctxDark } = useContext(context) || {};
|
|
209
218
|
const mode = inversed ? THEME_MODES_INVERSED[ctxMode] : ctxMode;
|
|
210
219
|
const isDark = inversed ? !ctxDark : ctxDark;
|
|
211
|
-
|
|
212
|
-
return useMemo(() => ({
|
|
213
|
-
theme,
|
|
214
|
-
mode,
|
|
215
|
-
isDark,
|
|
216
|
-
isLight
|
|
217
|
-
}), [
|
|
220
|
+
return {
|
|
218
221
|
theme,
|
|
219
222
|
mode,
|
|
220
223
|
isDark,
|
|
221
|
-
isLight
|
|
222
|
-
|
|
224
|
+
isLight: !isDark
|
|
225
|
+
};
|
|
223
226
|
};
|
|
224
227
|
|
|
225
228
|
//#endregion
|
|
@@ -293,37 +296,34 @@ const pickStyledAttrs = (props, keywords) => {
|
|
|
293
296
|
return result;
|
|
294
297
|
};
|
|
295
298
|
const calculateChainOptions = (options) => (args) => {
|
|
296
|
-
|
|
297
|
-
if (isEmpty(options)) return result;
|
|
299
|
+
if (isEmpty(options)) return {};
|
|
298
300
|
return options.reduce((acc, item) => Object.assign(acc, item(...args)), {});
|
|
299
301
|
};
|
|
300
302
|
const calculateStylingAttrs = ({ useBooleans, multiKeys }) => ({ props, dimensions }) => {
|
|
301
303
|
const result = {};
|
|
302
|
-
|
|
304
|
+
for (const item in dimensions) {
|
|
303
305
|
const pickedProp = props[item];
|
|
304
306
|
const t = typeof pickedProp;
|
|
305
307
|
if (multiKeys?.[item] && Array.isArray(pickedProp)) result[item] = pickedProp;
|
|
306
308
|
else if (t === "string" || t === "number") result[item] = pickedProp;
|
|
307
309
|
else result[item] = void 0;
|
|
308
|
-
}
|
|
310
|
+
}
|
|
309
311
|
if (useBooleans) {
|
|
310
312
|
const propsKeys = Object.keys(props);
|
|
311
|
-
|
|
312
|
-
const isMultiKey = multiKeys[key];
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
break;
|
|
322
|
-
}
|
|
313
|
+
for (const key in result) if (!result[key]) {
|
|
314
|
+
const isMultiKey = multiKeys?.[key];
|
|
315
|
+
let newDimensionValue;
|
|
316
|
+
const dimObj = dimensions[key];
|
|
317
|
+
if (isMultiKey) newDimensionValue = propsKeys.filter((k) => Object.hasOwn(dimObj, k));
|
|
318
|
+
else for (let i = propsKeys.length - 1; i >= 0; i--) {
|
|
319
|
+
const k = propsKeys[i];
|
|
320
|
+
if (Object.hasOwn(dimObj, k) && props[k]) {
|
|
321
|
+
newDimensionValue = k;
|
|
322
|
+
break;
|
|
323
323
|
}
|
|
324
|
-
result[key] = newDimensionValue;
|
|
325
324
|
}
|
|
326
|
-
|
|
325
|
+
result[key] = newDimensionValue;
|
|
326
|
+
}
|
|
327
327
|
}
|
|
328
328
|
return result;
|
|
329
329
|
};
|
|
@@ -333,33 +333,66 @@ const calculateStylingAttrs = ({ useBooleans, multiKeys }) => ({ props, dimensio
|
|
|
333
333
|
/**
|
|
334
334
|
* HOC that resolves the `.attrs()` chain before the inner component renders.
|
|
335
335
|
* Evaluates both regular and priority attrs callbacks with the current theme
|
|
336
|
-
* and mode, then merges the results with explicit props (priority attrs
|
|
337
|
-
*
|
|
336
|
+
* and mode, then merges the results with explicit props (priority attrs are
|
|
337
|
+
* applied first, regular attrs can be overridden by direct props).
|
|
338
|
+
*
|
|
339
|
+
* Fast path: when no chain is configured (the common case for rocketstyle
|
|
340
|
+
* components built with .theme()/dimensions only), skip the deep-equal
|
|
341
|
+
* stabilization + memo dance and forward props directly. Mirrors the
|
|
342
|
+
* pattern in @vitus-labs/attrs' attrsHoc.
|
|
338
343
|
*/
|
|
339
344
|
const rocketStyleHOC = ({ inversed, attrs, priorityAttrs }) => {
|
|
340
345
|
const calculateAttrs = calculateChainOptions(attrs);
|
|
341
346
|
const calculatePriorityAttrs = calculateChainOptions(priorityAttrs);
|
|
347
|
+
const hasAttrs = (attrs?.length ?? 0) > 0;
|
|
348
|
+
const hasPriorityAttrs = (priorityAttrs?.length ?? 0) > 0;
|
|
349
|
+
if (!(hasAttrs || hasPriorityAttrs)) {
|
|
350
|
+
const Enhanced = (WrappedComponent) => {
|
|
351
|
+
const HOC = ({ ref, ...props }) => {
|
|
352
|
+
useThemeAttrs({ inversed });
|
|
353
|
+
return /* @__PURE__ */ jsx(WrappedComponent, {
|
|
354
|
+
$rocketstyleRef: ref,
|
|
355
|
+
...props
|
|
356
|
+
});
|
|
357
|
+
};
|
|
358
|
+
return HOC;
|
|
359
|
+
};
|
|
360
|
+
return Enhanced;
|
|
361
|
+
}
|
|
342
362
|
const Enhanced = (WrappedComponent) => {
|
|
343
363
|
const HOC = ({ ref, ...props }) => {
|
|
344
364
|
const { theme, mode, isDark, isLight } = useThemeAttrs({ inversed });
|
|
345
|
-
const
|
|
365
|
+
const stableProps = useStableValue(props);
|
|
366
|
+
const filteredProps = useMemo(() => removeUndefinedProps(stableProps), [stableProps]);
|
|
367
|
+
const themeBag = useMemo(() => ({
|
|
346
368
|
render,
|
|
347
369
|
mode,
|
|
348
370
|
isDark,
|
|
349
371
|
isLight
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
...
|
|
362
|
-
|
|
372
|
+
}), [
|
|
373
|
+
mode,
|
|
374
|
+
isDark,
|
|
375
|
+
isLight
|
|
376
|
+
]);
|
|
377
|
+
return /* @__PURE__ */ jsx(WrappedComponent, { ...useMemo(() => {
|
|
378
|
+
const callbackParams = [theme, themeBag];
|
|
379
|
+
const prioritizedAttrs = hasPriorityAttrs ? calculatePriorityAttrs([filteredProps, ...callbackParams]) : null;
|
|
380
|
+
const finalAttrs = hasAttrs ? calculateAttrs([prioritizedAttrs ? {
|
|
381
|
+
...prioritizedAttrs,
|
|
382
|
+
...filteredProps
|
|
383
|
+
} : filteredProps, ...callbackParams]) : null;
|
|
384
|
+
return {
|
|
385
|
+
$rocketstyleRef: ref,
|
|
386
|
+
...prioritizedAttrs,
|
|
387
|
+
...finalAttrs,
|
|
388
|
+
...filteredProps
|
|
389
|
+
};
|
|
390
|
+
}, [
|
|
391
|
+
filteredProps,
|
|
392
|
+
ref,
|
|
393
|
+
theme,
|
|
394
|
+
themeBag
|
|
395
|
+
]) });
|
|
363
396
|
};
|
|
364
397
|
return HOC;
|
|
365
398
|
};
|
|
@@ -374,14 +407,22 @@ const chainOptions = (opts, defaultOpts = []) => {
|
|
|
374
407
|
else if (typeof opts === "object") result.push(() => opts);
|
|
375
408
|
return result;
|
|
376
409
|
};
|
|
377
|
-
const chainOrOptions = (keys, opts, defaultOpts) =>
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
410
|
+
const chainOrOptions = (keys, opts, defaultOpts) => {
|
|
411
|
+
const result = {};
|
|
412
|
+
for (let i = 0; i < keys.length; i++) {
|
|
413
|
+
const item = keys[i];
|
|
414
|
+
result[item] = opts[item] || defaultOpts[item];
|
|
415
|
+
}
|
|
416
|
+
return result;
|
|
417
|
+
};
|
|
418
|
+
const chainReservedKeyOptions = (keys, opts, defaultOpts) => {
|
|
419
|
+
const result = {};
|
|
420
|
+
for (let i = 0; i < keys.length; i++) {
|
|
421
|
+
const item = keys[i];
|
|
422
|
+
result[item] = chainOptions(opts[item], defaultOpts[item]);
|
|
423
|
+
}
|
|
424
|
+
return result;
|
|
425
|
+
};
|
|
385
426
|
|
|
386
427
|
//#endregion
|
|
387
428
|
//#region src/utils/compose.ts
|
|
@@ -450,10 +491,11 @@ const calculateStyles = (styles) => {
|
|
|
450
491
|
|
|
451
492
|
//#endregion
|
|
452
493
|
//#region src/utils/collection.ts
|
|
453
|
-
const removeNullableValues = (obj) =>
|
|
454
|
-
|
|
455
|
-
[k]
|
|
456
|
-
|
|
494
|
+
const removeNullableValues = (obj) => {
|
|
495
|
+
const result = {};
|
|
496
|
+
for (const [k, v] of Object.entries(obj)) if (v != null && v !== false) result[k] = v;
|
|
497
|
+
return result;
|
|
498
|
+
};
|
|
457
499
|
|
|
458
500
|
//#endregion
|
|
459
501
|
//#region src/utils/theme.ts
|
|
@@ -625,6 +667,11 @@ const rocketComponent = (options) => {
|
|
|
625
667
|
useBooleans: options.useBooleans
|
|
626
668
|
}), [themes]);
|
|
627
669
|
const RESERVED_STYLING_PROPS_KEYS = useMemo(() => Object.keys(reservedPropNames), [reservedPropNames]);
|
|
670
|
+
const omitKeysSet = useMemo(() => new Set([
|
|
671
|
+
...RESERVED_STYLING_PROPS_KEYS,
|
|
672
|
+
...PSEUDO_KEYS,
|
|
673
|
+
...options.filterAttrs
|
|
674
|
+
]), [RESERVED_STYLING_PROPS_KEYS]);
|
|
628
675
|
const { pseudo, ...mergeProps } = {
|
|
629
676
|
...localCtx,
|
|
630
677
|
...props
|
|
@@ -657,11 +704,7 @@ const rocketComponent = (options) => {
|
|
|
657
704
|
theme
|
|
658
705
|
]);
|
|
659
706
|
const finalProps = {
|
|
660
|
-
...omit(mergeProps,
|
|
661
|
-
...RESERVED_STYLING_PROPS_KEYS,
|
|
662
|
-
...PSEUDO_KEYS,
|
|
663
|
-
...options.filterAttrs
|
|
664
|
-
]),
|
|
707
|
+
...omit(mergeProps, omitKeysSet),
|
|
665
708
|
...options.passProps ? pick(mergeProps, options.passProps) : {},
|
|
666
709
|
ref: ref ?? $rocketstyleRef ? internalRef : void 0,
|
|
667
710
|
$rocketstyle: rocketstyle,
|
|
@@ -684,7 +727,7 @@ const rocketComponent = (options) => {
|
|
|
684
727
|
}
|
|
685
728
|
return /* @__PURE__ */ jsx(RenderComponent, { ...finalProps });
|
|
686
729
|
};
|
|
687
|
-
const RocketComponent = compose(...hocsFuncs)(EnhancedComponent);
|
|
730
|
+
const RocketComponent = compose(...hocsFuncs)(memo(EnhancedComponent));
|
|
688
731
|
RocketComponent.IS_ROCKETSTYLE = true;
|
|
689
732
|
RocketComponent.displayName = componentName;
|
|
690
733
|
hoistNonReactStatics(RocketComponent, options.component);
|
|
@@ -694,8 +737,6 @@ const rocketComponent = (options) => {
|
|
|
694
737
|
func: cloneAndEnhance,
|
|
695
738
|
options
|
|
696
739
|
});
|
|
697
|
-
RocketComponent.IS_ROCKETSTYLE = true;
|
|
698
|
-
RocketComponent.displayName = componentName;
|
|
699
740
|
RocketComponent.meta = {};
|
|
700
741
|
createStaticsEnhancers({
|
|
701
742
|
context: RocketComponent.meta,
|
|
@@ -744,16 +785,14 @@ const rocketComponent = (options) => {
|
|
|
744
785
|
|
|
745
786
|
//#endregion
|
|
746
787
|
//#region src/init.ts
|
|
788
|
+
const RESERVED_KEYS_SET = new Set(ALL_RESERVED_KEYS);
|
|
747
789
|
const validateInit = (name, component, dimensions) => {
|
|
748
790
|
const errors = {};
|
|
749
791
|
if (!component) errors.component = "Parameter `component` is missing in params!";
|
|
750
792
|
if (!name) errors.name = "Parameter `name` is missing in params!";
|
|
751
793
|
if (isEmpty(dimensions)) errors.dimensions = "Parameter `dimensions` is missing in params!";
|
|
752
|
-
else
|
|
753
|
-
const definedDimensions = getKeys(dimensions);
|
|
754
|
-
if (ALL_RESERVED_KEYS.some((item) => definedDimensions.some((d) => d === item))) errors.invalidDimensions = `Some of your \`dimensions\` is invalid and uses reserved static keys which are
|
|
794
|
+
else if (getKeys(dimensions).some((d) => RESERVED_KEYS_SET.has(d))) errors.invalidDimensions = `Some of your \`dimensions\` is invalid and uses reserved static keys which are
|
|
755
795
|
${DEFAULT_DIMENSIONS.toString()}`;
|
|
756
|
-
}
|
|
757
796
|
if (!isEmpty(errors)) throw Error(JSON.stringify(errors));
|
|
758
797
|
};
|
|
759
798
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitus-labs/rocketstyle",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Vit Bokisch <vit@bokisch.cz>",
|
|
6
6
|
"maintainers": [
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"prepublish": "bun run build",
|
|
57
57
|
"build": "bun run vl_rolldown_build",
|
|
58
58
|
"build:watch": "bun run vl_rolldown_build-watch",
|
|
59
|
+
"bench": "bun benchmarks/render-bench.tsx",
|
|
59
60
|
"lint": "biome check src/",
|
|
60
61
|
"test": "vitest run",
|
|
61
62
|
"test:coverage": "vitest run --coverage",
|
|
@@ -64,15 +65,19 @@
|
|
|
64
65
|
"typecheck": "tsc --noEmit"
|
|
65
66
|
},
|
|
66
67
|
"peerDependencies": {
|
|
67
|
-
"@vitus-labs/core": "^2.
|
|
68
|
+
"@vitus-labs/core": "^2.7.0",
|
|
68
69
|
"react": ">= 19"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
72
|
+
"@vitus-labs/connector-styler": "workspace:*",
|
|
71
73
|
"@vitus-labs/core": "workspace:*",
|
|
72
74
|
"@vitus-labs/elements": "workspace:*",
|
|
73
|
-
"@vitus-labs/
|
|
74
|
-
"@vitus-labs/tools-
|
|
75
|
-
"@vitus-labs/tools-
|
|
76
|
-
"@vitus-labs/
|
|
75
|
+
"@vitus-labs/styler": "workspace:*",
|
|
76
|
+
"@vitus-labs/tools-rolldown": "2.5.0",
|
|
77
|
+
"@vitus-labs/tools-storybook": "2.5.0",
|
|
78
|
+
"@vitus-labs/tools-typescript": "2.5.0",
|
|
79
|
+
"@vitus-labs/unistyle": "workspace:*",
|
|
80
|
+
"jsdom": "^29.1.1",
|
|
81
|
+
"tinybench": "^6.0.1"
|
|
77
82
|
}
|
|
78
83
|
}
|