@@ -28,7 +28,8 @@ export namespace DOMUtils {
2828 [ DOMUtils . STYLE_DX_VALUES ] :Map < string , Datex . ReactiveValue < string > > ,
2929 [ DOMUtils . STYLE_WEAK_PROPS ] :Map < string , boolean > ,
3030 [ DOMUtils . CHILDREN_DX_VALUES ] :Set < Datex . ReactiveValue > ,
31- [ DOMUtils . DATEX_UPDATE_TYPE ] ?: string
31+ [ DOMUtils . DATEX_UPDATE_TYPE ] ?: string ,
32+ [ DOMUtils . ATTR_SELECTED_BINDING ] ?: Datex . ReactiveValue
3233 }
3334}
3435
@@ -45,6 +46,7 @@ export class DOMUtils {
4546 static readonly STYLE_DX_VALUES : unique symbol = Symbol . for ( "DOMUtils.STYLE_DX_VALUES" ) ;
4647 static readonly STYLE_WEAK_PROPS : unique symbol = Symbol . for ( "DOMUtils.STYLE_WEAK_PROPS" ) ;
4748 static readonly CHILDREN_DX_VALUES : unique symbol = Symbol . for ( "DOMUtils.CHILDREN_DX_VALUES" ) ;
49+ static readonly ATTR_SELECTED_BINDING : unique symbol = Symbol . for ( "DOMUtils.ATTR_SELECTED_BINDING" ) ;
4850
4951 static readonly DATEX_UPDATE_TYPE : unique symbol = Symbol . for ( "DOMUtils.DATEX_UPDATE_TYPE" ) ;
5052 static readonly PLACEHOLDER_CONTENT : unique symbol = Symbol . for ( "DOMUtils.PLACEHOLDER_CONTENT" ) ;
@@ -344,6 +346,11 @@ export class DOMUtils {
344346 if ( element instanceof this . context . HTMLInputElement && attr == "value" && element . getAttribute ( "type" ) == "checkbox" && typeof Datex . ReactiveValue . collapseValue ( value , true , true ) == "boolean" ) {
345347 logger . warn ( `You are assigning the "value" attribute of an <input type="checkbox"> to a boolean value. This has no effect on the checkbox state. Did you mean to use the "checked" attribute instead\\?` )
346348 }
349+
350+ // value:selected is only allowed for input type="radio"
351+ else if ( attr == "value:selected" && ! ( element instanceof this . context . HTMLInputElement && ( element as HTMLInputElement ) . type == "radio" ) ) {
352+ throw new Error ( "The \"value:selected\" attribute is only allowed for <input type=\"radio\"> elements" ) ;
353+ }
347354
348355 value = value ?. [ JSX_INSERT_STRING ] ? value . val : value ; // collapse safely injected strings
349356
@@ -435,13 +442,13 @@ export class DOMUtils {
435442 }
436443
437444 // :out attributes
438- if ( ( isSelectElement || isInputElement || isTextareaElement ) && ( attr == "value:out" || attr == "value:in" || attr == "value" ) ) {
445+ if ( ( isSelectElement || isInputElement || isTextareaElement ) && ( attr == "value:selected" || attr == "value: out" || attr == "value:in" || attr == "value" ) ) {
439446
440447 const event = isSelectElement ? 'change' : 'input' ;
441448 const inputElement = element as unknown as HTMLInputElement ;
442449
443450 // out
444- if ( attr == "value" || attr == "value:out" ) {
451+ if ( attr == "value" || attr == "value:out" || attr == "value:selected" ) {
445452 if ( ! ( type instanceof Datex . Type ) ) {
446453 console . warn ( "Value has no type" , value ) ;
447454 }
@@ -451,11 +458,22 @@ export class DOMUtils {
451458 else if ( type . matchesType ( Datex . Type . std . boolean ) ) element . addEventListener ( event , ( ) => this . handleSetVal ( value , inputElement , inputElement . value , "boolean" ) )
452459 else if ( type . matchesType ( Datex . Type . std . void ) || type . matchesType ( Datex . Type . std . null ) ) { console . warn ( "setting value attribute to " + type , element ) }
453460 else if ( type . matchesType ( Datex . Type . std . time ) ) element . addEventListener ( event , ( ) => {
454- handleSetVal ( new Time ( ( element as unknown as HTMLInputElement ) . valueAsDate ?? new Date ( ( element as unknown as HTMLInputElement ) . value ) ) )
461+ this . handleSetVal ( value , inputElement , new Time ( ( element as unknown as HTMLInputElement ) . valueAsDate ?? new Date ( ( element as unknown as HTMLInputElement ) . value ) ) , "time" )
455462 } )
456463 else throw new Error ( "The type " + type + " is not supported for the '" + attr + "' attribute of the <" + element . tagName . toLowerCase ( ) + "> element" ) ;
464+
465+
466+ // for all input elements that support a max, min or step attribute: listen for attribute changes and update the output value
467+ if ( isInputElement && ( inputElement . type == "number" || inputElement . type == "range" ) ) {
468+ const observer = new MutationObserver ( ( ) => {
469+ if ( type . matchesType ( Datex . Type . std . decimal ) ) this . handleSetVal ( value , inputElement , inputElement . value , "number" ) ;
470+ else if ( type . matchesType ( Datex . Type . std . integer ) ) this . handleSetVal ( value , inputElement , inputElement . value , "bigint" ) ;
471+ } ) ;
472+ observer . observe ( inputElement , { attributes : true , attributeFilter : [ "max" , "min" , "step" ] } )
473+ }
474+
457475 }
458-
476+
459477 // in
460478 if ( attr == "value" || attr == "value:in" ) {
461479 const valid = this . setAttribute ( element , "value" , value . val , rootPath )
@@ -488,6 +506,13 @@ export class DOMUtils {
488506 return valid ;
489507 }
490508
509+ // value:selected initial state
510+ if ( attr == "value:selected" ) {
511+ ( < DOMUtils . elWithUIXAttributes > < unknown > inputElement ) [ DOMUtils . ATTR_SELECTED_BINDING ] = value ;
512+ // check if the defined value of the input is equal to the value of the pointer
513+ if ( inputElement . value == value . val ) inputElement . checked = true ;
514+ }
515+
491516 return true ;
492517 }
493518
@@ -596,7 +621,7 @@ export class DOMUtils {
596621 }
597622
598623 // invalid :out attributes here
599- if ( attr . endsWith ( ":out" ) ) throw new Error ( "Invalid value for " + attr + " attribute - must be a pointer" ) ;
624+ if ( attr . endsWith ( ":out" ) || attr . endsWith ( ":selected" ) ) throw new Error ( "Invalid value for " + attr + " attribute - must be a pointer" ) ;
600625
601626 // value attribute
602627 else if ( attr == "value" ) {
@@ -615,6 +640,13 @@ export class DOMUtils {
615640 else {
616641 ( element as HTMLInputElement ) . value = this . formatAttributeValue ( val , root_path , element )
617642 }
643+
644+ // handle DOMUtils.ATTR_SELECTED_BINDING
645+ if ( ( < DOMUtils . elWithUIXAttributes > element ) [ DOMUtils . ATTR_SELECTED_BINDING ] ) {
646+ if ( ( < DOMUtils . elWithUIXAttributes > element ) [ DOMUtils . ATTR_SELECTED_BINDING ] . val == val ) {
647+ ( element as HTMLInputElement ) . checked = true ;
648+ }
649+ }
618650 }
619651
620652 // set datex-update
0 commit comments