import * as React from "react"; import Children from "./children"; import type { SlotChildren, CreateSlot, SlotProps, SlotComponent, HasSlot, } from "./types"; import { COMPONENT_TYPE } from "./constants"; import { HiddenArg } from "./HiddenArg"; import { OverrideConfig } from "./OverrideNode"; class SlotProxyFactory { private children = new Children(); private slotProxy: CreateSlot; constructor() { this.slotProxy = new Proxy({} as CreateSlot, { get: (target, property) => { if ( Object.prototype.hasOwnProperty.call(target, property) || typeof property === "symbol" ) { return Reflect.get(target, property); } const addedProperty = this.getSlotComponent(property); Reflect.set(target, property, addedProperty); return addedProperty; }, }); } private getSlotComponent(slotName: string): SlotComponent { const that = this; const SlotComponent: SlotComponent = Object.assign( ( defaultNode?: React.ReactNode | SlotProps<{}>, props?: {}, key?: React.Key, hiddenPreviousConfig?: HiddenArg, hiddenPreviousDefaultNode?: HiddenArg, ) => { if ( !React.isValidElement(defaultNode) && typeof defaultNode === "object" && defaultNode !== null && !(Symbol.iterator in defaultNode) ) { throw new Error( "To use slots as JSX elements, it's essential to enable `babel-plugin-transform-react-slots`. Alternatively, you can opt for the function signature instead.", ); } let _props; let _slotNameAttr; let _key = key; if (props == undefined) { _props = {}; } else if (typeof props !== "object") { throw new Error( "`props` must be an object, instead saw " + typeof props, ); } else if ("key" in props || "slot-name" in props) { let { key, "slot-name": slotNameAttr, ...rest } = props as any; _props = rest; _slotNameAttr = slotNameAttr; _key = key; } else { _props = props; } return that.children.get( slotName, defaultNode, _props, _key, _slotNameAttr, hiddenPreviousConfig && hiddenPreviousConfig instanceof HiddenArg ? hiddenPreviousConfig.arg : [], hiddenPreviousDefaultNode && hiddenPreviousDefaultNode instanceof HiddenArg ? hiddenPreviousDefaultNode.arg : [], ); }, { [COMPONENT_TYPE]: "slot" as const, }, ); return SlotComponent; } build(children: T): SlotProxyFactory { this.children.build(children); return this; } getHasSlot(): HasSlot { let hasSlot: { [index: string]: true | undefined } = {}; for (const key of this.children.keys()) { hasSlot[key] = true; } return hasSlot as HasSlot; } getSlotProxy(): CreateSlot { return this.slotProxy; } } export function useSlot( children: T, ): { slot: CreateSlot; hasSlot: HasSlot } { const proxyCreator = new SlotProxyFactory(); // const prevChildren = React.useRef(); // if (prevChildren.current !== children) { proxyCreator.build(children); // } let proxy = proxyCreator.getSlotProxy(); // prevChildren.current = children; return { slot: proxy, hasSlot: proxyCreator.getHasSlot() }; }