{"version":3,"file":"index.cjs","names":["React","getElementRef","useMergeRefs","mergeProps"],"sources":["../../../src/components/Slot/index.tsx"],"sourcesContent":["import { useMergeRefs } from '../../hooks/useMergeRefs';\nimport { mergeProps, getElementRef } from './Slot.utils';\nimport React from 'react';\n\nexport interface SlotProps extends React.HTMLAttributes<HTMLElement> {\n  children?: React.ReactNode;\n}\n\n/**\n * @description 주어진 Props를 직계 자식 컴포넌트에 병합하고, 자식 컴포넌트를 렌더링하는 컴포넌트입니다.\n *\n * Slot은 부모 컴포넌트의 기능을 자식 컴포넌트에 전달하는 합성 패턴을 구현합니다. 이를 통해:\n * - 부모 컴포넌트의 `props`, `ref`, `이벤트 핸들러` 등을 자식 컴포넌트에 전달할 수 있습니다.\n * - 자식 컴포넌트의 구현을 변경하지 않고도 새로운 기능을 추가할 수 있습니다.\n * - 컴포넌트 간의 결합도를 낮추고 재사용성을 높일 수 있습니다.\n *\n * Slot은 아래와 같은 특징이 있습니다.\n * - 자식 요소로 `단일 요소`만 허용됩니다.\n * - 자식 요소로 컴포넌트가 온다면 해당 컴포넌트는 필수적으로 `forwardRef`, `props`를 허용해야 합니다. 허용하지 않으면 기능이 정상적으로 동작하지 않습니다.\n *  - Slot을 사용 할 경우 아래 링크를 참고하세요.\n *\n * @see https://www.radix-ui.com/primitives/docs/guides/composition#your-component-must-spread-props\n * @see https://www.radix-ui.com/primitives/docs/guides/composition#your-component-must-forward-ref\n *\n * @example\n * ```tsx\n * import React from \"react\";\n * import { Slot } from \"@modern-kit/react\";\n *\n * function Button({ asChild, ...props }) {\n *   const Comp = asChild ? Slot : \"button\";\n *   return <Comp {...props} />;\n * }\n *\n * // default\n * <Button onClick={onClick}>Button</Button>\n *\n * // asChild\n * <Button asChild onClick={onClick}>\n *   <div>asChild Button</div>\n * </Button>\n * ```\n */\nexport const Slot = React.forwardRef<HTMLElement, SlotProps>(\n  (props, forwardedRef) => {\n    const { children, ...slotProps } = props;\n    const childrenArray = React.Children.toArray(children);\n    const slottable = childrenArray.find(isSlottable);\n\n    if (slottable) {\n      // 렌더링할 새 요소로 `Slottable`의 children으로 전달된 요소를 할당한다.\n      const newElement = slottable.props.children;\n\n      // childrenArray를 순회하면서 새로운 자식 요소를 생성한다.\n      // Slottable 컴포넌트를 발견하면 해당 자식 요소를 새로운 자식으로 교체한다.\n      const newChildren = childrenArray.map((child) => {\n        if (child === slottable) {\n          if (React.Children.count(newElement) > 1) {\n            return React.Children.only(null);\n          }\n          return React.isValidElement(newElement)\n            ? ((newElement as React.ReactElement<React.PropsWithChildren>).props.children)\n            : null;\n        }\n        return child;\n      });\n\n      return (\n        <SlotClone {...slotProps} ref={forwardedRef}>\n          {React.isValidElement(newElement)\n            ? React.cloneElement(newElement, undefined, newChildren)\n            : null}\n        </SlotClone>\n      );\n    }\n\n    // Slottable 컴포넌트가 존재하지 않을 경우, children을 그대로 렌더링한다.\n    return (\n      <SlotClone {...slotProps} ref={forwardedRef}>\n        {children}\n      </SlotClone>\n    );\n  }\n);\n\nSlot.displayName = 'Slot';\n\ninterface SlotCloneProps {\n  children: React.ReactNode;\n}\n\n/**\n * @description Slot의 자식 요소를 복제하고, slotProps와 자식 요소의 props를 병합한다.\n */\nconst SlotClone = React.forwardRef<HTMLElement, SlotCloneProps>(\n  (props, forwardedRef) => {\n    const { children, ...slotProps } = props;\n\n    const childRef = React.isValidElement(children)\n      ? getElementRef(children)\n      : null;\n    const mergedRefs = useMergeRefs(forwardedRef, childRef);\n\n    if (!React.isValidElement(children)) {\n      return React.Children.count(children) > 1\n        ? React.Children.only(null)\n        : null;\n    }\n\n    return React.cloneElement(children, {\n      ...mergeProps(slotProps, children.props as Record<string, any>),\n      // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n      // @ts-ignore\n      ref: forwardedRef ? mergedRefs : childRef,\n    });\n  }\n);\n\nSlotClone.displayName = 'SlotClone';\n\ninterface SlottableProps {\n  children?: React.ReactNode;\n}\n\n/**\n * @description Slot 컴포넌트의 자식 요소로 사용되며, Slot이 렌더링할 대상이다.\n */\nexport const Slottable = ({ children }: SlottableProps) => {\n  return <>{children}</>;\n};\n\n/**\n * @description child가 유효한 React 요소인지 확인하고, type이 'Slottable' 컴포넌트인지 확인한다.\n */\nfunction isSlottable(\n  child: React.ReactNode\n): child is React.ReactElement<React.PropsWithChildren> {\n  return React.isValidElement(child) && child.type === Slottable;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,OAAOA,MAAAA,QAAM,YACvB,OAAO,iBAAiB;CACvB,MAAM,EAAE,UAAU,GAAG,cAAc;CACnC,MAAM,gBAAgBA,MAAAA,QAAM,SAAS,QAAQ,SAAS;CACtD,MAAM,YAAY,cAAc,KAAK,YAAY;CAEjD,IAAI,WAAW;EAEb,MAAM,aAAa,UAAU,MAAM;EAInC,MAAM,cAAc,cAAc,KAAK,UAAU;GAC/C,IAAI,UAAU,WAAW;IACvB,IAAIA,MAAAA,QAAM,SAAS,MAAM,WAAW,GAAG,GACrC,OAAOA,MAAAA,QAAM,SAAS,KAAK,KAAK;IAElC,OAAOA,MAAAA,QAAM,eAAe,WAAW,GACjC,WAA2D,MAAM,WACnE;;GAEN,OAAO;IACP;EAEF,OACE,iBAAA,GAAA,kBAAA,KAAC,WAAD;GAAW,GAAI;GAAW,KAAK;aAC5BA,MAAAA,QAAM,eAAe,WAAW,GAC7BA,MAAAA,QAAM,aAAa,YAAY,KAAA,GAAW,YAAY,GACtD;GACM,CAAA;;CAKhB,OACE,iBAAA,GAAA,kBAAA,KAAC,WAAD;EAAW,GAAI;EAAW,KAAK;EAC5B;EACS,CAAA;EAGjB;AAED,KAAK,cAAc;;;;AASnB,MAAM,YAAYA,MAAAA,QAAM,YACrB,OAAO,iBAAiB;CACvB,MAAM,EAAE,UAAU,GAAG,cAAc;CAEnC,MAAM,WAAWA,MAAAA,QAAM,eAAe,SAAS,GAC3CC,mBAAAA,cAAc,SAAS,GACvB;CACJ,MAAM,aAAaC,cAAAA,aAAa,cAAc,SAAS;CAEvD,IAAI,CAACF,MAAAA,QAAM,eAAe,SAAS,EACjC,OAAOA,MAAAA,QAAM,SAAS,MAAM,SAAS,GAAG,IACpCA,MAAAA,QAAM,SAAS,KAAK,KAAK,GACzB;CAGN,OAAOA,MAAAA,QAAM,aAAa,UAAU;EAClC,GAAGG,mBAAAA,WAAW,WAAW,SAAS,MAA6B;EAG/D,KAAK,eAAe,aAAa;EAClC,CAAC;EAEL;AAED,UAAU,cAAc;;;;AASxB,MAAa,aAAa,EAAE,eAA+B;CACzD,OAAO,iBAAA,GAAA,kBAAA,KAAA,kBAAA,UAAA,EAAG,UAAY,CAAA;;;;;AAMxB,SAAS,YACP,OACsD;CACtD,OAAOH,MAAAA,QAAM,eAAe,MAAM,IAAI,MAAM,SAAS"}