import type { ImageryProvider } from "cesium"; import type { HTMLAttributes, ForwardRefExoticComponent, PropsWithoutRef, RefAttributes, ForwardRefRenderFunction, PropsWithChildren} from "react"; import { forwardRef } from "react"; import { CesiumContext } from "./context"; import type { Options } from "./hooks"; import { useCesiumComponent } from "./hooks"; import { pick } from "./util"; export type CesiumComponentOptions< Element, Props extends RootComponentInternalProps, State = any, > = Options & { renderContainer?: boolean; noChildren?: boolean; containerProps?: (keyof Props)[] | ((props: Props) => HTMLAttributes); defaultProps?: Partial; /** * Render-phase hook to resolve prop overrides before the Cesium element is * created. Use this together with `useCesiumResource` to integrate async * resource loading with React Suspense. Returns a partial props object that * is merged over the incoming props. */ useResource?: (props: Props) => Partial | undefined; }; export type CesiumComponentRef = { cesiumElement?: Element; }; export type CesiumComponentType = ForwardRefExoticComponent< PropsWithoutRef & RefAttributes> >; export type RootComponentInternalProps = { onUpdate?: () => void; }; export type RootComponentInternalValues = { imageryLayerWaitingList?: (Promise | ImageryProvider)[]; }; const noResource = (): undefined => undefined; export const createCesiumComponent = ({ renderContainer, noChildren, containerProps, defaultProps, useResource = noResource, ...options }: CesiumComponentOptions): CesiumComponentType => { const component: ForwardRefRenderFunction, PropsWithoutRef> = ( props, ref, ) => { const baseProps = { ...defaultProps, ...props, } as Props; // May suspend (throw a promise) to integrate async loading with . // eslint-disable-next-line react-hooks/rules-of-hooks const resolved = useResource(baseProps); const mergedProps = resolved ? ({ ...baseProps, ...resolved } as Props) : baseProps; // eslint-disable-next-line react-hooks/rules-of-hooks const [provided, mounted, wrapperRef] = useCesiumComponent( options, mergedProps, ref, ); if (noChildren) return null; const children = mounted ? "children" in mergedProps ? (mergedProps as PropsWithChildren).children : null : null; const wrappedChildren = renderContainer ? (
{children}
) : children ? ( <>{children} ) : null; if (provided) { return {wrappedChildren}; } return wrappedChildren; }; component.displayName = options.name; return forwardRef(component); };