@vitus-labs/rocketstyle 2.0.0-alpha.26 → 2.0.0-alpha.28

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/README.md CHANGED
@@ -5,7 +5,7 @@ Multi-dimensional styling system for React.
5
5
  [![npm](https://img.shields.io/npm/v/@vitus-labs/rocketstyle)](https://www.npmjs.com/package/@vitus-labs/rocketstyle)
6
6
  [![license](https://img.shields.io/npm/l/@vitus-labs/rocketstyle)](https://github.com/vitus-labs/ui-system/blob/main/LICENSE)
7
7
 
8
- Organize component styles by dimensions — states, sizes, variants — instead of flat props. Chain theme values, attach styled-components CSS, and get full TypeScript inference. Built-in pseudo-state handling, light/dark mode, and provider/consumer patterns for parent-child state propagation.
8
+ Organize component styles by dimensions — states, sizes, variants — instead of flat props. Chain theme values, attach CSS via the active connector, and get full TypeScript inference. Built-in pseudo-state handling, light/dark mode, and provider/consumer patterns for parent-child state propagation.
9
9
 
10
10
  ## Features
11
11
 
@@ -21,7 +21,7 @@ Organize component styles by dimensions — states, sizes, variants — instead
21
21
  ## Installation
22
22
 
23
23
  ```bash
24
- npm install @vitus-labs/rocketstyle @vitus-labs/core styled-components
24
+ npm install @vitus-labs/rocketstyle @vitus-labs/core
25
25
  ```
26
26
 
27
27
  ## Quick Start
@@ -110,7 +110,7 @@ Pseudo-state keys nest directly in the theme object:
110
110
 
111
111
  ### Styles Function
112
112
 
113
- The `.styles()` method defines the styled-components template that receives the computed theme:
113
+ The `.styles()` method defines the CSS template that receives the computed theme:
114
114
 
115
115
  ```tsx
116
116
  .styles((css) => css`
@@ -200,7 +200,7 @@ Button.states((theme, mode, css) => ({
200
200
 
201
201
  ### .styles(callback)
202
202
 
203
- Define the styled-components CSS template.
203
+ Define the CSS template using the active connector's `css` tagged template.
204
204
 
205
205
  ```tsx
206
206
  Button.styles((css) => css`
package/lib/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { config, context, render } from "@vitus-labs/core";
2
- import { ComponentType, FC, FocusEventHandler, ForwardRefExoticComponent, MouseEventHandler, ReactNode } from "react";
2
+ import { ComponentType, FC, FocusEventHandler, MouseEventHandler, ReactNode } from "react";
3
3
 
4
4
  //#region src/context/context.d.ts
5
5
  type Theme$1 = {
@@ -35,6 +35,11 @@ declare const DEFAULT_DIMENSIONS: {
35
35
  readonly propName: "multiple";
36
36
  readonly multi: true;
37
37
  };
38
+ readonly modifiers: {
39
+ readonly propName: "modifier";
40
+ readonly multi: true;
41
+ readonly transform: true;
42
+ };
38
43
  };
39
44
  type DefaultDimensions = typeof DEFAULT_DIMENSIONS;
40
45
  //#endregion
@@ -61,11 +66,9 @@ type PseudoProps = Partial<PseudoState & PseudoActions>;
61
66
  type TObj = Record<string, unknown>;
62
67
  type TFn = (...args: any) => any;
63
68
  type CallBackParam = TObj | TFn;
64
- type ElementType<T extends TObj | unknown = any> = (ComponentType<T> & Partial<{
65
- [key: string]: any;
66
- }>) | (ForwardRefExoticComponent<T> & {
69
+ type ElementType<T extends TObj | unknown = any> = ComponentType<T> & Partial<{
67
70
  [key: string]: any;
68
- });
71
+ }>;
69
72
  type ValueOf<T> = T[keyof T];
70
73
  type ArrayOfValues<T> = T[keyof T][];
71
74
  type ArrayOfKeys<T> = Array<keyof T>;
@@ -78,7 +81,7 @@ type ExtractNullableKeys<T> = { [P in keyof T as IsAny<T[P]> extends true ? P :
78
81
  type SpreadTwo<L, R> = Id<Pick<L, Exclude<keyof L, keyof R>> & R>;
79
82
  type Spread<A extends readonly [...any]> = A extends [infer L, ...infer R] ? SpreadTwo<L, Spread<R>> : unknown;
80
83
  type MergeTypes<A extends readonly [...any]> = ExtractNullableKeys<Spread<A>>;
81
- type ExtractProps<TComponentOrTProps> = TComponentOrTProps extends ComponentType<infer TProps> ? TProps : TComponentOrTProps extends ForwardRefExoticComponent<infer TProps> ? TProps : TComponentOrTProps;
84
+ type ExtractProps<TComponentOrTProps> = TComponentOrTProps extends ComponentType<infer TProps> ? TProps : TComponentOrTProps;
82
85
  //#endregion
83
86
  //#region src/types/styles.d.ts
84
87
  interface StylesDefault {}
@@ -129,16 +132,17 @@ type ExtractDimensionMulti<T extends DimensionValue> = T extends DimensionValueO
129
132
  type DimensionValuePrimitive = string;
130
133
  type DimensionValueObj = {
131
134
  propName: string;
132
- multi?: boolean;
135
+ multi?: boolean; /** When true, this dimension is evaluated last and its values receive the accumulated theme as argument. */
136
+ transform?: boolean;
133
137
  };
134
138
  type DimensionValue = DimensionValuePrimitive | DimensionValueObj;
135
139
  type Dimensions = Record<string, DimensionValue>;
136
140
  type MultiKeys<T extends Dimensions = Dimensions> = Partial<Record<ExtractDimensionKey<T[keyof T]>, true>>;
137
141
  type DeepPartial<T> = { [K in keyof T]?: T[K] extends ((...args: any[]) => any) ? T[K] : NonNullable<T[K]> extends Record<string, any> ? DeepPartial<NonNullable<T[K]>> | Extract<T[K], null> : T[K] };
138
- type DimensionResult<CT> = Record<string, boolean | null | DeepPartial<CT>>;
139
- type DimensionObj<CT> = DimensionResult<CT>;
140
- type DimensionCb<T, CT> = (theme: T, mode: ThemeModeCallback, css: Css) => DimensionResult<CT>;
141
- type DimensionCallbackParam<T, CT> = DimensionObj<CT> | DimensionCb<T, CT>;
142
+ type DimensionResult<CT, T = any> = Record<string, boolean | null | DeepPartial<CT> | ((theme: CT, appTheme: T, mode: ThemeModeCallback, css: Css) => DeepPartial<CT>)>;
143
+ type DimensionObj<CT, T = any> = DimensionResult<CT, T>;
144
+ type DimensionCb<T, CT> = (theme: T, mode: ThemeModeCallback, css: Css) => DimensionResult<CT, T>;
145
+ type DimensionCallbackParam<T, CT> = DimensionObj<CT, T> | DimensionCb<T, CT>;
142
146
  type TDKP = Record<ExtractDimensionKey<Dimensions[keyof Dimensions]>, Record<string, boolean | never | Record<string, boolean>> | unknown>;
143
147
  type DimensionProps<K extends DimensionValue, D extends Dimensions, P extends CallBackParam, DKP extends TDKP> = { [I in ExtractDimensionKey<D[keyof D]>]: I extends ExtractDimensionKey<K> ? ExtractNullableDimensionKeys<Spread<[DKP[I], NullableKeys<ReturnCbParam<P>>]>> : DKP[I] };
144
148
  type DimensionTypesHelper<DKP extends TDKP> = { [I in keyof DKP]: keyof DKP[I] };
@@ -181,6 +185,7 @@ type InitConfiguration<C, D> = {
181
185
  dimensionKeys: ArrayOfKeys<D>;
182
186
  dimensionValues: ArrayOfValues<D>;
183
187
  multiKeys: MultiKeys;
188
+ transformKeys: Partial<Record<string, true>>;
184
189
  };
185
190
  type Configuration<C = ElementType | unknown, D extends Dimensions = Dimensions> = InitConfiguration<C, D> & {
186
191
  provider?: boolean;
@@ -212,10 +217,6 @@ type GenericHoc = (component: ElementType) => ElementType;
212
217
  type ComposeParam = Record<string, GenericHoc | null | undefined | false>;
213
218
  //#endregion
214
219
  //#region src/types/rocketstyle.d.ts
215
- interface ExoticComponent<P = {}> {
216
- (props: P): ReactNode;
217
- readonly $$typeof: symbol;
218
- }
219
220
  type RocketStyleComponent<OA extends TObj = {}, EA extends TObj = {}, T extends TObj = {}, CSS extends TObj = {}, S extends TObj = {}, HOC extends TObj = {}, D extends Dimensions = Dimensions, UB extends boolean = boolean, DKP extends TDKP = TDKP> = IRocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DKP> & { [I in keyof D]: <K extends DimensionValue = D[I], P extends DimensionCallbackParam<Theme<T>, Styles<CSS>> = DimensionCallbackParam<Theme<T>, Styles<CSS>>>(param: P) => P extends DimensionCallbackParam<Theme<T>, Styles<CSS>> ? RocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DimensionProps<K, D, P, DKP>> : RocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DKP> };
220
221
  /**
221
222
  * @param OA Origin component props params.
@@ -228,7 +229,10 @@ type RocketStyleComponent<OA extends TObj = {}, EA extends TObj = {}, T extends
228
229
  * @param DKP Dimensions key props.
229
230
  * @param DFP Calculated final component props
230
231
  */
231
- interface IRocketStyleComponent<OA extends TObj = {}, EA extends TObj = {}, T extends TObj = {}, CSS extends TObj = {}, S extends TObj = {}, HOC extends TObj = {}, D extends Dimensions = Dimensions, UB extends boolean = boolean, DKP extends TDKP = TDKP, DFP = MergeTypes<[OA, EA, DefaultProps, ExtractDimensionProps<D, DKP, UB>]>> extends ExoticComponent<DFP> {
232
+ interface IRocketStyleComponent<OA extends TObj = {}, EA extends TObj = {}, T extends TObj = {}, CSS extends TObj = {}, S extends TObj = {}, HOC extends TObj = {}, D extends Dimensions = Dimensions, UB extends boolean = boolean, DKP extends TDKP = TDKP, DFP = MergeTypes<[OA, EA, DefaultProps, ExtractDimensionProps<D, DKP, UB>]>> {
233
+ (props: DFP & {
234
+ ref?: any;
235
+ }): ReactNode;
232
236
  /**
233
237
  * A chaining method to define default component theme
234
238
  * @param param _object_
@@ -568,11 +572,11 @@ type Rocketstyle = <D extends Dimensions = DefaultDimensions, UB extends boolean
568
572
  name: string;
569
573
  component: C;
570
574
  }) => ReturnType<RocketComponent<C, {}, {}, D, UB>>;
571
- declare const rocketstyle: Rocketstyle;
575
+ declare const typedRocketstyle: Rocketstyle;
572
576
  //#endregion
573
577
  //#region src/isRocketComponent.d.ts
574
578
  type IsRocketComponent = <T>(component: T) => boolean;
575
579
  declare const isRocketComponent: IsRocketComponent;
576
580
  //#endregion
577
- export { type AttrsCb, type ComposeParam, type ConfigAttrs, type ConsumerCb, type ConsumerCtxCBValue, type ConsumerCtxCb, type DefaultProps, type DimensionCallbackParam, type DimensionProps, type DimensionValue, type Dimensions, type ElementType, type ExtractDimensionProps, type ExtractDimensions, type ExtractProps, type GenericHoc, type IRocketStyleComponent, type IsRocketComponent, type MergeTypes, Provider, type RocketComponentType, type RocketProviderState, type RocketStyleComponent, type RocketStyleInterpolationProps, type Rocketstyle, type StylesCb, type StylesDefault, type TDKP, type TObj, type TProvider, type ThemeCb, type ThemeDefault, type ThemeMode, type ThemeModeCallback, type ThemeModeKeys, context, rocketstyle as default, rocketstyle, isRocketComponent };
581
+ export { type AttrsCb, type ComposeParam, type ConfigAttrs, type ConsumerCb, type ConsumerCtxCBValue, type ConsumerCtxCb, type DefaultProps, type DimensionCallbackParam, type DimensionProps, type DimensionValue, type Dimensions, type ElementType, type ExtractDimensionProps, type ExtractDimensions, type ExtractProps, type GenericHoc, type IRocketStyleComponent, type IsRocketComponent, type MergeTypes, Provider, type RocketComponentType, type RocketProviderState, type RocketStyleComponent, type RocketStyleInterpolationProps, type Rocketstyle, type StylesCb, type StylesDefault, type TDKP, type TObj, type TProvider, type ThemeCb, type ThemeDefault, type ThemeMode, type ThemeModeCallback, type ThemeModeKeys, context, typedRocketstyle as default, typedRocketstyle as rocketstyle, isRocketComponent };
578
582
  //# sourceMappingURL=index2.d.ts.map
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, forwardRef, useCallback, useContext, useImperativeHandle, useMemo, useRef, useState } from "react";
2
+ import { createContext, useCallback, useContext, useImperativeHandle, useMemo, useRef, useState } from "react";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
 
5
5
  //#region src/constants/index.ts
@@ -85,6 +85,11 @@ const DEFAULT_DIMENSIONS = {
85
85
  multiple: {
86
86
  propName: "multiple",
87
87
  multi: true
88
+ },
89
+ modifiers: {
90
+ propName: "modifier",
91
+ multi: true,
92
+ transform: true
88
93
  }
89
94
  };
90
95
 
@@ -197,12 +202,18 @@ const useThemeAttrs = ({ inversed }) => {
197
202
  const { theme = {}, mode: ctxMode = "light", isDark: ctxDark } = useContext(context) || {};
198
203
  const mode = inversed ? THEME_MODES_INVERSED[ctxMode] : ctxMode;
199
204
  const isDark = inversed ? !ctxDark : ctxDark;
200
- return {
205
+ const isLight = !isDark;
206
+ return useMemo(() => ({
201
207
  theme,
202
208
  mode,
203
209
  isDark,
204
- isLight: !isDark
205
- };
210
+ isLight
211
+ }), [
212
+ theme,
213
+ mode,
214
+ isDark,
215
+ isLight
216
+ ]);
206
217
  };
207
218
 
208
219
  //#endregion
@@ -213,13 +224,16 @@ const useThemeAttrs = ({ inversed }) => {
213
224
  * to its rocketstyle children.
214
225
  */
215
226
  const context$1 = createContext({});
227
+ const EMPTY_CTX = { pseudo: {} };
216
228
  const useLocalContext = (consumer) => {
217
229
  const ctx = useContext(context$1);
218
- if (!consumer) return { pseudo: {} };
219
- return {
220
- pseudo: {},
221
- ...consumer((callback) => callback(ctx))
222
- };
230
+ return useMemo(() => {
231
+ if (!consumer) return EMPTY_CTX;
232
+ return {
233
+ pseudo: {},
234
+ ...consumer((callback) => callback(ctx))
235
+ };
236
+ }, [consumer, ctx]);
223
237
  };
224
238
  const LocalProvider = context$1.Provider;
225
239
 
@@ -230,32 +244,35 @@ const LocalProvider = context$1.Provider;
230
244
  * detecting pseudo-states (hover, focus, pressed) via mouse/focus events
231
245
  * and broadcasting them through local context to child rocketstyle components.
232
246
  */
233
- const RocketStyleProviderComponent = (WrappedComponent) => forwardRef(({ onMouseEnter, onMouseLeave, onMouseUp, onMouseDown, onFocus, onBlur, $rocketstate, ...props }, ref) => {
234
- const { state: pseudoState, events: pseudoEvents } = usePseudoState({
235
- onMouseEnter,
236
- onMouseLeave,
237
- onMouseUp,
238
- onMouseDown,
239
- onFocus,
240
- onBlur
241
- });
242
- const updatedState = useStableValue({
243
- ...$rocketstate,
244
- pseudo: {
245
- ...$rocketstate.pseudo,
246
- ...pseudoState
247
- }
248
- });
249
- return /* @__PURE__ */ jsx(LocalProvider, {
250
- value: updatedState,
251
- children: /* @__PURE__ */ jsx(WrappedComponent, {
252
- ...props,
253
- ...pseudoEvents,
254
- ref,
255
- $rocketstate: updatedState
256
- })
257
- });
258
- });
247
+ const RocketStyleProviderComponent = (WrappedComponent) => {
248
+ const HOC = ({ onMouseEnter, onMouseLeave, onMouseUp, onMouseDown, onFocus, onBlur, $rocketstate, ref, ...props }) => {
249
+ const { state: pseudoState, events: pseudoEvents } = usePseudoState({
250
+ onMouseEnter,
251
+ onMouseLeave,
252
+ onMouseUp,
253
+ onMouseDown,
254
+ onFocus,
255
+ onBlur
256
+ });
257
+ const updatedState = useStableValue({
258
+ ...$rocketstate,
259
+ pseudo: {
260
+ ...$rocketstate.pseudo,
261
+ ...pseudoState
262
+ }
263
+ });
264
+ return /* @__PURE__ */ jsx(LocalProvider, {
265
+ value: updatedState,
266
+ children: /* @__PURE__ */ jsx(WrappedComponent, {
267
+ ...props,
268
+ ...pseudoEvents,
269
+ ref,
270
+ $rocketstate: updatedState
271
+ })
272
+ });
273
+ };
274
+ return HOC;
275
+ };
259
276
 
260
277
  //#endregion
261
278
  //#region src/utils/attrs.ts
@@ -283,17 +300,20 @@ const calculateStylingAttrs = ({ useBooleans, multiKeys }) => ({ props, dimensio
283
300
  else result[item] = void 0;
284
301
  });
285
302
  if (useBooleans) {
286
- const propsKeys = Object.keys(props).reverse();
303
+ const propsKeys = Object.keys(props);
287
304
  Object.entries(result).forEach(([key, value]) => {
288
305
  const isMultiKey = multiKeys[key];
289
306
  if (!value) {
290
307
  let newDimensionValue;
291
- const keywords = Object.keys(dimensions[key]);
292
- if (isMultiKey) newDimensionValue = propsKeys.filter((key) => keywords.includes(key));
293
- else newDimensionValue = propsKeys.find((key) => {
294
- if (keywords.includes(key) && props[key]) return key;
295
- return false;
296
- });
308
+ const keywordSet = new Set(Object.keys(dimensions[key]));
309
+ if (isMultiKey) newDimensionValue = propsKeys.filter((key) => keywordSet.has(key));
310
+ else for (let i = propsKeys.length - 1; i >= 0; i--) {
311
+ const k = propsKeys[i];
312
+ if (keywordSet.has(k) && props[k]) {
313
+ newDimensionValue = k;
314
+ break;
315
+ }
316
+ }
297
317
  result[key] = newDimensionValue;
298
318
  }
299
319
  });
@@ -312,27 +332,30 @@ const calculateStylingAttrs = ({ useBooleans, multiKeys }) => ({ props, dimensio
312
332
  const rocketStyleHOC = ({ inversed, attrs, priorityAttrs }) => {
313
333
  const calculateAttrs = calculateChainOptions(attrs);
314
334
  const calculatePriorityAttrs = calculateChainOptions(priorityAttrs);
315
- const Enhanced = (WrappedComponent) => forwardRef((props, ref) => {
316
- const { theme, mode, isDark, isLight } = useThemeAttrs({ inversed });
317
- const callbackParams = [theme, {
318
- render,
319
- mode,
320
- isDark,
321
- isLight
322
- }];
323
- const filteredProps = removeUndefinedProps(props);
324
- const prioritizedAttrs = calculatePriorityAttrs([filteredProps, ...callbackParams]);
325
- const finalAttrs = calculateAttrs([{
326
- ...prioritizedAttrs,
327
- ...filteredProps
328
- }, ...callbackParams]);
329
- return /* @__PURE__ */ jsx(WrappedComponent, {
330
- $rocketstyleRef: ref,
331
- ...prioritizedAttrs,
332
- ...finalAttrs,
333
- ...filteredProps
334
- });
335
- });
335
+ const Enhanced = (WrappedComponent) => {
336
+ const HOC = ({ ref, ...props }) => {
337
+ const { theme, mode, isDark, isLight } = useThemeAttrs({ inversed });
338
+ const callbackParams = [theme, {
339
+ render,
340
+ mode,
341
+ isDark,
342
+ isLight
343
+ }];
344
+ const filteredProps = removeUndefinedProps(props);
345
+ const prioritizedAttrs = calculatePriorityAttrs([filteredProps, ...callbackParams]);
346
+ const finalAttrs = calculateAttrs([{
347
+ ...prioritizedAttrs,
348
+ ...filteredProps
349
+ }, ...callbackParams]);
350
+ return /* @__PURE__ */ jsx(WrappedComponent, {
351
+ $rocketstyleRef: ref,
352
+ ...prioritizedAttrs,
353
+ ...finalAttrs,
354
+ ...filteredProps
355
+ });
356
+ };
357
+ return HOC;
358
+ };
336
359
  return Enhanced;
337
360
  };
338
361
 
@@ -393,6 +416,12 @@ const getMultipleDimensions = (obj) => getValues(obj).reduce((accumulator, value
393
416
  }
394
417
  return accumulator;
395
418
  }, {});
419
+ const getTransformDimensions = (obj) => getValues(obj).reduce((accumulator, value) => {
420
+ if (typeof value === "object") {
421
+ if (value.transform === true) accumulator[value.propName] = true;
422
+ }
423
+ return accumulator;
424
+ }, {});
396
425
 
397
426
  //#endregion
398
427
  //#region src/utils/statics.ts
@@ -421,12 +450,17 @@ const removeNullableValues = (obj) => Object.entries(obj).filter(([, v]) => v !=
421
450
 
422
451
  //#endregion
423
452
  //#region src/utils/theme.ts
453
+ const MODE_CALLBACK_BRAND = Symbol.for("vl.themeModeCallback");
424
454
  /** Creates a mode-switching function that returns the light or dark value based on the active mode. */
425
- const themeModeCallback = (light, dark) => (mode) => {
426
- if (!mode || mode === "light") return light;
427
- return dark;
455
+ const themeModeCallback = (light, dark) => {
456
+ const fn = (mode) => {
457
+ if (!mode || mode === "light") return light;
458
+ return dark;
459
+ };
460
+ fn.__brand = MODE_CALLBACK_BRAND;
461
+ return fn;
428
462
  };
429
- const isModeCallback = (value) => typeof value === "function" && value.toString() === themeModeCallback().toString();
463
+ const isModeCallback = (value) => typeof value === "function" && value.__brand === MODE_CALLBACK_BRAND;
430
464
  const getThemeFromChain = (options, theme) => {
431
465
  const result = {};
432
466
  if (!options || isEmpty(options)) return result;
@@ -442,15 +476,22 @@ const getDimensionThemes = (theme, options) => {
442
476
  return acc;
443
477
  }, result);
444
478
  };
445
- const getTheme = ({ rocketstate, themes, baseTheme }) => {
446
- let finalTheme = { ...baseTheme };
479
+ const getTheme = ({ rocketstate, themes, baseTheme, transformKeys, appTheme }) => {
480
+ const finalTheme = { ...baseTheme };
481
+ const deferredTransforms = [];
447
482
  Object.entries(rocketstate).forEach(([key, value]) => {
448
483
  const keyTheme = themes[key];
449
- if (Array.isArray(value)) value.forEach((item) => {
450
- finalTheme = merge({}, finalTheme, keyTheme[item]);
451
- });
452
- else finalTheme = merge({}, finalTheme, keyTheme[value]);
484
+ const isTransform = transformKeys?.[key];
485
+ const mergeValue = (item) => {
486
+ const val = keyTheme[item];
487
+ if (val == null) return;
488
+ if (isTransform && typeof val === "function") deferredTransforms.push(val);
489
+ else merge(finalTheme, val);
490
+ };
491
+ if (Array.isArray(value)) value.forEach(mergeValue);
492
+ else mergeValue(value);
453
493
  });
494
+ for (const transform of deferredTransforms) merge(finalTheme, transform(finalTheme, appTheme ?? {}, themeModeCallback, config.css));
454
495
  return finalTheme;
455
496
  };
456
497
  const getThemeByMode = (object, mode) => Object.keys(object).reduce((acc, key) => {
@@ -463,6 +504,32 @@ const getThemeByMode = (object, mode) => Object.keys(object).reduce((acc, key) =
463
504
 
464
505
  //#endregion
465
506
  //#region src/rocketstyle.tsx
507
+ /**
508
+ * Core rocketstyle component factory. Creates a fully-featured React component
509
+ * that integrates theme management (with light/dark mode support), multi-tier
510
+ * WeakMap caching, dimension-based styling props, pseudo-state detection, and
511
+ * chainable static methods (`.attrs()`, `.theme()`, `.styles()`, `.config()`, etc.).
512
+ * @module rocketstyle
513
+ */
514
+ const arraysEqual = (a, b) => {
515
+ if (a.length !== b.length) return false;
516
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
517
+ return true;
518
+ };
519
+ const isShallowEqualRocketstate = (a, b) => {
520
+ if (a === b) return true;
521
+ if (!a || !b) return false;
522
+ const aKeys = Object.keys(a);
523
+ if (aKeys.length !== Object.keys(b).length) return false;
524
+ for (const k of aKeys) {
525
+ const av = a[k];
526
+ const bv = b[k];
527
+ if (av === bv) continue;
528
+ if (Array.isArray(av) && Array.isArray(bv) && arraysEqual(av, bv)) continue;
529
+ return false;
530
+ }
531
+ return true;
532
+ };
466
533
  /** Clones the current configuration and merges new options, returning a fresh rocketComponent. */
467
534
  const cloneAndEnhance = (defaultOpts, opts) => rocketComponent({
468
535
  ...defaultOpts,
@@ -486,7 +553,7 @@ const cloneAndEnhance = (defaultOpts, opts) => rocketComponent({
486
553
  * chainable static enhancers attached to the returned component.
487
554
  */
488
555
  const rocketComponent = (options) => {
489
- const { component, styles } = options;
556
+ const { component, styles, transformKeys } = options;
490
557
  const { styled } = config;
491
558
  const _calculateStylingAttrs = calculateStylingAttrs({
492
559
  multiKeys: options.multiKeys,
@@ -499,7 +566,7 @@ const rocketComponent = (options) => {
499
566
  const RenderComponent = options.provider ? RocketStyleProviderComponent(STYLED_COMPONENT) : STYLED_COMPONENT;
500
567
  const ThemeManager$1 = new ThemeManager();
501
568
  const hocsFuncs = [rocketStyleHOC(options), ...calculateHocsFuncs(options.compose)];
502
- const EnhancedComponent = forwardRef(({ $rocketstyleRef, ...props }, ref) => {
569
+ const EnhancedComponent = ({ $rocketstyleRef, ref, ...props }) => {
503
570
  const internalRef = useRocketstyleRef({
504
571
  $rocketstyleRef,
505
572
  ref
@@ -547,20 +614,20 @@ const rocketComponent = (options) => {
547
614
  ...rocketstate,
548
615
  pseudo: pseudoRocketstate
549
616
  };
550
- let rsKey = "";
551
- for (const k in rocketstate) {
552
- const v = rocketstate[k];
553
- rsKey += k;
554
- rsKey += Array.isArray(v) ? v.join(",") : v;
555
- }
617
+ const rocketstateRef = useRef(rocketstate);
618
+ if (!isShallowEqualRocketstate(rocketstateRef.current, rocketstate)) rocketstateRef.current = rocketstate;
619
+ const stableRocketstate = rocketstateRef.current;
556
620
  const rocketstyle = useMemo(() => getTheme({
557
- rocketstate,
621
+ rocketstate: stableRocketstate,
558
622
  themes: currentModeThemes,
559
- baseTheme: currentModeBaseTheme
623
+ baseTheme: currentModeBaseTheme,
624
+ transformKeys,
625
+ appTheme: theme
560
626
  }), [
561
- rsKey,
627
+ stableRocketstate,
562
628
  currentModeThemes,
563
- currentModeBaseTheme
629
+ currentModeBaseTheme,
630
+ theme
564
631
  ]);
565
632
  const finalProps = {
566
633
  ...omit(mergeProps, [
@@ -589,7 +656,7 @@ const rocketComponent = (options) => {
589
656
  }
590
657
  }
591
658
  return /* @__PURE__ */ jsx(RenderComponent, { ...finalProps });
592
- });
659
+ };
593
660
  const RocketComponent = compose(...hocsFuncs)(EnhancedComponent);
594
661
  RocketComponent.IS_ROCKETSTYLE = true;
595
662
  RocketComponent.displayName = componentName;
@@ -662,31 +729,44 @@ const validateInit = (name, component, dimensions) => {
662
729
  }
663
730
  if (!isEmpty(errors)) throw Error(JSON.stringify(errors));
664
731
  };
665
- const rocketstyle = ({ dimensions = DEFAULT_DIMENSIONS, useBooleans = true } = {}) => ({ name, component }) => {
666
- if (process.env.NODE_ENV !== "production") validateInit(name, component, dimensions);
667
- return rocketComponent({
668
- name,
669
- component,
670
- useBooleans,
671
- dimensions,
672
- dimensionKeys: getKeys(dimensions),
673
- dimensionValues: getDimensionsValues(dimensions),
674
- multiKeys: getMultipleDimensions(dimensions),
675
- styled: true
676
- });
732
+ /**
733
+ * Generic implementation. The `D | DefaultDimensions` runtime fallback is
734
+ * narrowed to `D` via a single cast — semantically correct because when no
735
+ * user dimensions are supplied, `D` *is* the default (its type-parameter
736
+ * default is `DefaultDimensions`). This single boundary cast replaces the
737
+ * file-wide `@ts-nocheck` previously used here.
738
+ */
739
+ const rocketstyle = (params) => {
740
+ const dimensions = params?.dimensions ?? DEFAULT_DIMENSIONS;
741
+ const useBooleans = params?.useBooleans ?? true;
742
+ return ({ name, component }) => {
743
+ if (process.env.NODE_ENV !== "production") validateInit(name, component, dimensions);
744
+ return rocketComponent({
745
+ name,
746
+ component,
747
+ useBooleans,
748
+ dimensions,
749
+ dimensionKeys: getKeys(dimensions),
750
+ dimensionValues: getDimensionsValues(dimensions),
751
+ multiKeys: getMultipleDimensions(dimensions),
752
+ transformKeys: getTransformDimensions(dimensions),
753
+ styled: true
754
+ });
755
+ };
677
756
  };
757
+ const typedRocketstyle = rocketstyle;
678
758
 
679
759
  //#endregion
680
760
  //#region src/isRocketComponent.tsx
681
761
  const isRocketComponent = (component) => {
682
- if (component && typeof component === "object" && component !== null && Object.hasOwn(component, "IS_ROCKETSTYLE")) return true;
762
+ if (component && (typeof component === "object" || typeof component === "function") && Object.hasOwn(component, "IS_ROCKETSTYLE")) return true;
683
763
  return false;
684
764
  };
685
765
 
686
766
  //#endregion
687
767
  //#region src/index.ts
688
- var src_default = rocketstyle;
768
+ var src_default = typedRocketstyle;
689
769
 
690
770
  //#endregion
691
- export { Provider, context, src_default as default, isRocketComponent, rocketstyle };
771
+ export { Provider, context, src_default as default, isRocketComponent, typedRocketstyle as rocketstyle };
692
772
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitus-labs/rocketstyle",
3
- "version": "2.0.0-alpha.26",
3
+ "version": "2.0.0-alpha.28",
4
4
  "license": "MIT",
5
5
  "author": "Vit Bokisch <vit@bokisch.cz>",
6
6
  "maintainers": [
@@ -14,6 +14,7 @@
14
14
  "types": "./lib/index.d.ts"
15
15
  },
16
16
  "types": "./lib/index.d.ts",
17
+ "main": "./lib/index.js",
17
18
  "files": [
18
19
  "lib",
19
20
  "!lib/**/*.map",
@@ -64,16 +65,15 @@
64
65
  "version": "node ../../scripts/sync-peer-deps.mjs"
65
66
  },
66
67
  "peerDependencies": {
67
- "@vitus-labs/core": "2.0.0-alpha.25",
68
+ "@vitus-labs/core": "2.0.0-alpha.28",
68
69
  "react": ">= 19"
69
70
  },
70
71
  "devDependencies": {
71
- "@vitus-labs/core": "2.0.0-alpha.26",
72
- "@vitus-labs/elements": "2.0.0-alpha.26",
73
- "@vitus-labs/tools-rolldown": "1.9.1-alpha.19",
74
- "@vitus-labs/tools-storybook": "1.9.1-alpha.19",
75
- "@vitus-labs/tools-typescript": "1.9.1-alpha.19",
76
- "@vitus-labs/unistyle": "2.0.0-alpha.26"
77
- },
78
- "gitHead": "69d22f18b093f86a0bee235356ef3064f90c2d40"
72
+ "@vitus-labs/core": "workspace:*",
73
+ "@vitus-labs/elements": "workspace:*",
74
+ "@vitus-labs/tools-rolldown": "2.0.0",
75
+ "@vitus-labs/tools-storybook": "2.0.0",
76
+ "@vitus-labs/tools-typescript": "2.0.0",
77
+ "@vitus-labs/unistyle": "workspace:*"
78
+ }
79
79
  }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2023-present Vit Bokisch
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.