@diskette/use-render 0.8.0 → 0.9.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 +51 -0
- package/dist/types.d.ts +1 -1
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -136,3 +136,54 @@ These extend the element's native props, adding `render` and (for stateful hooks
|
|
|
136
136
|
- **Ref composition** — refs from consumer, base props, and options are merged
|
|
137
137
|
- **Event handler chaining** — consumer handlers run first, then base handlers
|
|
138
138
|
- **className/style merging** — static values combine; functions receive state and the resolved base value as parameters
|
|
139
|
+
|
|
140
|
+
## Ref Composition
|
|
141
|
+
|
|
142
|
+
Component libraries often need internal ref access for focus management, measurements, or imperative APIs—while still letting consumers attach their own refs. The hooks handle this automatically.
|
|
143
|
+
|
|
144
|
+
**Component author:**
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { useRef, useImperativeHandle } from 'react'
|
|
148
|
+
import { useRender, ComponentProps } from '@diskette/use-render'
|
|
149
|
+
|
|
150
|
+
type State = { open: boolean }
|
|
151
|
+
type ComboboxProps = ComponentProps<'input', State>
|
|
152
|
+
|
|
153
|
+
export interface ComboboxRef {
|
|
154
|
+
focus: () => void
|
|
155
|
+
clear: () => void
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function Combobox({ ref, ...props }: ComboboxProps & { ref?: React.Ref<ComboboxRef> }) {
|
|
159
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
160
|
+
const state: State = { open: false }
|
|
161
|
+
|
|
162
|
+
// Expose imperative API to consumers
|
|
163
|
+
useImperativeHandle(ref, () => ({
|
|
164
|
+
focus: () => inputRef.current?.focus(),
|
|
165
|
+
clear: () => {
|
|
166
|
+
if (inputRef.current) inputRef.current.value = ''
|
|
167
|
+
},
|
|
168
|
+
}))
|
|
169
|
+
|
|
170
|
+
// Internal ref composes with any ref passed through props
|
|
171
|
+
return useRender('input', state, { props, ref: inputRef })
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Consumer:**
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
179
|
+
const comboboxRef = useRef<ComboboxRef>(null)
|
|
180
|
+
|
|
181
|
+
// Direct element access
|
|
182
|
+
<Combobox ref={inputRef} />
|
|
183
|
+
|
|
184
|
+
// Imperative handle access
|
|
185
|
+
<Combobox ref={comboboxRef} />
|
|
186
|
+
comboboxRef.current?.focus()
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The `options.ref` parameter accepts a single ref or an array of refs. All refs—from `options.ref`, `baseProps.ref`, and consumer `props.ref`—are composed into a single callback ref that updates all sources and handles cleanup.
|
package/dist/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type Style<State> = ((state: State, baseStyle?: CSSProperties) => CSSProp
|
|
|
4
4
|
export type ComponentRenderer<S> = (props: HTMLAttributes<any> & {
|
|
5
5
|
ref?: Ref<any> | undefined;
|
|
6
6
|
}, state: S) => ReactNode;
|
|
7
|
-
export type DataAttributes = Record<`data-${string}`, string | number | boolean>;
|
|
7
|
+
export type DataAttributes = Record<`data-${string}`, string | number | boolean | undefined>;
|
|
8
8
|
export type BaseComponentProps<T extends ElementType> = Omit<ComponentPropsWithRef<T>, 'children' | 'className' | 'style'>;
|
|
9
9
|
export type ComponentProps<T extends ElementType, S> = BaseComponentProps<T> & {
|
|
10
10
|
children?: ReactNode | {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diskette/use-render",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.0",
|
|
5
5
|
"exports": "./dist/index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
@@ -21,18 +21,18 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@changesets/cli": "^2.29.
|
|
25
|
-
"@types/react": "^19.2.
|
|
24
|
+
"@changesets/cli": "^2.29.8",
|
|
25
|
+
"@types/react": "^19.2.7",
|
|
26
26
|
"@types/react-dom": "^19.2.3",
|
|
27
|
-
"@vitejs/plugin-react": "^5.1.
|
|
28
|
-
"@vitest/browser-playwright": "^4.0.
|
|
29
|
-
"oxlint": "^1.
|
|
30
|
-
"oxlint-tsgolint": "^0.8.
|
|
31
|
-
"prettier": "^3.
|
|
32
|
-
"react": "^19.2.
|
|
33
|
-
"react-dom": "^19.2.
|
|
27
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
28
|
+
"@vitest/browser-playwright": "^4.0.15",
|
|
29
|
+
"oxlint": "^1.32.0",
|
|
30
|
+
"oxlint-tsgolint": "^0.8.6",
|
|
31
|
+
"prettier": "^3.7.4",
|
|
32
|
+
"react": "^19.2.3",
|
|
33
|
+
"react-dom": "^19.2.3",
|
|
34
34
|
"typescript": "^5.9.3",
|
|
35
|
-
"vitest": "^4.0.
|
|
35
|
+
"vitest": "^4.0.15",
|
|
36
36
|
"vitest-browser-react": "^2.0.2"
|
|
37
37
|
},
|
|
38
38
|
"description": "_description_",
|