/* The code for a set of glass options, as the playground shows it and the
`liquid-glass.snippet` tool returns it. */
import { PACKAGE } from "./api";
export type PlaygroundOptions = {
variant?: "regular" | "clear" | undefined;
level?: number | undefined;
floating?: boolean | "dialog" | undefined;
interactive?: boolean | undefined;
swell?: boolean | undefined;
hue?: string | null | undefined;
hueStrength?: number | undefined;
refraction?: boolean | undefined;
samples?: 1 | 4 | undefined;
geometry?: { maxFall?: number; bezel?: number; thickness?: number; edge?: number; glint?: number } | undefined;
shape?: "capsule" | "card" | "circle" | undefined;
};
/** Defaults the snippet leaves out. */
const DEFAULTS = { maxFall: 0.45, bezel: 17, thickness: 24, edge: 0.75, glint: 0.4 } as const;
const DEFAULT_LEVEL = 0.5;
const round = (value: number) => Number(value.toFixed(2));
function geometryEntries(options: PlaygroundOptions) {
const geometry = options.geometry ?? {};
return (Object.keys(DEFAULTS) as (keyof typeof DEFAULTS)[])
.filter(key => geometry[key] !== undefined && round(geometry[key]!) !== DEFAULTS[key])
.map(key => [key, round(geometry[key]!)] as const);
}
/** The options that differ from the defaults, as [name, JSX value, object value]. */
function entries(options: PlaygroundOptions): [string, string, string][] {
const out: [string, string, string][] = [["appearance", '"class"', '"class"']];
if (options.variant === "clear") out.push(["variant", '"clear"', '"clear"']);
if (options.variant !== "clear" && options.level !== undefined && round(options.level) !== DEFAULT_LEVEL) {
out.push(["level", `{${round(options.level)}}`, String(round(options.level))]);
}
if (options.floating === "dialog") out.push(["floating", '"dialog"', '"dialog"']);
else if (options.floating) out.push(["floating", "", "true"]);
if (options.interactive) out.push(["interactive", "", "true"]);
// Interactive glass swells unless told not to.
const swell = options.swell ?? Boolean(options.interactive);
if (swell !== Boolean(options.interactive)) out.push(["swell", swell ? "" : "{false}", String(swell)]);
if (options.hue) out.push(["hue", `"${options.hue}"`, `"${options.hue}"`]);
if (options.hue && options.hueStrength !== undefined) out.push(["hueStrength", `{${round(options.hueStrength)}}`, String(round(options.hueStrength))]);
if (options.refraction === false) out.push(["refraction", "{false}", "false"]);
if (options.samples === 1) out.push(["samples", "{1}", "1"]);
const geometry = geometryEntries(options);
if (geometry.length) {
const literal = `{ ${geometry.map(([key, value]) => `${key}: ${value}`).join(", ")} }`;
out.push(["geometry", `{${literal}}`, literal]);
}
return out;
}
/** JSX props for the options, in a stable order. */
export function jsxProps(options: PlaygroundOptions) {
return entries(options).map(([name, value]) => (value ? `${name}=${value}` : name));
}
function objectLiteral(options: PlaygroundOptions) {
return entries(options).map(([name, , value]) => `${name}: ${value}`);
}
const SHAPE_CLASSES = {
capsule: "relative rounded-full px-6 py-3",
card: "relative rounded-[32px] p-6",
circle: "relative size-40 rounded-full",
} as const;
export function reactSnippet(options: PlaygroundOptions) {
const props = jsxProps(options);
const glass = props.join(" ").length > 56 ? `