From f005eedc732df2512c01f129c8f3542a4eaa9729 Mon Sep 17 00:00:00 2001 From: Vincent Fretin Date: Thu, 23 Apr 2026 11:36:49 +0200 Subject: [PATCH 1/2] Support editing a property of type array without oneOf like animation startEvents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit InputWidget now owns its (de)serialization (mirroring SelectWidget's pattern): it accepts value as string, array, or custom object, and when a schema is provided it stringifies non-string values via schema.stringify for display and parses the input back via schema.parse on change for array-typed properties. This ensures A-Frame always receives an array for array-type properties — avoiding arrayStringify being called on a raw string downstream (which would crash with "t.join is not a function"). The "custom string type like event-set" stringify previously done in PropertyRow is also moved here. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/components/PropertyRow.js | 9 ++----- src/components/widgets/InputWidget.js | 31 +++++++++++++++++++++--- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/components/components/PropertyRow.js b/src/components/components/PropertyRow.js index bbe90a389..faef3fa08 100644 --- a/src/components/components/PropertyRow.js +++ b/src/components/components/PropertyRow.js @@ -70,16 +70,11 @@ export default class PropertyRow extends React.Component { const props = this.props; const type = this.getType(); - let value = + const value = type === 'selector' ? props.entity.getDOMAttribute(props.componentname)?.[props.name] : props.data; - if (type === 'string' && value && typeof value !== 'string') { - // Allow editing a custom type like event-set component schema - value = props.schema.stringify(value); - } - const widgetProps = { name: props.name, onChange: function (name, value) { @@ -136,7 +131,7 @@ export default class PropertyRow extends React.Component { return ; } default: { - return ; + return ; } } } diff --git a/src/components/widgets/InputWidget.js b/src/components/widgets/InputWidget.js index 6411d2d68..c8b25b459 100644 --- a/src/components/widgets/InputWidget.js +++ b/src/components/widgets/InputWidget.js @@ -7,27 +7,50 @@ export default class InputWidget extends React.Component { name: PropTypes.string.isRequired, onBlur: PropTypes.func, onChange: PropTypes.func, + schema: PropTypes.object, value: PropTypes.any }; constructor(props) { super(props); - this.state = { value: this.props.value || '' }; + this.state = { value: this.stringifyValue(props.value) }; this.input = React.createRef(); } + stringifyValue = (value) => { + // For selector and selectorAll types, getDOMAttribute returns null for + // single-property schema and undefined for multi-property schema when the + // property is not set. + if (value === undefined || value === null) return ''; + if (typeof value === 'string') return value; + // Non-string value (array, custom object like event-set): stringify for display + if (this.props.schema) return this.props.schema.stringify(value); + return String(value); + }; + + parseInput = (value) => { + // The type array doesn't bailout-on-string in its stringify + // (arrayStringify), so we need to parse the input value before calling + // onChange. That could potentially happen for a custom property that + // implements its own parse/stringify functions. + if (this.props.schema) { + return this.props.schema.parse(value); + } + return value; + }; + onChange = (event) => { const value = event.target.value; this.setState({ value: value }); if (this.props.onChange) { - this.props.onChange(this.props.name, value); + this.props.onChange(this.props.name, this.parseInput(value)); } }; onBlur = (event) => { if (this.props.onBlur) { const value = event.target.value; - this.props.onBlur(this.props.name, value); + this.props.onBlur(this.props.name, this.parseInput(value)); } }; @@ -43,7 +66,7 @@ export default class InputWidget extends React.Component { componentDidUpdate(prevProps) { if (this.props.value !== prevProps.value) { - this.setState({ value: this.props.value || '' }); + this.setState({ value: this.stringifyValue(this.props.value) }); } } From 296d3d7aa3de06d9c568534f9dc5a784f5668d8e Mon Sep 17 00:00:00 2001 From: Vincent Fretin Date: Sat, 25 Apr 2026 12:25:10 +0200 Subject: [PATCH 2/2] PropertyRow: support selector and selectorAll via InputWidget on blur MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For selector and selectorAll types, read the value from getDOMAttribute (the raw selector string) rather than props.data (the parsed Element/NodeList), and commit on blur instead of on every keystroke — querying the DOM on each character is wasteful and a partial selector is rarely valid. Omit the schema when rendering InputWidget so the typed string is passed through to setAttribute as-is, which A-Frame preserves verbatim in attrValue even if it doesn't resolve. Co-Authored-By: Claude Opus 4.7 (1M context) --- examples/index.html | 4 ++- src/components/components/PropertyRow.js | 43 +++++++++++++++++------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/examples/index.html b/examples/index.html index 11c29114f..c14923230 100644 --- a/examples/index.html +++ b/examples/index.html @@ -9,7 +9,9 @@