@ehsaneha/react-slot
Version:
A React <Slot> component that merges props and forwards refs to a single child element, similar to Radix UI’s Slot.
61 lines (60 loc) • 2.7 kB
JavaScript
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { cloneElement, isValidElement, forwardRef, Fragment, } from "react";
function mergeProps(slotProps, childProps) {
const merged = Object.assign(Object.assign({}, slotProps), childProps);
for (const key in slotProps) {
const isEvent = key.startsWith("on") && typeof slotProps[key] === "function";
if (isEvent && typeof childProps[key] === "function") {
merged[key] = (...args) => {
var _a, _b, _c, _d;
(_b = (_a = slotProps)[key]) === null || _b === void 0 ? void 0 : _b.call(_a, ...args);
(_d = (_c = childProps)[key]) === null || _d === void 0 ? void 0 : _d.call(_c, ...args);
};
}
}
merged.className = [slotProps.className, childProps.className]
.filter(Boolean)
.join(" ");
merged.style = Object.assign(Object.assign({}, slotProps.style), childProps.style);
return merged;
}
// Helper to merge two refs (function or object refs)
function mergeRefs(ref1, ref2) {
return (value) => {
if (typeof ref1 === "function")
ref1(value);
else if (ref1 && typeof ref1 === "object")
ref1.current = value;
if (typeof ref2 === "function")
ref2(value);
else if (ref2 && typeof ref2 === "object")
ref2.current = value;
};
}
const Slot = forwardRef((_a, forwardedRef) => {
var { children } = _a, slotProps = __rest(_a, ["children"]);
if (!isValidElement(children) ||
children.type === Fragment ||
Array.isArray(children)) {
if (process.env.NODE_ENV !== "production") {
console.warn("<Slot> expects exactly one valid React element as its child, and does not accept React.Fragment or multiple children.");
}
return null;
}
const child = children;
const mergedProps = mergeProps(slotProps, child.props);
// Merge the forwarded ref and child's original ref
const combinedRef = forwardedRef; // mergeRefs(forwardedRef, child.ref);
return cloneElement(child, Object.assign(Object.assign({}, mergedProps), { ref: combinedRef }));
});
export default Slot;