move tsx kit -> uikit

This commit is contained in:
2026-07-13 09:17:11 -04:00
parent 98978e4930
commit 9243d142e9
37 changed files with 20 additions and 19 deletions

115
web/uikit/Popovers.tsx Normal file
View File

@@ -0,0 +1,115 @@
import { createContext, JSXElement, useContext } from "solid-js";
import { FloatingRoot, FloatingTrigger, FloatingContent, useFloatingHover, Placement } from "./Floating.tsx";
const POPOVER_CLS = "bg-white rounded-default shadow-lg border border-neutral-200";
interface PopoverProps {
open?: boolean;
onOpenChange?: (open: boolean) => void;
placement?: Placement;
offset?: number;
// Nested floating that shouldn't close (or be closed by) an ancestor popover.
standalone?: boolean;
children?: JSXElement;
}
interface HoverPopoverProps extends PopoverProps {
hoverDelay?: number;
hoverCloseDelay?: number;
}
export function Popover(props: PopoverProps) {
return (
<FloatingRoot open={props.open} onOpenChange={(v: boolean) => props.onOpenChange?.(v)} placement={props.placement ?? "bottom-start"} offset={props.offset ?? 8} flip={true} shift={true} standalone={props.standalone}>
{props.children}
</FloatingRoot>
);
}
interface PopoverTriggerProps {
class?: string;
title?: string;
children?: JSXElement;
}
export function PopoverTrigger(props: PopoverTriggerProps) {
return (
<FloatingTrigger openOnHover={false} class={props.class || ""} title={props.title}>
{props.children}
</FloatingTrigger>
);
}
interface PopoverContentProps {
class?: string;
style?: Record<string, string | number>;
children?: JSXElement;
}
export function PopoverContent(props: PopoverContentProps) {
return (
<FloatingContent class={POPOVER_CLS + " " + (props.class || "")} style={props.style}>
{props.children}
</FloatingContent>
);
}
export function HoverPopover(props: HoverPopoverProps) {
return (
<FloatingRoot open={props.open} onOpenChange={(v: boolean) => props.onOpenChange?.(v)} placement={props.placement ?? "bottom-start"} offset={props.offset ?? 8} flip={true} shift={true} standalone={props.standalone}>
<HoverPopoverInner hoverDelay={props.hoverDelay ?? 0} hoverCloseDelay={props.hoverCloseDelay ?? 150}>
{props.children}
</HoverPopoverInner>
</FloatingRoot>
);
}
interface HoverPopoverContextValue {
hoverDelay: number;
hoverCloseDelay: number;
}
const HoverPopoverContext = createContext<HoverPopoverContextValue | null>(null);
interface HoverPopoverInnerProps {
hoverDelay: number;
hoverCloseDelay: number;
children?: JSXElement;
}
function HoverPopoverInner(props: HoverPopoverInnerProps) {
return (
<HoverPopoverContext.Provider value={{
get hoverDelay() { return props.hoverDelay; },
get hoverCloseDelay() { return props.hoverCloseDelay; },
}}>
{props.children}
</HoverPopoverContext.Provider>
);
}
interface HoverPopoverTriggerProps {
asChild?: boolean;
class?: string;
children?: JSXElement;
}
export function HoverPopoverTrigger(props: HoverPopoverTriggerProps) {
const ctx = useContext(HoverPopoverContext);
return (
<FloatingTrigger openOnHover={true} class={props.class || ""} hoverDelay={ctx?.hoverDelay ?? 0} hoverCloseDelay={ctx?.hoverCloseDelay ?? 150}>
{props.children}
</FloatingTrigger>
);
}
export function HoverPopoverContent(props: PopoverContentProps) {
const ctx = useContext(HoverPopoverContext);
const hoverProps = useFloatingHover(true, ctx?.hoverCloseDelay ?? 150);
return (
<FloatingContent class={POPOVER_CLS + " " + (props.class || "")} style={props.style} onMouseEnter={hoverProps.onMouseEnter} onMouseLeave={hoverProps.onMouseLeave}>
{props.children}
</FloatingContent>
);
}