/* eslint-disable @typescript-eslint/ban-types */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { cx, styled } from '@fuel-ui/css'; import type { ForwardedRef, ReactElement, ReactNode } from 'react'; import { createElement, useMemo, forwardRef, cloneElement } from 'react'; import type { StoreDefs } from '~/defs'; import { useComponentProps } from '../hooks/useComponentProps'; import { useElementProps } from '../hooks/useElementProps'; import type { BaseProps } from './types'; export function createComponent< Props, ObjProps = unknown, ToOmit = unknown, HTMLElement = any, FinalProps = ToOmit extends string ? Omit, ToOmit> : BaseProps, >( render: ( props: FinalProps & { ref: ForwardedRef }, ) => ReactElement | null, ) { const Component = forwardRef((props, ref) => render({ ref, ...props }), ); return Component as typeof Component & ObjProps; } export function useCreateStyledElement( type: any, style: any, styleProps: any, props: any, children?: ReactNode, ) { const Component = useMemo(() => styled(type, style), []); return ( {children} ); } export type CreateComponent< Item extends { type: string; component: keyof StoreDefs; props: Record; element: unknown; namespace?: Record | undefined; omit?: string | undefined; styles?: string | undefined; }, > = { type: Item['type']; component: Item['component']; props: Item['props']; element: Item['element']; namespace: Item['namespace'] extends Record ? Item['namespace'] : null; omit: Item['omit'] extends string ? Item['omit'] : null; styles: Item['styles'] extends string ? Item['styles'] : null; }; type GetProps> = Def['omit'] extends string ? Omit, Def['omit']> : BaseProps; type RenderFn> = ( props: GetProps & { ref: ForwardedRef }, ) => ReactElement> | null; export function _unstable_createComponent< Def extends CreateComponent, Component extends keyof StoreDefs = Def['component'], >(component: Component, render: RenderFn) { const Comp = forwardRef>((initProps, ref) => { const props = useComponentProps( component, initProps as GetProps, ) as GetProps; const el = render({ ref, ...props }); const className = cx(el?.props?.className, props.className); return el ? (cloneElement(el as any, { ...(el?.props || {}), className, }) as ReturnType>) : null; }); return Comp as Def['namespace'] extends Record ? typeof Comp & Def['namespace'] : typeof Comp; } export function _unstable_createEl< E extends React.ElementType, P extends Record, >(el: E, props: P, children?: ReactNode) { const elProps = useElementProps(props); const child = children ?? elProps?.children; return createElement(el, elProps, child); } type ExtendedProps = OverrideProps & Omit; type ElementType = | keyof JSX.IntrinsicElements | React.JSXElementConstructor; type PropsOf = JSX.LibraryManagedAttributes< C, React.ComponentPropsWithoutRef >; type ComponentProp = { as?: C; }; type InheritedProps = ExtendedProps< PropsOf, Props >; export type PolymorphicRef = C extends React.ElementType ? React.ComponentPropsWithRef['ref'] : never; export type PolymorphicComponentProps< C, Props = {}, > = C extends React.ElementType ? InheritedProps> & { ref?: PolymorphicRef } : Props & { as: React.ElementType }; export function createPolymorphicComponent>( component: ReturnType>, ) { type Props = Omit, 'as'>; type ComponentProps = PolymorphicComponentProps; type _PolymorphicComponent = ( props: ComponentProps, ) => React.ReactElement; type ComponentProperties = Omit, never>; type PolymorphicComponent = _PolymorphicComponent & ComponentProperties & typeof component; const Comp = component as PolymorphicComponent; return Comp as Def['namespace'] extends Record ? typeof Comp & Def['namespace'] : typeof Comp; }