@kitn.ai/ui
Version:
Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. Authored in SolidJS.
80 lines (79 loc) • 3.53 kB
TypeScript
import { Accessor, JSX } from 'solid-js';
import { Placement } from '@floating-ui/dom';
/**
* Keep a node mounted through its CSS exit animation.
* Open -> present=true, state='open' (enter animation via base classes)
* Close -> state='closed' (data-closed triggers tw-animate-css animate-out),
* then unmount on `animationend`. If no animation is defined
* (e.g. jsdom), unmount on the next microtask.
*/
export declare function createPresence(show: Accessor<boolean>): {
present: Accessor<boolean>;
state: Accessor<"open" | "closed">;
setRef: (el: Element) => void;
};
export type AsTag = string | ((props: Record<string, any>) => JSX.Element);
/**
* Polymorphic element. `as` may be a tag name (default 'span') or a render
* function that receives the forwarded props (render-prop style `as={fn}`).
* Uses splitProps (NOT destructuring) so reactive forwarded props such as
* aria-expanded stay reactive. All extra props (incl. `ref`, event handlers,
* aria-*) are forwarded. `children` is left in `rest` so it forwards naturally.
*/
export declare function As(props: {
as?: AsTag;
children?: JSX.Element;
[k: string]: any;
}): JSX.Element;
export interface UsePositionOptions {
placement?: Placement;
gutter?: number;
arrowEl?: Accessor<HTMLElement | undefined>;
/**
* Fired ONCE when the reference (anchor) "goes away" — either removed from the
* document OR hidden behind an `inert` ancestor (e.g. a modal/takeover that
* inert-s the background while open). In both cases the anchor is no longer a
* valid, interactive target, so the caller should close the overlay and unmount
* its portal instead of leaving it orphaned (floating over an inert background or
* anchored to a node that no longer exists). Anchored overlays portal OUT of the
* trigger's subtree, so Solid's onCleanup does not catch an anchor that is
* removed/inerted independently of the overlay component. Wire this to set the
* overlay's open state to false.
*/
onDisconnect?: () => void;
}
/**
* Position `floating` relative to `reference` using fixed strategy + autoUpdate,
* so the element tracks the trigger on scroll/resize (fix DD-2). Writes
* position into the returned `pos` signal; caller applies it as inline style.
*
* `options` (placement/gutter) are read at setup time — pass static values;
* reactive option changes won't reposition until the next autoUpdate tick.
*/
export declare function usePosition(reference: Accessor<HTMLElement | undefined>, floating: Accessor<HTMLElement | undefined>, options?: UsePositionOptions): {
pos: Accessor<{
x: number;
y: number;
placement: Placement;
}>;
arrowPos: Accessor<{
x?: number;
y?: number;
}>;
hidden: Accessor<boolean>;
};
export type DismissReason = 'escape' | 'outside';
export interface UseDismissOptions {
enabled: Accessor<boolean>;
onDismiss: (reason: DismissReason) => void;
/** Elements considered "inside" (trigger + content). Pointerdown outside all of them dismisses. */
refs: () => (HTMLElement | undefined)[];
}
/**
* Escape key + outside-pointerdown dismissal. Does NOT lock page scroll (fix DD-1).
*
* `onDismiss` and `refs` are captured at call time (component setup), which is
* fine in SolidJS since components don't re-run — ensure they close over mutable
* variables, not stale values.
*/
export declare function useDismiss(opts: UseDismissOptions): void;