@@ -5,10 +5,11 @@ import { LocalizeMixin } from '@lion/ui/localize-no-side-effects.js';
5
5
import { OverlayMixin , withDropdownConfig } from '@lion/ui/overlays.js' ;
6
6
import { css , html } from 'lit' ;
7
7
import { makeMatchingTextBold , unmakeMatchingTextBold } from './utils/makeMatchingTextBold.js' ;
8
+ import {
9
+ fixOptionA11yForSafari ,
10
+ cleanupOptionA11yForSafari ,
11
+ } from './utils/fixOptionA11yForSafari.js' ;
8
12
import { MatchesOption } from './validators.js' ;
9
- import { CustomChoiceGroupMixin } from '../../form-core/src/choice-group/CustomChoiceGroupMixin.js' ;
10
-
11
- const matchA11ySpanReverseFns = new WeakMap ( ) ;
12
13
13
14
// TODO: make ListboxOverlayMixin that is shared between SelectRich and Combobox
14
15
// TODO: extract option matching based on 'typed character cache' and share that logic
@@ -28,29 +29,17 @@ const matchA11ySpanReverseFns = new WeakMap();
28
29
* LionCombobox: implements the wai-aria combobox design pattern and integrates it as a Lion
29
30
* FormControl
30
31
*/
31
- export class LionCombobox extends LocalizeMixin ( OverlayMixin ( CustomChoiceGroupMixin ( LionListbox ) ) ) {
32
+ export class LionCombobox extends LocalizeMixin ( OverlayMixin ( LionListbox ) ) {
32
33
/** @type {any } */
33
- static get properties ( ) {
34
- return {
35
- autocomplete : { type : String , reflect : true } ,
36
- matchMode : {
37
- type : String ,
38
- attribute : 'match-mode' ,
39
- } ,
40
- showAllOnEmpty : {
41
- type : Boolean ,
42
- attribute : 'show-all-on-empty' ,
43
- } ,
44
- requireOptionMatch : {
45
- type : Boolean ,
46
- } ,
47
- allowCustomChoice : {
48
- type : Boolean ,
49
- attribute : 'allow-custom-choice' ,
50
- } ,
51
- __shouldAutocompleteNextUpdate : Boolean ,
52
- } ;
53
- }
34
+ static properties = {
35
+ autocomplete : { type : String , reflect : true } ,
36
+ matchMode : { type : String , attribute : 'match-mode' } ,
37
+ showAllOnEmpty : { type : Boolean , attribute : 'show-all-on-empty' } ,
38
+ // N.B.: deprecated: use allowCustomChoice instead
39
+ requireOptionMatch : { type : Boolean } ,
40
+ allowCustomChoice : { type : Boolean , attribute : 'allow-custom-choice' } ,
41
+ __shouldAutocompleteNextUpdate : { type : Boolean , state : true } ,
42
+ } ;
54
43
55
44
static get styles ( ) {
56
45
return [
@@ -358,7 +347,7 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
358
347
*/
359
348
get _listboxNode ( ) {
360
349
return /** @type {LionOptions } */ (
361
- ( this . _overlayCtrl && this . _overlayCtrl . contentNode ) ||
350
+ this . _overlayCtrl ?. contentNode ||
362
351
Array . from ( this . children ) . find ( child => child . slot === 'listbox' )
363
352
) ;
364
353
}
@@ -372,7 +361,8 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
372
361
}
373
362
374
363
/**
375
- * @returns {boolean }
364
+ * @type {boolean }
365
+ * @deprecated
376
366
*/
377
367
get requireOptionMatch ( ) {
378
368
return ! this . allowCustomChoice ;
@@ -407,11 +397,6 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
407
397
* By default, the listbox closes on empty, similar to wai-aria example and <datalist>
408
398
*/
409
399
this . showAllOnEmpty = false ;
410
- /**
411
- * If set to false, the value is allowed to not match any of the options.
412
- * We set the default to true for backwards compatibility
413
- */
414
- this . requireOptionMatch = true ;
415
400
/**
416
401
* @configure ListboxMixin: the wai-aria pattern and <datalist> rotate
417
402
*/
@@ -490,7 +475,7 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
490
475
}
491
476
if ( name === 'modelValue' && this . modelValue && this . modelValue !== oldValue ) {
492
477
if ( this . _syncToTextboxCondition ( this . modelValue , this . _oldModelValue ) ) {
493
- if ( ! this . multipleChoice ) {
478
+ if ( this . _isSingleChoice ) {
494
479
const textboxValue = this . _getTextboxValueFromOption (
495
480
this . formElements [ /** @type {number } */ ( this . checkedIndex ) ] ,
496
481
) ;
@@ -507,13 +492,13 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
507
492
508
493
/**
509
494
* Converts viewValue to modelValue
510
- * @override CustomChoiceGroupMixin
495
+ * @override ChoiceGroupMixin
511
496
* @param {string|string[] } value - viewValue: the formatted value inside <input>
512
- * @returns {* } modelValue
497
+ * @returns {any } modelValue
513
498
*/
514
499
parser ( value ) {
515
500
if (
516
- this . requireOptionMatch &&
501
+ ! this . allowCustomChoice &&
517
502
this . checkedIndex === - 1 &&
518
503
value !== '' &&
519
504
! Array . isArray ( value )
@@ -525,7 +510,7 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
525
510
526
511
/**
527
512
* When textbox value doesn't match checkedIndex anymore, update accordingly...
528
- * @protected
513
+ * @private
529
514
*/
530
515
__unsyncCheckedIndexOnInputChange ( ) {
531
516
const autoselect = this . _autoSelectCondition ( ) ;
@@ -782,25 +767,7 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
782
767
// eslint-disable-next-line class-methods-use-this
783
768
_highlightMatchedOption ( option , matchingString ) {
784
769
makeMatchingTextBold ( option , matchingString ) ;
785
-
786
- // For Safari, we need to add a label to the element
787
- if ( option . textContent ) {
788
- const a11ySpan = document . createElement ( 'span' ) ;
789
- a11ySpan . setAttribute ( 'aria-label' , option . textContent . replace ( / \s + / g, ' ' ) ) ;
790
- Array . from ( option . childNodes ) . forEach ( childNode => {
791
- a11ySpan . appendChild ( childNode ) ;
792
- } ) ;
793
- option . appendChild ( a11ySpan ) ;
794
-
795
- matchA11ySpanReverseFns . set ( option , ( ) => {
796
- Array . from ( a11ySpan . childNodes ) . forEach ( childNode => {
797
- option . appendChild ( childNode ) ;
798
- } ) ;
799
- if ( option . contains ( a11ySpan ) ) {
800
- option . removeChild ( a11ySpan ) ;
801
- }
802
- } ) ;
803
- }
770
+ fixOptionA11yForSafari ( option ) ;
804
771
}
805
772
806
773
/**
@@ -826,10 +793,7 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
826
793
// eslint-disable-next-line class-methods-use-this
827
794
_unhighlightMatchedOption ( option ) {
828
795
unmakeMatchingTextBold ( option ) ;
829
-
830
- if ( matchA11ySpanReverseFns . has ( option ) ) {
831
- matchA11ySpanReverseFns . get ( option ) ( ) ;
832
- }
796
+ cleanupOptionA11yForSafari ( option ) ;
833
797
}
834
798
/* eslint-enable no-param-reassign */
835
799
@@ -1095,7 +1059,7 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
1095
1059
break ;
1096
1060
case 'Backspace' :
1097
1061
case 'Delete' :
1098
- if ( this . requireOptionMatch ) {
1062
+ if ( ! this . allowCustomChoice ) {
1099
1063
super . _listboxOnKeyDown ( ev ) ;
1100
1064
} else {
1101
1065
this . opened = false ;
@@ -1107,7 +1071,7 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
1107
1071
}
1108
1072
1109
1073
if (
1110
- ! this . requireOptionMatch &&
1074
+ this . allowCustomChoice &&
1111
1075
this . multipleChoice &&
1112
1076
( ! this . formElements [ this . activeIndex ] ||
1113
1077
this . formElements [ this . activeIndex ] . hasAttribute ( 'aria-hidden' ) ||
@@ -1158,14 +1122,15 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(CustomChoiceGroupMi
1158
1122
*/
1159
1123
// eslint-disable-next-line no-unused-vars
1160
1124
_syncToTextboxMultiple ( modelValue , oldModelValue = [ ] ) {
1161
- if ( this . requireOptionMatch ) {
1162
- const diff = modelValue . filter ( x => ! oldModelValue . includes ( x ) ) ;
1163
- const newValue = this . formElements
1164
- . filter ( option => diff . includes ( option . choiceValue ) )
1165
- . map ( option => this . _getTextboxValueFromOption ( option ) )
1166
- . join ( ' ' ) ;
1167
- this . _setTextboxValue ( newValue ) ; // or last selected value?
1125
+ if ( this . allowCustomChoice ) {
1126
+ return ;
1168
1127
}
1128
+ const diff = modelValue . filter ( x => ! oldModelValue . includes ( x ) ) ;
1129
+ const newValue = this . formElements
1130
+ . filter ( option => diff . includes ( option . choiceValue ) )
1131
+ . map ( option => this . _getTextboxValueFromOption ( option ) )
1132
+ . join ( ' ' ) ;
1133
+ this . _setTextboxValue ( newValue ) ; // or last selected value?
1169
1134
}
1170
1135
1171
1136
/**
0 commit comments