import type { JSX, Signal } from 'solid-js';
import { createEffect, createSignal } from 'solid-js';
import type { OmitAndMerge } from './types';
export type ValidElements = keyof JSX.IntrinsicElements;
export type ValidComponent
= (props: P) => JSX.Element;
export type ValidConstructor =
| ValidElements
| ValidComponent
| (string & {});
export type DynamicProps = T extends ValidElements
? JSX.IntrinsicElements[T]
: T extends ValidComponent
? U
: Record;
type UnboxIntrinsicElements = T extends JSX.HTMLAttributes
? U
: never;
type RefCallback = (el: T) => void;
type RefField = T | RefCallback;
type UnboxComponentProp = U extends { ref: infer X } ? X : never;
export type DynamicNode = T extends ValidElements
? UnboxIntrinsicElements
: T extends ValidComponent
? UnboxComponentProp
: never;
// Just a dynamic way to make a `ref` property
// based on the constructor
export interface WithRef {
ref?: RefField>;
}
export interface DynamicComponent {
as?: T;
}
export interface DynamicComponentWithRef
extends WithRef {
as?: T;
}
export type HeadlessProps = OmitAndMerge<
V & DynamicComponent,
DynamicProps
>;
export type HeadlessPropsWithRef<
T extends ValidConstructor,
V = {},
> = OmitAndMerge, DynamicProps>;
function isRefFunction(
callback?: RefField>,
): callback is RefCallback> {
return typeof callback === 'function';
}
// `props.ref` could have been used however it doesn't enforce
// proper timing. We want to make sure that `ref` is called in
// a way that it behaves the same way as if it were called
// natively on an element (which runs in the same scope as the component)
// This is useful if the ref function itself has ownership-based calls
// like createEffect
export function createForwardRef(
props: WithRef,
): Signal | undefined> {
const [ref, setRef] = createSignal>();
createEffect(() => {
const current = ref();
// Technically Solid compiles refs on components into
// a function, despite the fact that its type definition
// says that it is either a function or the ref type
if (current && 'ref' in props && isRefFunction(props.ref)) {
props.ref(current);
}
});
return [ref, setRef];
}