@bento/use-props 0.1.1 → 0.2.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 +28 -1
- package/README.mdx +28 -1
- package/dist/index.cjs +38 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +38 -7
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
- package/src/index.ts +77 -12
package/README.md
CHANGED
|
@@ -32,7 +32,34 @@ function Component(args) {
|
|
|
32
32
|
As seen from the example above, the `useProps` hook takes two
|
|
33
33
|
arguments:
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
### Forward Refs
|
|
36
|
+
|
|
37
|
+
The `useProps` hook also accepts an optional third parameter for forwarded refs. When provided, it merges the forwarded ref with any refs from props and slots:
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
import { useProps } from '@bento/use-props';
|
|
41
|
+
import { withSlots } from '@bento/slots';
|
|
42
|
+
import React from 'react';
|
|
43
|
+
|
|
44
|
+
const Button = withSlots(
|
|
45
|
+
'Button',
|
|
46
|
+
React.forwardRef(function ButtonComponent(args, forwardedRef) {
|
|
47
|
+
const { props, apply, ref } = useProps(args, {}, forwardedRef);
|
|
48
|
+
|
|
49
|
+
// ref contains the merged reference from forwardedRef, props.ref, and slots
|
|
50
|
+
return <button {...apply({}, ['ref'])} ref={ref}>{props.children}</button>;
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The merged `ref` combines:
|
|
56
|
+
- The forwarded ref from the parent component
|
|
57
|
+
- Any ref passed through props
|
|
58
|
+
- Any ref supplied via slots
|
|
59
|
+
|
|
60
|
+
All refs are properly merged using `mergeRefs` from `@react-aria/utils`, ensuring all refs are called/updated when the element is mounted.
|
|
61
|
+
|
|
62
|
+
The `useProps` hook returns an object with three properties:
|
|
36
63
|
|
|
37
64
|
### props
|
|
38
65
|
|
package/README.mdx
CHANGED
|
@@ -42,7 +42,34 @@ arguments:
|
|
|
42
42
|
|
|
43
43
|
<ArgTypes of={Stories.useProps} />
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
### Forward Refs
|
|
46
|
+
|
|
47
|
+
The `useProps` hook also accepts an optional third parameter for forwarded refs. When provided, it merges the forwarded ref with any refs from props and slots:
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
import { useProps } from '@bento/use-props';
|
|
51
|
+
import { withSlots } from '@bento/slots';
|
|
52
|
+
import React from 'react';
|
|
53
|
+
|
|
54
|
+
const Button = withSlots(
|
|
55
|
+
'Button',
|
|
56
|
+
React.forwardRef(function ButtonComponent(args, forwardedRef) {
|
|
57
|
+
const { props, apply, ref } = useProps(args, {}, forwardedRef);
|
|
58
|
+
|
|
59
|
+
// ref contains the merged reference from forwardedRef, props.ref, and slots
|
|
60
|
+
return <button {...apply({}, ['ref'])} ref={ref}>{props.children}</button>;
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The merged `ref` combines:
|
|
66
|
+
- The forwarded ref from the parent component
|
|
67
|
+
- Any ref passed through props
|
|
68
|
+
- Any ref supplied via slots
|
|
69
|
+
|
|
70
|
+
All refs are properly merged using `mergeRefs` from `@react-aria/utils`, ensuring all refs are called/updated when the element is mounted.
|
|
71
|
+
|
|
72
|
+
The `useProps` hook returns an object with three properties:
|
|
46
73
|
|
|
47
74
|
<ArgTypes of={Stories.hook} />
|
|
48
75
|
|
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var box = require('@bento/box');
|
|
4
4
|
var internalProps = require('@bento/internal-props');
|
|
5
|
+
var utils = require('@react-aria/utils');
|
|
5
6
|
var react = require('react');
|
|
6
7
|
|
|
7
8
|
// src/index.ts
|
|
@@ -20,41 +21,71 @@ function renderProp(name, args) {
|
|
|
20
21
|
const { props, slots, original } = args;
|
|
21
22
|
return execute(name, slots, args) || execute(name, props, args) || original;
|
|
22
23
|
}
|
|
23
|
-
function
|
|
24
|
+
function mergeRefList(refs) {
|
|
25
|
+
const filtered = refs.filter((ref) => ref != null);
|
|
26
|
+
if (!filtered.length) return void 0;
|
|
27
|
+
if (filtered.length === 1) return filtered[0];
|
|
28
|
+
return utils.mergeRefs(...filtered);
|
|
29
|
+
}
|
|
30
|
+
function useProps(...rest) {
|
|
31
|
+
let forwardedRef;
|
|
32
|
+
let args;
|
|
33
|
+
let state;
|
|
34
|
+
if (Array.isArray(rest[0])) {
|
|
35
|
+
[args, forwardedRef] = rest[0];
|
|
36
|
+
state = rest[1];
|
|
37
|
+
} else {
|
|
38
|
+
[args, state, forwardedRef] = rest;
|
|
39
|
+
}
|
|
24
40
|
const { slots } = react.useContext(box.Box);
|
|
25
41
|
const [props, internal] = internalProps.useInternalProps(args);
|
|
26
42
|
const { namespace, assigned } = slots;
|
|
27
43
|
const dot = namespace.join(".");
|
|
28
44
|
const slotted = assigned[dot] || {};
|
|
29
|
-
const
|
|
45
|
+
const ref = mergeRefList([
|
|
46
|
+
props?.ref,
|
|
47
|
+
slotted?.ref,
|
|
48
|
+
forwardedRef
|
|
49
|
+
]);
|
|
50
|
+
const slotNoRef = { ...slotted };
|
|
51
|
+
const propsNoRef = { ...props };
|
|
52
|
+
delete slotNoRef.ref;
|
|
53
|
+
delete propsNoRef.ref;
|
|
54
|
+
const propsy = { ...internal, ...propsNoRef, ...slotNoRef };
|
|
30
55
|
function apply(attributes, except) {
|
|
31
56
|
const data = attributes || propsy;
|
|
32
57
|
const returned = {};
|
|
33
58
|
function reduce(memo, key) {
|
|
34
59
|
if (except && except.includes(key)) return memo;
|
|
35
60
|
memo[key] = renderProp(key, {
|
|
61
|
+
props: { ...props, ...internal },
|
|
36
62
|
original: data[key],
|
|
37
63
|
slots: slotted,
|
|
38
|
-
props: { ...props, ...internal },
|
|
39
64
|
state
|
|
40
65
|
});
|
|
41
66
|
return memo;
|
|
42
67
|
}
|
|
43
|
-
|
|
44
|
-
|
|
68
|
+
let result;
|
|
69
|
+
if (!attributes) result = Object.keys(propsy).reduce(reduce, returned);
|
|
70
|
+
else result = Object.keys(propsy).reduce(reduce, Object.keys(attributes).reduce(reduce, returned));
|
|
71
|
+
if (except && except.includes("ref")) return result;
|
|
72
|
+
if (ref) result.ref = ref;
|
|
73
|
+
return result;
|
|
45
74
|
}
|
|
46
75
|
return {
|
|
47
76
|
props: new Proxy(propsy, {
|
|
48
77
|
get: function getter(_, name) {
|
|
78
|
+
if (name === "ref") return ref;
|
|
49
79
|
return renderProp(name, {
|
|
50
80
|
original: isRenderProp(name, props[name]) ? void 0 : props[name],
|
|
51
|
-
slots: slotted,
|
|
52
81
|
props: { ...props, ...internal },
|
|
82
|
+
slots: slotted,
|
|
53
83
|
state
|
|
54
84
|
});
|
|
55
85
|
}
|
|
56
86
|
}),
|
|
57
|
-
apply
|
|
87
|
+
apply,
|
|
88
|
+
ref
|
|
58
89
|
};
|
|
59
90
|
}
|
|
60
91
|
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["useContext","Box","useInternalProps"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Box, type BoxContext } from '@bento/box';\nimport { useInternalProps } from '@bento/internal-props';\nimport { AnyObject } from '@bento/types';\nimport { useContext } from 'react';\n\nexport interface RenderPropData {\n /**\n * If the component is assigning a default value to the given prop,\n * the original value be a reference to the previous assigned value.\n * @default The original assigned value\n */\n original?: unknown;\n\n /** All the props that were passed to the component. @default {} */\n props: AnyObject;\n\n /** If slots are used to modify the component, this will contain a reference to the original slots object. @default {} */\n slots: AnyObject;\n\n /** The exposed state of the component. @default {} */\n state: AnyObject;\n}\n\n/**\n * Checks if the given string is an event listener name. An event listener name\n * is defined as a string that starts with \"on\" followed by an uppercase letter.\n *\n * @param name - The string to check.\n * @returns `true` if the string is an event listener name, otherwise `false`.\n * @private\n */\nexport function isEventListener(name: string): boolean {\n return /^on[A-Z]/.test(name);\n}\n\n/**\n * Determines if a given value is a render prop.\n *\n * A render prop is a function that is used to dynamically generate\n * UI elements. This function checks if the provided value is a function\n * and not an event listener.\n *\n * @param name - The name of the prop.\n * @param value - The value of the prop to check.\n * @returns `true` if the value is a render prop, otherwise `false`.\n */\nexport function isRenderProp(name: string, value: any): boolean {\n return typeof value === 'function' && !isEventListener(name);\n}\n\n/**\n * Executes a function or returns a value from the given data object.\n *\n * @param name - The name of the property to execute or retrieve.\n * @param data - The data object containing the property.\n * @param args - The arguments to pass if the property is a function.\n * @returns The result of the function execution or the value of the property.\n * @private\n */\nexport function execute(name: string, data: AnyObject, args: RenderPropData): any {\n const value = data[name];\n\n if (isRenderProp(name, value)) return value(args);\n return value;\n}\n\n/**\n * Retrieves a property value, potentially overridden by a slotted value.\n *\n * @param name - The name of the property to retrieve.\n * @param options.props - The original properties.\n * @param options.slots - The slotted values that can override the original properties.\n * @param options.state - The current state.\n * @param options.original - The original value of the property.\n * @returns The original property value or the overridden value if provided.\n */\nexport function renderProp(name: string, args: RenderPropData): any {\n const { props, slots, original } = args;\n\n return execute(name, slots, args) || execute(name, props, args) || original;\n}\n\nexport interface Returns {\n /**\n * Proxy object that have access to the original props, slotted values, and internal props. When\n * accessing a property, it will first check the slotted values, then the original props, and finally\n * the internal props. If the property is a render prop, it will execute the function with the provided\n * arguments.\n *\n * @default { ...props, ...slots }\n */\n props: AnyObject;\n /**\n * Applies the given attributes as default values to the props. If no attributes are provided, it will\n * use the props as default values. The resulting object will contain all the properties of the props,\n * except for the ones specified in the `except` array. The values of the properties will be the result\n * of executing the render prop function with the provided arguments.\n *\n * @default function(attr)\n */\n apply: (attributes?: object, except?: string[]) => object;\n}\n\n/**\n * Hook that merges props with slotted props and provides a proxy for accessing them.\n *\n * @param args - The initial props to use.\n * @param state - The state object to use.\n * @returns An object containing the proxy based props object and the apply function.\n * @throws {BentoError} If the hook is used outside of a @bento/slots component.\n *\n * @example\n * const { props, apply } = useProps({ foo: 'bar' });\n * if (props.a) doSomething()\n * return <a {...apply({ className: 'foo' }) }>{ props.children }</a>;\n */\nexport function useProps(args: AnyObject, state: object = {}): Returns {\n const { slots } = useContext<BoxContext<AnyObject>>(Box);\n const [props, internal] = useInternalProps(args);\n const { namespace, assigned } = slots;\n const dot = namespace.join('.');\n const slotted = assigned[dot] || {};\n const propsy = { ...internal, ...props, ...slotted };\n\n /**\n * Applies the given attributes to an object.\n *\n * @param attributes - The attributes to apply. If not provided, defaults to `propsy`.\n * @param except - An array of keys to exclude from the resulting object.\n * @returns The resulting object with applied attributes.\n * @public\n */\n function apply(attributes?: object, except?: string[]): object {\n const data = attributes || propsy;\n const returned = {};\n\n function reduce(memo: AnyObject, key: string) {\n if (except && except.includes(key)) return memo;\n\n memo[key] = renderProp(key, {\n original: data[key],\n slots: slotted,\n props: { ...props, ...internal },\n state\n });\n\n return memo;\n }\n\n if (!attributes) return Object.keys(propsy).reduce(reduce, returned);\n return Object.keys(propsy).reduce(reduce, Object.keys(attributes).reduce(reduce, returned));\n }\n\n return {\n props: new Proxy(propsy, {\n get: function getter(_: object, name: string) {\n return renderProp(name, {\n original: isRenderProp(name, props[name]) ? undefined : props[name],\n slots: slotted,\n props: { ...props, ...internal },\n state\n });\n }\n }),\n apply\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["mergeRefs","useContext","Box","useInternalProps"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Box, type BoxContext } from '@bento/box';\nimport { useInternalProps } from '@bento/internal-props';\nimport { AnyObject } from '@bento/types';\nimport { mergeRefs } from '@react-aria/utils';\nimport { useContext } from 'react';\nimport type { ForwardedRef, Ref } from 'react';\n\nexport interface RenderPropData {\n /**\n * If the component is assigning a default value to the given prop,\n * the original value be a reference to the previous assigned value.\n * @default The original assigned value\n */\n original?: unknown;\n\n /** All the props that were passed to the component. @default {} */\n props: AnyObject;\n\n /** If slots are used to modify the component, this will contain a reference to the original slots object. @default {} */\n slots: AnyObject;\n\n /** The exposed state of the component. @default {} */\n state: AnyObject;\n}\n\n/**\n * Checks if the given string is an event listener name. An event listener name\n * is defined as a string that starts with \"on\" followed by an uppercase letter.\n *\n * @param name - The string to check.\n * @returns `true` if the string is an event listener name, otherwise `false`.\n * @private\n */\nexport function isEventListener(name: string): boolean {\n return /^on[A-Z]/.test(name);\n}\n\n/**\n * Determines if a given value is a render prop.\n *\n * A render prop is a function that is used to dynamically generate\n * UI elements. This function checks if the provided value is a function\n * and not an event listener.\n *\n * @param name - The name of the prop.\n * @param value - The value of the prop to check.\n * @returns `true` if the value is a render prop, otherwise `false`.\n */\nexport function isRenderProp(name: string, value: any): boolean {\n return typeof value === 'function' && !isEventListener(name);\n}\n\n/**\n * Executes a function or returns a value from the given data object.\n *\n * @param name - The name of the property to execute or retrieve.\n * @param data - The data object containing the property.\n * @param args - The arguments to pass if the property is a function.\n * @returns The result of the function execution or the value of the property.\n * @private\n */\nexport function execute(name: string, data: AnyObject, args: RenderPropData): any {\n const value = data[name];\n\n if (isRenderProp(name, value)) return value(args);\n return value;\n}\n\n/**\n * Retrieves a property value, potentially overridden by a slotted value.\n *\n * @param name - The name of the property to retrieve.\n * @param options.props - The original properties.\n * @param options.slots - The slotted values that can override the original properties.\n * @param options.state - The current state.\n * @param options.original - The original value of the property.\n * @returns The original property value or the overridden value if provided.\n */\nexport function renderProp(name: string, args: RenderPropData): any {\n const { props, slots, original } = args;\n\n return execute(name, slots, args) || execute(name, props, args) || original;\n}\n\n/**\n * Merges multiple refs into a single ref callback.\n *\n * @param refs - Array of refs to merge (can include undefined/null).\n * @returns A single merged ref, undefined if no refs provided.\n * @private\n */\nfunction mergeRefList(refs: Array<ForwardedRef<any> | Ref<any> | undefined>): Ref<any> | undefined {\n const filtered = refs.filter((ref): ref is ForwardedRef<any> | Ref<any> => ref != null);\n\n if (!filtered.length) return undefined;\n if (filtered.length === 1) return filtered[0] as Ref<any>;\n\n return mergeRefs(...(filtered as Array<Ref<any>>)) as Ref<any>;\n}\n\nexport interface Returns {\n /**\n * Proxy object that have access to the original props, slotted values, and internal props. When\n * accessing a property, it will first check the slotted values, then the original props, and finally\n * the internal props. If the property is a render prop, it will execute the function with the provided\n * arguments.\n *\n * @default { ...props, ...slots }\n */\n props: AnyObject;\n /**\n * Applies the given attributes as default values to the props. If no attributes are provided, it will\n * use the props as default values. The resulting object will contain all the properties of the props,\n * except for the ones specified in the `except` array. The values of the properties will be the result\n * of executing the render prop function with the provided arguments.\n *\n * @default function(attr)\n */\n apply: (attributes?: object, except?: string[]) => object;\n /**\n * The merged ref combining forwarded refs with any ref supplied via slots.\n */\n ref?: Ref<any>;\n}\n\n/**\n * Hook that merges props with slotted props and provides a proxy for accessing them.\n *\n * @param args - The initial props to use, or array when using rest parameters.\n * @param state - The state object to use.\n * @param forwardedRef - The ref forwarded to the component.\n * @returns An object containing the proxy based props object and the apply function.\n * @throws {BentoError} If the hook is used outside of a @bento/slots component.\n *\n * @example\n * const { props, apply, ref } = useProps({ foo: 'bar' }, {}, forwardedRef);\n * if (props.a) doSomething()\n * return <a {...apply({ className: 'foo' }, ['ref']) } ref={ref}>{ props.children }</a>;\n *\n * @example\n * // With rest parameters\n * function Component(...rest) {\n * const { props, apply, ref } = useProps(rest);\n * return <div {...apply()} />;\n * }\n */\nexport function useProps(...rest: any[]): Returns {\n let forwardedRef: ForwardedRef<any> | undefined;\n let args: AnyObject;\n let state: object;\n\n if (Array.isArray(rest[0])) {\n [args, forwardedRef] = rest[0];\n state = rest[1];\n } else {\n [args, state, forwardedRef] = rest;\n }\n\n const { slots } = useContext<BoxContext<AnyObject>>(Box);\n const [props, internal] = useInternalProps(args);\n const { namespace, assigned } = slots;\n const dot = namespace.join('.');\n const slotted = assigned[dot] || {};\n\n const ref = mergeRefList([\n (props as AnyObject)?.ref as ForwardedRef<any> | Ref<any> | undefined,\n (slotted as AnyObject)?.ref as ForwardedRef<any> | Ref<any> | undefined,\n forwardedRef\n ]);\n\n const slotNoRef = { ...slotted } as AnyObject;\n const propsNoRef = { ...props } as AnyObject;\n\n delete slotNoRef.ref;\n delete propsNoRef.ref;\n\n const propsy: AnyObject = { ...internal, ...propsNoRef, ...slotNoRef };\n\n /**\n * Applies the given attributes to an object.\n *\n * @param attributes - The attributes to apply. If not provided, defaults to `propsy`.\n * @param except - An array of keys to exclude from the resulting object.\n * @returns The resulting object with applied attributes.\n * @public\n */\n function apply(attributes?: object, except?: string[]): object {\n const data = (attributes || propsy) as AnyObject;\n const returned: AnyObject = {};\n\n function reduce(memo: AnyObject, key: string) {\n if (except && except.includes(key)) return memo;\n\n memo[key] = renderProp(key, {\n props: { ...props, ...internal },\n original: data[key],\n slots: slotted,\n state\n });\n\n return memo;\n }\n\n let result: AnyObject;\n\n if (!attributes) result = Object.keys(propsy).reduce(reduce, returned);\n else result = Object.keys(propsy).reduce(reduce, Object.keys(attributes).reduce(reduce, returned));\n\n // Always include the merged ref unless specifically excluded\n if (except && except.includes('ref')) return result;\n if (ref) result.ref = ref;\n\n return result;\n }\n\n return {\n props: new Proxy(propsy, {\n get: function getter(_: object, name: string) {\n if (name === 'ref') return ref;\n\n return renderProp(name, {\n original: isRenderProp(name, props[name]) ? undefined : props[name],\n props: { ...props, ...internal },\n slots: slotted,\n state\n });\n }\n }),\n apply,\n ref\n };\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AnyObject } from '@bento/types';
|
|
2
|
+
import { Ref } from 'react';
|
|
2
3
|
|
|
3
4
|
interface RenderPropData {
|
|
4
5
|
/**
|
|
@@ -75,20 +76,32 @@ interface Returns {
|
|
|
75
76
|
* @default function(attr)
|
|
76
77
|
*/
|
|
77
78
|
apply: (attributes?: object, except?: string[]) => object;
|
|
79
|
+
/**
|
|
80
|
+
* The merged ref combining forwarded refs with any ref supplied via slots.
|
|
81
|
+
*/
|
|
82
|
+
ref?: Ref<any>;
|
|
78
83
|
}
|
|
79
84
|
/**
|
|
80
85
|
* Hook that merges props with slotted props and provides a proxy for accessing them.
|
|
81
86
|
*
|
|
82
|
-
* @param args - The initial props to use.
|
|
87
|
+
* @param args - The initial props to use, or array when using rest parameters.
|
|
83
88
|
* @param state - The state object to use.
|
|
89
|
+
* @param forwardedRef - The ref forwarded to the component.
|
|
84
90
|
* @returns An object containing the proxy based props object and the apply function.
|
|
85
91
|
* @throws {BentoError} If the hook is used outside of a @bento/slots component.
|
|
86
92
|
*
|
|
87
93
|
* @example
|
|
88
|
-
* const { props, apply } = useProps({ foo: 'bar' });
|
|
94
|
+
* const { props, apply, ref } = useProps({ foo: 'bar' }, {}, forwardedRef);
|
|
89
95
|
* if (props.a) doSomething()
|
|
90
|
-
* return <a {...apply({ className: 'foo' }) }>{ props.children }</a>;
|
|
96
|
+
* return <a {...apply({ className: 'foo' }, ['ref']) } ref={ref}>{ props.children }</a>;
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* // With rest parameters
|
|
100
|
+
* function Component(...rest) {
|
|
101
|
+
* const { props, apply, ref } = useProps(rest);
|
|
102
|
+
* return <div {...apply()} />;
|
|
103
|
+
* }
|
|
91
104
|
*/
|
|
92
|
-
declare function useProps(
|
|
105
|
+
declare function useProps(...rest: any[]): Returns;
|
|
93
106
|
|
|
94
107
|
export { type RenderPropData, type Returns, execute, isEventListener, isRenderProp, renderProp, useProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AnyObject } from '@bento/types';
|
|
2
|
+
import { Ref } from 'react';
|
|
2
3
|
|
|
3
4
|
interface RenderPropData {
|
|
4
5
|
/**
|
|
@@ -75,20 +76,32 @@ interface Returns {
|
|
|
75
76
|
* @default function(attr)
|
|
76
77
|
*/
|
|
77
78
|
apply: (attributes?: object, except?: string[]) => object;
|
|
79
|
+
/**
|
|
80
|
+
* The merged ref combining forwarded refs with any ref supplied via slots.
|
|
81
|
+
*/
|
|
82
|
+
ref?: Ref<any>;
|
|
78
83
|
}
|
|
79
84
|
/**
|
|
80
85
|
* Hook that merges props with slotted props and provides a proxy for accessing them.
|
|
81
86
|
*
|
|
82
|
-
* @param args - The initial props to use.
|
|
87
|
+
* @param args - The initial props to use, or array when using rest parameters.
|
|
83
88
|
* @param state - The state object to use.
|
|
89
|
+
* @param forwardedRef - The ref forwarded to the component.
|
|
84
90
|
* @returns An object containing the proxy based props object and the apply function.
|
|
85
91
|
* @throws {BentoError} If the hook is used outside of a @bento/slots component.
|
|
86
92
|
*
|
|
87
93
|
* @example
|
|
88
|
-
* const { props, apply } = useProps({ foo: 'bar' });
|
|
94
|
+
* const { props, apply, ref } = useProps({ foo: 'bar' }, {}, forwardedRef);
|
|
89
95
|
* if (props.a) doSomething()
|
|
90
|
-
* return <a {...apply({ className: 'foo' }) }>{ props.children }</a>;
|
|
96
|
+
* return <a {...apply({ className: 'foo' }, ['ref']) } ref={ref}>{ props.children }</a>;
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* // With rest parameters
|
|
100
|
+
* function Component(...rest) {
|
|
101
|
+
* const { props, apply, ref } = useProps(rest);
|
|
102
|
+
* return <div {...apply()} />;
|
|
103
|
+
* }
|
|
91
104
|
*/
|
|
92
|
-
declare function useProps(
|
|
105
|
+
declare function useProps(...rest: any[]): Returns;
|
|
93
106
|
|
|
94
107
|
export { type RenderPropData, type Returns, execute, isEventListener, isRenderProp, renderProp, useProps };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Box } from '@bento/box';
|
|
2
2
|
import { useInternalProps } from '@bento/internal-props';
|
|
3
|
+
import { mergeRefs } from '@react-aria/utils';
|
|
3
4
|
import { useContext } from 'react';
|
|
4
5
|
|
|
5
6
|
// src/index.ts
|
|
@@ -18,41 +19,71 @@ function renderProp(name, args) {
|
|
|
18
19
|
const { props, slots, original } = args;
|
|
19
20
|
return execute(name, slots, args) || execute(name, props, args) || original;
|
|
20
21
|
}
|
|
21
|
-
function
|
|
22
|
+
function mergeRefList(refs) {
|
|
23
|
+
const filtered = refs.filter((ref) => ref != null);
|
|
24
|
+
if (!filtered.length) return void 0;
|
|
25
|
+
if (filtered.length === 1) return filtered[0];
|
|
26
|
+
return mergeRefs(...filtered);
|
|
27
|
+
}
|
|
28
|
+
function useProps(...rest) {
|
|
29
|
+
let forwardedRef;
|
|
30
|
+
let args;
|
|
31
|
+
let state;
|
|
32
|
+
if (Array.isArray(rest[0])) {
|
|
33
|
+
[args, forwardedRef] = rest[0];
|
|
34
|
+
state = rest[1];
|
|
35
|
+
} else {
|
|
36
|
+
[args, state, forwardedRef] = rest;
|
|
37
|
+
}
|
|
22
38
|
const { slots } = useContext(Box);
|
|
23
39
|
const [props, internal] = useInternalProps(args);
|
|
24
40
|
const { namespace, assigned } = slots;
|
|
25
41
|
const dot = namespace.join(".");
|
|
26
42
|
const slotted = assigned[dot] || {};
|
|
27
|
-
const
|
|
43
|
+
const ref = mergeRefList([
|
|
44
|
+
props?.ref,
|
|
45
|
+
slotted?.ref,
|
|
46
|
+
forwardedRef
|
|
47
|
+
]);
|
|
48
|
+
const slotNoRef = { ...slotted };
|
|
49
|
+
const propsNoRef = { ...props };
|
|
50
|
+
delete slotNoRef.ref;
|
|
51
|
+
delete propsNoRef.ref;
|
|
52
|
+
const propsy = { ...internal, ...propsNoRef, ...slotNoRef };
|
|
28
53
|
function apply(attributes, except) {
|
|
29
54
|
const data = attributes || propsy;
|
|
30
55
|
const returned = {};
|
|
31
56
|
function reduce(memo, key) {
|
|
32
57
|
if (except && except.includes(key)) return memo;
|
|
33
58
|
memo[key] = renderProp(key, {
|
|
59
|
+
props: { ...props, ...internal },
|
|
34
60
|
original: data[key],
|
|
35
61
|
slots: slotted,
|
|
36
|
-
props: { ...props, ...internal },
|
|
37
62
|
state
|
|
38
63
|
});
|
|
39
64
|
return memo;
|
|
40
65
|
}
|
|
41
|
-
|
|
42
|
-
|
|
66
|
+
let result;
|
|
67
|
+
if (!attributes) result = Object.keys(propsy).reduce(reduce, returned);
|
|
68
|
+
else result = Object.keys(propsy).reduce(reduce, Object.keys(attributes).reduce(reduce, returned));
|
|
69
|
+
if (except && except.includes("ref")) return result;
|
|
70
|
+
if (ref) result.ref = ref;
|
|
71
|
+
return result;
|
|
43
72
|
}
|
|
44
73
|
return {
|
|
45
74
|
props: new Proxy(propsy, {
|
|
46
75
|
get: function getter(_, name) {
|
|
76
|
+
if (name === "ref") return ref;
|
|
47
77
|
return renderProp(name, {
|
|
48
78
|
original: isRenderProp(name, props[name]) ? void 0 : props[name],
|
|
49
|
-
slots: slotted,
|
|
50
79
|
props: { ...props, ...internal },
|
|
80
|
+
slots: slotted,
|
|
51
81
|
state
|
|
52
82
|
});
|
|
53
83
|
}
|
|
54
84
|
}),
|
|
55
|
-
apply
|
|
85
|
+
apply,
|
|
86
|
+
ref
|
|
56
87
|
};
|
|
57
88
|
}
|
|
58
89
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { Box, type BoxContext } from '@bento/box';\nimport { useInternalProps } from '@bento/internal-props';\nimport { AnyObject } from '@bento/types';\nimport { useContext } from 'react';\n\nexport interface RenderPropData {\n /**\n * If the component is assigning a default value to the given prop,\n * the original value be a reference to the previous assigned value.\n * @default The original assigned value\n */\n original?: unknown;\n\n /** All the props that were passed to the component. @default {} */\n props: AnyObject;\n\n /** If slots are used to modify the component, this will contain a reference to the original slots object. @default {} */\n slots: AnyObject;\n\n /** The exposed state of the component. @default {} */\n state: AnyObject;\n}\n\n/**\n * Checks if the given string is an event listener name. An event listener name\n * is defined as a string that starts with \"on\" followed by an uppercase letter.\n *\n * @param name - The string to check.\n * @returns `true` if the string is an event listener name, otherwise `false`.\n * @private\n */\nexport function isEventListener(name: string): boolean {\n return /^on[A-Z]/.test(name);\n}\n\n/**\n * Determines if a given value is a render prop.\n *\n * A render prop is a function that is used to dynamically generate\n * UI elements. This function checks if the provided value is a function\n * and not an event listener.\n *\n * @param name - The name of the prop.\n * @param value - The value of the prop to check.\n * @returns `true` if the value is a render prop, otherwise `false`.\n */\nexport function isRenderProp(name: string, value: any): boolean {\n return typeof value === 'function' && !isEventListener(name);\n}\n\n/**\n * Executes a function or returns a value from the given data object.\n *\n * @param name - The name of the property to execute or retrieve.\n * @param data - The data object containing the property.\n * @param args - The arguments to pass if the property is a function.\n * @returns The result of the function execution or the value of the property.\n * @private\n */\nexport function execute(name: string, data: AnyObject, args: RenderPropData): any {\n const value = data[name];\n\n if (isRenderProp(name, value)) return value(args);\n return value;\n}\n\n/**\n * Retrieves a property value, potentially overridden by a slotted value.\n *\n * @param name - The name of the property to retrieve.\n * @param options.props - The original properties.\n * @param options.slots - The slotted values that can override the original properties.\n * @param options.state - The current state.\n * @param options.original - The original value of the property.\n * @returns The original property value or the overridden value if provided.\n */\nexport function renderProp(name: string, args: RenderPropData): any {\n const { props, slots, original } = args;\n\n return execute(name, slots, args) || execute(name, props, args) || original;\n}\n\nexport interface Returns {\n /**\n * Proxy object that have access to the original props, slotted values, and internal props. When\n * accessing a property, it will first check the slotted values, then the original props, and finally\n * the internal props. If the property is a render prop, it will execute the function with the provided\n * arguments.\n *\n * @default { ...props, ...slots }\n */\n props: AnyObject;\n /**\n * Applies the given attributes as default values to the props. If no attributes are provided, it will\n * use the props as default values. The resulting object will contain all the properties of the props,\n * except for the ones specified in the `except` array. The values of the properties will be the result\n * of executing the render prop function with the provided arguments.\n *\n * @default function(attr)\n */\n apply: (attributes?: object, except?: string[]) => object;\n}\n\n/**\n * Hook that merges props with slotted props and provides a proxy for accessing them.\n *\n * @param args - The initial props to use.\n * @param state - The state object to use.\n * @returns An object containing the proxy based props object and the apply function.\n * @throws {BentoError} If the hook is used outside of a @bento/slots component.\n *\n * @example\n * const { props, apply } = useProps({ foo: 'bar' });\n * if (props.a) doSomething()\n * return <a {...apply({ className: 'foo' }) }>{ props.children }</a>;\n */\nexport function useProps(args: AnyObject, state: object = {}): Returns {\n const { slots } = useContext<BoxContext<AnyObject>>(Box);\n const [props, internal] = useInternalProps(args);\n const { namespace, assigned } = slots;\n const dot = namespace.join('.');\n const slotted = assigned[dot] || {};\n const propsy = { ...internal, ...props, ...slotted };\n\n /**\n * Applies the given attributes to an object.\n *\n * @param attributes - The attributes to apply. If not provided, defaults to `propsy`.\n * @param except - An array of keys to exclude from the resulting object.\n * @returns The resulting object with applied attributes.\n * @public\n */\n function apply(attributes?: object, except?: string[]): object {\n const data = attributes || propsy;\n const returned = {};\n\n function reduce(memo: AnyObject, key: string) {\n if (except && except.includes(key)) return memo;\n\n memo[key] = renderProp(key, {\n original: data[key],\n slots: slotted,\n props: { ...props, ...internal },\n state\n });\n\n return memo;\n }\n\n if (!attributes) return Object.keys(propsy).reduce(reduce, returned);\n return Object.keys(propsy).reduce(reduce, Object.keys(attributes).reduce(reduce, returned));\n }\n\n return {\n props: new Proxy(propsy, {\n get: function getter(_: object, name: string) {\n return renderProp(name, {\n original: isRenderProp(name, props[name]) ? undefined : props[name],\n slots: slotted,\n props: { ...props, ...internal },\n state\n });\n }\n }),\n apply\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { Box, type BoxContext } from '@bento/box';\nimport { useInternalProps } from '@bento/internal-props';\nimport { AnyObject } from '@bento/types';\nimport { mergeRefs } from '@react-aria/utils';\nimport { useContext } from 'react';\nimport type { ForwardedRef, Ref } from 'react';\n\nexport interface RenderPropData {\n /**\n * If the component is assigning a default value to the given prop,\n * the original value be a reference to the previous assigned value.\n * @default The original assigned value\n */\n original?: unknown;\n\n /** All the props that were passed to the component. @default {} */\n props: AnyObject;\n\n /** If slots are used to modify the component, this will contain a reference to the original slots object. @default {} */\n slots: AnyObject;\n\n /** The exposed state of the component. @default {} */\n state: AnyObject;\n}\n\n/**\n * Checks if the given string is an event listener name. An event listener name\n * is defined as a string that starts with \"on\" followed by an uppercase letter.\n *\n * @param name - The string to check.\n * @returns `true` if the string is an event listener name, otherwise `false`.\n * @private\n */\nexport function isEventListener(name: string): boolean {\n return /^on[A-Z]/.test(name);\n}\n\n/**\n * Determines if a given value is a render prop.\n *\n * A render prop is a function that is used to dynamically generate\n * UI elements. This function checks if the provided value is a function\n * and not an event listener.\n *\n * @param name - The name of the prop.\n * @param value - The value of the prop to check.\n * @returns `true` if the value is a render prop, otherwise `false`.\n */\nexport function isRenderProp(name: string, value: any): boolean {\n return typeof value === 'function' && !isEventListener(name);\n}\n\n/**\n * Executes a function or returns a value from the given data object.\n *\n * @param name - The name of the property to execute or retrieve.\n * @param data - The data object containing the property.\n * @param args - The arguments to pass if the property is a function.\n * @returns The result of the function execution or the value of the property.\n * @private\n */\nexport function execute(name: string, data: AnyObject, args: RenderPropData): any {\n const value = data[name];\n\n if (isRenderProp(name, value)) return value(args);\n return value;\n}\n\n/**\n * Retrieves a property value, potentially overridden by a slotted value.\n *\n * @param name - The name of the property to retrieve.\n * @param options.props - The original properties.\n * @param options.slots - The slotted values that can override the original properties.\n * @param options.state - The current state.\n * @param options.original - The original value of the property.\n * @returns The original property value or the overridden value if provided.\n */\nexport function renderProp(name: string, args: RenderPropData): any {\n const { props, slots, original } = args;\n\n return execute(name, slots, args) || execute(name, props, args) || original;\n}\n\n/**\n * Merges multiple refs into a single ref callback.\n *\n * @param refs - Array of refs to merge (can include undefined/null).\n * @returns A single merged ref, undefined if no refs provided.\n * @private\n */\nfunction mergeRefList(refs: Array<ForwardedRef<any> | Ref<any> | undefined>): Ref<any> | undefined {\n const filtered = refs.filter((ref): ref is ForwardedRef<any> | Ref<any> => ref != null);\n\n if (!filtered.length) return undefined;\n if (filtered.length === 1) return filtered[0] as Ref<any>;\n\n return mergeRefs(...(filtered as Array<Ref<any>>)) as Ref<any>;\n}\n\nexport interface Returns {\n /**\n * Proxy object that have access to the original props, slotted values, and internal props. When\n * accessing a property, it will first check the slotted values, then the original props, and finally\n * the internal props. If the property is a render prop, it will execute the function with the provided\n * arguments.\n *\n * @default { ...props, ...slots }\n */\n props: AnyObject;\n /**\n * Applies the given attributes as default values to the props. If no attributes are provided, it will\n * use the props as default values. The resulting object will contain all the properties of the props,\n * except for the ones specified in the `except` array. The values of the properties will be the result\n * of executing the render prop function with the provided arguments.\n *\n * @default function(attr)\n */\n apply: (attributes?: object, except?: string[]) => object;\n /**\n * The merged ref combining forwarded refs with any ref supplied via slots.\n */\n ref?: Ref<any>;\n}\n\n/**\n * Hook that merges props with slotted props and provides a proxy for accessing them.\n *\n * @param args - The initial props to use, or array when using rest parameters.\n * @param state - The state object to use.\n * @param forwardedRef - The ref forwarded to the component.\n * @returns An object containing the proxy based props object and the apply function.\n * @throws {BentoError} If the hook is used outside of a @bento/slots component.\n *\n * @example\n * const { props, apply, ref } = useProps({ foo: 'bar' }, {}, forwardedRef);\n * if (props.a) doSomething()\n * return <a {...apply({ className: 'foo' }, ['ref']) } ref={ref}>{ props.children }</a>;\n *\n * @example\n * // With rest parameters\n * function Component(...rest) {\n * const { props, apply, ref } = useProps(rest);\n * return <div {...apply()} />;\n * }\n */\nexport function useProps(...rest: any[]): Returns {\n let forwardedRef: ForwardedRef<any> | undefined;\n let args: AnyObject;\n let state: object;\n\n if (Array.isArray(rest[0])) {\n [args, forwardedRef] = rest[0];\n state = rest[1];\n } else {\n [args, state, forwardedRef] = rest;\n }\n\n const { slots } = useContext<BoxContext<AnyObject>>(Box);\n const [props, internal] = useInternalProps(args);\n const { namespace, assigned } = slots;\n const dot = namespace.join('.');\n const slotted = assigned[dot] || {};\n\n const ref = mergeRefList([\n (props as AnyObject)?.ref as ForwardedRef<any> | Ref<any> | undefined,\n (slotted as AnyObject)?.ref as ForwardedRef<any> | Ref<any> | undefined,\n forwardedRef\n ]);\n\n const slotNoRef = { ...slotted } as AnyObject;\n const propsNoRef = { ...props } as AnyObject;\n\n delete slotNoRef.ref;\n delete propsNoRef.ref;\n\n const propsy: AnyObject = { ...internal, ...propsNoRef, ...slotNoRef };\n\n /**\n * Applies the given attributes to an object.\n *\n * @param attributes - The attributes to apply. If not provided, defaults to `propsy`.\n * @param except - An array of keys to exclude from the resulting object.\n * @returns The resulting object with applied attributes.\n * @public\n */\n function apply(attributes?: object, except?: string[]): object {\n const data = (attributes || propsy) as AnyObject;\n const returned: AnyObject = {};\n\n function reduce(memo: AnyObject, key: string) {\n if (except && except.includes(key)) return memo;\n\n memo[key] = renderProp(key, {\n props: { ...props, ...internal },\n original: data[key],\n slots: slotted,\n state\n });\n\n return memo;\n }\n\n let result: AnyObject;\n\n if (!attributes) result = Object.keys(propsy).reduce(reduce, returned);\n else result = Object.keys(propsy).reduce(reduce, Object.keys(attributes).reduce(reduce, returned));\n\n // Always include the merged ref unless specifically excluded\n if (except && except.includes('ref')) return result;\n if (ref) result.ref = ref;\n\n return result;\n }\n\n return {\n props: new Proxy(propsy, {\n get: function getter(_: object, name: string) {\n if (name === 'ref') return ref;\n\n return renderProp(name, {\n original: isRenderProp(name, props[name]) ? undefined : props[name],\n props: { ...props, ...internal },\n slots: slotted,\n state\n });\n }\n }),\n apply,\n ref\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bento/use-props",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Context aware props transformation for Bento",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -41,8 +41,11 @@
|
|
|
41
41
|
"package.json"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@bento/box": "^0.
|
|
44
|
+
"@bento/box": "^0.2.0",
|
|
45
45
|
"@bento/internal-props": "^0.1.1",
|
|
46
|
+
"@react-aria/utils": "^3.30.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
46
49
|
"@bento/types": "^0.1.0"
|
|
47
50
|
},
|
|
48
51
|
"peerDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Box, type BoxContext } from '@bento/box';
|
|
2
2
|
import { useInternalProps } from '@bento/internal-props';
|
|
3
3
|
import { AnyObject } from '@bento/types';
|
|
4
|
+
import { mergeRefs } from '@react-aria/utils';
|
|
4
5
|
import { useContext } from 'react';
|
|
6
|
+
import type { ForwardedRef, Ref } from 'react';
|
|
5
7
|
|
|
6
8
|
export interface RenderPropData {
|
|
7
9
|
/**
|
|
@@ -80,6 +82,22 @@ export function renderProp(name: string, args: RenderPropData): any {
|
|
|
80
82
|
return execute(name, slots, args) || execute(name, props, args) || original;
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Merges multiple refs into a single ref callback.
|
|
87
|
+
*
|
|
88
|
+
* @param refs - Array of refs to merge (can include undefined/null).
|
|
89
|
+
* @returns A single merged ref, undefined if no refs provided.
|
|
90
|
+
* @private
|
|
91
|
+
*/
|
|
92
|
+
function mergeRefList(refs: Array<ForwardedRef<any> | Ref<any> | undefined>): Ref<any> | undefined {
|
|
93
|
+
const filtered = refs.filter((ref): ref is ForwardedRef<any> | Ref<any> => ref != null);
|
|
94
|
+
|
|
95
|
+
if (!filtered.length) return undefined;
|
|
96
|
+
if (filtered.length === 1) return filtered[0] as Ref<any>;
|
|
97
|
+
|
|
98
|
+
return mergeRefs(...(filtered as Array<Ref<any>>)) as Ref<any>;
|
|
99
|
+
}
|
|
100
|
+
|
|
83
101
|
export interface Returns {
|
|
84
102
|
/**
|
|
85
103
|
* Proxy object that have access to the original props, slotted values, and internal props. When
|
|
@@ -99,28 +117,64 @@ export interface Returns {
|
|
|
99
117
|
* @default function(attr)
|
|
100
118
|
*/
|
|
101
119
|
apply: (attributes?: object, except?: string[]) => object;
|
|
120
|
+
/**
|
|
121
|
+
* The merged ref combining forwarded refs with any ref supplied via slots.
|
|
122
|
+
*/
|
|
123
|
+
ref?: Ref<any>;
|
|
102
124
|
}
|
|
103
125
|
|
|
104
126
|
/**
|
|
105
127
|
* Hook that merges props with slotted props and provides a proxy for accessing them.
|
|
106
128
|
*
|
|
107
|
-
* @param args - The initial props to use.
|
|
129
|
+
* @param args - The initial props to use, or array when using rest parameters.
|
|
108
130
|
* @param state - The state object to use.
|
|
131
|
+
* @param forwardedRef - The ref forwarded to the component.
|
|
109
132
|
* @returns An object containing the proxy based props object and the apply function.
|
|
110
133
|
* @throws {BentoError} If the hook is used outside of a @bento/slots component.
|
|
111
134
|
*
|
|
112
135
|
* @example
|
|
113
|
-
* const { props, apply } = useProps({ foo: 'bar' });
|
|
136
|
+
* const { props, apply, ref } = useProps({ foo: 'bar' }, {}, forwardedRef);
|
|
114
137
|
* if (props.a) doSomething()
|
|
115
|
-
* return <a {...apply({ className: 'foo' }) }>{ props.children }</a>;
|
|
138
|
+
* return <a {...apply({ className: 'foo' }, ['ref']) } ref={ref}>{ props.children }</a>;
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* // With rest parameters
|
|
142
|
+
* function Component(...rest) {
|
|
143
|
+
* const { props, apply, ref } = useProps(rest);
|
|
144
|
+
* return <div {...apply()} />;
|
|
145
|
+
* }
|
|
116
146
|
*/
|
|
117
|
-
export function useProps(
|
|
147
|
+
export function useProps(...rest: any[]): Returns {
|
|
148
|
+
let forwardedRef: ForwardedRef<any> | undefined;
|
|
149
|
+
let args: AnyObject;
|
|
150
|
+
let state: object;
|
|
151
|
+
|
|
152
|
+
if (Array.isArray(rest[0])) {
|
|
153
|
+
[args, forwardedRef] = rest[0];
|
|
154
|
+
state = rest[1];
|
|
155
|
+
} else {
|
|
156
|
+
[args, state, forwardedRef] = rest;
|
|
157
|
+
}
|
|
158
|
+
|
|
118
159
|
const { slots } = useContext<BoxContext<AnyObject>>(Box);
|
|
119
160
|
const [props, internal] = useInternalProps(args);
|
|
120
161
|
const { namespace, assigned } = slots;
|
|
121
162
|
const dot = namespace.join('.');
|
|
122
163
|
const slotted = assigned[dot] || {};
|
|
123
|
-
|
|
164
|
+
|
|
165
|
+
const ref = mergeRefList([
|
|
166
|
+
(props as AnyObject)?.ref as ForwardedRef<any> | Ref<any> | undefined,
|
|
167
|
+
(slotted as AnyObject)?.ref as ForwardedRef<any> | Ref<any> | undefined,
|
|
168
|
+
forwardedRef
|
|
169
|
+
]);
|
|
170
|
+
|
|
171
|
+
const slotNoRef = { ...slotted } as AnyObject;
|
|
172
|
+
const propsNoRef = { ...props } as AnyObject;
|
|
173
|
+
|
|
174
|
+
delete slotNoRef.ref;
|
|
175
|
+
delete propsNoRef.ref;
|
|
176
|
+
|
|
177
|
+
const propsy: AnyObject = { ...internal, ...propsNoRef, ...slotNoRef };
|
|
124
178
|
|
|
125
179
|
/**
|
|
126
180
|
* Applies the given attributes to an object.
|
|
@@ -131,37 +185,48 @@ export function useProps(args: AnyObject, state: object = {}): Returns {
|
|
|
131
185
|
* @public
|
|
132
186
|
*/
|
|
133
187
|
function apply(attributes?: object, except?: string[]): object {
|
|
134
|
-
const data = attributes || propsy;
|
|
135
|
-
const returned = {};
|
|
188
|
+
const data = (attributes || propsy) as AnyObject;
|
|
189
|
+
const returned: AnyObject = {};
|
|
136
190
|
|
|
137
191
|
function reduce(memo: AnyObject, key: string) {
|
|
138
192
|
if (except && except.includes(key)) return memo;
|
|
139
193
|
|
|
140
194
|
memo[key] = renderProp(key, {
|
|
195
|
+
props: { ...props, ...internal },
|
|
141
196
|
original: data[key],
|
|
142
197
|
slots: slotted,
|
|
143
|
-
props: { ...props, ...internal },
|
|
144
198
|
state
|
|
145
199
|
});
|
|
146
200
|
|
|
147
201
|
return memo;
|
|
148
202
|
}
|
|
149
203
|
|
|
150
|
-
|
|
151
|
-
|
|
204
|
+
let result: AnyObject;
|
|
205
|
+
|
|
206
|
+
if (!attributes) result = Object.keys(propsy).reduce(reduce, returned);
|
|
207
|
+
else result = Object.keys(propsy).reduce(reduce, Object.keys(attributes).reduce(reduce, returned));
|
|
208
|
+
|
|
209
|
+
// Always include the merged ref unless specifically excluded
|
|
210
|
+
if (except && except.includes('ref')) return result;
|
|
211
|
+
if (ref) result.ref = ref;
|
|
212
|
+
|
|
213
|
+
return result;
|
|
152
214
|
}
|
|
153
215
|
|
|
154
216
|
return {
|
|
155
217
|
props: new Proxy(propsy, {
|
|
156
218
|
get: function getter(_: object, name: string) {
|
|
219
|
+
if (name === 'ref') return ref;
|
|
220
|
+
|
|
157
221
|
return renderProp(name, {
|
|
158
222
|
original: isRenderProp(name, props[name]) ? undefined : props[name],
|
|
159
|
-
slots: slotted,
|
|
160
223
|
props: { ...props, ...internal },
|
|
224
|
+
slots: slotted,
|
|
161
225
|
state
|
|
162
226
|
});
|
|
163
227
|
}
|
|
164
228
|
}),
|
|
165
|
-
apply
|
|
229
|
+
apply,
|
|
230
|
+
ref
|
|
166
231
|
};
|
|
167
232
|
}
|