fix(components): type-coerce string properties + reseat on pininfo-change
Root cause of the 'digits=4 display seated with the 1-digit COM pinout' bug: property values arrive as STRINGS (agent set_component_property, the property dialog's text inputs) and were assigned to the web component verbatim — wokwi's 7segment does switch(this.digits) with numeric cases, so el.digits='4' silently fell back to the 1-digit pinout (and 'false' stayed truthy for boolean props like colon). - DynamicComponent now coerces string values to the TYPE of the metadata default for that key (number/boolean) before assigning. - New pininfo-change listener: when a property swaps the element's pin set (digits, flip, pins edge), the elements announce it — re-derive the breadboard seating then, with the fresh pinout, instead of never.
This commit is contained in:
parent
aab5e1be08
commit
206cda78af
|
|
@ -293,20 +293,57 @@ export const DynamicComponent: React.FC<DynamicComponentProps> = ({
|
|||
const isInteractive = logic?.attachEvents !== undefined;
|
||||
|
||||
/**
|
||||
* Sync React properties to Web Component
|
||||
* Sync React properties to Web Component.
|
||||
*
|
||||
* Values arriving as strings (agent set_component_property, the text
|
||||
* inputs in the property dialog) are coerced to the type of the
|
||||
* metadata DEFAULT for that key. Without this, `el.digits = '4'`
|
||||
* (string) silently breaks wokwi elements that strict-match
|
||||
* (`switch (this.digits) { case 4: ... }` -> falls back to the 1-digit
|
||||
* pinout), and `'false'` stays truthy for boolean props like colon.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!elementRef.current) return;
|
||||
|
||||
Object.entries(properties).forEach(([key, value]) => {
|
||||
try {
|
||||
(elementRef.current as any)[key] = value;
|
||||
let coerced: any = value;
|
||||
if (typeof value === 'string') {
|
||||
const def = metadata.defaultValues?.[key];
|
||||
if (typeof def === 'number' && value.trim() !== '' && !Number.isNaN(Number(value))) {
|
||||
coerced = Number(value);
|
||||
} else if (typeof def === 'boolean') {
|
||||
coerced = value === 'true' || value === '1';
|
||||
}
|
||||
}
|
||||
(elementRef.current as any)[key] = coerced;
|
||||
} catch (error) {
|
||||
console.warn(`Failed to set property ${key} on ${metadata.tagName}:`, error);
|
||||
}
|
||||
});
|
||||
}, [properties, metadata.tagName]);
|
||||
|
||||
/**
|
||||
* Property changes that swap the element's pin SET (7segment digits,
|
||||
* LED flip, display pins edge) re-render asynchronously and announce
|
||||
* themselves with a 'pininfo-change' event. Re-derive the breadboard
|
||||
* seating then — reseating synchronously on the property write would
|
||||
* read the STALE pinout and seat ghost pins.
|
||||
*/
|
||||
useEffect(() => {
|
||||
const el = elementRef.current;
|
||||
if (!el) return;
|
||||
const onPinInfoChange = () => {
|
||||
try {
|
||||
useSimulatorStore.getState().reseatComponentOnBreadboard(id);
|
||||
} catch {
|
||||
// headless / tests
|
||||
}
|
||||
};
|
||||
el.addEventListener('pininfo-change', onPinInfoChange);
|
||||
return () => el.removeEventListener('pininfo-change', onPinInfoChange);
|
||||
}, [id, metadata.tagName]);
|
||||
|
||||
/**
|
||||
* Extract pinInfo from web component after it initializes
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2479,6 +2479,13 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
updates.properties && 'rotation' in updates.properties;
|
||||
if (updates.x !== undefined || updates.y !== undefined || rotationChanged) {
|
||||
get().updateWirePositions(id);
|
||||
// Reseat ONLY on geometry changes (move/rotate) — the DOM pinInfo is
|
||||
// still valid for those. Property changes that swap the pin SET
|
||||
// (7segment digits, LED flip) re-render asynchronously; reseating
|
||||
// now would read the STALE pinout and seat ghost pins (seen live:
|
||||
// a digits=4 display seated with the 1-digit COM pinout). Those go
|
||||
// through the element's 'pininfo-change' event instead
|
||||
// (DynamicComponent listener).
|
||||
get().reseatComponentOnBreadboard(id);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue