@vitus-labs/rocketstyle 2.0.0-alpha.9 → 2.0.0-beta.1

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,11 +81,11 @@ 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 {}
85
- type Styles<S> = S extends unknown ? StylesDefault : MergeTypes<[StylesDefault, S]>;
88
+ type Styles<S = unknown> = StylesDefault;
86
89
  type Css = typeof config.css;
87
90
  /**
88
91
  * Props available inside `.styles()` interpolation functions.
@@ -129,15 +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
- type DimensionResult<CT> = Record<string, boolean | null | Partial<CT>>;
138
- type DimensionObj<CT> = DimensionResult<CT>;
139
- type DimensionCb<T, CT> = (theme: T, mode: ThemeModeCallback, css: Css) => DimensionResult<CT>;
140
- type DimensionCallbackParam<T, CT> = DimensionObj<CT> | DimensionCb<T, CT>;
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] };
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>;
141
146
  type TDKP = Record<ExtractDimensionKey<Dimensions[keyof Dimensions]>, Record<string, boolean | never | Record<string, boolean>> | unknown>;
142
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] };
143
148
  type DimensionTypesHelper<DKP extends TDKP> = { [I in keyof DKP]: keyof DKP[I] };
@@ -180,6 +185,7 @@ type InitConfiguration<C, D> = {
180
185
  dimensionKeys: ArrayOfKeys<D>;
181
186
  dimensionValues: ArrayOfValues<D>;
182
187
  multiKeys: MultiKeys;
188
+ transformKeys: Partial<Record<string, true>>;
183
189
  };
184
190
  type Configuration<C = ElementType | unknown, D extends Dimensions = Dimensions> = InitConfiguration<C, D> & {
185
191
  provider?: boolean;
@@ -211,10 +217,6 @@ type GenericHoc = (component: ElementType) => ElementType;
211
217
  type ComposeParam = Record<string, GenericHoc | null | undefined | false>;
212
218
  //#endregion
213
219
  //#region src/types/rocketstyle.d.ts
214
- interface ExoticComponent<P = {}> {
215
- (props: P): ReactNode;
216
- readonly $$typeof: symbol;
217
- }
218
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> };
219
221
  /**
220
222
  * @param OA Origin component props params.
@@ -227,7 +229,10 @@ type RocketStyleComponent<OA extends TObj = {}, EA extends TObj = {}, T extends
227
229
  * @param DKP Dimensions key props.
228
230
  * @param DFP Calculated final component props
229
231
  */
230
- 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;
231
236
  /**
232
237
  * A chaining method to define default component theme
233
238
  * @param param _object_
@@ -365,7 +370,7 @@ interface IRocketStyleComponent<OA extends TObj = {}, EA extends TObj = {}, T ex
365
370
  * }))
366
371
  * ```
367
372
  */
