@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.
@@ -1,179 +0,0 @@
1
- import { h } from '@pyreon/core'
2
- import createAttrsHOC from '../hoc/attrsHoc'
3
-
4
- const Receiver = (props: any) => ({
5
- type: 'div',
6
- props: { ...props, 'data-testid': 'receiver' },
7
- children: props.label ?? '',
8
- key: null,
9
- })
10
-
11
- // --------------------------------------------------------
12
- // attrsHoc - props merging
13
- // --------------------------------------------------------
14
- describe('attrsHoc - props merging', () => {
15
- it('should pass through props unchanged when no attrs defined', () => {
16
- const hoc = createAttrsHOC({ attrs: [], priorityAttrs: [] })
17
- const Enhanced = hoc(Receiver)
18
-
19
- const result = Enhanced({ label: 'hello', 'data-custom': 'yes' }) as any
20
- expect(result.children).toBe('hello')
21
- expect(result.props['data-custom']).toBe('yes')
22
- })
23
-
24
- it('should apply attrs as default props', () => {
25
- const hoc = createAttrsHOC({
26
- attrs: [(_props: any) => ({ label: 'default' })],
27
- priorityAttrs: [],
28
- })
29
- const Enhanced = hoc(Receiver)
30
-
31
- const result = Enhanced({}) as any
32
- expect(result.children).toBe('default')
33
- })
34
-
35
- it('should let explicit props override attrs', () => {
36
- const hoc = createAttrsHOC({
37
- attrs: [() => ({ label: 'from-attrs' })],
38
- priorityAttrs: [],
39
- })
40
- const Enhanced = hoc(Receiver)
41
-
42
- const result = Enhanced({ label: 'explicit' }) as any
43
- expect(result.children).toBe('explicit')
44
- })
45
-
46
- it('should apply priorityAttrs with lowest precedence', () => {
47
- const hoc = createAttrsHOC({
48
- attrs: [(_props: any) => ({ label: 'from-attrs' })],
49
- priorityAttrs: [(_props: any) => ({ label: 'from-priority' })],
50
- })
51
- const Enhanced = hoc(Receiver)
52
-
53
- const result = Enhanced({}) as any
54
- expect(result.children).toBe('from-attrs')
55
- })
56
-
57
- it('should merge results from multiple attrs functions', () => {
58
- const hoc = createAttrsHOC({
59
- attrs: [() => ({ 'data-first': 'a' }), () => ({ 'data-second': 'b' })],
60
- priorityAttrs: [],
61
- })
62
- const Enhanced = hoc(Receiver)
63
-
64
- const result = Enhanced({}) as any
65
- expect(result.props['data-first']).toBe('a')
66
- expect(result.props['data-second']).toBe('b')
67
- })
68
-
69
- it('should remove undefined props so they dont override defaults', () => {
70
- const hoc = createAttrsHOC({
71
- attrs: [() => ({ label: 'default-label' })],
72
- priorityAttrs: [],
73
- })
74
- const Enhanced = hoc(Receiver)
75
-
76
- const result = Enhanced({ label: undefined }) as any
77
- expect(result.children).toBe('default-label')
78
- })
79
-
80
- it('should allow null to override defaults', () => {
81
- const hoc = createAttrsHOC({
82
- attrs: [() => ({ label: 'default-label' })],
83
- priorityAttrs: [],
84
- })
85
- const Enhanced = hoc(Receiver)
86
-
87
- const result = Enhanced({ label: null }) as any
88
- expect(result.children).toBe('')
89
- })
90
- })
91
-
92
- // --------------------------------------------------------
93
- // attrsHoc - attrs callback receives props
94
- // --------------------------------------------------------
95
- describe('attrsHoc - attrs callback receives props', () => {
96
- it('should pass filtered props to attrs callback', () => {
97
- const attrsFn = vi.fn(() => ({}))
98
- const hoc = createAttrsHOC({
99
- attrs: [attrsFn],
100
- priorityAttrs: [],
101
- })
102
- const Enhanced = hoc(Receiver)
103
-
104
- Enhanced({ variant: 'primary', size: 'lg' })
105
- expect(attrsFn).toHaveBeenCalledWith(
106
- expect.objectContaining({ variant: 'primary', size: 'lg' }),
107
- )
108
- })
109
-
110
- it('should pass priority attrs merged with props to attrs callback', () => {
111
- const attrsFn = vi.fn(() => ({}))
112
- const hoc = createAttrsHOC({
113
- attrs: [attrsFn],
114
- priorityAttrs: [() => ({ fromPriority: true })],
115
- })
116
- const Enhanced = hoc(Receiver)
117
-
118
- Enhanced({ variant: 'primary' })
119
- expect(attrsFn).toHaveBeenCalledWith(
120
- expect.objectContaining({ variant: 'primary', fromPriority: true }),
121
- )
122
- })
123
- })
124
-
125
- // --------------------------------------------------------
126
- // attrsHoc - ref passthrough
127
- // --------------------------------------------------------
128
- describe('attrsHoc - ref passthrough', () => {
129
- it('should pass ref as a normal prop to wrapped component', () => {
130
- const hoc = createAttrsHOC({ attrs: [], priorityAttrs: [] })
131
- const Enhanced = hoc(Receiver)
132
-
133
- const refObj = { current: null }
134
- const result = Enhanced({ ref: refObj }) as any
135
- expect(result.props.ref).toBe(refObj)
136
- })
137
- })
138
-
139
- // ─── attrsHoc — real h() round-trip (parallel to the Receiver mock) ──
140
- //
141
- // The Receiver above is a mock that returns a `{ type, props,
142
- // children, key }` literal. The tests assert against that shape.
143
- // This block re-runs the core HOC contracts against a Receiver that
144
- // builds its return value via real `h()` from `@pyreon/core` —
145
- // catches divergence between the mock shape and the actual VNode
146
- // shape h() produces.
147
-
148
- describe('attrsHoc — real h() round-trip', () => {
149
- // Receiver that returns a real VNode via h() instead of a literal.
150
- const ReceiverH = (props: any) =>
151
- h('div', { ...props, 'data-testid': 'receiver' }, props.label ?? '')
152
-
153
- it('passes through props unchanged when no attrs defined', () => {
154
- const hoc = createAttrsHOC({ attrs: [], priorityAttrs: [] })
155
- const Enhanced = hoc(ReceiverH as any)
156
- const result = Enhanced({ label: 'hello', 'data-custom': 'yes' }) as any
157
- expect(result.props.label).toBe('hello')
158
- expect(result.props['data-custom']).toBe('yes')
159
- expect(result.type).toBe('div')
160
- })
161
-
162
- it('applies attrs as default props through real h()', () => {
163
- const hoc = createAttrsHOC({
164
- attrs: [(_props: any) => ({ label: 'default' })],
165
- priorityAttrs: [],
166
- })
167
- const Enhanced = hoc(ReceiverH as any)
168
- const result = Enhanced({}) as any
169
- expect(result.props.label).toBe('default')
170
- })
171
-
172
- it('passes ref through real h() output unchanged', () => {
173
- const hoc = createAttrsHOC({ attrs: [], priorityAttrs: [] })
174
- const Enhanced = hoc(ReceiverH as any)
175
- const refObj = { current: null }
176
- const result = Enhanced({ ref: refObj }) as any
177
- expect(result.props.ref).toBe(refObj)
178
- })
179
- })
@@ -1,241 +0,0 @@
1
- import { calculateChainOptions, removeUndefinedProps } from '../utils/attrs'
2
- import { chainOptions } from '../utils/chaining'
3
- import { removeNullableValues } from '../utils/collection'
4
- import { calculateHocsFuncs } from '../utils/compose'
5
- import { createStaticsEnhancers } from '../utils/statics'
6
-
7
- // --------------------------------------------------------
8
- // removeUndefinedProps
9
- // --------------------------------------------------------
10
- describe('removeUndefinedProps', () => {
11
- it('should remove properties with undefined values', () => {
12
- const result = removeUndefinedProps({
13
- a: 1,
14
- b: undefined,
15
- c: 'hello',
16
- })
17
- expect(result).toEqual({ a: 1, c: 'hello' })
18
- })
19
-
20
- it('should keep null values', () => {
21
- const result = removeUndefinedProps({ a: null, b: undefined })
22
- expect(result).toEqual({ a: null })
23
- })
24
-
25
- it('should keep false values', () => {
26
- const result = removeUndefinedProps({ a: false, b: undefined })
27
- expect(result).toEqual({ a: false })
28
- })
29
-
30
- it('should keep zero values', () => {
31
- const result = removeUndefinedProps({ a: 0, b: undefined })
32
- expect(result).toEqual({ a: 0 })
33
- })
34
-
35
- it('should keep empty string values', () => {
36
- const result = removeUndefinedProps({ a: '', b: undefined })
37
- expect(result).toEqual({ a: '' })
38
- })
39
-
40
- it('should return empty object when all values are undefined', () => {
41
- const result = removeUndefinedProps({ a: undefined, b: undefined })
42
- expect(result).toEqual({})
43
- })
44
-
45
- it('should return all props when none are undefined', () => {
46
- const input = { a: 1, b: 'test', c: true }
47
- const result = removeUndefinedProps(input)
48
- expect(result).toEqual(input)
49
- })
50
-
51
- it('should handle empty object', () => {
52
- const result = removeUndefinedProps({})
53
- expect(result).toEqual({})
54
- })
55
- })
56
-
57
- // --------------------------------------------------------
58
- // removeNullableValues
59
- // --------------------------------------------------------
60
- describe('removeNullableValues', () => {
61
- it('should remove null values', () => {
62
- const result = removeNullableValues({ a: 1, b: null })
63
- expect(result).toEqual({ a: 1 })
64
- })
65
-
66
- it('should remove undefined values', () => {
67
- const result = removeNullableValues({ a: 1, b: undefined })
68
- expect(result).toEqual({ a: 1 })
69
- })
70
-
71
- it('should remove false values', () => {
72
- const result = removeNullableValues({ a: 1, b: false })
73
- expect(result).toEqual({ a: 1 })
74
- })
75
-
76
- it('should keep zero values', () => {
77
- const result = removeNullableValues({ a: 0, b: null })
78
- expect(result).toEqual({ a: 0 })
79
- })
80
-
81
- it('should keep empty string values', () => {
82
- const result = removeNullableValues({ a: '', b: null })
83
- expect(result).toEqual({ a: '' })
84
- })
85
-
86
- it('should keep truthy values', () => {
87
- const result = removeNullableValues({ a: 1, b: 'test', c: true })
88
- expect(result).toEqual({ a: 1, b: 'test', c: true })
89
- })
90
-
91
- it('should handle empty object', () => {
92
- const result = removeNullableValues({})
93
- expect(result).toEqual({})
94
- })
95
- })
96
-
97
- // --------------------------------------------------------
98
- // calculateChainOptions
99
- // --------------------------------------------------------
100
- describe('calculateChainOptions', () => {
101
- it('should return empty object when no options provided', () => {
102
- const calculate = calculateChainOptions(undefined)
103
- const result = calculate([{}])
104
- expect(result).toEqual({})
105
- })
106
-
107
- it('should return empty object for empty options array', () => {
108
- const calculate = calculateChainOptions([])
109
- const result = calculate([{}])
110
- expect(result).toEqual({})
111
- })
112
-
113
- it('should execute a single option function', () => {
114
- const fn = (props: any) => ({
115
- color: props.variant === 'primary' ? 'blue' : 'gray',
116
- })
117
- const calculate = calculateChainOptions([fn])
118
- const result = calculate([{ variant: 'primary' }])
119
- expect(result).toEqual({ color: 'blue' })
120
- })
121
-
122
- it('should merge results from multiple option functions', () => {
123
- const fn1 = (_: any) => ({ color: 'blue' })
124
- const fn2 = (_: any) => ({ size: 'large' })
125
- const calculate = calculateChainOptions([fn1, fn2])
126
- const result = calculate([{}])
127
- expect(result).toEqual({ color: 'blue', size: 'large' })
128
- })
129
-
130
- it('should let later functions override earlier ones', () => {
131
- const fn1 = (_: any) => ({ color: 'blue' })
132
- const fn2 = (_: any) => ({ color: 'red' })
133
- const calculate = calculateChainOptions([fn1, fn2])
134
- const result = calculate([{}])
135
- expect(result).toEqual({ color: 'red' })
136
- })
137
-
138
- it('should pass arguments to each option function', () => {
139
- const fn = vi.fn((_: any) => ({}))
140
- const calculate = calculateChainOptions([fn])
141
- const props = { variant: 'primary' }
142
- calculate([props])
143
- expect(fn).toHaveBeenCalledWith(props)
144
- })
145
- })
146
-
147
- // --------------------------------------------------------
148
- // chainOptions
149
- // --------------------------------------------------------
150
- describe('chainOptions', () => {
151
- it('should return default options when opts is undefined', () => {
152
- const defaults = [() => ({})]
153
- const result = chainOptions(undefined, defaults)
154
- expect(result).toEqual(defaults)
155
- })
156
-
157
- it('should append function to defaults', () => {
158
- const fn1 = () => ({ a: 1 })
159
- const fn2 = () => ({ b: 2 })
160
- const result = chainOptions(fn2, [fn1])
161
- expect(result).toHaveLength(2)
162
- expect(result[0]).toBe(fn1)
163
- expect(result[1]).toBe(fn2)
164
- })
165
-
166
- it('should wrap object in a function and append', () => {
167
- const obj = { color: 'blue' }
168
- const result = chainOptions(obj, [])
169
- expect(result).toHaveLength(1)
170
- expect(result[0]?.()).toEqual(obj)
171
- })
172
-
173
- it('should return empty array when no defaults and undefined opts', () => {
174
- const result = chainOptions(undefined, [])
175
- expect(result).toEqual([])
176
- })
177
-
178
- it('should not mutate the defaults array', () => {
179
- const defaults = [() => ({})]
180
- const fn = () => ({ a: 1 })
181
- const result = chainOptions(fn, defaults)
182
- expect(defaults).toHaveLength(1)
183
- expect(result).toHaveLength(2)
184
- })
185
- })
186
-
187
- // --------------------------------------------------------
188
- // createStaticsEnhancers
189
- // --------------------------------------------------------
190
- describe('createStaticsEnhancers', () => {
191
- it('should assign options to context', () => {
192
- const context: Record<string, any> = {}
193
- createStaticsEnhancers({
194
- context,
195
- options: { theme: 'dark', variant: 'primary' },
196
- })
197
- expect(context).toEqual({ theme: 'dark', variant: 'primary' })
198
- })
199
-
200
- it('should not modify context when options is empty', () => {
201
- const context: Record<string, any> = { existing: true }
202
- createStaticsEnhancers({ context, options: {} })
203
- expect(context).toEqual({ existing: true })
204
- })
205
-
206
- it('should merge with existing context properties', () => {
207
- const context: Record<string, any> = { existing: true }
208
- createStaticsEnhancers({ context, options: { newProp: 'value' } })
209
- expect(context).toEqual({ existing: true, newProp: 'value' })
210
- })
211
- })
212
-
213
- // --------------------------------------------------------
214
- // calculateHocsFuncs
215
- // --------------------------------------------------------
216
- describe('calculateHocsFuncs', () => {
217
- it('should return empty array for empty options', () => {
218
- const result = calculateHocsFuncs({})
219
- expect(result).toEqual([])
220
- })
221
-
222
- it('should filter out non-function values', () => {
223
- const fn = (x: any) => x
224
- const result = calculateHocsFuncs({ a: fn, b: 'string', c: 123 })
225
- expect(result).toHaveLength(1)
226
- expect(result[0]).toBe(fn)
227
- })
228
-
229
- it('should reverse the order of functions', () => {
230
- const fn1 = (x: any) => x
231
- const fn2 = (x: any) => x
232
- const result = calculateHocsFuncs({ a: fn1, b: fn2 })
233
- expect(result[0]).toBe(fn2)
234
- expect(result[1]).toBe(fn1)
235
- })
236
-
237
- it('should return empty array for undefined input', () => {
238
- const result = calculateHocsFuncs(undefined as any)
239
- expect(result).toEqual([])
240
- })
241
- })
package/src/attrs.ts DELETED
@@ -1,131 +0,0 @@
1
- import { compose, hoistNonReactStatics, omit, pick } from '@pyreon/ui-core'
2
- import { attrsHoc } from './hoc'
3
- import type { AttrsComponent as AttrsComponentType } from './types/AttrsComponent'
4
- import type { Configuration, ExtendedConfiguration } from './types/configuration'
5
- import type { InitAttrsComponent } from './types/InitAttrsComponent'
6
- import { calculateChainOptions } from './utils/attrs'
7
- import { chainOptions } from './utils/chaining'
8
- import { calculateHocsFuncs } from './utils/compose'
9
- import { createStaticsEnhancers } from './utils/statics'
10
-
11
- // Dev-mode gate. `import.meta.env.DEV` is literal-replaced by Vite at build
12
- // time and tree-shakes to zero bytes in prod. The previous
13
- // `process.env.NODE_ENV !== 'production'` form was dead code in real Vite
14
- // browser bundles (Vite does not polyfill `process`).
15
- const __DEV__ = process.env.NODE_ENV !== 'production'
16
-
17
- /**
18
- * Clones the current configuration and merges new options, then creates a
19
- * fresh component. This makes the chaining API immutable — each `.attrs()`
20
- * / `.config()` / `.statics()` call returns a brand-new component with an
21
- * updated configuration rather than mutating the existing one.
22
- */
23
- type CloneAndEnhance = (
24
- defaultOpts: Configuration,
25
- opts: Partial<ExtendedConfiguration>,
26
- ) => ReturnType<typeof attrsComponent>
27
-
28
- const cloneAndEnhance: CloneAndEnhance = (defaultOpts, opts) =>
29
- attrsComponent({
30
- ...defaultOpts,
31
- ...(opts.name ? { name: opts.name } : undefined),
32
- ...(opts.component ? { component: opts.component } : undefined),
33
- attrs: chainOptions(opts.attrs, defaultOpts.attrs),
34
- filterAttrs: [...(defaultOpts.filterAttrs ?? []), ...(opts.filterAttrs ?? [])],
35
- priorityAttrs: chainOptions(opts.priorityAttrs, defaultOpts.priorityAttrs),
36
- statics: { ...defaultOpts.statics, ...opts.statics },
37
- compose: { ...defaultOpts.compose, ...opts.compose },
38
- } as Parameters<typeof attrsComponent>[0])
39
-
40
- /**
41
- * Core factory that builds an attrs-enhanced Pyreon component.
42
- *
43
- * Creates a plain ComponentFn that:
44
- * 1. Wraps the original with attrsHoc (default props) + user HOCs from `.compose()`.
45
- * 2. Filters out internal props listed in `filterAttrs`.
46
- * 3. Attaches `data-attrs` attribute in development for debugging.
47
- *
48
- * Then adds chaining methods (`.attrs()`, `.config()`, `.compose()`, `.statics()`)
49
- * as static properties — each calls `cloneAndEnhance` to produce a new component.
50
- *
51
- * In Pyreon, there is no forwardRef — ref flows as a normal prop.
52
- * Components are plain functions that run once per mount.
53
- */
54
- const attrsComponent: InitAttrsComponent = (options) => {
55
- const componentName = options.name ?? options.component.displayName ?? options.component.name
56
-
57
- const RenderComponent = options.component
58
-
59
- // Build the HOC chain: attrsHoc is always first (resolves default props),
60
- // followed by user-composed HOCs in reverse order (outermost wraps first).
61
- const hocsFuncs = [attrsHoc(options), ...calculateHocsFuncs(options.compose)]
62
-
63
- // The inner component receives already-computed props from the HOC chain.
64
- // It handles prop filtering and final rendering.
65
- const EnhancedComponent = (props: Record<string, any>) => {
66
- const needsFiltering = options.filterAttrs && options.filterAttrs.length > 0
67
-
68
- const filteredProps = needsFiltering ? omit(props, options.filterAttrs) : props
69
-
70
- const finalProps = __DEV__
71
- ? { ...filteredProps, 'data-attrs': componentName }
72
- : filteredProps
73
-
74
- return RenderComponent(finalProps)
75
- }
76
-
77
- // Apply the full HOC chain: compose(attrsHoc, ...userHocs)(EnhancedComponent)
78
- const AttrsComponent: AttrsComponentType = compose(...hocsFuncs)(EnhancedComponent)
79
-
80
- AttrsComponent.IS_ATTRS = true
81
- AttrsComponent.displayName = componentName
82
- AttrsComponent.meta = {}
83
-
84
- // Copy static properties from the original component.
85
- hoistNonReactStatics(AttrsComponent, options.component)
86
-
87
- // Populate `component.meta` with user-defined statics from `.statics()`.
88
- createStaticsEnhancers({
89
- context: AttrsComponent.meta,
90
- options: options.statics,
91
- })
92
-
93
- // ─── Chaining Methods ──────────────────────────────────
94
- // Each method creates a new component via cloneAndEnhance.
95
- // The original component is never mutated.
96
- Object.assign(AttrsComponent, {
97
- attrs: (attrs: any, { priority, filter }: any = {}) => {
98
- const result: Record<string, any> = {}
99
-
100
- if (filter) {
101
- result.filterAttrs = filter
102
- }
103
-
104
- if (priority) {
105
- result.priorityAttrs = attrs as ExtendedConfiguration['priorityAttrs']
106
-
107
- return cloneAndEnhance(options, result)
108
- }
109
-
110
- result.attrs = attrs as ExtendedConfiguration['attrs']
111
-
112
- return cloneAndEnhance(options, result)
113
- },
114
-
115
- config: (opts: any = {}) => {
116
- const result = pick(opts)
117
-
118
- return cloneAndEnhance(options, result)
119
- },
120
-
121
- compose: (opts: any) => cloneAndEnhance(options, { compose: opts }),
122
-
123
- statics: (opts: any) => cloneAndEnhance(options, { statics: opts }),
124
-
125
- getDefaultAttrs: (props: any) => calculateChainOptions(options.attrs)([props]),
126
- })
127
-
128
- return AttrsComponent
129
- }
130
-
131
- export default attrsComponent
package/src/env.d.ts DELETED
@@ -1,6 +0,0 @@
1
- /**
2
- * Minimal process type — just enough for `process.env.NODE_ENV` checks.
3
- * Avoids requiring @types/node in consumers that import pyreon source
4
- * via the `"bun"` export condition.
5
- */
6
- declare var process: { env: { NODE_ENV?: string } }
@@ -1,74 +0,0 @@
1
- import type { Configuration } from '../types/configuration'
2
- import type { ComponentFn } from '../types/utils'
3
- import { calculateChainOptions, removeUndefinedProps } from '../utils/attrs'
4
-
5
- export type AttrsStyleHOC = ({
6
- attrs,
7
- priorityAttrs,
8
- }: Pick<Configuration, 'attrs' | 'priorityAttrs'>) => (
9
- WrappedComponent: ComponentFn<any>,
10
- ) => ComponentFn<any>
11
-
12
- /**
13
- * Creates the core HOC that computes default props from the `.attrs()` chain.
14
- *
15
- * This is always the outermost HOC in the compose chain, so it runs first.
16
- * It resolves both priority and normal attrs callbacks, then merges them
17
- * with the consumer's explicit props following this precedence:
18
- *
19
- * priorityAttrs < normalAttrs < explicit props (last wins)
20
- *
21
- * In Pyreon, components are plain functions — no forwardRef needed.
22
- * The ref flows as a normal prop through the chain.
23
- */
24
- const createAttrsHOC: AttrsStyleHOC = ({ attrs, priorityAttrs }) => {
25
- // Pre-build the chain reducers once (not per render).
26
- const calculateAttrs = calculateChainOptions(attrs)
27
- const calculatePriorityAttrs = calculateChainOptions(priorityAttrs)
28
- // Most components never call .attrs() — short-circuit the merge work below
29
- // so the no-chain mount path skips 2 reducer invocations + 3 object spreads.
30
- // Mirrors vitus-labs's attrsHoc fast-path; React-side memoization
31
- // (useMemo / useStableValue) is omitted because Pyreon components run once
32
- // per mount, not on every render.
33
- const hasAttrs = (attrs?.length ?? 0) > 0
34
- const hasPriorityAttrs = (priorityAttrs?.length ?? 0) > 0
35
- const hasAnyChain = hasAttrs || hasPriorityAttrs
36
-
37
- const attrsHoc = (WrappedComponent: ComponentFn<any>) => {
38
- const HOCComponent: ComponentFn<any> = (props) => {
39
- // Strip undefined values so they don't shadow defaults from attrs callbacks.
40
- const filteredProps = removeUndefinedProps(props)
41
-
42
- // Fast path: no attrs configured — skip reducers + spreads entirely.
43
- if (!hasAnyChain) return WrappedComponent(filteredProps)
44
-
45
- // 1. Resolve priority attrs (lowest precedence defaults).
46
- const prioritizedAttrs = hasPriorityAttrs
47
- ? calculatePriorityAttrs([filteredProps])
48
- : null
49
- // 2. Resolve normal attrs — these see priority + explicit props as input.
50
- const finalAttrs = hasAttrs
51
- ? calculateAttrs([
52
- prioritizedAttrs
53
- ? { ...prioritizedAttrs, ...filteredProps }
54
- : filteredProps,
55
- ])
56
- : null
57
-
58
- // 3. Merge: priority < normal attrs < explicit props (last wins).
59
- const finalProps = {
60
- ...prioritizedAttrs,
61
- ...finalAttrs,
62
- ...filteredProps,
63
- }
64
-
65
- return WrappedComponent(finalProps)
66
- }
67
-
68
- return HOCComponent
69
- }
70
-
71
- return attrsHoc
72
- }
73
-
74
- export default createAttrsHOC
package/src/hoc/index.ts DELETED
@@ -1,3 +0,0 @@
1
- import attrsHoc from './attrsHoc'
2
-
3
- export { attrsHoc }
package/src/index.ts DELETED
@@ -1,26 +0,0 @@
1
- import type { Attrs } from './init'
2
- import attrs from './init'
3
- import type { IsAttrsComponent } from './isAttrsComponent'
4
- import isAttrsComponent from './isAttrsComponent'
5
- import type { AttrsComponent } from './types/AttrsComponent'
6
- import type { AttrsCb } from './types/attrs'
7
- import type { AttrsComponentType, ConfigAttrs } from './types/config'
8
- import type { ComposeParam, GenericHoc } from './types/hoc'
9
- import type { ComponentFn, ElementType, TObj } from './types/utils'
10
-
11
- export type {
12
- Attrs,
13
- AttrsCb,
14
- AttrsComponent,
15
- AttrsComponentType,
16
- ComponentFn,
17
- ComposeParam,
18
- ConfigAttrs,
19
- ElementType,
20
- GenericHoc,
21
- IsAttrsComponent,
22
- TObj,
23
- }
24
-
25
- export { attrs, isAttrsComponent }
26
- export default attrs