@pyreon/attrs 0.24.5 → 0.24.6

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/src/init.ts DELETED
@@ -1,65 +0,0 @@
1
- import { isEmpty } from '@pyreon/ui-core'
2
- import attrsComponent from './attrs'
3
- import type { InitAttrsComponent } from './types/InitAttrsComponent'
4
- import type { ElementType } from './types/utils'
5
-
6
- // Dev-mode gate. `import.meta.env.DEV` is literal-replaced by Vite at build
7
- // time and tree-shakes to zero bytes in prod. The previous
8
- // `process.env.NODE_ENV !== 'production'` form was dead code in real Vite
9
- // browser bundles (Vite does not polyfill `process`).
10
- const __DEV__ = process.env.NODE_ENV !== 'production'
11
-
12
- /**
13
- * Public entry point for creating an attrs-enhanced component.
14
- *
15
- * ```tsx
16
- * const Button = attrs({ name: 'Button', component: Element })
17
- * .attrs({ tag: 'button' })
18
- * .attrs<{ primary?: boolean }>(({ primary }) => ({
19
- * backgroundColor: primary ? 'blue' : 'gray',
20
- * }))
21
- * ```
22
- */
23
- export type Attrs = <C extends ElementType>({
24
- name,
25
- component,
26
- }: {
27
- name: string
28
- component: C
29
- }) => ReturnType<InitAttrsComponent<C>>
30
-
31
- const attrs: Attrs = ({ name, component }) => {
32
- // Validate required params in development — fail fast with clear errors.
33
- if (__DEV__) {
34
- type Errors = Partial<{
35
- component: string
36
- name: string
37
- }>
38
-
39
- const errors: Errors = {}
40
- if (!component) {
41
- errors.component = 'Parameter `component` is missing in params!'
42
- }
43
-
44
- if (!name) {
45
- errors.name = 'Parameter `name` is missing in params!'
46
- }
47
-
48
- if (!isEmpty(errors)) {
49
- throw Error(JSON.stringify(errors))
50
- }
51
- }
52
-
53
- // Bootstrap with empty configuration — all chains start from scratch.
54
- return attrsComponent({
55
- name,
56
- component,
57
- attrs: [],
58
- priorityAttrs: [],
59
- filterAttrs: [],
60
- compose: {},
61
- statics: {},
62
- })
63
- }
64
-
65
- export default attrs
@@ -1,16 +0,0 @@
1
- export type IsAttrsComponent = <T>(component: T) => boolean
2
-
3
- /** Runtime type guard — checks if a component was created by `attrs()`. */
4
- const isAttrsComponent: IsAttrsComponent = (component) => {
5
- if (
6
- component &&
7
- (typeof component === 'object' || typeof component === 'function') &&
8
- Object.hasOwn(component as object, 'IS_ATTRS')
9
- ) {
10
- return true
11
- }
12
-
13
- return false
14
- }
15
-
16
- export default isAttrsComponent
@@ -1,83 +0,0 @@
1
- import type { VNode } from '@pyreon/core'
2
- import type { AttrsCb } from './attrs'
3
- import type { ConfigAttrs } from './config'
4
- import type { ComposeParam } from './hoc'
5
- import type { ElementType, ExtractProps, MergeTypes, TObj } from './utils'
6
-
7
- /**
8
- * Props passed to the inner enhanced component.
9
- * In Pyreon there's no forwardRef — ref flows as a normal prop.
10
- */
11
- export type InnerComponentProps = {
12
- 'data-attrs'?: string | undefined
13
- }
14
-
15
- /**
16
- * @param OA Origin component props params.
17
- * @param EA Extended prop types
18
- * @param S Defined statics
19
- * @param HOC High-order components
20
- * @param DFP Calculated final component props
21
- */
22
- export interface AttrsComponent<
23
- C extends ElementType = ElementType,
24
- // original component props
25
- OA extends TObj = {},
26
- // extended component props
27
- EA extends TObj = {},
28
- // statics
29
- S extends TObj = {},
30
- // hocs
31
- HOC extends TObj = {},
32
- // calculated final props
33
- DFP extends Record<string, any> = MergeTypes<[OA, EA]>,
34
- > {
35
- // The component is callable — Pyreon components are plain functions
36
- (props: DFP): VNode | null
37
-
38
- // CONFIG chaining method
39
- config: <NC extends ElementType | unknown = unknown>({
40
- name,
41
- component: NC,
42
- DEBUG,
43
- }: ConfigAttrs<NC>) => NC extends ElementType
44
- ? AttrsComponent<NC, ExtractProps<NC>, EA, S, HOC>
45
- : AttrsComponent<C, OA, EA, S, HOC>
46
-
47
- // ATTRS chaining method
48
- attrs: <P extends TObj | unknown = unknown>(
49
- param: P extends TObj ? Partial<DFP & P> | AttrsCb<DFP & P> : Partial<DFP> | AttrsCb<DFP>,
50
- config?: Partial<{
51
- priority: boolean
52
- filter: unknown extends P ? string[] : (keyof (EA & P))[]
53
- }>,
54
- ) => P extends TObj
55
- ? AttrsComponent<C, OA, MergeTypes<[EA, P]>, S, HOC>
56
- : AttrsComponent<C, OA, EA, S, HOC>
57
-
58
- // COMPOSE chaining method
59
- compose: <P extends ComposeParam>(
60
- param: P,
61
- ) => P extends TObj
62
- ? AttrsComponent<C, OA, EA, S, MergeTypes<[HOC, P]>>
63
- : AttrsComponent<C, OA, EA, S, HOC>
64
-
65
- // STATICS chaining method
66
- statics: <P extends TObj | unknown = unknown>(
67
- param: P,
68
- ) => P extends TObj
69
- ? AttrsComponent<C, OA, EA, MergeTypes<[S, P]>, HOC>
70
- : AttrsComponent<C, OA, EA, S, HOC>
71
-
72
- /** Access to all defined statics on the component. */
73
- meta: S
74
-
75
- getDefaultAttrs: (props: TObj) => TObj
76
-
77
- readonly $$originTypes: OA
78
- readonly $$extendedTypes: EA
79
- readonly $$types: DFP
80
-
81
- IS_ATTRS: true
82
- displayName: string
83
- }
@@ -1,19 +0,0 @@
1
- import type { AttrsComponent } from './AttrsComponent'
2
- import type { Configuration } from './configuration'
3
- import type { ElementType, ExtractProps } from './utils'
4
-
5
- /**
6
- * Type of the internal `attrsComponent` factory function.
7
- * Takes a full Configuration and returns an AttrsComponent whose
8
- * original props (OA) are extracted from the component type C,
9
- * with all extension slots (EA, S, HOC) starting empty.
10
- */
11
- export type InitAttrsComponent<C extends ElementType = ElementType> = (
12
- params: Configuration<C>,
13
- ) => AttrsComponent<
14
- C,
15
- ExtractProps<C>, // OA — original component props
16
- {}, // EA — extended props (empty initially)
17
- {}, // S — statics (empty initially)
18
- {} // HOC — composed HOCs (empty initially)
19
- >
@@ -1,2 +0,0 @@
1
- /** Callback form of `.attrs()` — receives current props and returns partial overrides. */
2
- export type AttrsCb<A> = (props: Partial<A>) => Partial<A>
@@ -1,13 +0,0 @@
1
- import type { ElementType } from './utils'
2
-
3
- /** A component that has been enhanced by attrs — identified by the `IS_ATTRS` marker. */
4
- export type AttrsComponentType = ElementType & {
5
- IS_ATTRS: true
6
- }
7
-
8
- /** Parameters accepted by the `.config()` chaining method. */
9
- export type ConfigAttrs<C extends ElementType | unknown> = Partial<{
10
- name: string
11
- component: C
12
- DEBUG: boolean
13
- }>
@@ -1,40 +0,0 @@
1
- import type { ElementType, TFn } from './utils'
2
-
3
- export type OptionFunc = (...arg: unknown[]) => Record<string, unknown>
4
-
5
- export type InitConfiguration<C> = {
6
- name?: string | undefined
7
- component: C
8
- }
9
-
10
- /**
11
- * Internal configuration accumulated across the chaining API.
12
- * Arrays hold the full chain — each `.attrs()` call appends to these.
13
- */
14
- export type Configuration<C = ElementType | unknown> = InitConfiguration<C> & {
15
- DEBUG?: boolean | undefined
16
- /** Chain of default-props callbacks (resolved in order, later wins). */
17
- attrs: OptionFunc[]
18
- /** Chain of priority-props callbacks (resolved before `attrs`, can be overridden by both). */
19
- priorityAttrs: OptionFunc[]
20
- /** Prop names to omit before passing to the underlying component. */
21
- filterAttrs: string[]
22
- /** Named HOCs — set to null/false to remove from chain. */
23
- compose: Record<string, TFn | null | undefined | false>
24
- /** Metadata accessible via `Component.meta`. */
25
- statics: Record<string, any>
26
- } & Record<string, any>
27
-
28
- /**
29
- * Single-item variant of Configuration — represents what a single
30
- * `.attrs()` / `.config()` call contributes (one function, not an array).
31
- * Used by `cloneAndEnhance` to merge into the accumulated Configuration.
32
- */
33
- export type ExtendedConfiguration<C = ElementType | unknown> = InitConfiguration<C> & {
34
- DEBUG?: boolean | undefined
35
- attrs: OptionFunc
36
- priorityAttrs: OptionFunc
37
- filterAttrs: string[]
38
- compose: Record<string, TFn | null | undefined | false>
39
- statics: Record<string, any>
40
- } & Record<string, any>
package/src/types/hoc.ts DELETED
@@ -1,10 +0,0 @@
1
- import type { ElementType } from './utils'
2
-
3
- export type GenericHoc = (component: ElementType) => ElementType
4
-
5
- /**
6
- * Parameters for `.compose()` — a record of named HOCs.
7
- * Setting a key to `null`, `undefined`, or `false` removes a
8
- * previously defined HOC from the chain.
9
- */
10
- export type ComposeParam = Record<string, GenericHoc | null | undefined | false>
@@ -1,137 +0,0 @@
1
- import type { VNodeChild } from '@pyreon/core'
2
-
3
- // ─── Base Types ───────────────────────────────────────────────
4
-
5
- export type TObj = Record<string, unknown>
6
- export type TFn = (...args: any) => any
7
- export type CallBackParam = TObj | TFn
8
- export type DisplayName = string
9
-
10
- /**
11
- * A Pyreon component function that accepts additional static properties.
12
- * Aligned with @pyreon/core's ComponentFn — returns VNodeChild (not just VNode | null)
13
- * to support components returning strings, numbers, undefined, etc.
14
- */
15
- export type ComponentFn<P = any> = ((props: P) => VNodeChild) & Partial<Record<string, any>>
16
-
17
- /**
18
- * An element type — either a Pyreon component function or an intrinsic tag string.
19
- */
20
- export type ElementType<T extends TObj | unknown = any> = ComponentFn<T>
21
-
22
- export type ValueOf<T> = T[keyof T]
23
-
24
- export type ArrayOfValues<T> = T[keyof T]
25
-
26
- export type ArrayOfKeys<T> = keyof T[]
27
-
28
- /**
29
- * A HOC that wraps a component and merges additional props `P`
30
- * with the wrapped component's own props.
31
- */
32
- export type SimpleHoc<P extends Record<string, unknown> = Record<string, unknown>> = <
33
- T extends ComponentFn<any>,
34
- >(
35
- WrappedComponent: T,
36
- ) => ComponentFn<MergeTypes<[P, ExtractProps<T>]>>
37
-
38
- /** Maps each key to `never` if the value is null, undefined, or false. */
39
- type IsFalseOrNullable<T> = T extends null | undefined | false ? never : true
40
- export type NullableKeys<T> = { [K in keyof T]: IsFalseOrNullable<T[K]> }
41
-
42
- /** Unwraps a callback to its return type, or returns the object as-is. */
43
- export type ReturnCbParam<P extends TFn | TObj> = P extends TFn ? ReturnType<P> : P
44
-
45
- // ─── MergeTypes ───────────────────────────────────────────────
46
- //
47
- // Merges a tuple of object types left-to-right (like Object.assign),
48
- // then strips keys whose values resolved to `never`, `null`, or `undefined`.
49
- //
50
- // Usage: MergeTypes<[BaseProps, ExtendedProps, OverrideProps]>
51
- //
52
- // This is the backbone of the chaining API — each `.attrs<P>()`
53
- // call produces MergeTypes<[PreviousProps, P]> so later definitions
54
- // override earlier ones while preserving the rest.
55
- // ──────────────────────────────────────────────────────────────
56
-
57
- /**
58
- * Forces TypeScript to expand/flatten a type for better IDE display.
59
- * Short-circuits for `any` — the mapped type would turn `any` into
60
- * `{ [x: string]: any; [x: number]: any; [x: symbol]: any }`, losing
61
- * its identity as `any` and breaking downstream type checks.
62
- */
63
- type Id<T> = 0 extends 1 & T ? T : T extends infer U ? { [K in keyof U]: U[K] } : never
64
-
65
- /**
66
- * Strips keys whose values are `never`, `null`, or `undefined`.
67
- * Uses tuple wrapping `[T[P]] extends [never]` to avoid distribution
68
- * over union types (a bare `T[P] extends never` would incorrectly
69
- * match union members).
70
- *
71
- * Short-circuits for `any` — the `as` clause in mapped types loses
72
- * index signatures, which would turn `any` into an empty type.
73
- */
74
- type IsAny<T> = 0 extends 1 & T ? true : false
75
-
76
- type ExtractNullableKeys<T> = 0 extends 1 & T
77
- ? T
78
- : {
79
- [P in keyof T as IsAny<T[P]> extends true
80
- ? P
81
- : [T[P]] extends [never]
82
- ? never
83
- : [T[P]] extends [null | undefined]
84
- ? never
85
- : P]: T[P]
86
- }
87
-
88
- /** Merges two types: keeps all keys from L that don't exist in R, then adds all of R. */
89
- type SpreadTwo<L, R> = Id<Pick<L, Exclude<keyof L, keyof R>> & R>
90
-
91
- /** Recursively spreads a tuple of types left-to-right. */
92
- type Spread<A extends readonly [...any]> = A extends [infer L, ...infer R]
93
- ? SpreadTwo<L, Spread<R>>
94
- : unknown
95
-
96
- /** Recursively checks whether any element in the tuple is `any`. */
97
- type _HasAny<A> = A extends [infer L, ...infer R] ? (0 extends 1 & L ? true : _HasAny<R>) : false
98
-
99
- export type MergeTypes<A extends readonly [...any]> =
100
- _HasAny<A> extends true ? any : ExtractNullableKeys<Spread<A>>
101
-
102
- // ─── ExtractProps ─────────────────────────────────────────────
103
-
104
- /**
105
- * Extracts the props type from a Pyreon component function — or passes
106
- * through the input unchanged when it's already a props type.
107
- *
108
- * Multi-overload aware: matches up to 4 call signatures and produces the
109
- * UNION of their first-argument types. Iterator / List / Element are
110
- * 3-overload primitives — without overload-aware extraction, wrapping
111
- * them through `attrs()` silently downgraded their public prop surface
112
- * to the LAST (loosest) overload. Mirrors vitus-labs PR #222.
113
- *
114
- * See `@pyreon/rocketstyle` `ExtractProps` for the canonical shape — kept
115
- * in sync across the 4 copies in core / elements / attrs / rocketstyle.
116
- */
117
- export type ExtractProps<TComponentOrTProps> = TComponentOrTProps extends {
118
- (props: infer P1, ...args: any): any
119
- (props: infer P2, ...args: any): any
120
- (props: infer P3, ...args: any): any
121
- (props: infer P4, ...args: any): any
122
- }
123
- ? P1 | P2 | P3 | P4
124
- : TComponentOrTProps extends {
125
- (props: infer P1, ...args: any): any
126
- (props: infer P2, ...args: any): any
127
- (props: infer P3, ...args: any): any
128
- }
129
- ? P1 | P2 | P3
130
- : TComponentOrTProps extends {
131
- (props: infer P1, ...args: any): any
132
- (props: infer P2, ...args: any): any
133
- }
134
- ? P1 | P2
135
- : TComponentOrTProps extends ComponentFn<infer TProps>
136
- ? TProps
137
- : TComponentOrTProps
@@ -1,46 +0,0 @@
1
- import { isEmpty } from '@pyreon/ui-core'
2
-
3
- /**
4
- * Strips keys with `undefined` values from a props object.
5
- * This prevents undefined consumer props from overriding defaults
6
- * computed by `.attrs()` callbacks. Only explicitly set values
7
- * (including `null`) should override defaults.
8
- */
9
- type RemoveUndefinedProps = <T extends Record<string, any>>(
10
- props: T,
11
- ) => { [I in keyof T as T[I] extends undefined ? never : I]: T[I] }
12
-
13
- export const removeUndefinedProps = (<T extends Record<string, any>>(props: T) => {
14
- // Direct for-in loop avoids the `Object.keys` array allocation that
15
- // the prior `reduce` over `Object.keys(props)` paid on every render.
16
- // The hot path fires on every content-equal re-render of any attrs-
17
- // wrapped component. Ported from vitus-labs `b003de47`.
18
- const result: Record<string, unknown> = {}
19
- for (const key in props) {
20
- const value = props[key]
21
- if (value !== undefined) result[key] = value
22
- }
23
- return result
24
- }) as RemoveUndefinedProps
25
-
26
- /**
27
- * Reduces an array of option functions (from chained `.attrs()` calls)
28
- * into a single merged result. Each function is called with `args`
29
- * (typically the current props) and its return value is merged
30
- * left-to-right via Object.assign — so later `.attrs()` calls
31
- * override earlier ones.
32
- *
33
- * Returns a curried function: first call binds the chain, second
34
- * call provides the arguments and executes the reduction.
35
- */
36
- type OptionFunc<A> = (...arg: A[]) => Record<string, unknown>
37
- type CalculateChainOptions = <A>(
38
- options?: OptionFunc<A>[],
39
- ) => (args: A[]) => ReturnType<OptionFunc<A>>
40
-
41
- export const calculateChainOptions: CalculateChainOptions = (options) => (args) => {
42
- const result = {}
43
- if (!options || isEmpty(options)) return result
44
-
45
- return options.reduce((acc, item) => Object.assign(acc, item(...args)), {})
46
- }
@@ -1,21 +0,0 @@
1
- type Func = (...args: any) => Record<string, unknown>
2
- type Obj = Record<string, unknown>
3
-
4
- /**
5
- * Appends a new attrs option to the existing chain of option functions.
6
- *
7
- * The `.attrs()` API accepts either an object or a callback. This function
8
- * normalizes both forms into a function (objects are wrapped in `() => obj`)
9
- * and appends to the existing array. The array is cloned so each chained
10
- * component gets its own copy — maintaining immutability across the chain.
11
- */
12
- type ChainOptions = (opts: Obj | Func | undefined, defaultOpts: Func[]) => Func[]
13
-
14
- export const chainOptions: ChainOptions = (opts, defaultOpts = []) => {
15
- const result = [...defaultOpts]
16
-
17
- if (typeof opts === 'function') result.push(opts)
18
- else if (typeof opts === 'object') result.push(() => opts)
19
-
20
- return result
21
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Filters out keys with `null`, `undefined`, or `false` values.
3
- * Used to clean compose config — setting a HOC to `false`/`null` removes it.
4
- */
5
- type RemoveNullableValues = (obj: Record<string, any>) => Record<string, any>
6
- export const removeNullableValues: RemoveNullableValues = (obj) => {
7
- const result: Record<string, any> = {}
8
- for (const [k, v] of Object.entries(obj)) {
9
- if (v != null && v !== false) {
10
- result[k] = v
11
- }
12
- }
13
- return result
14
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Extracts HOC functions from the `.compose()` configuration and reverses
3
- * them for correct application order. Setting a key to `null`, `undefined`,
4
- * or `false` removes a previously defined HOC — only actual functions are kept.
5
- *
6
- * The reversal is needed because `compose(a, b, c)(Component)` applies as
7
- * `a(b(c(Component)))`, so the last-defined HOC should wrap innermost.
8
- */
9
- type CalculateHocsFuncs = (options: Record<string, any>) => ((arg: any) => any)[]
10
-
11
- export const calculateHocsFuncs: CalculateHocsFuncs = (options = {}) =>
12
- Object.values(options)
13
- .filter((item) => typeof item === 'function')
14
- .reverse()
@@ -1,16 +0,0 @@
1
- import { isEmpty } from '@pyreon/ui-core'
2
-
3
- /**
4
- * Copies user-defined statics from `.statics()` into the component's
5
- * `meta` object. These are accessible at `Component.meta.myStatic`.
6
- */
7
- type CreateStaticsEnhancers = (params: {
8
- context: Record<string, any>
9
- options: Record<string, any>
10
- }) => void
11
-
12
- export const createStaticsEnhancers: CreateStaticsEnhancers = ({ context, options }) => {
13
- if (!isEmpty(options)) {
14
- Object.assign(context, options)
15
- }
16
- }