368
- theme: <P extends TObj = {}>(param: Partial<P> | ThemeCb<P, Theme<T>>) => RocketStyleComponent<OA, EA, T, MergeTypes<[CSS, P]>, S, HOC, D, UB, DKP>;
373
+ theme: <P extends TObj = {}>(param: Partial<P> | Partial<Styles<CSS>> | ThemeCb<P, Theme<T>>) => RocketStyleComponent<OA, EA, T, MergeTypes<[CSS, P]>, S, HOC, D, UB, DKP>;
369
374
  /**
370
375
  * A chaining method to define default rendered styles
371
376
  * @param param Callback of styled-components `css` function
@@ -567,11 +572,11 @@ type Rocketstyle = <D extends Dimensions = DefaultDimensions, UB extends boolean
567
572
  name: string;
568
573
  component: C;
569
574
  }) => ReturnType<RocketComponent<C, {}, {}, D, UB>>;
570
- declare const rocketstyle: Rocketstyle;
575
+ declare const typedRocketstyle: Rocketstyle;
571
576
  //#endregion
572
577
  //#region src/isRocketComponent.d.ts
573
578
  type IsRocketComponent = <T>(component: T) => boolean;
574
579
  declare const isRocketComponent: IsRocketComponent;
575
580
  //#endregion
576
- 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 };
577
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,43 +244,43 @@ 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
262
- const removeUndefinedProps = (props) => Object.keys(props).reduce((acc, key) => {
263
- const currentValue = props[key];
264
- if (currentValue !== void 0) return {
265
- ...acc,
266
- [key]: currentValue
267
- };
268
- return acc;
269
- }, {});
279
+ const removeUndefinedProps = (props) => {
280
+ const result = {};
281
+ for (const key in props) if (props[key] !== void 0) result[key] = props[key];
282
+ return result;
283
+ };
270
284
  const pickStyledAttrs = (props, keywords) => Object.keys(props).reduce((acc, key) => {
271
285
  if (keywords[key] && props[key]) acc[key] = props[key];
272
286
  return acc;
@@ -280,23 +294,26 @@ const calculateStylingAttrs = ({ useBooleans, multiKeys }) => ({ props, dimensio
280
294
  const result = {};
281
295
  Object.keys(dimensions).forEach((item) => {
282
296
  const pickedProp = props[item];
283
- const valueTypes = ["number", "string"];
297
+ const t = typeof pickedProp;
284
298
  if (multiKeys?.[item] && Array.isArray(pickedProp)) result[item] = pickedProp;
285
- else if (valueTypes.includes(typeof pickedProp)) result[item] = pickedProp;
299
+ else if (t === "string" || t === "number") result[item] = pickedProp;
286
300
  else result[item] = void 0;
287
301
  });
288
302
  if (useBooleans) {
289
- const propsKeys = Object.keys(props).reverse();
303
+ const propsKeys = Object.keys(props);
290
304
  Object.entries(result).forEach(([key, value]) => {
291
305
  const isMultiKey = multiKeys[key];
292
306
  if (!value) {
293
307
  let newDimensionValue;
294
- const keywords = Object.keys(dimensions[key]);
295
- if (isMultiKey) newDimensionValue = propsKeys.filter((key) => keywords.includes(key));
296
- else newDimensionValue = propsKeys.find((key) => {
297
- if (keywords.includes(key) && props[key]) return key;
298
- return false;
299
- });
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
+ }
300
317
  result[key] = newDimensionValue;
301
318
  }
302
319
  });
@@ -315,27 +332,30 @@ const calculateStylingAttrs = ({ useBooleans, multiKeys }) => ({ props, dimensio
315
332
  const rocketStyleHOC = ({ inversed, attrs, priorityAttrs }) => {
316
333
  const calculateAttrs = calculateChainOptions(attrs);
317
334
  const calculatePriorityAttrs = calculateChainOptions(priorityAttrs);
318
- const Enhanced = (WrappedComponent) => forwardRef((props, ref) => {
319
- const { theme, mode, isDark, isLight } = useThemeAttrs({ inversed });
320
- const callbackParams = [theme, {
321
- render,
322
- mode,
323
- isDark,
324
- isLight
325
- }];
326
- const filteredProps = removeUndefinedProps(props);
327
- const prioritizedAttrs = calculatePriorityAttrs([filteredProps, ...callbackParams]);
328
- const finalAttrs = calculateAttrs([{
329
- ...prioritizedAttrs,
330
- ...filteredProps
331
- }, ...callbackParams]);
332
- return /* @__PURE__ */ jsx(WrappedComponent, {
333
- $rocketstyleRef: ref,
334
- ...prioritizedAttrs,
335
- ...finalAttrs,
336
- ...filteredProps
337
- });
338
- });
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
+ };
339
359
  return Enhanced;
340
360
  };
341
361
 
@@ -396,6 +416,12 @@ const getMultipleDimensions = (obj) => getValues(obj).reduce((accumulator, value
396
416
  }
397
417
  return accumulator;
398
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
+ }, {});
399
425
 
400
426
  //#endregion
401
427
  //#region src/utils/statics.ts
