import * as React from "react"; import { IconArrowsMove, IconRefresh } from "@tabler/icons-react"; import { Button, Card, cn, Label, SegmentedControl, Slider, Switch, Tabs, TabsContent, TabsList, TabsTrigger, } from "@colineapp/ui"; import { LiquidGlass, type LiquidGlassHandle } from "@colineapp/liquid-glass/react"; import { CodeBlock } from "./code"; import { reactSnippet, vanillaSnippet, type PlaygroundOptions } from "./snippet"; import { BACKDROPS, Stage, useDraggable, type BackdropKind } from "./stage"; type Shape = "capsule" | "card" | "circle"; type Floating = "off" | "menu" | "dialog"; const SHAPES: Record = { capsule: { className: "h-[72px] w-[260px] rounded-full" }, card: { className: "h-[180px] w-[280px] rounded-[32px]" }, circle: { className: "size-[164px] rounded-full" }, }; const HUES = [ { value: "none", color: null, label: "None" }, { value: "blue", color: "#3d6bff", label: "Blue" }, { value: "violet", color: "#8b5cf6", label: "Violet" }, { value: "pink", color: "#ff4fa3", label: "Pink" }, { value: "green", color: "#2fb36d", label: "Green" }, { value: "amber", color: "#f0a020", label: "Amber" }, ] as const; type Hue = (typeof HUES)[number]["value"]; const DEFAULTS = { backdrop: "landscape" as BackdropKind, shape: "capsule" as Shape, variant: "regular" as "regular" | "clear", floating: "off" as Floating, interactive: true, swell: true, refraction: true, samples: false, maxFall: 0.8, bezel: 17, thickness: 24, edge: 0.75, glint: 0.4, hue: "none" as Hue, hueStrength: 0.5, }; type State = typeof DEFAULTS; function Control({ label, value, children }: { label: string; value?: string; children: React.ReactNode }) { return (
{value !== undefined ? {value} : null}
{children}
); } function Range({ label, value, min, max, step, format = (n) => String(n), onChange, }: { label: string; value: number; min: number; max: number; step: number; format?: (value: number) => string; onChange: (value: number) => void; }) { // ui 2.1.3's Slider puts aria-label on its root, but the focusable part is // the thumb, which it gives no props; name the thumb here. const box = React.useRef(null); React.useEffect(() => { box.current?.querySelector('[role="slider"]')?.setAttribute("aria-label", label); }, [label]); return (
next !== undefined && onChange(next)} />
); } function Toggle({ label, description, checked, onChange }: { label: string; description: string; checked: boolean; onChange: (checked: boolean) => void }) { const id = React.useId(); return (

{description}

); } function Sample({ state, glass }: { state: State; glass: React.RefObject }) { const drag = useDraggable({ initial: { x: 0.5, y: 0.42 } }); const hue = HUES.find((item) => item.value === state.hue)?.color ?? null; // Geometry rebuilds the rim, so it follows the sliders a beat behind. const geometry = React.useDeferredValue( React.useMemo( () => ({ maxFall: state.maxFall, bezel: state.bezel, thickness: state.thickness, edge: state.edge, glint: state.glint }), [state.maxFall, state.bezel, state.thickness, state.edge, state.glint], ), ); return (
{state.shape === "capsule" ? ( Drag me around ) : null} {state.shape === "card" ? (
Liquid Glass Drag it over the picture and watch the rim bend what's behind it.
) : null}
); } export function Playground() { const [state, setState] = React.useState(DEFAULTS); const set = (key: Key) => (value: State[Key]) => setState((current) => ({ ...current, [key]: value })); const glass = React.useRef(null); const hue = HUES.find((item) => item.value === state.hue)?.color ?? null; const options: PlaygroundOptions = { shape: state.shape, variant: state.variant, floating: state.floating === "off" ? false : state.floating === "dialog" ? "dialog" : true, interactive: state.interactive, swell: state.swell, refraction: state.refraction, samples: state.samples ? 4 : 1, hue, hueStrength: hue ? state.hueStrength : undefined, geometry: { maxFall: state.maxFall, bezel: state.bezel, thickness: state.thickness, edge: state.edge, glint: state.glint }, }; return (
({ value: item.value, label: item.color ? ( ) : ( "None" ), }))} /> {hue ? n.toFixed(2)} onChange={set("hueStrength")} /> : null}
n.toFixed(2)} onChange={set("maxFall")} /> `${n}px`} onChange={set("bezel")} /> `${n}px`} onChange={set("thickness")} /> `${n.toFixed(2)}px`} onChange={set("edge")} /> n.toFixed(2)} onChange={set("glint")} />
React TypeScript
); }