@pyreon/rocketstyle 0.11.0 → 0.11.2

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.
Files changed (51) hide show
  1. package/package.json +14 -12
  2. package/src/__tests__/attrs.test.ts +190 -0
  3. package/src/__tests__/chaining.test.ts +86 -0
  4. package/src/__tests__/collection.test.ts +35 -0
  5. package/src/__tests__/compose.test.ts +36 -0
  6. package/src/__tests__/context.test.ts +200 -0
  7. package/src/__tests__/createLocalProvider.test.ts +248 -0
  8. package/src/__tests__/dimensions.test.ts +183 -0
  9. package/src/__tests__/e2e-styler.test.ts +291 -0
  10. package/src/__tests__/hooks.test.ts +207 -0
  11. package/src/__tests__/isRocketComponent.test.ts +48 -0
  12. package/src/__tests__/misc.test.ts +204 -0
  13. package/src/__tests__/providerConsumer.test.ts +248 -0
  14. package/src/__tests__/rocketstyleIntegration.test.ts +615 -0
  15. package/src/__tests__/themeUtils.test.ts +463 -0
  16. package/src/cache/LocalThemeManager.ts +14 -0
  17. package/src/cache/index.ts +3 -0
  18. package/src/constants/booleanTags.ts +32 -0
  19. package/src/constants/defaultDimensions.ts +23 -0
  20. package/src/constants/index.ts +44 -0
  21. package/src/context/context.ts +51 -0
  22. package/src/context/createLocalProvider.ts +84 -0
  23. package/src/context/localContext.ts +37 -0
  24. package/src/hoc/index.ts +3 -0
  25. package/src/hoc/rocketstyleAttrsHoc.ts +63 -0
  26. package/src/hooks/index.ts +4 -0
  27. package/src/hooks/usePseudoState.ts +79 -0
  28. package/src/hooks/useTheme.ts +36 -0
  29. package/src/index.ts +77 -0
  30. package/src/init.ts +93 -0
  31. package/src/isRocketComponent.ts +16 -0
  32. package/src/rocketstyle.ts +320 -0
  33. package/src/types/attrs.ts +13 -0
  34. package/src/types/config.ts +48 -0
  35. package/src/types/configuration.ts +69 -0
  36. package/src/types/dimensions.ts +106 -0
  37. package/src/types/hoc.ts +5 -0
  38. package/src/types/pseudo.ts +19 -0
  39. package/src/types/rocketComponent.ts +24 -0
  40. package/src/types/rocketstyle.ts +156 -0
  41. package/src/types/styles.ts +46 -0
  42. package/src/types/theme.ts +19 -0
  43. package/src/types/utils.ts +55 -0
  44. package/src/utils/attrs.ts +134 -0
  45. package/src/utils/chaining.ts +58 -0
  46. package/src/utils/collection.ts +9 -0
  47. package/src/utils/compose.ts +11 -0
  48. package/src/utils/dimensions.ts +126 -0
  49. package/src/utils/statics.ts +44 -0
  50. package/src/utils/styles.ts +18 -0
  51. package/src/utils/theme.ts +196 -0