@@ -424,12 +450,17 @@ const removeNullableValues = (obj) => Object.entries(obj).filter(([, v]) => v !=
424
450
 
425
451
  //#endregion
426
452
  //#region src/utils/theme.ts
453
+ const MODE_CALLBACK_BRAND = Symbol.for("vl.themeModeCallback");
427
454
  /** Creates a mode-switching function that returns the light or dark value based on the active mode. */
428
- const themeModeCallback = (light, dark) => (mode) => {
429
- if (!mode || mode === "light") return light;
430
- 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;
431
462
  };
432
- const isModeCallback = (value) => typeof value === "function" && value.toString() === themeModeCallback().toString();
463
+ const isModeCallback = (value) => typeof value === "function" && value.__brand === MODE_CALLBACK_BRAND;
433
464
  const getThemeFromChain = (options, theme) => {
434
465
  const result = {};
435
466
  if (!options || isEmpty(options)) return result;
@@ -445,15 +476,22 @@ const getDimensionThemes = (theme, options) => {
445
476
  return acc;
446
477
  }, result);
447
478
  };
448
- const getTheme = ({ rocketstate, themes, baseTheme }) => {
449
- let finalTheme = { ...baseTheme };
479
+ const getTheme = ({ rocketstate, themes, baseTheme, transformKeys, appTheme }) => {
480
+ const finalTheme = { ...baseTheme };
481
+ const deferredTransforms = [];
450
482
  Object.entries(rocketstate).forEach(([key, value]) => {
451
483
  const keyTheme = themes[key];
452
- if (Array.isArray(value)) value.forEach((item) => {
453
- finalTheme = merge({}, finalTheme, keyTheme[item]);
454
- });
455
- 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);
456
493
  });
494
+ for (const transform of deferredTransforms) merge(finalTheme, transform(finalTheme, appTheme ?? {}, themeModeCallback, config.css));
457
495
  return finalTheme;
458
496
  };
