diff --git a/admin/src/components/StyleMode.tsx b/admin/src/components/StyleMode.tsx new file mode 100644 index 0000000..7e9a1c9 --- /dev/null +++ b/admin/src/components/StyleMode.tsx @@ -0,0 +1,224 @@ +import React, { useState } from 'react'; +import { FormField, FormSettings } from '../types'; +import { FormTheme, DEFAULT_THEME, resolveThemeVars, PaletteKey } from '../theme'; +import { FORM_CSS } from '../form-css'; +import { C, FF } from '../ui'; + +interface Props { + title: string; + description: string; + fields: FormField[]; + settings: FormSettings; + theme: FormTheme; + onChange: (t: FormTheme) => void; +} + +/* ── small controls ────────────────────────────────────────────────────── */ +const grpT: React.CSSProperties = { font: `700 10px ${FF}`, letterSpacing: '.6px', textTransform: 'uppercase', color: C.n500, margin: '0 0 10px' }; +const ctlLbl: React.CSSProperties = { font: `600 13px ${FF}`, color: C.n800, display: 'block', margin: '0 0 7px' }; + +function Seg({ value, options, onChange }: { value: T; options: { v: T; label: string }[]; onChange: (v: T) => void }) { + return ( +
+ {options.map((o, i) => { + const on = value === o.v; + return ( + + ); + })} +
+ ); +} + +function Palette({ colors, value, onSelect, onAdd, onDelete }: { + colors: string[]; value: string; + onSelect: (c: string) => void; onAdd: (c: string) => void; onDelete: (c: string) => void; +}) { + const [hover, setHover] = useState(null); + return ( +
+ {colors.map((c) => { + const on = value.toLowerCase() === c.toLowerCase(); + return ( +
setHover(c)} onMouseLeave={() => setHover(null)}> + + )} +
+ ); + })} + {/* add a color to the palette via the native picker */} + +
+ ); +} + +function Group({ title, children }: { title: string; children: React.ReactNode }) { + return ( +
+
{title}
+
{children}
+
+ ); +} + +/* ── faithful preview: same .sfb-* classes + FORM_CSS as the embed ─────── */ +function PreviewInput({ field }: { field: FormField }) { + const ph = field.placeholder || ''; + if (field.type === 'textarea') return