@@ -0,0 +1,84 @@
1
+ import { provide } from "@pyreon/core"
2
+ import { signal } from "@pyreon/reactivity"
3
+ import type { PseudoProps } from "../types/pseudo"
4
+ import type { ComponentFn } from "../types/utils"
5
+ import { localContext } from "./localContext"
6
+
7
+ type Props = PseudoProps & Record<string, any>
8
+
9
+ /**
10
+ * Higher-order component that wraps a component with a LocalProvider,
11
+ * detecting pseudo-states (hover, focus, pressed) via mouse/focus events
12
+ * and broadcasting them through local context to child rocketstyle components.
13
+ *
14
+ * In Pyreon, context is provided via provide(), and state is managed
15
+ * with signals instead of useState.
16
+ */
17
+ const createLocalProvider = (WrappedComponent: ComponentFn<any>) => {
18
+ const HOCComponent: ComponentFn<Props> = ({
19
+ onMouseEnter,
20
+ onMouseLeave,
21
+ onMouseUp,
22
+ onMouseDown,
23
+ onFocus,
24
+ onBlur,
25
+ $rocketstate,
26
+ ...props
27
+ }) => {
28
+ const hover = signal(false)
29
+ const focus = signal(false)
30
+ const pressed = signal(false)
31
+
32
+ const pseudoState = () => ({
33
+ hover: hover(),
34
+ focus: focus(),
35
+ pressed: pressed(),
36
+ })
37
+
38
+ const events = {
39
+ onMouseEnter: (e: MouseEvent) => {
40
+ hover.set(true)
41
+ if (onMouseEnter) onMouseEnter(e)
42
+ },
43
+ onMouseLeave: (e: MouseEvent) => {
44
+ hover.set(false)
45
+ pressed.set(false)
46
+ if (onMouseLeave) onMouseLeave(e)
47
+ },
48
+ onMouseDown: (e: MouseEvent) => {
49
+ pressed.set(true)
50
+ if (onMouseDown) onMouseDown(e)
51
+ },
52
+ onMouseUp: (e: MouseEvent) => {
53
+ pressed.set(false)
54
+ if (onMouseUp) onMouseUp(e)
55
+ },
56
+ onFocus: (e: FocusEvent) => {
57
+ focus.set(true)
58
+ if (onFocus) onFocus(e)
59
+ },
60
+ onBlur: (e: FocusEvent) => {
61
+ focus.set(false)
62
+ if (onBlur) onBlur(e)
63
+ },
64
+ }
65
+
66
+ const updatedState = {
67
+ ...$rocketstate,
68
+ pseudo: { ...$rocketstate?.pseudo, ...pseudoState() },
69
+ }
70
+
71
+ // Provide local context for child rocketstyle components
72
+ provide(localContext, updatedState)
73
+
74
+ return WrappedComponent({
75
+ ...props,
76
+ ...events,
77
+ $rocketstate: updatedState,
78
+ })
79
+ }
80
+
81
+ return HOCComponent
82
+ }
83
+
84
+ export default createLocalProvider
@@ -0,0 +1,37 @@
1
+ import { createContext, useContext } from "@pyreon/core"
2
+ import type { PseudoState } from "../types/pseudo"
3
+
4
+ type LocalContext = Partial<
5
+ {
6
+ pseudo: PseudoState
7
+ } & Record<string, string>
8
+ >
9
+
10
+ /**
11
+ * Local context for propagating pseudo-state (hover, focus, pressed)
12
+ * and additional styling attributes from a parent provider component
13
+ * to its rocketstyle children.
14
+ */
15
+ const localContext = createContext<LocalContext>({})
16
+
17
+ const EMPTY_CTX = { pseudo: {} } as LocalContext
18
+
19
+ /**
20
+ * Retrieves the local pseudo-state context. When a consumer callback
21
+ * is provided, it transforms the raw context; otherwise returns defaults.
22
+ *
23
+ * In Pyreon, components are plain functions that run once — no useMemo needed.
24
+ */
25
+ type UseLocalContext = (consumer: any) => LocalContext
26
+ export const useLocalContext: UseLocalContext = (consumer) => {
27
+ const ctx = useContext(localContext)
28
+
29
+ if (!consumer) return EMPTY_CTX
30
+
31
+ const result = consumer((callback: any) => callback(ctx))
32
+ return { pseudo: {}, ...result }
33
+ }
34
+
35
+ export { localContext }
36
+
37
+ export default localContext
@@ -0,0 +1,3 @@
1
+ import rocketstyleAttrsHoc from "./rocketstyleAttrsHoc"
2
+
3
+ export { rocketstyleAttrsHoc }
@@ -0,0 +1,63 @@
1
+ import { render } from "@pyreon/ui-core"
2
+ import { useTheme } from "../hooks"
3
+ import type { Configuration } from "../types/configuration"
4
+ import type { ComponentFn } from "../types/utils"
5
+ import { calculateChainOptions, removeUndefinedProps } from "../utils/attrs"
6
+
7
+ export type RocketStyleHOC = ({
8
+ inversed,
9
+ attrs,
10
+ priorityAttrs,
11
+ }: Pick<Configuration, "inversed" | "attrs" | "priorityAttrs">) => (
12
+ WrappedComponent: ComponentFn<any>,
13
+ ) => ComponentFn<any>
14
+
15
+ /**
16
+ * HOC that resolves the `.attrs()` chain before the inner component renders.
17
+ * Evaluates both regular and priority attrs callbacks with the current theme
18
+ * and mode, then merges the results with explicit props (priority attrs
19
+ * are applied first, regular attrs can be overridden by direct props).
20
+ *
21
+ * In Pyreon, there is no forwardRef — ref flows as a normal prop.
22
+ * Components are plain functions.
23
+ */
24
+ const rocketStyleHOC: RocketStyleHOC = ({ inversed, attrs, priorityAttrs }) => {
25
+ const calculateAttrs = calculateChainOptions(attrs)
26
+ const calculatePriorityAttrs = calculateChainOptions(priorityAttrs)
27
+
28
+ const Enhanced = (WrappedComponent: ComponentFn<any>) => {
29
+ const HOCComponent: ComponentFn<any> = (props) => {
30
+ const { theme, mode, isDark, isLight } = useTheme({
31
+ inversed,
32
+ })
33
+
34
+ const callbackParams = [theme, { render, mode, isDark, isLight }]
35
+
36
+ // Remove undefined props not to override potential default props
37
+ const filteredProps = removeUndefinedProps(props)
38
+
39
+ const prioritizedAttrs = calculatePriorityAttrs([filteredProps, ...callbackParams])
40
+
41
+ const finalAttrs = calculateAttrs([
42
+ {
43
+ ...prioritizedAttrs,
44
+ ...filteredProps,
45
+ },
46
+ ...callbackParams,
47
+ ])
48
+
49
+ const finalProps = {
50
+ ...prioritizedAttrs,
51
+ ...finalAttrs,
52
+ ...filteredProps,
53
+ }
54
+
55
+ return WrappedComponent(finalProps)
56
+ }
57
+ return HOCComponent
58
+ }
59
+
60
+ return Enhanced
61
+ }
62
+
63
+ export default rocketStyleHOC
@@ -0,0 +1,4 @@
1
+ import usePseudoState from "./usePseudoState"
2
+ import useTheme from "./useTheme"
3
+
4
+ export { usePseudoState, useTheme }
@@ -0,0 +1,79 @@
1
+ import { signal } from "@pyreon/reactivity"
2
+ import type { PseudoActions, PseudoState } from "../types/pseudo"
3
+
4
+ type UsePseudoState = ({
5
+ onMouseEnter,
6
+ onMouseLeave,
7
+ onMouseDown,
8
+ onMouseUp,
9
+ onFocus,
10
+ onBlur,
11
+ }: Partial<PseudoActions>) => {
12
+ state: Pick<PseudoState, "hover" | "focus" | "pressed">
13
+ events: PseudoActions
14
+ }
15
+
16
+ /**
17
+ * Tracks hover, focus, and pressed pseudo-states via mouse and focus
18
+ * event handlers. Returns the current state flags and wrapped event
19
+ * callbacks that preserve any user-provided handlers.
20
+ *
21
+ * In Pyreon, uses signals instead of useState. Components are plain
22
+ * functions that run once — no useCallback/useMemo needed.
23
+ */
24
+ const usePseudoState: UsePseudoState = ({
25
+ onBlur,
26
+ onFocus,
27
+ onMouseDown,
28
+ onMouseEnter,
29
+ onMouseLeave,
30
+ onMouseUp,
31
+ }) => {
32
+ const hover = signal(false)
33
+ const focus = signal(false)
34
+ const pressed = signal(false)
35
+
36
+ const state = {
37
+ get hover() {
38
+ return hover()
39
+ },
40
+ get focus() {
41
+ return focus()
42
+ },
43
+ get pressed() {
44
+ return pressed()
45
+ },
46
+ }
47
+
48
+ const events: PseudoActions = {
49
+ onMouseEnter: (e) => {
50
+ hover.set(true)
51
+ if (onMouseEnter) onMouseEnter(e)
52
+ },
53
+ onMouseLeave: (e) => {
54
+ hover.set(false)
55
+ pressed.set(false)
56
+ if (onMouseLeave) onMouseLeave(e)
57
+ },
58
+ onMouseDown: (e) => {
59
+ pressed.set(true)
60
+ if (onMouseDown) onMouseDown(e)
61
+ },
62
+ onMouseUp: (e) => {
63
+ pressed.set(false)
64
+ if (onMouseUp) onMouseUp(e)
65
+ },
66
+ onFocus: (e) => {
67
+ focus.set(true)
68
+ if (onFocus) onFocus(e)
69
+ },
70
+ onBlur: (e) => {
71
+ focus.set(false)
72
+ if (onBlur) onBlur(e)
73
+ },
74
+ }
75
+
76
+ return { state, events }
77
+ }
78
+
79
+ export default usePseudoState
@@ -0,0 +1,36 @@
1
+ import { useContext } from "@pyreon/core"
2
+ import { THEME_MODES_INVERSED } from "../constants"
3
+ import { context } from "../context/context"
4
+ import type { ThemeModeKeys } from "../types/theme"
5
+
6
+ type Context = {
7
+ theme: Record<string, unknown>
8
+ mode: ThemeModeKeys
9
+ isDark: boolean
10
+ isLight: boolean
11
+ }
12
+
13
+ type UseThemeAttrs = ({ inversed }: { inversed?: boolean | undefined }) => Context
14
+
15
+ /**
16
+ * Retrieves the current theme object and resolved mode from context.
17
+ * Supports mode inversion so nested components can flip between
18
+ * light and dark without a new provider.
19
+ *
20
+ * In Pyreon, components run once — no useMemo needed.
21
+ */
22
+ const useThemeAttrs: UseThemeAttrs = ({ inversed }) => {
23
+ const {
24
+ theme = {},
25
+ mode: ctxMode = "light",
26
+ isDark: ctxDark,
27
+ } = useContext<Context>(context) || {}
28
+
29
+ const mode = inversed ? THEME_MODES_INVERSED[ctxMode] : ctxMode
30
+ const isDark = inversed ? !ctxDark : ctxDark
31
+ const isLight = !isDark
32
+
33
+ return { theme, mode, isDark, isLight }
34
+ }
35
+
36
+ export default useThemeAttrs
package/src/index.ts ADDED
@@ -0,0 +1,77 @@
1
+ import type { TProvider } from "./context/context"
2
+ import Provider, { context } from "./context/context"
3
+ import type { Rocketstyle } from "./init"
4
+ import rocketstyle from "./init"
5
+ import type { IsRocketComponent } from "./isRocketComponent"
6
+ import isRocketComponent from "./isRocketComponent"
7
+ import type { AttrsCb } from "./types/attrs"
8
+ import type {
9
+ ConfigAttrs,
10
+ ConsumerCb,
11
+ ConsumerCtxCBValue,
12
+ ConsumerCtxCb,
13
+ RocketComponentType,
14
+ RocketProviderState,
15
+ } from "./types/config"
16
+ import type { DefaultProps } from "./types/configuration"
17
+ import type {
18
+ DimensionCallbackParam,
19
+ DimensionProps,
20
+ Dimensions,
21
+ DimensionValue,
22
+ ExtractDimensionProps,
23
+ ExtractDimensions,
24
+ TDKP,
25
+ } from "./types/dimensions"
26
+ import type { ComposeParam, GenericHoc } from "./types/hoc"
27
+ import type { IRocketStyleComponent, RocketStyleComponent } from "./types/rocketstyle"
28
+ import type { RocketStyleInterpolationProps, StylesCb, StylesDefault } from "./types/styles"
29
+ import type {
30
+ ThemeCb,
31
+ ThemeDefault,
32
+ ThemeMode,
33
+ ThemeModeCallback,
34
+ ThemeModeKeys,
35
+ } from "./types/theme"
36
+ import type { ComponentFn, ElementType, ExtractProps, MergeTypes, TObj } from "./types/utils"
37
+
38
+ export type {
39
+ AttrsCb,
40
+ ComponentFn,
41
+ ComposeParam,
42
+ ConfigAttrs,
43
+ ConsumerCb,
44
+ ConsumerCtxCBValue,
45
+ ConsumerCtxCb,
46
+ DefaultProps,
47
+ DimensionCallbackParam,
48
+ DimensionProps,
49
+ Dimensions,
50
+ DimensionValue,
51
+ ElementType,
52
+ ExtractDimensionProps,
53
+ ExtractDimensions,
54
+ ExtractProps,
55
+ GenericHoc,
56
+ IRocketStyleComponent,
57
+ IsRocketComponent,
58
+ MergeTypes,
59
+ RocketComponentType,
60
+ RocketProviderState,
61
+ RocketStyleComponent,
62
+ RocketStyleInterpolationProps,
63
+ Rocketstyle,
64
+ StylesCb,
65
+ StylesDefault,
66
+ TDKP,
67
+ ThemeCb,
68
+ ThemeDefault,
69
+ ThemeMode,
70
+ ThemeModeCallback,
71
+ ThemeModeKeys,
72
+ TObj,
73
+ TProvider,
74
+ }
75
+
76
+ export { context, isRocketComponent, Provider, rocketstyle }
77
+ export default rocketstyle
package/src/init.ts ADDED
@@ -0,0 +1,93 @@
1
+ import { isEmpty } from "@pyreon/ui-core"
2
+ import { ALL_RESERVED_KEYS } from "./constants"
3
+ import defaultDimensions from "./constants/defaultDimensions"
4
+ import rocketComponent from "./rocketstyle"
5
+ import type { DefaultDimensions, Dimensions } from "./types/dimensions"
6
+ import type { RocketComponent } from "./types/rocketComponent"
7
+ import type { ElementType } from "./types/utils"
8
+ import {
9
+ getDimensionsValues,
10
+ getKeys,
11
+ getMultipleDimensions,
12
+ getTransformDimensions,
13
+ } from "./utils/dimensions"
14
+
15
+ export type Rocketstyle = <
16
+ const D extends Dimensions = DefaultDimensions,
17
+ UB extends boolean = true,
18
+ >({
19
+ dimensions,
20
+ useBooleans,
21
+ }?: {
22
+ dimensions?: D
23
+ useBooleans?: UB
24
+ }) => <C extends ElementType>({
25
+ name,
26
+ component,
27
+ }: {
28
+ name: string
29
+ component: C
30
+ }) => ReturnType<RocketComponent<C, {}, {}, D, UB>>
31
+
32
+ /**
33
+ * Factory initializer for rocketstyle components. Validates dimension
34
+ * configurations against reserved keys, then delegates to the core
35
+ * `rocketComponent` builder with pre-computed dimension metadata.
36
+ */
37
+ type InitErrors = Partial<{
38
+ component: string
39
+ name: string
40
+ dimensions: string
41
+ invalidDimensions: string
42
+ }>
43
+
44
+ const validateInit = (name: string, component: unknown, dimensions: Dimensions) => {
45
+ const errors: InitErrors = {}
46
+
47
+ if (!component) {
48
+ errors.component = "Parameter `component` is missing in params!"
49
+ }
50
+
51
+ if (!name) {
52
+ errors.name = "Parameter `name` is missing in params!"
53
+ }
54
+
55
+ if (isEmpty(dimensions)) {
56
+ errors.dimensions = "Parameter `dimensions` is missing in params!"
57
+ } else {
58
+ const definedDimensions = getKeys(dimensions)
59
+ const invalidDimension = ALL_RESERVED_KEYS.some((item) =>
60
+ definedDimensions.some((d) => d === item),
61
+ )
62
+
63
+ if (invalidDimension) {
64
+ errors.invalidDimensions = `Some of your \`dimensions\` is invalid and uses reserved static keys which are
65
+ ${defaultDimensions.toString()}`
66
+ }
67
+ }
68
+
69
+ if (!isEmpty(errors)) {
70
+ throw Error(JSON.stringify(errors))
71
+ }
72
+ }
73
+
74
+ const rocketstyle = (({ dimensions = defaultDimensions, useBooleans = true } = {}) =>
75
+ ({ name, component }: { name: string; component: any }) => {
76
+ if (process.env.NODE_ENV !== "production") {
77
+ validateInit(name, component, dimensions)
78
+ }
79
+
80
+ return (rocketComponent as any)({
81
+ name,
82
+ component,
83
+ useBooleans,
84
+ dimensions,
85
+ dimensionKeys: getKeys(dimensions),
86
+ dimensionValues: getDimensionsValues(dimensions),
87
+ multiKeys: getMultipleDimensions(dimensions),
88
+ transformKeys: getTransformDimensions(dimensions),
89
+ styled: true,
90
+ })
91
+ }) as unknown as Rocketstyle
92
+
93
+ export default rocketstyle
@@ -0,0 +1,16 @@
1
+ export type IsRocketComponent = <T>(component: T) => boolean
2
+
3
+ /** Runtime type guard — checks if a component was created by `rocketstyle()`. */
4
+ const isRocketComponent: IsRocketComponent = (component) => {
5
+ if (
6
+ component &&
7
+ (typeof component === "object" || typeof component === "function") &&
8
+ Object.hasOwn(component as object, "IS_ROCKETSTYLE")
9
+ ) {
10
+ return true
11
+ }
12
+
13
+ return false
14
+ }
15
+
16
+ export default isRocketComponent