11import { animateTo , stopAnimations } from '../../internal/animate.js' ;
2- import { property , query , state } from 'lit/decorators.js' ;
3- import { type CSSResultGroup , html , TemplateResult , unsafeCSS } from 'lit' ;
4- import { watch } from '../../internal/watch' ;
5- import ZincElement , { ZincFormControl } from '../../internal/zinc-element' ;
62import { classMap } from "lit/directives/class-map.js" ;
7- import { FormControlController , validValidityState } from "../../internal/form" ;
8- import { scrollIntoView } from "../../internal/scroll" ;
3+ import { type CSSResultGroup , html , type TemplateResult , unsafeCSS } from 'lit' ;
94import { defaultValue } from "../../internal/default-value" ;
5+ import { FormControlController } from "../../internal/form" ;
6+ import { getAnimation , setDefaultAnimation } from "../../utilities/animation-registry" ;
107import { HasSlotController } from "../../internal/slot" ;
118import { LocalizeController } from "../../utilities/localize" ;
12- import { ZnRemoveEvent } from "../../events/zn-remove" ;
9+ import { property , query , state } from 'lit/decorators.js' ;
10+ import { scrollIntoView } from "../../internal/scroll" ;
1311import { unsafeHTML } from "lit/directives/unsafe-html.js" ;
14- import { getAnimation , setDefaultAnimation } from "../../utilities/animation-registry" ;
1512import { waitForEvent } from "../../internal/event" ;
13+ import { watch } from '../../internal/watch' ;
14+ import type { ZincFormControl } from '../../internal/zinc-element' ;
15+ import ZincElement from '../../internal/zinc-element' ;
1616import ZnChip from "../chip" ;
1717import ZnIcon from "../icon" ;
18- import ZnOption from "../option" ;
1918import ZnPopup from "../popup" ;
19+ import type { ZnRemoveEvent } from "../../events/zn-remove" ;
20+ import type ZnOption from "../option" ;
2021
2122import styles from './select.scss' ;
2223
@@ -94,22 +95,38 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
9495 @state ( ) displayLabel = '' ;
9596 @state ( ) currentOption : ZnOption ;
9697 @state ( ) selectedOptions : ZnOption [ ] = [ ] ;
98+ @state ( ) private valueHasChanged : boolean = false ;
9799
98100 /** The name of the select, submitted as a name/value pair with form data. */
99101 @property ( ) name = '' ;
100102
103+ private _value : string | string [ ] = '' ;
104+
105+ get value ( ) {
106+ return this . _value
107+ }
108+
109+
101110 /**
102111 * The current value of the select, submitted as a name/value pair with form data. When `multiple` is enabled, the
103112 * value attribute will be a space-delimited list of values based on the options selected, and the value property will
104113 * be an array. **For this reason, values must not contain spaces.**
105114 */
106- @property ( {
107- converter : {
108- fromAttribute : ( value : string ) => value . split ( ' ' ) ,
109- toAttribute : ( value : string [ ] ) => value . join ( ' ' )
115+ @state ( )
116+ set value ( val : string | string [ ] ) {
117+ if ( this . multiple ) {
118+ val = Array . isArray ( val ) ? val : val . split ( ' ' ) ;
119+ } else {
120+ val = Array . isArray ( val ) ? val . join ( ' ' ) : val ;
121+ }
122+
123+ if ( this . _value === val ) {
124+ return ;
110125 }
111- } )
112- value : string | string [ ] = '' ;
126+
127+ this . valueHasChanged = true ;
128+ this . _value = val ;
129+ }
113130
114131 /** The default value of the form control. Primarily used for resetting the form control. */
115132 @defaultValue ( ) defaultValue : string | string [ ] = '' ;
@@ -209,12 +226,12 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
209226
210227 /** Gets the validity state object */
211228 get validity ( ) {
212- return this . valueInput ? this . valueInput . validity : validValidityState
229+ return this . valueInput . validity ;
213230 }
214231
215232 /** Gets the validation message */
216233 get validationMessage ( ) {
217- return this . valueInput ? .validationMessage ;
234+ return this . valueInput . validationMessage ;
218235 }
219236
220237 connectedCallback ( ) {
@@ -237,7 +254,15 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
237254
238255
239256 private addOpenListeners ( ) {
240- const root = this . getRootNode ( ) ;
257+ document . addEventListener ( 'focusin' , this . handleDocumentFocusIn ) ;
258+ document . addEventListener ( 'keydown' , this . handleDocumentKeyDown ) ;
259+ document . addEventListener ( 'mousedown' , this . handleDocumentMouseDown ) ;
260+
261+ // If the component is rendered in a shadow root, we need to attach the focusin listener there too
262+ if ( this . getRootNode ( ) !== document ) {
263+ this . getRootNode ( ) . addEventListener ( 'focusin' , this . handleDocumentFocusIn ) ;
264+ }
265+
241266 if ( 'CloseWatcher' in window ) {
242267 this . closeWatcher ?. destroy ( ) ;
243268 this . closeWatcher = new CloseWatcher ( ) ;
@@ -248,16 +273,18 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
248273 }
249274 } ;
250275 }
251- root . addEventListener ( 'focusin' , this . handleDocumentFocusIn ) ;
252- root . addEventListener ( 'keydown' , this . handleDocumentKeyDown ) ;
253- root . addEventListener ( 'mousedown' , this . handleDocumentMouseDown ) ;
254276 }
255277
256278 private removeOpenListeners ( ) {
257279 const root = this . getRootNode ( ) ;
258280 root . removeEventListener ( 'focusin' , this . handleDocumentFocusIn ) ;
259281 root . removeEventListener ( 'keydown' , this . handleDocumentKeyDown ) ;
260282 root . removeEventListener ( 'mousedown' , this . handleDocumentMouseDown ) ;
283+
284+ if ( this . getRootNode ( ) !== document ) {
285+ this . getRootNode ( ) . removeEventListener ( 'focusin' , this . handleDocumentFocusIn ) ;
286+ }
287+
261288 this . closeWatcher ?. destroy ( ) ;
262289 }
263290
@@ -312,6 +339,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
312339
313340 // If it is open, update the value based on the current selection and close it
314341 if ( this . currentOption && ! this . currentOption . disabled ) {
342+ this . valueHasChanged = true ;
315343 if ( this . multiple ) {
316344 this . toggleOptionSelection ( this . currentOption ) ;
317345 } else {
@@ -472,6 +500,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
472500 const oldValue = this . value ;
473501
474502 if ( option && ! option . disabled ) {
503+ this . valueHasChanged = true
475504 if ( this . multiple ) {
476505 this . toggleOptionSelection ( option ) ;
477506 } else {
@@ -593,6 +622,9 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
593622 // Update selected options cache
594623 this . selectedOptions = this . getAllOptions ( ) . filter ( el => el . selected ) ;
595624
625+ // Keep a reference to the previous `valueHasChanged`. Changes made here don't count has changing the value.
626+ const cachedValueHasChanged : boolean = this . valueHasChanged ;
627+
596628 // Update the value and display label
597629 if ( this . multiple ) {
598630 this . value = this . selectedOptions . map ( el => el . value ) ;
@@ -604,10 +636,13 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
604636 this . displayLabel = this . localize . term ( 'numOptionsSelected' , this . selectedOptions . length ) ;
605637 }
606638 } else {
607- this . value = this . selectedOptions [ 0 ] ?. value ?? '' ;
608- this . displayLabel = this . selectedOptions [ 0 ] ?. getTextLabel ( ) ?? '' ;
639+ const selectedOption = this . selectedOptions [ 0 ] ;
640+ this . value = selectedOption ?. value ?? '' ;
641+ this . displayLabel = selectedOption ?. getTextLabel ( ) ?? '' ;
609642 }
610643
644+ this . valueHasChanged = cachedValueHasChanged ;
645+
611646 // Update validity
612647 this . updateComplete . then ( ( ) => {
613648 this . formControlController . updateValidity ( ) ;
@@ -647,6 +682,13 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
647682
648683 @watch ( 'value' , { waitUntilFirstUpdate : true } )
649684 handleValueChange ( ) {
685+ if ( ! this . valueHasChanged ) {
686+ const cachedValueHasChanged = this . valueHasChanged ;
687+ this . value = this . defaultValue ;
688+
689+ // Set it back to false since this isn't an interaction
690+ this . valueHasChanged = cachedValueHasChanged ;
691+ }
650692 const allOptions = this . getAllOptions ( ) ;
651693 const value = Array . isArray ( this . value ) ? this . value : [ this . value ] ;
652694
@@ -860,7 +902,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
860902 type ="text "
861903 ?disabled =${ this . disabled }
862904 ?required =${ this . required }
863- . value=${ Array . isArray ( this . value ) ? this . value . join ( ', ' ) : this . value }
905+ value=${ Array . isArray ( this . value ) ? this . value . join ( ', ' ) : this . value }
864906 tabindex="-1"
865907 aria-hidden="true"
866908 @focus=${ ( ) => this . focus ( ) }
0 commit comments