UNPKG

solid-floating-ui

Version:
129 lines (113 loc) 3.01 kB
import { createEffect, createMemo, createSignal, onCleanup } from 'solid-js'; import type { ComputePositionConfig, ComputePositionReturn, ReferenceElement, } from '@floating-ui/dom'; import { computePosition } from '@floating-ui/dom'; export interface UseFloatingOptions< R extends ReferenceElement, F extends HTMLElement, > extends Partial<ComputePositionConfig> { whileElementsMounted?: ( reference: R, floating: F, update: () => void, // biome-ignore lint/suspicious/noConfusingVoidType: void is a valid return type ) => void | (() => void); } interface UseFloatingState extends Omit<ComputePositionReturn, 'x' | 'y'> { x?: number | null; y?: number | null; } export interface UseFloatingResult extends UseFloatingState { update(): void; } export function useFloating<R extends ReferenceElement, F extends HTMLElement>( reference: () => R | undefined | null, floating: () => F | undefined | null, options?: UseFloatingOptions<R, F>, ): UseFloatingResult { const placement = () => options?.placement ?? 'bottom'; const strategy = () => options?.strategy ?? 'absolute'; const [data, setData] = createSignal<UseFloatingState>({ x: null, y: null, placement: placement(), strategy: strategy(), middlewareData: {}, }); const [error, setError] = createSignal<{ value: any } | undefined>(); createEffect(() => { const currentError = error(); if (currentError) { throw currentError.value; } }); const version = createMemo(() => { reference(); floating(); return {}; }); function update() { const currentReference = reference(); const currentFloating = floating(); if (currentReference && currentFloating) { const capturedVersion = version(); computePosition(currentReference, currentFloating, { middleware: options?.middleware, placement: placement(), strategy: strategy(), }).then( currentData => { // Check if it's still valid if (capturedVersion === version()) { setData(currentData); } }, err => { setError(err); }, ); } } createEffect(() => { const currentReference = reference(); const currentFloating = floating(); options?.middleware; placement(); strategy(); if (currentReference && currentFloating) { if (options?.whileElementsMounted) { const cleanup = options.whileElementsMounted( currentReference, currentFloating, update, ); if (cleanup) { onCleanup(cleanup); } } else { update(); } } }); return { get x() { return data().x; }, get y() { return data().y; }, get placement() { return data().placement; }, get strategy() { return data().strategy; }, get middlewareData() { return data().middlewareData; }, update, }; }