459
497
  const getThemeByMode = (object, mode) => Object.keys(object).reduce((acc, key) => {
@@ -466,6 +504,32 @@ const getThemeByMode = (object, mode) => Object.keys(object).reduce((acc, key) =
466
504
 
467
505
  //#endregion
468
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
+ };
469
533
  /** Clones the current configuration and merges new options, returning a fresh rocketComponent. */
470
534
  const cloneAndEnhance = (defaultOpts, opts) => rocketComponent({
471
535
  ...defaultOpts,
@@ -489,7 +553,7 @@ const cloneAndEnhance = (defaultOpts, opts) => rocketComponent({
489
553
  * chainable static enhancers attached to the returned component.
490
554
  */
491
555
  const rocketComponent = (options) => {
492
- const { component, styles } = options;
556
+ const { component, styles, transformKeys } = options;
493
557
  const { styled } = config;
494
558
  const _calculateStylingAttrs = calculateStylingAttrs({
495
559
  multiKeys: options.multiKeys,
@@ -502,7 +566,7 @@ const rocketComponent = (options) => {
502
566
  const RenderComponent = options.provider ? RocketStyleProviderComponent(STYLED_COMPONENT) : STYLED_COMPONENT;
503
567
  const ThemeManager$1 = new ThemeManager();
504
568
  const hocsFuncs = [rocketStyleHOC(options), ...calculateHocsFuncs(options.compose)];
505
- const EnhancedComponent = forwardRef(({ $rocketstyleRef, ...props }, ref) => {
569
+ const EnhancedComponent = ({ $rocketstyleRef, ref, ...props }) => {
506
570
  const internalRef = useRocketstyleRef({
507
571
  $rocketstyleRef,
508
572
  ref
@@ -550,11 +614,21 @@ const rocketComponent = (options) => {
550
614
  ...rocketstate,
551
615
  pseudo: pseudoRocketstate
552
616
  };
553
- const rocketstyle = getTheme({
554
- rocketstate,
617
+ const rocketstateRef = useRef(rocketstate);
618
+ if (!isShallowEqualRocketstate(rocketstateRef.current, rocketstate)) rocketstateRef.current = rocketstate;
619
+ const stableRocketstate = rocketstateRef.current;
620
+ const rocketstyle = useMemo(() => getTheme({
621
+ rocketstate: stableRocketstate,
555
622
  themes: currentModeThemes,
556
- baseTheme: currentModeBaseTheme
557
- });
623
+ baseTheme: currentModeBaseTheme,
624
+ transformKeys,
625
+ appTheme: theme
626
+ }), [
627
+ stableRocketstate,
628
+ currentModeThemes,
629
+ currentModeBaseTheme,
630
+ theme
631
+ ]);
558
632
  const finalProps = {
559
633
  ...omit(mergeProps, [
560
634
  ...RESERVED_STYLING_PROPS_KEYS,
@@ -582,7 +656,7 @@ const rocketComponent = (options) => {
582
656
  }
583
657
  }
584
658
  return /* @__PURE__ */ jsx(RenderComponent, { ...finalProps });
585
- });
659
+ };
586
660
  const RocketComponent = compose(...hocsFuncs)(EnhancedComponent);
587
661
  RocketComponent.IS_ROCKETSTYLE = true;
588
662
  RocketComponent.displayName = componentName;
@@ -655,31 +729,44 @@ const validateInit = (name, component, dimensions) => {
655
729
  }
656
730
  if (!isEmpty(errors)) throw Error(JSON.stringify(errors));
657
731
  };
658
- const rocketstyle = ({ dimensions = DEFAULT_DIMENSIONS, useBooleans = true } = {}) => ({ name, component }) => {
659
- if (process.env.NODE_ENV !== "production") validateInit(name, component, dimensions);
660
- return rocketComponent({
661
- name,
662
- component,
663
- useBooleans,
664
- dimensions,
665
- dimensionKeys: getKeys(dimensions),
666
- dimensionValues: getDimensionsValues(dimensions),
667
- multiKeys: getMultipleDimensions(dimensions),
668
- styled: true
669
- });
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
+ };
670
756
  };
757
+ const typedRocketstyle = rocketstyle;
671
758
 
672
759
  //#endregion
673
760
  //#region src/isRocketComponent.tsx
674
761
  const isRocketComponent = (component) => {
675
- 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;
676
763
  return false;
677
764
  };
678
765
 
679
766
  //#endregion
680
767
  //#region src/index.ts
681
- var src_default = rocketstyle;
768
+ var src_default = typedRocketstyle;
682
769
 
683
770
  //#endregion
684
- export { Provider, context, src_default as default, isRocketComponent, rocketstyle };
771
+ export { Provider, context, src_default as default, isRocketComponent, typedRocketstyle as rocketstyle };
685
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.9+f2f47db",
3
+ "version": "2.0.0-beta.1",
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",
@@ -60,19 +61,19 @@
60
61
  "test:coverage": "vitest run --coverage",
61
62
  "test:watch": "vitest",
62
63
  "cover": "coveralls < .coverage/lcov.info",
63
- "typecheck": "tsc --noEmit"
64
+ "typecheck": "tsc --noEmit",
65
+ "version": "node ../../scripts/sync-peer-deps.mjs"
64
66
  },
65
67
  "peerDependencies": {
66
- "@vitus-labs/core": "^1.4.0",
68
+ "@vitus-labs/core": "2.0.0-beta.1",
67
69
  "react": ">= 19"
68
70
  },
69
71
  "devDependencies": {
70
- "@vitus-labs/core": "2.0.0-alpha.9+f2f47db",
71
- "@vitus-labs/elements": "2.0.0-alpha.9+f2f47db",
72
- "@vitus-labs/tools-rolldown": "^1.6.0",
73
- "@vitus-labs/tools-storybook": "^1.6.0",
74
- "@vitus-labs/tools-typescript": "^1.6.0",
75
- "@vitus-labs/unistyle": "2.0.0-alpha.9+f2f47db"
76
- },
77
- "gitHead": "f2f47db887d6a846ee5f9e96f290c59cdde4772a"
72
+ "@vitus-labs/core": "workspace:*",
73
+ "@vitus-labs/elements": "workspace:*",
74
+ "@vitus-labs/tools-rolldown": "2.2.0",
75
+ "@vitus-labs/tools-storybook": "2.2.0",
76
+ "@vitus-labs/tools-typescript": "2.1.0",
77
+ "@vitus-labs/unistyle": "workspace:*"
78
+ }
78
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.