import { type ComponentPropsWithoutRef, type ElementType, forwardRef, type ReactElement, type ReactNode, type Ref, useEffect, useLayoutEffect, useState, } from 'react' import { createPortal } from 'react-dom' /* Simple switch to return a child tag from a parent tag argument. Returns a div by default. */ export const getChildTag = (parentTag?: ElementType) => { switch (parentTag) { case 'ul': case 'ol': return 'li' default: return 'div' } } /* @johannes' one weird trick for fixing TypeScript autocomplete */ export function identityType () { function inner (u: U): U { return u } return inner } /* Logs a warning to the console when the condition is true, only in dev */ export const devWarning = (condition: boolean, message: string) => { if (process.env.NODE_ENV !== 'production') { if (condition) { console.error(message) } } } /* forwardRefWithAs lets us forward refs while keeping the correct component type, which can be specified by the `as` prop. */ type ElementTagNameMap = HTMLElementTagNameMap & Pick> type AsProp = { as?: Comp ref?: Ref< Comp extends keyof ElementTagNameMap ? ElementTagNameMap[Comp] : Comp extends new (...args: any) => any ? InstanceType : undefined > } & Omit, 'as' | keyof Props> type CompWithAsProp = < Comp extends ElementType = DefaultElementType >( props: AsProp & Props ) => ReactElement export const forwardRefWithAs = ( render: ( props: BaseProps & { as?: ElementType }, ref: React.Ref ) => Exclude ): CompWithAsProp => { // @ts-expect-error return forwardRef(render) } /* A helper for making valid IDs from a set of inputs */ export function makeId (...args: (string | number | null | undefined)[]) { return args.filter(val => val != null).join('--') } /* A helper for handling string OR array values e.g. VS */ export const mapResponsiveProp = < Map extends Record, Keys extends keyof Map >( value: Keys | readonly (Keys | null)[], valueMap: Map ) => { if (Array.isArray(value)) { return value.map(k => (k == null ? null : valueMap[k])) } // @ts-expect-error return valueMap[value] } /** * Utils below are ported with thanks from @reach-ui * Copyright (c) 2018-present, React Training LLC */ // Autogenerate IDs to facilitate WAI-ARIA and server rendering. For reasoning, see // https://github.com/reach/reach-ui/blob/develop/packages/auto-id/src/index.tsx let serverHandoffComplete = false let id = 0 const genId = () => ++id export const useId = (idFromProps?: string | null) => { const initialId = idFromProps || (serverHandoffComplete ? genId() : null) const [id, setId] = useState(initialId) useSafeLayoutEffect(() => { if (id === null) { setId(genId()) } }, []) useEffect(() => { if (serverHandoffComplete === false) { serverHandoffComplete = true } }, []) return id != null ? String(id) : undefined } // Works around useLayoutEffect throwing a warning when used in SSR export const useSafeLayoutEffect = typeof window === 'undefined' ? () => {} : useLayoutEffect type Props = { children: ReactElement } export const Portal = ({ children }: Props): React.ReactPortal | null => { if (typeof document === 'undefined') { return null } return createPortal(children, document.body) }