11"use client" ;
22
33import { useState , Children , type ReactNode } from "react" ;
4+ import { safeEval } from "@/lib/math/safe-eval" ;
45
56// eslint-disable-next-line @typescript-eslint/no-explicit-any
67type AnyElement = { props : Record < string , any > } ;
@@ -12,8 +13,14 @@ type AnyElement = { props: Record<string, any> };
1213interface ValueProps {
1314 /** Display label for this computed value. */
1415 label : string ;
15- /** Compute function: receives all slider/toggle vars, returns display string. */
16- fn : ( vars : Record < string , number > ) => string ;
16+ /** Math expression string evaluated with all slider/toggle vars. */
17+ expr : string ;
18+ /** Number of decimal places in output (default: 2). */
19+ decimals ?: number ;
20+ /** Prefix before the number (e.g. "$", "€"). */
21+ prefix ?: string ;
22+ /** Suffix after the number (e.g. "%", " CZK"). */
23+ suffix ?: string ;
1724}
1825
1926/** Displays a computed value inside `<Interactive>`. */
@@ -65,8 +72,8 @@ export function Interactive({ title, children }: InteractiveProps) {
6572 if ( ! child || typeof child !== "object" || ! ( "props" in child ) ) return ;
6673 const p = ( child as AnyElement ) . props ;
6774
68- if ( typeof p . fn === "function " && typeof p . label === "string" ) {
69- // Value: has fn + label, no name
75+ if ( typeof p . expr === "string " && typeof p . label === "string" ) {
76+ // Value: has expr + label, no name
7077 valueDefs . push ( p as ValueProps ) ;
7178 } else if (
7279 typeof p . name === "string" &&
@@ -91,11 +98,11 @@ export function Interactive({ title, children }: InteractiveProps) {
9198
9299 /* ── Compute values ── */
93100 const computedValues = valueDefs . map ( ( vd ) => {
94- try {
95- return { label : vd . label , value : vd . fn ( vars ) } ;
96- } catch {
97- return { label : vd . label , value : "—" } ;
98- }
101+ const raw = safeEval ( vd . expr , vars ) ;
102+ if ( ! isFinite ( raw ) ) return { label : vd . label , value : "—" } ;
103+ const decimals = vd . decimals ?? 2 ;
104+ const formatted = ` ${ vd . prefix ?? "" } ${ raw . toFixed ( decimals ) } ${ vd . suffix ?? "" } ` ;
105+ return { label : vd . label , value : formatted } ;
99106 } ) ;
100107
101108 return (
0 commit comments