@@ -28,6 +28,9 @@ export interface BlnSelectProps {
2828 ariaLabel : string ;
2929 ariaLabelledby : string ;
3030 ariaDescribedby : string ;
31+ /** Optional externe Validierungsfunktion. Nur als Property (nicht als Attribut) setzbar. */
32+ validator ?: ( value : string | string [ ] , el : BlnSelect ) => boolean | { valid : boolean ; message ?: string } ;
33+
3134}
3235
3336@customElement ( "bln-select" )
@@ -57,12 +60,42 @@ export class BlnSelect extends TailwindElement {
5760
5861 // Options (programmatisch gesetzt); alternativ kann Slot <option> genutzt werden
5962 @property ( { attribute : false } ) options : BlnSelectProps [ "options" ] = [ ] ;
63+ /** Externe Validierungsfunktion: nur als Property setzbar (attribute: false). */
64+ @property ( { attribute : false } ) validator ?: BlnSelectProps [ 'validator' ] ;
65+
6066
6167 // interner IDs
6268 @state ( ) private _selectId = `bln-select-${ Math . random ( ) . toString ( 36 ) . slice ( 2 ) } ` ;
6369 @state ( ) private _hintId = `bln-select-hint-${ Math . random ( ) . toString ( 36 ) . slice ( 2 ) } ` ;
6470 @state ( ) private _isValidSet = false ;
6571
72+ private runValidation ( ) {
73+ if ( ! this . validator ) return ;
74+
75+ try {
76+ const result = this . validator ( this . value , this ) ;
77+ if ( typeof result === 'boolean' ) {
78+ this . isValid = result ;
79+ } else if ( result && typeof result === 'object' ) {
80+ this . isValid = result . valid ;
81+ // Optional: Fehlermeldung im hint anzeigen
82+ // if (result.message) { ... }
83+ }
84+ this . dispatchEvent ( new CustomEvent ( 'validitychange' , {
85+ detail : {
86+ valid : this . isValid ,
87+ value : this . value
88+ } ,
89+ bubbles : true ,
90+ composed : true
91+ } ) ) ;
92+ } catch ( e ) {
93+ console . error ( 'Validation error:' , e ) ;
94+ this . isValid = false ;
95+ }
96+ }
97+
98+
6699 private onChange = ( e : Event ) => {
67100 const sel = e . currentTarget as HTMLSelectElement ;
68101 if ( this . multiple ) {
@@ -71,6 +104,9 @@ export class BlnSelect extends TailwindElement {
71104 } else {
72105 this . value = sel . value ;
73106 }
107+ // Run external validation if provided
108+ this . runValidation ( ) ;
109+
74110 // Events nach außen weiterreichen
75111 this . dispatchEvent ( new Event ( "change" , { bubbles : true , composed : true } ) ) ;
76112 this . dispatchEvent ( new Event ( "input" , { bubbles : true , composed : true } ) ) ;
@@ -115,8 +151,13 @@ export class BlnSelect extends TailwindElement {
115151
116152 protected willUpdate ( changed : Map < string , any > ) {
117153 if ( changed . has ( 'isValid' ) ) this . _isValidSet = true ;
154+ if ( changed . has ( 'value' ) ) {
155+ // When value changes programmatically, also re-run validation if provided
156+ this . runValidation ( ) ;
157+ }
118158 }
119159
160+
120161 protected render ( ) {
121162 // A11y: aria-describedby automatisch, wenn hint vorhanden
122163 const describedBy = [
0 commit comments