/** * Preview component for the live swatch display in the admin settings UI. */ import React, { useEffect, useMemo } from "react"; import SwatchGroup from "../../frontend/components/SwatchGroup"; import type { Settings, PreviewMode } from "../types"; import { buildPreviewSelect, getLivePreviewDemoConfig } from "../previewUtils"; export type PreviewProps = { rawSettings: Settings; showLabels: boolean; showLabelsColors: boolean; enableTooltips: boolean; layoutPaddingTop: number; layoutPaddingRight: number; layoutPaddingBottom: number; layoutPaddingLeft: number; labelFontSize: number | string; labelTextColor: string; mode: PreviewMode; selectedColor: string; swatchWidth: number; swatchHeight: number; swatchGap: number; selectedIndex: number; onSelectIndex: (idx: number) => void; }; export function Preview(props: PreviewProps): React.ReactElement { const { rawSettings, showLabels, showLabelsColors, enableTooltips, layoutPaddingTop, layoutPaddingRight, layoutPaddingBottom, layoutPaddingLeft, labelFontSize, labelTextColor, mode, selectedColor, swatchWidth, swatchHeight, swatchGap, selectedIndex, onSelectIndex, } = props; // Live Preview UX: buttons always show text; colors use their dedicated label setting. const showLabelsEffective = (() => { if (mode === "buttons") return true; if (mode === "colors") return showLabelsColors; return showLabels; // fallback })(); const demo = useMemo(() => getLivePreviewDemoConfig(), []); const values = useMemo(() => { if (mode === "colors") return demo.colors.map(({ value, label }) => ({ value, label })); return demo.buttons.map(({ value, label }) => ({ value, label })); }, [demo, mode]); const selectEl = useMemo(() => buildPreviewSelect(values), [values]); useEffect(() => { const clamped = Math.min(Math.max(selectedIndex, 0), values.length - 1); const nextValue = values[clamped]?.value; if (!nextValue) return; if (selectEl.value !== nextValue) { selectEl.value = nextValue; selectEl.dispatchEvent(new Event("change", { bubbles: true })); } }, [selectedIndex, selectEl, values]); useEffect(() => { const onChange = () => { const idx = values.findIndex((v) => v.value === selectEl.value); if (idx >= 0 && idx !== selectedIndex) onSelectIndex(idx); }; selectEl.addEventListener("change", onChange); return () => selectEl.removeEventListener("change", onChange); }, [onSelectIndex, selectEl, selectedIndex, values]); const items = useMemo(() => { const colorsByValue = [...demo.colors, ...demo.buttons].reduce( (acc, c) => { acc[c.value] = c.color; return acc; }, {} as Record, ); return { colorsByValue }; }, [demo]); const swatches = useMemo(() => { const base = values.reduce( (acc, item) => { acc[item.value] = { label: item.label, hide_label: !showLabelsEffective, }; return acc; }, {} as Record, ); if (mode === "colors") { return values.reduce((acc, item) => { const color = items.colorsByValue[item.value] || "#22c55e"; acc[item.value] = { ...base[item.value], type: "color", color, label: item.label, }; return acc; }, {} as any); } return values.reduce((acc, item) => { const color = items.colorsByValue[item.value] || "#22c55e"; acc[item.value] = { ...base[item.value], type: "button", color, label: item.label, }; return acc; }, {} as any); }, [items, mode, showLabelsEffective, values]); const settingsOverride = useMemo( () => ({ // Keep the shared preview payload limited to the base preview shape. padding_top: rawSettings.padding_top, padding_right: rawSettings.padding_right, padding_bottom: rawSettings.padding_bottom, padding_left: rawSettings.padding_left, external_padding_top: rawSettings.external_padding_top, external_padding_right: rawSettings.external_padding_right, external_padding_bottom: rawSettings.external_padding_bottom, external_padding_left: rawSettings.external_padding_left, shape: "square", layout: "wrap", swatch_style: "underline", show_labels: showLabelsEffective, show_labels_colors: showLabelsColors, enable_tooltips: enableTooltips, selected_color: selectedColor, swatch_width: swatchWidth, swatch_height: swatchHeight, label_font_size: labelFontSize, label_text_color: labelTextColor, layout_padding_top: layoutPaddingTop, layout_padding_right: layoutPaddingRight, layout_padding_bottom: layoutPaddingBottom, layout_padding_left: layoutPaddingLeft, swatch_gap: swatchGap, }), [ rawSettings, enableTooltips, labelFontSize, labelTextColor, layoutPaddingBottom, layoutPaddingLeft, layoutPaddingRight, layoutPaddingTop, selectedColor, showLabelsEffective, showLabelsColors, swatchGap, swatchHeight, swatchWidth, ], ); return ( ); } export default Preview;