+ {CONDITION_KINDS.map((k) => {
+ const active = conditions.some((c) => c.kind === k);
+ return (
+
+
+ {k === 'near_object' && active && nearSelect && (
+
+ )}
+
+ );
+ })}
+
+ );
+
+ const renderPresetCard = (p: FurniturePreset) => {
+ const open = openId === 'preset_' + p.id;
+ return (
+
+
toggleOpen('preset_' + p.id)}>
+ ▶
+
+ {p.name}
+ e.stopPropagation()}>
+ setPresetAmount(p.id, v)}
+ />
+
+
+ {open && (
+
+
+
+
+
+
+ Default Conditions
+
+ {conditionsEditor(p.conditions, (k, enabled) => togglePresetCondition(p.id, k, enabled))}
+
+ )}
+
+ );
+ };
+
const renderObjectCard = (f: FurnitureConfig) => {
const open = openId === f.id;
const selected = selectedId === f.id;
return (
-
+
toggleOpen(f.id)}>
- {'\u25B6'}
+ ▶
{f.name}
{f.lockedForOptimization && locked}
{open && (
-
+
-
-
+
e.stopPropagation()}
+ onChange={(e) => {
+ const updated = state.furniture.map((x) => (x.id === f.id ? { ...x, name: e.target.value } : x));
+ onStateChange({ ...state, furniture: updated });
+ }}
+ />
+
+
-
-
+
-
Conditions
-
- {CONDITION_KINDS.map((k) => {
- const active = f.conditions.some((c) => c.kind === k);
- const targetId = (f.conditions.find((c) => c.kind === k)?.params as { targetId?: string } | undefined)
- ?.targetId;
- return (
-
-
- {k === 'near_object' && active && (
-
- )}
-
- );
- })}
-
+ {conditionsEditor(
+ f.conditions,
+ (k, enabled) => toggleFurnitureCondition(f.id, k, enabled),
+ {
+ value:
+ (f.conditions.find((c) => c.kind === 'near_object')?.params as { targetId?: string } | undefined)
+ ?.targetId || '',
+ onChange: (v) => setNearObjectTarget(f.id, v),
+ options: state.furniture.filter((o) => o.id !== f.id).map((o) => ({ id: o.id, name: o.name })),
+ },
+ )}
)}
@@ -243,16 +321,10 @@ export function ControlPanel({
Venue Planner
-
@@ -266,18 +338,20 @@ export function ControlPanel({
@@ -296,41 +370,8 @@ export function ControlPanel({
-
Furniture Presets
-
+
Presets
+
{(state.presets || []).map(renderPresetCard)}
)}
- {state.furniture.length === 0 && !state.entrance ? (
+ {state.furniture.length === 0 ? (
No objects yet — add a preset or create one.
) : (
{state.furniture.map(renderObjectCard)}
@@ -407,11 +448,9 @@ export function ControlPanel({
{optimizing ? `Optimizing… ${timeLeft}s` : 'Optimize Layout'}
{optimizing && (
- <>
-
- >
+
)}
)}
diff --git a/src/components/NumberField.tsx b/src/components/NumberField.tsx
new file mode 100644
index 0000000..67c7589
--- /dev/null
+++ b/src/components/NumberField.tsx
@@ -0,0 +1,73 @@
+import { useEffect, useState } from 'react';
+
+interface NumberFieldProps {
+ value: number;
+ onChange: (value: number) => void;
+ min?: number;
+ max?: number;
+ step?: number;
+ width?: number | string;
+ className?: string;
+ title?: string;
+}
+
+export function NumberField({ value, onChange, min, max, step, width, className, title }: NumberFieldProps) {
+ const [text, setText] = useState