UNPKG

@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
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;