@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 +4 -4
- package/lib/index.d.ts +25 -20
- package/lib/index.js +192 -105
- package/package.json +12 -11
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Multi-dimensional styling system for React.
|
|
|
5
5
|
[](https://www.npmjs.com/package/@vitus-labs/rocketstyle)
|
|
6
6
|
[](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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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> =
|
|
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
|
|
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
|
|
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
|
|
138
|
-
type
|
|
139
|
-
type
|
|
140
|
-
type
|
|
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>]>>
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
|
|
205
|
+
const isLight = !isDark;
|
|
206
|
+
return useMemo(() => ({
|
|
201
207
|
theme,
|
|
202
208
|
mode,
|
|
203
209
|
isDark,
|
|
204
|
-
isLight
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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) =>
|
|
234
|
-
const {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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) =>
|
|
263
|
-
const
|
|
264
|
-
if (
|
|
265
|
-
|
|
266
|
-
|
|
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
|
|
297
|
+
const t = typeof pickedProp;
|
|
284
298
|
if (multiKeys?.[item] && Array.isArray(pickedProp)) result[item] = pickedProp;
|
|
285
|
-
else if (
|
|
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)
|
|
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
|
|
295
|
-
if (isMultiKey) newDimensionValue = propsKeys.filter((key) =>
|
|
296
|
-
else
|
|
297
|
-
|
|
298
|
-
|
|
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) =>
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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) =>
|
|
429
|
-
|
|
430
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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 =
|
|
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
|
|
554
|
-
|
|
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
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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"
|
|
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 =
|
|
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-
|
|
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": "
|
|
68
|
+
"@vitus-labs/core": "2.0.0-beta.1",
|
|
67
69
|
"react": ">= 19"
|
|
68
70
|
},
|
|
69
71
|
"devDependencies": {
|
|
70
|
-
"@vitus-labs/core": "
|
|
71
|
-
"@vitus-labs/elements": "
|
|
72
|
-
"@vitus-labs/tools-rolldown": "
|
|
73
|
-
"@vitus-labs/tools-storybook": "
|
|
74
|
-
"@vitus-labs/tools-typescript": "
|
|
75
|
-
"@vitus-labs/unistyle": "
|
|
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.
|