/**
* Shared swatch-picker preview icons.
*
* Shape, layout and selector-style icons are identical between the base
* General tab and extension extras, so they live here and are keyed by plain
* strings to avoid coupling separate shape/layout/style unions.
*/
import React from "react";
export const MATERIAL_IMAGE_DEMO_ITEMS = [
{
value: "oak",
label: "Oak",
background: "#b98245",
accent: "#6f4624",
motif: "grain",
},
{
value: "denim",
label: "Denim",
background: "#2f5f8f",
accent: "#dbeafe",
motif: "stitch",
},
{
value: "marble",
label: "Marble",
background: "#f8fafc",
accent: "#94a3b8",
motif: "vein",
},
{
value: "clay",
label: "Clay",
background: "#c65f3f",
accent: "#fed7aa",
motif: "arc",
},
{
value: "moss",
label: "Moss",
background: "#556b3f",
accent: "#c7d2a6",
motif: "leaf",
},
{
value: "carbon",
label: "Carbon",
background: "#111827",
accent: "#64748b",
motif: "grid",
},
] as const;
export function buildMaterialSvgDataUri(
item: (typeof MATERIAL_IMAGE_DEMO_ITEMS)[number],
): string {
const motifs: Record<
(typeof MATERIAL_IMAGE_DEMO_ITEMS)[number]["motif"],
string
> = {
grain: ``,
stitch: ``,
vein: ``,
arc: ``,
leaf: ``,
grid: ``,
};
const svg = ``;
// encodeURIComponent leaves ( ) ' untouched, but the gradient reference
// `fill="url(#g)"` must not close an unquoted CSS `url(...)` early, so
// escape those characters too.
const encoded = encodeURIComponent(svg).replace(
/[()']/g,
(char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,
);
return `data:image/svg+xml;charset=utf-8,${encoded}`;
}
/**
* Layout picker icon shared by the free General tab and the extension extras.
* Slider and flow icons carry animated parts (keyframes live in
* src/admin/index.css so both tiers ship them).
*/
export function LayoutPreviewIcon({
layout,
}: {
layout: string;
}): React.ReactElement {
switch (layout) {
case "grid":
return (
);
case "pill":
return (
);
case "slider":
return (
);
case "flow":
return (
);
case "wrap":
default:
return (
);
}
}
/**
* Selector-style picker icon shared by the free General tab and the extension
* extras. The selected-state indicator (underline bar, outline ring or
* background halo) animates in on a loop; keyframes live in src/admin/index.css.
*/
export function SelectorStylePreviewIcon({
style,
}: {
style: string;
}): React.ReactElement {
const idle = (
);
if (style === "outline") {
return (
);
}
if (style === "background") {
return (
);
}
return (
);
}
export function ShapePreviewIcon({
shape,
}: {
shape: string;
}): React.ReactElement {
switch (shape) {
case "soft":
return (
);
case "rounded":
return (
);
case "squircle":
return (
);
case "circle":
return (
);
case "diamond":
return (
);
case "hexagon":
return (
);
case "square":
default:
return (
);
}
}
/**
* Tooltip position picker icon shared by the free Tooltips tab. The bubble
* fades in from the swatch side (keyframes in src/admin/index.css).
*/
export function TooltipPositionPreviewIcon({
position,
selected,
}: {
position: "top" | "bottom" | "left" | "right";
selected: boolean;
}): React.ReactElement {
const swatchFill = selected ? "#3b82f6" : "#94a3b8";
const swatchStroke = selected ? "#2563eb" : "#cbd5e1";
const tooltipFill = selected ? "#1d4ed8" : "#64748b";
const caretFill = tooltipFill;
const lineFill = selected ? "#93c5fd" : "#cbd5e1";
/* ── swatch center ── */
const cx = 42;
const cy = 32;
const sw = 20; // swatch width
const sh = 14; // swatch height
/* ── tooltip pill ── */
const tw = 30; // tooltip width
const th = 8; // tooltip height
const gap = 4; // space between swatch edge and tooltip pill
const tooltip = {
top: { x: cx - tw / 2, y: cy - sh / 2 - gap - th },
bottom: { x: cx - tw / 2, y: cy + sh / 2 + gap },
left: { x: cx - sw / 2 - gap - tw, y: cy - th / 2 },
right: { x: cx + sw / 2 + gap, y: cy - th / 2 },
}[position];
/* ── caret (small triangle pointing toward swatch) ── */
const cs = 4; // caret size
const caret = {
top: `M${cx - cs},${tooltip.y + th} L${cx},${tooltip.y + th + cs} L${cx + cs},${tooltip.y + th}`,
bottom: `M${cx - cs},${tooltip.y} L${cx},${tooltip.y - cs} L${cx + cs},${tooltip.y}`,
left: `M${tooltip.x + tw},${cy - cs} L${tooltip.x + tw + cs},${cy} L${tooltip.x + tw},${cy + cs}`,
right: `M${tooltip.x},${cy - cs} L${tooltip.x - cs},${cy} L${tooltip.x},${cy + cs}`,
}[position];
/* ── text line inside tooltip ── */
const lineW = tw - 10;
const lineH = 2.5;
const lineX = tooltip.x + (tw - lineW) / 2;
const lineY = tooltip.y + (th - lineH) / 2;
return (
);
}