@pyreon/attrs 0.22.0 → 0.23.0
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 +61 -91
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,102 +1,81 @@
|
|
|
1
1
|
# @pyreon/attrs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Chainable HOC factory for default props, base swaps, composition, and statics.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`@pyreon/attrs` wraps a Pyreon component in an immutable, chainable builder that accumulates default props (`.attrs()`), reconfigures the base component (`.config()`), composes additional HOCs (`.compose()`), and attaches static metadata (`.statics()`). Every chain method returns a new component — the original is never mutated — and TypeScript generics accumulate so prop types stay correct after each `.attrs<P>({...})` call. It's the foundation `@pyreon/rocketstyle` builds on; you'll also use it directly when you want default-prop composition without the dimension-styling layer.
|
|
6
6
|
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
- **Immutable chaining** — every method returns a new component, never mutates the original
|
|
10
|
-
- **Props merge order** — `priorityAttrs` > `attrs` > explicit props, with full control over precedence
|
|
11
|
-
- **Prop filtering** — strip internal props before they reach the DOM
|
|
12
|
-
- **HOC composition** — named HOCs via `.compose()` with selective removal
|
|
13
|
-
- **Static metadata** — attach and access custom data via `.statics()` / `.meta`
|
|
14
|
-
- **TypeScript inference** — generics accumulate through the chain
|
|
15
|
-
|
|
16
|
-
## Installation
|
|
7
|
+
## Install
|
|
17
8
|
|
|
18
9
|
```bash
|
|
19
|
-
bun add @pyreon/attrs
|
|
10
|
+
bun add @pyreon/attrs @pyreon/core @pyreon/ui-core
|
|
20
11
|
```
|
|
21
12
|
|
|
22
|
-
## Quick
|
|
13
|
+
## Quick start
|
|
23
14
|
|
|
24
|
-
```
|
|
15
|
+
```tsx
|
|
25
16
|
import attrs from '@pyreon/attrs'
|
|
26
17
|
import { Element } from '@pyreon/elements'
|
|
27
18
|
|
|
28
|
-
const Button = attrs({ name: 'Button', component: Element })
|
|
29
|
-
tag: 'button',
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
})
|
|
19
|
+
const Button = attrs({ name: 'Button', component: Element })
|
|
20
|
+
.attrs({ tag: 'button', alignX: 'center', alignY: 'center' })
|
|
21
|
+
.attrs<{ primary?: boolean }>(({ primary }) => ({
|
|
22
|
+
backgroundColor: primary ? 'blue' : 'gray',
|
|
23
|
+
}))
|
|
33
24
|
|
|
34
|
-
// Renders Element with
|
|
35
|
-
Button
|
|
25
|
+
// Renders Element with the accumulated defaults
|
|
26
|
+
<Button label="Click me" />
|
|
36
27
|
|
|
37
|
-
// Explicit props override attrs defaults
|
|
38
|
-
Button
|
|
28
|
+
// Explicit props override .attrs() defaults (unless `priority: true`)
|
|
29
|
+
<Button tag="a" href="/x" label="Link button" />
|
|
39
30
|
```
|
|
40
31
|
|
|
41
32
|
## API
|
|
42
33
|
|
|
43
|
-
### attrs(
|
|
44
|
-
|
|
45
|
-
Creates an attrs-enhanced component.
|
|
34
|
+
### `attrs({ name, component })`
|
|
46
35
|
|
|
47
|
-
|
|
48
|
-
const Component = attrs({
|
|
49
|
-
name: 'ComponentName', // required — sets displayName
|
|
50
|
-
component: BaseComponent, // required — the Pyreon component to wrap
|
|
51
|
-
})
|
|
52
|
-
```
|
|
36
|
+
Factory entry. Returns a Pyreon `ComponentFn` enhanced with chainable methods. Both `name` (used as `displayName` and a dev `data-attrs` attribute) and `component` (the base) are required — dev mode throws on missing values.
|
|
53
37
|
|
|
54
|
-
###
|
|
38
|
+
### `.attrs(props | callback, options?)`
|
|
55
39
|
|
|
56
|
-
Add default props.
|
|
40
|
+
Add default props. Call multiple times — defaults stack left-to-right in the chain.
|
|
57
41
|
|
|
58
42
|
```ts
|
|
59
|
-
// Object form
|
|
43
|
+
// Object form
|
|
60
44
|
Button.attrs({ tag: 'button' })
|
|
61
45
|
|
|
62
|
-
// Callback form —
|
|
63
|
-
Button.attrs((props) => ({
|
|
46
|
+
// Callback form — receives the current resolved props
|
|
47
|
+
Button.attrs<{ label: string }>((props) => ({
|
|
64
48
|
'aria-label': props.label,
|
|
65
49
|
}))
|
|
66
50
|
|
|
67
|
-
// Priority —
|
|
51
|
+
// Priority — wins over EXPLICIT props at the call site
|
|
68
52
|
Button.attrs({ tag: 'button' }, { priority: true })
|
|
69
53
|
|
|
70
|
-
// Filter —
|
|
54
|
+
// Filter — strip these prop names before forwarding to the base
|
|
71
55
|
Button.attrs({}, { filter: ['internalFlag', 'variant'] })
|
|
72
56
|
```
|
|
73
57
|
|
|
74
|
-
**
|
|
58
|
+
**Merge order at render time:**
|
|
75
59
|
|
|
76
60
|
```text
|
|
77
|
-
priorityAttrs
|
|
61
|
+
priorityAttrs → attrs → explicit props → filterAttrs strips → base component
|
|
78
62
|
```
|
|
79
63
|
|
|
80
|
-
For regular attrs
|
|
64
|
+
For regular `attrs`, explicit props win. For `priorityAttrs`, the priority value wins (used by `rocketstyle` to lock structural props like `tag`).
|
|
81
65
|
|
|
82
|
-
###
|
|
66
|
+
### `.config({ name?, component?, DEBUG? })`
|
|
83
67
|
|
|
84
|
-
|
|
68
|
+
Swap the underlying component, rename, or toggle dev debugging. Returns a new instance.
|
|
85
69
|
|
|
86
70
|
```ts
|
|
87
|
-
|
|
88
|
-
Button.config({ name: 'PrimaryButton' })
|
|
89
|
-
|
|
90
|
-
// Swap the base component
|
|
91
|
-
Button.config({ component: AnotherComponent })
|
|
92
|
-
|
|
93
|
-
// Enable debug mode — adds data-attrs attribute in development
|
|
94
|
-
Button.config({ DEBUG: true })
|
|
71
|
+
const Anchor = Button.config({ component: 'a', name: 'Anchor' })
|
|
95
72
|
```
|
|
96
73
|
|
|
97
|
-
|
|
74
|
+
**Gotcha**: swapping `component` resets the `attrs` / `priorityAttrs` / `filterAttrs` / `compose` chains because they were tailored to the previous component's prop shape (applying them blindly leaks invalid attrs to the DOM). `theme` / `styles` / dimension chains are preserved. Re-chain shared attrs explicitly if you need them.
|
|
98
75
|
|
|
99
|
-
|
|
76
|
+
### `.compose({ hocName: hocFn })`
|
|
77
|
+
|
|
78
|
+
Attach named HOCs to the chain. Applied in registration order — outermost wraps first. Pass `null` to remove a previously composed HOC.
|
|
100
79
|
|
|
101
80
|
```ts
|
|
102
81
|
const Enhanced = Button.compose({
|
|
@@ -104,72 +83,63 @@ const Enhanced = Button.compose({
|
|
|
104
83
|
withTracking: trackingHoc,
|
|
105
84
|
})
|
|
106
85
|
|
|
107
|
-
|
|
108
|
-
const WithoutTracking = Enhanced.compose({ withTracking: null })
|
|
86
|
+
const NoTracking = Enhanced.compose({ withTracking: null })
|
|
109
87
|
```
|
|
110
88
|
|
|
111
|
-
###
|
|
89
|
+
### `.statics({ key: value })`
|
|
112
90
|
|
|
113
|
-
Attach metadata
|
|
91
|
+
Attach arbitrary metadata on `.meta`. Used by `@pyreon/document-primitives` (`_documentType`) and other systems that need post-construction component introspection.
|
|
114
92
|
|
|
115
93
|
```ts
|
|
116
|
-
const
|
|
94
|
+
const Btn = attrs({ name: 'Btn', component: Element }).statics({
|
|
117
95
|
category: 'action',
|
|
118
96
|
sizes: ['sm', 'md', 'lg'],
|
|
119
97
|
})
|
|
120
98
|
|
|
121
|
-
|
|
122
|
-
Button.meta.sizes // => ['sm', 'md', 'lg']
|
|
99
|
+
Btn.meta.category // 'action'
|
|
123
100
|
```
|
|
124
101
|
|
|
125
|
-
###
|
|
102
|
+
### `.getDefaultAttrs()`
|
|
126
103
|
|
|
127
|
-
|
|
104
|
+
Resolve the accumulated default props (calls every `.attrs()` callback with `{}`).
|
|
128
105
|
|
|
129
106
|
```ts
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
isAttrsComponent(Button) // => true
|
|
133
|
-
isAttrsComponent('div') // => false
|
|
107
|
+
Button.getDefaultAttrs() // { tag: 'button', alignX: 'center', alignY: 'center' }
|
|
134
108
|
```
|
|
135
109
|
|
|
136
|
-
###
|
|
110
|
+
### `isAttrsComponent(value)`
|
|
137
111
|
|
|
138
|
-
|
|
112
|
+
Runtime guard — returns `true` for components produced by `attrs()`.
|
|
139
113
|
|
|
140
114
|
```ts
|
|
141
|
-
|
|
115
|
+
import { isAttrsComponent } from '@pyreon/attrs'
|
|
116
|
+
isAttrsComponent(Button) // true
|
|
117
|
+
isAttrsComponent('div') // false
|
|
142
118
|
```
|
|
143
119
|
|
|
144
120
|
## TypeScript
|
|
145
121
|
|
|
146
|
-
Each `.attrs<P>()`
|
|
122
|
+
Each `.attrs<P>()` generic accumulates into the component's prop type. Three type-only properties expose the accumulated shapes:
|
|
147
123
|
|
|
148
124
|
```ts
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
size?: 'sm' | 'md' | 'lg'
|
|
153
|
-
}>({})
|
|
154
|
-
|
|
155
|
-
// Typed accepts: Element props + { variant, size? }
|
|
156
|
-
Typed({ variant: 'secondary', size: 'lg', label: 'Hello' })
|
|
125
|
+
type AllProps = typeof Button.$$types // origin + extended
|
|
126
|
+
type OriginProps = typeof Button.$$originTypes // base component's props
|
|
127
|
+
type ExtendedProps = typeof Button.$$extendedTypes // everything added through .attrs<P>()
|
|
157
128
|
```
|
|
158
129
|
|
|
159
|
-
|
|
130
|
+
Use `ExtractProps<typeof Button>` from `@pyreon/core` to recover the union when forwarding through another HOC.
|
|
160
131
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
132
|
+
## Gotchas
|
|
133
|
+
|
|
134
|
+
- **`.config({ component })` resets the prop chains.** Re-chain shared attrs explicitly if you swap the base. `theme` / `styles` / dimension chains survive.
|
|
135
|
+
- **Defaults are merged, not deep-merged.** Object-valued props (e.g. `style={{ color: 'red' }}`) get replaced, not combined.
|
|
136
|
+
- **The dev `data-attrs` attribute is added in dev builds** to aid debugging. Tree-shaken in production (gated on `process.env.NODE_ENV !== 'production'`).
|
|
137
|
+
- **`hoistNonReactStatics`** copies non-React statics from the base onto the wrapper, so `MyComponent.someStaticMethod` survives the HOC chain.
|
|
138
|
+
- **Generic accumulation has a depth limit** — TypeScript's recursive conditional-type inference caps at ~24-50 levels depending on the host environment. If you stack `.attrs<P>()` calls past that, narrow generics or split the component.
|
|
166
139
|
|
|
167
|
-
##
|
|
140
|
+
## Documentation
|
|
168
141
|
|
|
169
|
-
|
|
170
|
-
| --------------- | -------- |
|
|
171
|
-
| @pyreon/core | >= 0.0.1 |
|
|
172
|
-
| @pyreon/ui-core | >= 0.0.1 |
|
|
142
|
+
Full docs: [docs.pyreon.dev/docs/attrs](https://docs.pyreon.dev/docs/attrs) (or `docs/docs/attrs.md` in this repo).
|
|
173
143
|
|
|
174
144
|
## License
|
|
175
145
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/attrs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Attrs HOC chaining for Pyreon components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
"typecheck": "tsc --noEmit"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@pyreon/typescript": "^0.
|
|
45
|
-
"@vitus-labs/tools-rolldown": "^2.
|
|
44
|
+
"@pyreon/typescript": "^0.23.0",
|
|
45
|
+
"@vitus-labs/tools-rolldown": "^2.4.0"
|
|
46
46
|
},
|
|
47
47
|
"engines": {
|
|
48
48
|
"node": ">= 22"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@pyreon/core": "^0.
|
|
52
|
-
"@pyreon/ui-core": "^0.
|
|
51
|
+
"@pyreon/core": "^0.23.0",
|
|
52
|
+
"@pyreon/ui-core": "^0.23.0"
|
|
53
53
|
}
|
|
54
54
|
}
|