@vonage/vivid 4.24.0 → 4.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/custom-elements.json +12314 -4711
  2. package/elevation/index.cjs +1 -1
  3. package/elevation/index.js +1 -1
  4. package/index.cjs +112 -150
  5. package/index.js +37 -35
  6. package/lib/accordion-item/accordion-item.d.ts +6 -0
  7. package/lib/action-group/action-group.d.ts +6 -0
  8. package/lib/alert/alert.d.ts +20 -0
  9. package/lib/audio-player/audio-player.d.ts +6 -0
  10. package/lib/badge/badge.d.ts +9 -1
  11. package/lib/banner/banner.d.ts +25 -0
  12. package/lib/breadcrumb/breadcrumb.d.ts +6 -0
  13. package/lib/breadcrumb-item/breadcrumb-item.d.ts +63 -7
  14. package/lib/button/button.d.ts +732 -7
  15. package/lib/button/locale.d.ts +3 -0
  16. package/lib/calendar-event/calendar-event.d.ts +6 -0
  17. package/lib/card/card.d.ts +394 -2
  18. package/lib/card/card.template.d.ts +2 -1
  19. package/lib/checkbox/checkbox.d.ts +1822 -5
  20. package/lib/combobox/combobox.d.ts +1827 -20
  21. package/lib/data-grid/data-grid-cell.d.ts +339 -1
  22. package/lib/data-grid/locale.d.ts +5 -0
  23. package/lib/date-picker/date-picker.d.ts +1676 -207
  24. package/lib/date-range-picker/date-range-picker.d.ts +840 -107
  25. package/lib/date-time-picker/date-time-picker.d.ts +1678 -209
  26. package/lib/dial-pad/dial-pad.d.ts +8 -0
  27. package/lib/dialog/dialog.d.ts +12 -0
  28. package/lib/divider/divider.d.ts +6 -0
  29. package/lib/fab/fab.d.ts +6 -0
  30. package/lib/file-picker/file-picker.d.ts +1508 -30
  31. package/lib/header/header.d.ts +6 -0
  32. package/lib/icon/icon.d.ts +1 -0
  33. package/lib/icon/icon.template.d.ts +2 -1
  34. package/lib/menu/menu.d.ts +16 -7
  35. package/lib/menu/name.d.ts +1 -0
  36. package/lib/menu-item/menu-item.d.ts +14 -2
  37. package/lib/nav/nav.d.ts +6 -0
  38. package/lib/nav-disclosure/nav-disclosure.d.ts +13 -0
  39. package/lib/nav-item/nav-item.d.ts +405 -3
  40. package/lib/note/note.d.ts +6 -0
  41. package/lib/number-field/number-field.d.ts +1857 -36
  42. package/lib/option/option.d.ts +12 -0
  43. package/lib/progress/progress.d.ts +6 -0
  44. package/lib/progress-ring/progress-ring.d.ts +6 -0
  45. package/lib/radio/radio.d.ts +1137 -4
  46. package/lib/radio-group/radio-group.d.ts +20 -2
  47. package/lib/range-slider/range-slider.d.ts +752 -5
  48. package/lib/rich-text-editor/definition.d.ts +2 -2
  49. package/lib/rich-text-editor/facades/vivid-prose-mirror.facade.d.ts +3 -1
  50. package/lib/rich-text-editor/locale.d.ts +1 -0
  51. package/lib/rich-text-editor/menubar/menubar.d.ts +6 -0
  52. package/lib/rich-text-editor/rich-text-editor.d.ts +352 -2
  53. package/lib/searchable-select/option-tag.d.ts +6 -0
  54. package/lib/searchable-select/searchable-select.d.ts +1857 -36
  55. package/lib/select/select.d.ts +1843 -28
  56. package/lib/selectable-box/selectable-box.d.ts +6 -0
  57. package/lib/slider/slider.d.ts +384 -2
  58. package/lib/split-button/split-button.d.ts +18 -0
  59. package/lib/switch/switch.d.ts +386 -3
  60. package/lib/tab/tab.d.ts +18 -1
  61. package/lib/tab-panel/tab-panel.d.ts +6 -0
  62. package/lib/tag/tag.d.ts +12 -0
  63. package/lib/tag-group/tag-group.d.ts +6 -0
  64. package/lib/text-area/text-area.d.ts +2174 -19
  65. package/lib/text-field/text-field.d.ts +2195 -31
  66. package/lib/time-picker/time-picker.d.ts +839 -106
  67. package/lib/toggletip/toggletip.d.ts +10 -4
  68. package/lib/tooltip/tooltip.d.ts +10 -4
  69. package/lib/tree-item/tree-item.d.ts +12 -0
  70. package/lib/tree-view/tree-view.d.ts +6 -0
  71. package/lib/video-player/video-player.d.ts +6 -0
  72. package/lib/visually-hidden/definition.d.ts +4 -0
  73. package/lib/visually-hidden/visually-hidden.d.ts +3 -0
  74. package/lib/visually-hidden/visually-hidden.template.d.ts +3 -0
  75. package/locales/de-DE.cjs +33 -1
  76. package/locales/de-DE.js +33 -1
  77. package/locales/en-GB.cjs +33 -1
  78. package/locales/en-GB.js +33 -1
  79. package/locales/en-US.cjs +33 -1
  80. package/locales/en-US.js +33 -1
  81. package/locales/ja-JP.cjs +33 -1
  82. package/locales/ja-JP.js +33 -1
  83. package/locales/zh-CN.cjs +33 -1
  84. package/locales/zh-CN.js +33 -1
  85. package/menu/index.cjs +1 -1
  86. package/menu/index.js +1 -1
  87. package/nav/index.cjs +1 -1
  88. package/nav/index.js +1 -1
  89. package/nav-disclosure/index.cjs +1 -1
  90. package/nav-disclosure/index.js +1 -1
  91. package/nav-item/index.cjs +1 -1
  92. package/nav-item/index.js +1 -1
  93. package/note/index.cjs +1 -1
  94. package/note/index.js +1 -1
  95. package/number-field/index.cjs +1 -1
  96. package/number-field/index.js +1 -1
  97. package/option/index.cjs +1 -1
  98. package/option/index.js +1 -1
  99. package/package.json +1 -1
  100. package/pagination/index.cjs +1 -1
  101. package/pagination/index.js +1 -1
  102. package/popup/index.cjs +1 -1
  103. package/popup/index.js +1 -1
  104. package/progress/index.cjs +1 -1
  105. package/progress/index.js +1 -1
  106. package/progress-ring/index.cjs +1 -1
  107. package/progress-ring/index.js +1 -1
  108. package/radio/index.cjs +1 -1
  109. package/radio/index.js +1 -1
  110. package/radio-group/index.cjs +1 -1
  111. package/radio-group/index.js +1 -1
  112. package/range-slider/index.cjs +1 -1
  113. package/range-slider/index.js +1 -1
  114. package/rich-text-editor/index.cjs +1 -1
  115. package/rich-text-editor/index.js +1 -1
  116. package/searchable-select/index.cjs +1 -1
  117. package/searchable-select/index.js +1 -1
  118. package/select/index.cjs +1 -1
  119. package/select/index.js +1 -1
  120. package/selectable-box/index.cjs +1 -1
  121. package/selectable-box/index.js +1 -1
  122. package/shared/affix.cjs +13 -7
  123. package/shared/affix.js +13 -8
  124. package/shared/aria/delegates-aria.d.ts +6 -0
  125. package/shared/aria/host-semantics.d.ts +6 -0
  126. package/shared/breadcrumb-item.cjs +2 -5
  127. package/shared/breadcrumb-item.js +2 -5
  128. package/shared/button.cjs +13 -11
  129. package/shared/button.js +13 -11
  130. package/shared/calendar-picker.template.cjs +3 -3
  131. package/shared/calendar-picker.template.js +1 -1
  132. package/shared/char-count.cjs +92 -0
  133. package/shared/char-count.js +90 -0
  134. package/shared/definition.js +1 -1
  135. package/shared/definition10.js +1 -1
  136. package/shared/definition11.cjs +27 -44
  137. package/shared/definition11.js +28 -45
  138. package/shared/definition12.js +1 -1
  139. package/shared/definition13.js +1 -1
  140. package/shared/definition14.cjs +53 -22
  141. package/shared/definition14.js +54 -23
  142. package/shared/definition15.cjs +31 -36
  143. package/shared/definition15.js +30 -36
  144. package/shared/definition16.cjs +43 -63
  145. package/shared/definition16.js +42 -63
  146. package/shared/definition17.cjs +8 -4
  147. package/shared/definition17.js +8 -4
  148. package/shared/definition18.cjs +10 -14
  149. package/shared/definition18.js +9 -14
  150. package/shared/definition19.cjs +85 -100
  151. package/shared/definition19.js +75 -91
  152. package/shared/definition2.js +1 -1
  153. package/shared/definition20.cjs +15 -20
  154. package/shared/definition20.js +14 -20
  155. package/shared/definition21.cjs +22 -3
  156. package/shared/definition21.js +23 -4
  157. package/shared/definition22.cjs +4 -4
  158. package/shared/definition22.js +5 -5
  159. package/shared/definition23.cjs +5 -38
  160. package/shared/definition23.js +5 -37
  161. package/shared/definition24.cjs +2 -7
  162. package/shared/definition24.js +3 -8
  163. package/shared/definition25.js +1 -1
  164. package/shared/definition26.cjs +157 -171
  165. package/shared/definition26.js +156 -171
  166. package/shared/definition27.cjs +1 -1
  167. package/shared/definition27.js +2 -2
  168. package/shared/definition28.cjs +32 -17
  169. package/shared/definition28.js +33 -18
  170. package/shared/definition29.js +1 -1
  171. package/shared/definition3.js +1 -1
  172. package/shared/definition30.cjs +96 -482
  173. package/shared/definition30.js +99 -482
  174. package/shared/definition31.cjs +334 -57
  175. package/shared/definition31.js +333 -56
  176. package/shared/definition32.cjs +104 -19
  177. package/shared/definition32.js +105 -20
  178. package/shared/definition33.cjs +67 -15
  179. package/shared/definition33.js +66 -14
  180. package/shared/definition34.cjs +15 -50
  181. package/shared/definition34.js +14 -49
  182. package/shared/definition35.cjs +28 -397
  183. package/shared/definition35.js +27 -397
  184. package/shared/definition36.cjs +404 -54
  185. package/shared/definition36.js +404 -55
  186. package/shared/definition37.cjs +57 -234
  187. package/shared/definition37.js +57 -233
  188. package/shared/definition38.cjs +221 -66
  189. package/shared/definition38.js +220 -65
  190. package/shared/definition39.cjs +52 -44
  191. package/shared/definition39.js +51 -43
  192. package/shared/definition4.cjs +31 -24
  193. package/shared/definition4.js +33 -26
  194. package/shared/definition40.cjs +56 -266
  195. package/shared/definition40.js +55 -265
  196. package/shared/definition41.cjs +285 -142
  197. package/shared/definition41.js +285 -142
  198. package/shared/definition42.cjs +156 -564
  199. package/shared/definition42.js +156 -565
  200. package/shared/definition43.cjs +554 -14378
  201. package/shared/definition43.js +553 -14377
  202. package/shared/definition44.cjs +14418 -1155
  203. package/shared/definition44.js +14417 -1156
  204. package/shared/definition45.cjs +1049 -677
  205. package/shared/definition45.js +1050 -678
  206. package/shared/definition46.cjs +848 -113
  207. package/shared/definition46.js +847 -112
  208. package/shared/definition47.cjs +125 -90
  209. package/shared/definition47.js +124 -89
  210. package/shared/definition48.cjs +88 -455
  211. package/shared/definition48.js +87 -454
  212. package/shared/definition49.cjs +466 -109
  213. package/shared/definition49.js +466 -109
  214. package/shared/definition5.cjs +8 -7
  215. package/shared/definition5.js +6 -5
  216. package/shared/definition50.cjs +106 -106
  217. package/shared/definition50.js +105 -105
  218. package/shared/definition51.cjs +136 -15
  219. package/shared/definition51.js +135 -14
  220. package/shared/definition52.cjs +16 -115
  221. package/shared/definition52.js +15 -114
  222. package/shared/definition53.cjs +78 -412
  223. package/shared/definition53.js +77 -410
  224. package/shared/definition54.cjs +445 -23
  225. package/shared/definition54.js +443 -22
  226. package/shared/definition55.cjs +22 -136
  227. package/shared/definition55.js +21 -135
  228. package/shared/definition56.cjs +95 -291
  229. package/shared/definition56.js +95 -292
  230. package/shared/definition57.cjs +192 -480
  231. package/shared/definition57.js +190 -479
  232. package/shared/definition58.cjs +411 -24
  233. package/shared/definition58.js +410 -24
  234. package/shared/definition59.cjs +27 -144
  235. package/shared/definition59.js +27 -143
  236. package/shared/definition6.js +1 -1
  237. package/shared/definition60.cjs +83 -54
  238. package/shared/definition60.js +82 -53
  239. package/shared/definition61.cjs +78 -166
  240. package/shared/definition61.js +77 -164
  241. package/shared/definition62.cjs +143 -232
  242. package/shared/definition62.js +141 -231
  243. package/shared/definition63.cjs +234 -69417
  244. package/shared/definition63.js +233 -69416
  245. package/shared/definition64.cjs +69454 -28
  246. package/shared/definition64.js +69453 -27
  247. package/shared/definition65.cjs +28 -2168
  248. package/shared/definition65.js +27 -2166
  249. package/shared/definition66.cjs +27 -0
  250. package/shared/definition66.js +23 -0
  251. package/shared/definition67.cjs +2195 -0
  252. package/shared/definition67.js +2190 -0
  253. package/shared/definition7.cjs +11 -2
  254. package/shared/definition7.js +12 -3
  255. package/shared/definition8.cjs +24 -11
  256. package/shared/definition8.js +26 -13
  257. package/shared/definition9.cjs +1 -2
  258. package/shared/definition9.js +2 -3
  259. package/shared/delegates-aria.js +1 -1
  260. package/shared/deprecation/replaced-props.d.ts +20 -0
  261. package/shared/divider.cjs +41 -0
  262. package/shared/divider.js +38 -0
  263. package/shared/feedback/feedback-message.d.ts +345 -0
  264. package/shared/feedback/locale.d.ts +4 -0
  265. package/{lib/text-anchor/text-anchor.d.ts → shared/feedback/mixins.d.ts} +62 -39
  266. package/shared/form-associated.cjs +124 -100
  267. package/shared/form-associated.js +125 -101
  268. package/shared/form-element.cjs +84 -0
  269. package/shared/form-element.js +82 -0
  270. package/shared/foundation/button/button.d.ts +378 -2
  271. package/shared/foundation/form-associated/form-associated.d.ts +753 -49
  272. package/shared/foundation/listbox/listbox.d.ts +1 -1
  273. package/shared/foundation/vivid-element/vivid-element.d.ts +14 -0
  274. package/shared/host-semantics.js +1 -1
  275. package/shared/key-codes.js +1 -1
  276. package/shared/linkable.cjs +70 -0
  277. package/shared/linkable.js +68 -0
  278. package/shared/localization/Locale.d.ts +14 -0
  279. package/shared/mixins.cjs +306 -0
  280. package/shared/mixins.js +300 -0
  281. package/shared/patterns/affix.d.ts +16 -1
  282. package/shared/patterns/anchored.d.ts +20 -8
  283. package/shared/patterns/char-count/char-count.d.ts +351 -0
  284. package/shared/patterns/char-count/index.d.ts +1 -0
  285. package/shared/patterns/char-count/locale.d.ts +4 -0
  286. package/shared/patterns/form-elements/form-element.d.ts +744 -0
  287. package/shared/patterns/form-elements/index.d.ts +3 -1
  288. package/shared/patterns/form-elements/with-error-text.d.ts +1122 -0
  289. package/shared/patterns/form-elements/with-success-text.d.ts +341 -0
  290. package/shared/patterns/index.d.ts +2 -0
  291. package/shared/patterns/linkable.d.ts +394 -0
  292. package/shared/patterns/localized.d.ts +6 -0
  293. package/shared/patterns/trapped-focus.d.ts +6 -0
  294. package/shared/picker-field/mixins/calendar-picker.d.ts +420 -52
  295. package/shared/picker-field/mixins/calendar-picker.template.d.ts +420 -52
  296. package/shared/picker-field/mixins/inline-time-picker/inline-time-picker.d.ts +6 -0
  297. package/shared/picker-field/mixins/min-max-calendar-picker.d.ts +843 -107
  298. package/shared/picker-field/mixins/single-date-picker.d.ts +1259 -155
  299. package/shared/picker-field/mixins/single-value-picker.d.ts +417 -49
  300. package/shared/picker-field/mixins/time-selection-picker.d.ts +842 -106
  301. package/shared/picker-field/mixins/time-selection-picker.template.d.ts +839 -103
  302. package/shared/picker-field/picker-field.d.ts +1491 -15
  303. package/shared/picker-field.template.cjs +13 -22
  304. package/shared/picker-field.template.js +14 -23
  305. package/shared/repeat.js +1 -1
  306. package/shared/slider.template.cjs +1 -1
  307. package/shared/slider.template.js +1 -1
  308. package/shared/time-selection-picker.template.cjs +10 -15
  309. package/shared/time-selection-picker.template.js +10 -16
  310. package/shared/vivid-element.cjs +53 -4
  311. package/shared/vivid-element.js +53 -3
  312. package/shared/with-error-text.cjs +56 -0
  313. package/shared/with-error-text.js +54 -0
  314. package/shared/with-success-text.cjs +23 -0
  315. package/shared/with-success-text.js +21 -0
  316. package/side-drawer/index.cjs +1 -1
  317. package/side-drawer/index.js +1 -1
  318. package/slider/index.cjs +1 -1
  319. package/slider/index.js +1 -1
  320. package/split-button/index.cjs +1 -1
  321. package/split-button/index.js +1 -1
  322. package/styles/core/all.css +1 -1
  323. package/styles/core/theme.css +1 -1
  324. package/styles/core/typography.css +1 -1
  325. package/styles/tokens/theme-dark.css +4 -4
  326. package/styles/tokens/theme-light.css +4 -4
  327. package/styles/tokens/vivid-2-compat.css +1 -1
  328. package/switch/index.cjs +1 -1
  329. package/switch/index.js +1 -1
  330. package/tab/index.cjs +1 -1
  331. package/tab/index.js +1 -1
  332. package/tab-panel/index.cjs +1 -1
  333. package/tab-panel/index.js +1 -1
  334. package/tabs/index.cjs +1 -1
  335. package/tabs/index.js +1 -1
  336. package/tag/index.cjs +1 -1
  337. package/tag/index.js +1 -1
  338. package/tag-group/index.cjs +1 -1
  339. package/tag-group/index.js +1 -1
  340. package/text-area/index.cjs +1 -1
  341. package/text-area/index.js +1 -1
  342. package/text-field/index.cjs +1 -1
  343. package/text-field/index.js +1 -1
  344. package/time-picker/index.cjs +1 -1
  345. package/time-picker/index.js +1 -1
  346. package/toggletip/index.cjs +1 -1
  347. package/toggletip/index.js +1 -1
  348. package/tooltip/index.cjs +1 -1
  349. package/tooltip/index.js +1 -1
  350. package/tree-item/index.cjs +1 -1
  351. package/tree-item/index.js +1 -1
  352. package/tree-view/index.cjs +1 -1
  353. package/tree-view/index.js +1 -1
  354. package/video-player/index.cjs +1 -1
  355. package/video-player/index.js +1 -1
  356. package/visually-hidden/index.cjs +5 -0
  357. package/visually-hidden/index.js +3 -0
  358. package/vivid.api.json +744 -1490
  359. package/lib/checkbox/checkbox.form-associated.d.ts +0 -11
  360. package/lib/file-picker/file-picker.form-associated.d.ts +0 -11
  361. package/lib/number-field/number-field.form-associated.d.ts +0 -11
  362. package/lib/radio/radio.form-associated.d.ts +0 -13
  363. package/lib/range-slider/range-slider.form-associated.d.ts +0 -11
  364. package/lib/searchable-select/searchable-select.form-associated.d.ts +0 -11
  365. package/lib/select/select.form-associated.d.ts +0 -11
  366. package/lib/slider/slider.form-associated.d.ts +0 -11
  367. package/lib/switch/switch.form-associated.d.ts +0 -11
  368. package/lib/text-anchor/definition.d.ts +0 -2
  369. package/lib/text-anchor/text-anchor.template.d.ts +0 -3
  370. package/lib/text-area/text-area.form-associated.d.ts +0 -11
  371. package/lib/text-field/text-field.form-associated.d.ts +0 -11
  372. package/shared/anchor.cjs +0 -49
  373. package/shared/anchor.js +0 -47
  374. package/shared/apply-mixins.cjs +0 -23
  375. package/shared/apply-mixins.js +0 -21
  376. package/shared/applyMixinsWithObservables.cjs +0 -15
  377. package/shared/applyMixinsWithObservables.js +0 -13
  378. package/shared/direction.cjs +0 -17
  379. package/shared/direction.js +0 -15
  380. package/shared/form-elements.cjs +0 -209
  381. package/shared/form-elements.js +0 -202
  382. package/shared/foundation/anchor/anchor.d.ts +0 -11
  383. package/shared/foundation/utilities/apply-mixins.d.ts +0 -1
  384. package/shared/patterns/form-elements/form-elements.d.ts +0 -58
  385. package/shared/picker-field/picker-field.form-associated.d.ts +0 -11
  386. package/shared/text-anchor.cjs +0 -38
  387. package/shared/text-anchor.js +0 -36
  388. package/shared/text-anchor.template.cjs +0 -35
  389. package/shared/text-anchor.template.js +0 -33
  390. package/shared/utils/applyMixinsWithObservables.d.ts +0 -1
  391. package/text-anchor/index.cjs +0 -17
  392. package/text-anchor/index.js +0 -15
@@ -1,904 +1,1276 @@
1
- import { P as Popup, p as popupDefinition } from './definition65.js';
2
- import { i as iconDefinition } from './definition28.js';
3
- import { l as listboxOptionDefinition } from './definition36.js';
4
- import { D as DOM, O as Observable, o as observable, a as attr, v as volatile, h as html, g as createRegisterFunction, i as defineVividComponent } from './vivid-element.js';
5
- import { a as applyMixinsWithObservables } from './applyMixinsWithObservables.js';
6
- import { L as Listbox } from './listbox.js';
7
- import { H as HostSemantics, a as applyHostSemantics } from './host-semantics.js';
1
+ import { B as Button, c as chevronTemplateFactory, b as buttonDefinition } from './definition11.js';
2
+ import { P as Popup, p as popupDefinition } from './definition67.js';
3
+ import { I as Icon, i as iconDefinition } from './definition28.js';
4
+ import { P as ProgressRing, p as progressRingDefinition } from './definition39.js';
5
+ import { O as Observable, D as DOM, V as VividElement, a as attr, n as nullableNumberConverter, o as observable, h as html, d as createRegisterFunction, f as defineVividComponent } from './vivid-element.js';
6
+ import { a as WithFeedback, f as feedbackMessageDefinition } from './mixins.js';
7
+ import { s as scrollIntoView } from './scrollIntoView.js';
8
+ import { D as DelegatesAria, d as delegateAria } from './delegates-aria.js';
8
9
  import { F as FormAssociated } from './form-associated.js';
9
- import { i as inRange } from './numbers.js';
10
+ import { W as WithErrorText } from './with-error-text.js';
11
+ import { W as WithSuccessText } from './with-success-text.js';
12
+ import { F as FormElement } from './form-element.js';
10
13
  import { A as AffixIconWithTrailing, a as affixIconTemplateFactory, I as IconWrapper } from './affix.js';
11
- import { u as uniqueId } from './strings.js';
12
- import { a as keySpace, b as keyEscape, c as keyTab, d as keyEnd, e as keyArrowUp, f as keyArrowDown, g as keyHome, k as keyEnter } from './key-codes.js';
13
- import { F as FormElementSuccessText, a as FormElementHelperText, e as errorText, f as formElements, g as getFeedbackTemplate } from './form-elements.js';
14
- import { L as ListboxOption } from './option.js';
15
- import { c as chevronTemplateFactory } from './definition11.js';
16
- import { h as handleEscapeKeyAndStopPropogation } from './index.js';
17
- import { r as ref } from './ref.js';
14
+ import { L as Localized } from './localized.js';
15
+ import { i as isListboxOption } from './option.js';
18
16
  import { w as when } from './when.js';
17
+ import { r as ref } from './ref.js';
19
18
  import { s as slotted } from './slotted.js';
20
19
  import { c as classNames } from './class-names.js';
20
+ import { r as repeat } from './repeat.js';
21
21
 
22
- const styles = ".chevron{display:flex;flex-shrink:0;font:var(--vvd-typography-base-extended);transform:rotate(0);transition:transform .2s}:host([aria-expanded=true]) .chevron,:host([open]) .chevron{transform:rotate(180deg)}:host(:focus-visible){outline:none}:host{display:inline-flex;flex-direction:column;gap:4px;--_low-ink-color: var(--vvd-color-neutral-600);--focus-stroke-gap-color: transparent}:host([disabled]){--_low-ink-color: var(--vvd-color-neutral-400);cursor:not-allowed}.label{color:var(--vvd-color-canvas-text);contain:inline-size;font:var(--vvd-typography-base)}.control{--_appearance-color-text: var(--vvd-color-canvas-text);--_appearance-color-fill: var(--vvd-color-canvas);--_appearance-color-outline: var(--vvd-color-neutral-500)}.control.appearance-ghost{--_appearance-color-text: var(--_connotation-color-firm);--_appearance-color-fill: transparent;--_appearance-color-outline: transparent}.control:where(.hover,:hover):where(:not(.disabled,:disabled,.readonly)){--_appearance-color-text: var(--vvd-color-canvas-text);--_appearance-color-fill: var(--vvd-color-canvas);--_appearance-color-outline: var(--vvd-color-neutral-700)}.control:where(.hover,:hover):where(:not(.disabled,:disabled,.readonly)).appearance-ghost{--_appearance-color-text: var(--_connotation-color-firm);--_appearance-color-fill: var(--_connotation-color-faint);--_appearance-color-outline: transparent}.control:where(.disabled,:disabled){--_appearance-color-text: var(--vvd-color-neutral-300);--_appearance-color-fill: var(--vvd-color-neutral-100);--_appearance-color-outline: var(--vvd-color-neutral-300)}.control:where(.disabled,:disabled).appearance-ghost{--_appearance-color-text: var(--vvd-color-neutral-300);--_appearance-color-fill: transparent;--_appearance-color-outline: transparent}.control:where(.readonly):where(:not(.disabled,:disabled)){--_appearance-color-text: var(--vvd-color-canvas-text);--_appearance-color-fill: var(--vvd-color-neutral-200);--_appearance-color-outline: var(--vvd-color-neutral-400)}.control:where(.readonly):where(:not(.disabled,:disabled)).appearance-ghost{--_appearance-color-text: var(--vvd-color-neutral-600);--_appearance-color-fill: transparent;--_appearance-color-outline: transparent}.control:where(.error):where(:not(.disabled,:disabled)){--_appearance-color-text: notSet;--_appearance-color-fill: var(--vvd-color-alert-50);--_appearance-color-outline: var(--vvd-color-alert-500)}.control:where(.error):where(:not(.disabled,:disabled)).appearance-ghost{--_appearance-color-text: notSet;--_appearance-color-fill: var(--vvd-color-alert-50);--_appearance-color-outline: transparent}.control:where(.success):where(:not(.disabled,:disabled)){--_appearance-color-text: notSet;--_appearance-color-fill: var(--vvd-color-success-50);--_appearance-color-outline: var(--vvd-color-success-500)}.control:where(.success):where(:not(.disabled,:disabled)).appearance-ghost{--_appearance-color-text: notSet;--_appearance-color-fill: var(--vvd-color-success-50);--_appearance-color-outline: transparent}.control{--_connotation-color-primary: var(--vvd-select-accent-primary, var(--vvd-color-canvas-text));--_connotation-color-primary-text: var(--vvd-select-accent-primary-text, var(--vvd-color-canvas));--_connotation-color-primary-increment: var(--vvd-select-accent-primary-increment, var(--vvd-color-neutral-800));--_connotation-color-intermediate: var(--vvd-select-accent-intermediate, var(--vvd-color-neutral-500));--_connotation-color-faint: var(--vvd-select-accent-faint, var(--vvd-color-neutral-50));--_connotation-color-soft: var(--vvd-select-accent-soft, var(--vvd-color-neutral-100));--_connotation-color-firm: var(--vvd-select-accent-firm, var(--vvd-color-canvas-text));--_connotation-color-fierce: var(--vvd-select-accent-fierce, var(--vvd-color-neutral-700))}.control{border-radius:var(--_select-control-border-radius);block-size:var(--_select-block-size);padding-inline:var(--_select-padding-inline)}.control{--_select-icon-size: calc(1px*(40 + 4*clamp(-1, var(--vvd-size-density, 0), 2))/2) ;--_select-block-size: calc(1px*(40 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) ;--_select-padding-inline: calc(1px*(40 + 4*clamp(-1, var(--vvd-size-density, 0), 2))*.4) ;display:flex;align-items:center;justify-content:space-between;background-color:var(--_appearance-color-fill);box-shadow:inset 0 0 0 1px var(--_appearance-color-outline);color:var(--_appearance-color-text);font:var(--vvd-typography-base);gap:8px;transition:box-shadow .2s,background-color .2s}.control.size-condensed{--_select-icon-size: calc(1px*(40 + 4*clamp(-1, var(--vvd-size-density, 0), 2))*.4) ;--_select-block-size: calc(1px*(32 + 4*clamp(-1, var(--vvd-size-density, 0), 2))) ;--_select-padding-inline: calc(1px*(24 + 4*clamp(-1, var(--vvd-size-density, 0), 2))/2) }.control.size-condensed:not(.shape-pill){--_select-control-border-radius: 4px}.control-wrapper{position:relative}.control:not(.disabled){cursor:pointer}.control.disabled{pointer-events:none}.control:not(.shape-pill){--_select-control-border-radius: 8px}.control.shape-pill{--_select-control-border-radius: 24px}:host(:focus-visible) .control{box-shadow:0 0 0 4px color-mix(in srgb,var(--vvd-color-cta-500),transparent 85%),inset 0 0 0 3px var(--focus-stroke-gap-color, currentColor);outline:1px solid var(--focus-stroke-color, var(--vvd-color-cta-500));outline-offset:calc(-1px - var(--focus-inset, 0px))}.listbox{display:flex;max-height:var(--select-height, 408px);flex-direction:column;padding:4px;gap:2px;overflow-y:auto}:host([multiple]:focus-visible) .listbox{box-shadow:0 0 0 4px color-mix(in srgb,var(--vvd-color-cta-500),transparent 85%),inset 0 0 0 3px var(--focus-stroke-gap-color, currentColor);outline:1px solid var(--focus-stroke-color, var(--vvd-color-cta-500));outline-offset:calc(-1px - var(--focus-inset, 0px));border-radius:8px}.selected-value{display:flex;overflow:hidden;flex-grow:1;align-items:center;column-gap:12px;white-space:nowrap}.selected-value .text{overflow:hidden;max-inline-size:100%;text-overflow:ellipsis}.control.shows-placeholder .selected-value .text{color:var(--vvd-color-neutral-600)}.selected-value slot[name=icon]{flex:0 0 var(--_select-icon-size);font-size:var(--_select-icon-size);line-height:1}.control.has-meta .selected-value{padding-inline-end:8px}.feedback-wrapper{display:contents}::part(popup-base){inline-size:max-content;min-inline-size:var(--_select-fixed-width, 100%)}:host([multiple]) ::part(popup-base){position:static}";
22
+ const styles = ".chevron{display:flex;flex-shrink:0;font:var(--vvd-typography-base-extended);transform:rotate(0);transition:transform .2s}:host([data-expanded=true]) .chevron,:host([open]) .chevron{transform:rotate(180deg)}:not(.disabled) .chevron{cursor:pointer}.disabled .chevron{color:var(--_low-ink-color);cursor:not-allowed}:host(:focus-visible){outline:none}:host{display:inline-block;inline-size:300px;--_low-ink-color: var(--vvd-color-neutral-600)}:host([disabled]){--_low-ink-color: var(--vvd-color-neutral-400);cursor:not-allowed}.control-wrapper{display:flex;flex-direction:column;gap:4px}.label{color:var(--vvd-color-canvas-text);font:var(--vvd-typography-base)}.selection-count{color:var(--_low-ink-color);font:var(--vvd-typography-base)}.fieldset{--_connotation-color-primary: var(--vvd-searchable-select-accent-primary, var(--vvd-color-canvas-text));--_connotation-color-primary-text: var(--vvd-searchable-select-accent-primary-text, var(--vvd-color-canvas));--_connotation-color-primary-increment: var(--vvd-searchable-select-accent-primary-increment, var(--vvd-color-neutral-800));--_connotation-color-intermediate: var(--vvd-searchable-select-accent-intermediate, var(--vvd-color-neutral-500));--_connotation-color-faint: var(--vvd-searchable-select-accent-faint, var(--vvd-color-neutral-50));--_connotation-color-soft: var(--vvd-searchable-select-accent-soft, var(--vvd-color-neutral-100));--_connotation-color-firm: var(--vvd-searchable-select-accent-firm, var(--vvd-color-canvas-text));--_connotation-color-fierce: var(--vvd-searchable-select-accent-fierce, var(--vvd-color-neutral-700))}.fieldset{--_appearance-color-text: var(--vvd-color-canvas-text);--_appearance-color-fill: var(--vvd-color-canvas);--_appearance-color-outline: var(--vvd-color-neutral-500)}.fieldset.appearance-ghost{--_appearance-color-text: var(--_connotation-color-firm);--_appearance-color-fill: transparent;--_appearance-color-outline: transparent}.fieldset:where(.hover,:hover):where(:not(.disabled,:disabled,.readonly)){--_appearance-color-text: var(--vvd-color-canvas-text);--_appearance-color-fill: var(--vvd-color-canvas);--_appearance-color-outline: var(--vvd-color-neutral-700)}.fieldset:where(.hover,:hover):where(:not(.disabled,:disabled,.readonly)).appearance-ghost{--_appearance-color-text: var(--_connotation-color-firm);--_appearance-color-fill: var(--_connotation-color-faint);--_appearance-color-outline: transparent}.fieldset:where(.disabled,:disabled){--_appearance-color-text: var(--vvd-color-neutral-300);--_appearance-color-fill: var(--vvd-color-neutral-100);--_appearance-color-outline: var(--vvd-color-neutral-300)}.fieldset:where(.disabled,:disabled).appearance-ghost{--_appearance-color-text: var(--vvd-color-neutral-300);--_appearance-color-fill: transparent;--_appearance-color-outline: transparent}.fieldset:where(.readonly):where(:not(.disabled,:disabled)){--_appearance-color-text: var(--vvd-color-canvas-text);--_appearance-color-fill: var(--vvd-color-neutral-200);--_appearance-color-outline: var(--vvd-color-neutral-400)}.fieldset:where(.readonly):where(:not(.disabled,:disabled)).appearance-ghost{--_appearance-color-text: var(--vvd-color-neutral-600);--_appearance-color-fill: transparent;--_appearance-color-outline: transparent}.fieldset:where(.error):where(:not(.disabled,:disabled)){--_appearance-color-text: notSet;--_appearance-color-fill: var(--vvd-color-alert-50);--_appearance-color-outline: var(--vvd-color-alert-500)}.fieldset:where(.error):where(:not(.disabled,:disabled)).appearance-ghost{--_appearance-color-text: notSet;--_appearance-color-fill: var(--vvd-color-alert-50);--_appearance-color-outline: transparent}.fieldset:where(.success):where(:not(.disabled,:disabled)){--_appearance-color-text: notSet;--_appearance-color-fill: var(--vvd-color-success-50);--_appearance-color-outline: var(--vvd-color-success-500)}.fieldset:where(.success):where(:not(.disabled,:disabled)).appearance-ghost{--_appearance-color-text: notSet;--_appearance-color-fill: var(--vvd-color-success-50);--_appearance-color-outline: transparent}.fieldset{display:flex;align-items:center;justify-content:space-between;background-color:var(--_appearance-color-fill);box-shadow:inset 0 0 0 1px var(--_appearance-color-outline);color:var(--_appearance-color-text);font:var(--vvd-typography-base);gap:8px;padding-block:8px;padding-inline:16px;transition:box-shadow .2s,background-color .2s}.fieldset:focus-within{box-shadow:0 0 0 4px color-mix(in srgb,var(--vvd-color-cta-500),transparent 85%),inset 0 0 0 3px var(--focus-stroke-gap-color, currentColor);outline:1px solid var(--focus-stroke-color, var(--vvd-color-cta-500));outline-offset:calc(-1px - var(--focus-inset, 0px));--focus-stroke-gap-color: transparent}:host(:not([shape=pill])) .fieldset{border-radius:8px}:host([shape=pill]) .fieldset{border-radius:24px}.popup-wrapper{position:relative}.content-area{display:flex;overflow:hidden;flex:1;flex-direction:column;gap:8px;min-block-size:24px}.tag-row{display:flex;gap:8px;inline-size:100%}.tag-row.contains-only-input:not(:focus-within){display:contents}.tag-wrapper{overflow:hidden}.tag{max-inline-size:100%}input{box-sizing:border-box;flex:1;border:none;background:none;block-size:24px;font:var(--vvd-typography-base);max-inline-size:100%;min-inline-size:100px;outline:none}.contains-only-input input:not(:focus){position:absolute;block-size:0;inline-size:0;min-inline-size:0;opacity:0;pointer-events:none}.listbox{display:flex;flex-direction:column;padding:4px;gap:2px;max-block-size:var(--searchable-select-height, 408px);overflow-y:auto}.empty-message{display:flex;align-items:center;justify-content:center;color:var(--vvd-color-neutral-300);font:var(--vvd-typography-base);min-block-size:40px;text-align:center}::part(popup-base){inline-size:max-content;min-inline-size:var(--_searchable-select-fixed-width, 100%)}slot[name=icon]{font-size:20px}.visually-hidden{position:absolute;overflow:hidden;width:1px;height:1px;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}";
23
23
 
24
- class _Select extends Listbox {
25
- }
26
- class FormAssociatedSelect extends FormAssociated(_Select) {
27
- constructor() {
28
- super(...arguments);
29
- this.proxy = document.createElement("select");
30
- }
31
- }
24
+ const optionTagStyles = ".base.connotation-cta{--_connotation-color-contrast: var(--vvd-option-tag-cta-contrast, var(--vvd-color-cta-800))}.base:not(.connotation-cta){--_connotation-color-contrast: var(--vvd-option-tag-accent-contrast, var(--vvd-color-neutral-800))}.base{position:relative;display:inline-flex;box-sizing:border-box;align-items:center;background-color:var(--fill-color);block-size:calc(1px*(24 + 4*clamp(-1,var(--vvd-size-density, 0),2)));box-shadow:inset 0 0 0 1px var(--outline-color);color:var(--text-color);column-gap:8px;font:var(--vvd-typography-base-bold);max-inline-size:100%;padding-inline:8px;user-select:none;vertical-align:middle}.base:not(.disabled){--text-color: var(--_connotation-color-contrast);--fill-color: color-mix( in srgb, var(--_connotation-color-contrast), transparent 87.5% );--outline-color: transparent}.base.disabled{--text-color: var(--vvd-color-neutral-300);--fill-color: color-mix( in srgb, var(--vvd-color-neutral-800), transparent 87.5% );--outline-color: transparent}.base:not(.shape-pill){border-radius:4px}.base.shape-pill{border-radius:16px}.label{overflow:hidden;max-inline-size:100%;text-overflow:ellipsis;white-space:nowrap}slot[name=icon]{font-size:calc(calc(1px*(24 + 4*clamp(-1,var(--vvd-size-density, 0),2))) / 1.5);line-height:1}.icon-placeholder{inline-size:calc(calc(1px*(24 + 4*clamp(-1,var(--vvd-size-density, 0),2))) / 1.5)}.remove-button{display:flex;align-items:center;border-radius:inherit;cursor:pointer;outline:none}.disabled .remove-button{pointer-events:none}.remove-button:focus-visible:before{--focus-stroke-gap-color: transparent;box-shadow:0 0 0 4px color-mix(in srgb,var(--vvd-color-cta-500),transparent 85%),inset 0 0 0 3px var(--focus-stroke-gap-color, currentColor);outline:1px solid var(--focus-stroke-color, var(--vvd-color-cta-500));outline-offset:calc(-1px - var(--focus-inset, 0px));position:absolute;z-index:1;display:block;border-radius:inherit;content:\"\";inset:0;pointer-events:none}";
32
25
 
33
- var __defProp = Object.defineProperty;
34
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
35
- var __decorateClass = (decorators, target, key, kind) => {
36
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
26
+ var __defProp$1 = Object.defineProperty;
27
+ var __decorateClass$1 = (decorators, target, key, kind) => {
28
+ var result = void 0 ;
37
29
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
38
30
  if (decorator = decorators[i])
39
- result = (kind ? decorator(target, key, result) : decorator(result)) || result;
40
- if (kind && result) __defProp(target, key, result);
31
+ result = (decorator(target, key, result) ) || result;
32
+ if (result) __defProp$1(target, key, result);
41
33
  return result;
42
34
  };
43
- let Select = class extends HostSemantics(
44
- AffixIconWithTrailing(FormAssociatedSelect)
35
+ const TagGapPx = 8;
36
+ const InputMinWidthPx = 100;
37
+ const PageSize = 10;
38
+ const isFormAssociatedTryingToSetFormValue = (value) => typeof value === "string";
39
+ class SearchableSelect extends WithFeedback(
40
+ WithErrorText(
41
+ WithSuccessText(
42
+ FormElement(
43
+ DelegatesAria(
44
+ AffixIconWithTrailing(Localized(FormAssociated(VividElement)))
45
+ )
46
+ )
47
+ )
48
+ )
45
49
  ) {
46
50
  constructor() {
47
51
  super(...arguments);
48
- this.activeIndex = -1;
52
+ this.fixedDropdown = false;
53
+ this.open = false;
54
+ this.multiple = false;
55
+ this.externalTags = false;
56
+ this.maxLines = null;
57
+ this.values = [];
58
+ this.initialValues = [];
59
+ this._currentSearchText = null;
60
+ // --- Slotted options ---
49
61
  /**
50
- * The start index when checking a range of options.
51
- *
52
62
  * @internal
53
63
  */
54
- this.rangeStartIndex = -1;
55
- this.open = false;
64
+ this._areOptionsInitialized = false;
65
+ this.#slottedOptionsChangeHandler = {
66
+ handleChange: (source, _) => {
67
+ if (source.selected && !this.values.includes(source.value)) {
68
+ this.values = [...this.values, source.value];
69
+ } else if (!source.selected && this.values.includes(source.value)) {
70
+ this.values = this.values.filter((option) => option !== source.value);
71
+ }
72
+ }
73
+ };
74
+ // --- Option tag icons ---
75
+ this.#clonedTagIcons = /* @__PURE__ */ new Map();
76
+ this._filteredOptions = [];
77
+ this._filteredEnabledOptions = [];
78
+ this.loading = false;
79
+ this._highlightedOptionIndex = null;
80
+ this._numElidedTags = 0;
81
+ this._tagRows = [];
82
+ this._lastTagRow = [];
83
+ this.clearable = false;
84
+ this.maxSelected = null;
85
+ this._slottedDisabledOptions = [];
86
+ // --- Form handling ---
56
87
  /**
57
- * The unique id for the internal listbox element.
58
- *
59
88
  * @internal
60
89
  */
61
- this.listboxId = uniqueId("listbox-");
62
- this.maxHeight = 0;
63
- this.fixedDropdown = false;
64
- this.placeholderOption = null;
65
- this._feedbackWrapper = null;
90
+ this.proxy = document.createElement("input");
91
+ this.setFormValue = (value, state) => {
92
+ if (isFormAssociatedTryingToSetFormValue(value)) {
93
+ return;
94
+ }
95
+ super.setFormValue(value, state);
96
+ };
97
+ this._changeDescription = "";
98
+ // --- Core ---
99
+ this.#resizeObserver = new ResizeObserver(() => {
100
+ this.#updateTagLayout();
101
+ });
66
102
  }
67
103
  /**
68
- * Returns the last checked option.
69
- *
70
104
  * @internal
71
105
  */
72
- get activeOption() {
73
- return this.options[this.activeIndex];
106
+ openChanged() {
107
+ if (!this.open) {
108
+ this.#transitionHighlightedOptionTo(null);
109
+ }
74
110
  }
75
111
  /**
76
- * Returns the list of checked options.
77
- *
78
112
  * @internal
79
113
  */
80
- get checkedOptions() {
81
- return this.options.filter((o) => o.checked);
114
+ valuesChanged() {
115
+ if (!this._areOptionsInitialized) {
116
+ return;
117
+ }
118
+ if (!this.multiple && this.values.length > 1) {
119
+ this.values = [this.values[0]];
120
+ return;
121
+ }
122
+ if (this.values.some((value) => !this.#isValidValue(value))) {
123
+ this.values = this.values.filter((value) => this.#isValidValue(value));
124
+ return;
125
+ }
126
+ this.value = this.values.length ? this.values[0] : "";
127
+ this.#updateSelectionLimit();
128
+ this.#updateSelectedOnSlottedOptions();
129
+ if (this.$fastController.isConnected) {
130
+ this.#updateTagLayout();
131
+ }
132
+ this.#updateFormValue();
82
133
  }
83
- /**
84
- * Returns the index of the first selected option.
85
- *
86
- * @internal
87
- */
88
- get firstSelectedOptionIndex() {
89
- return this.options.indexOf(this.firstSelectedOption);
134
+ #updateValuesThroughUserInteraction(newValues) {
135
+ this.values = newValues;
136
+ this.$emit("change", void 0, {
137
+ bubbles: false
138
+ });
139
+ this.$emit("input", void 0, {
140
+ bubbles: false
141
+ });
90
142
  }
91
- /**
92
- * Updates the `ariaActiveDescendant` property when the active index changes.
93
- *
94
- * @internal
95
- */
96
- activeIndexChanged(_, next) {
97
- this._activeDescendant = this.options[next]?.id ?? "";
98
- this.focusAndScrollOptionIntoView();
143
+ #updateValuesWhileMaintainingOrder(newValues) {
144
+ const oldSet = new Set(this.values);
145
+ const newSet = new Set(newValues);
146
+ this.values = [...this.values].filter((v) => newSet.has(v)).concat([...newValues].filter((v) => !oldSet.has(v)));
99
147
  }
100
148
  /**
101
- * Toggles the checked state for the currently active option.
102
- *
103
- * @remarks
104
- * Multiple-selection mode only.
105
- *
106
149
  * @internal
107
150
  */
108
- checkActiveIndex() {
109
- const activeItem = this.activeOption;
110
- if (activeItem) {
111
- activeItem.checked = true;
151
+ initialValuesChanged() {
152
+ if (!this.dirtyValue) {
153
+ this.values = this.initialValues;
154
+ this.dirtyValue = false;
112
155
  }
113
156
  }
157
+ #isValidValue(value) {
158
+ return this._slottedOptions.some((option) => option.value === value);
159
+ }
114
160
  /**
115
- * Sets the active index to the first option and marks it as checked.
116
- *
117
- * @remarks
118
- * Multi-selection mode only.
119
- *
120
- * @param preserveChecked - mark all options unchecked before changing the active index
121
- *
122
161
  * @internal
123
162
  */
124
- checkFirstOption(preserveChecked) {
125
- if (preserveChecked) {
126
- if (this.rangeStartIndex === -1) {
127
- this.rangeStartIndex = this.activeIndex + 1;
128
- }
129
- this.options.forEach((o, i) => {
130
- o.checked = inRange(i, this.rangeStartIndex);
131
- });
163
+ valueChanged(prev, next) {
164
+ super.valueChanged(prev, next);
165
+ if (!this._areOptionsInitialized) {
166
+ return;
167
+ }
168
+ const isValidValue = this._slottedOptions.some(
169
+ (option) => option.value === next
170
+ );
171
+ if (this.values[0] !== next) {
172
+ this.values = isValidValue ? [next] : [];
173
+ }
174
+ }
175
+ get selectedIndex() {
176
+ if (this.values.length) {
177
+ return this._slottedOptions.findIndex(
178
+ (option) => option.value === this.values[0]
179
+ );
132
180
  } else {
133
- this.uncheckAllOptions();
181
+ return -1;
134
182
  }
135
- this.activeIndex = 0;
136
- this.checkActiveIndex();
183
+ }
184
+ set selectedIndex(index) {
185
+ this.value = this._slottedOptions[index]?.value ?? "";
186
+ }
187
+ get options() {
188
+ return [...this._slottedOptions];
189
+ }
190
+ get selectedOptions() {
191
+ return this._slottedOptions.filter(
192
+ (option) => this.values.includes(option.value)
193
+ );
137
194
  }
138
195
  /**
139
- * Decrements the active index and sets the matching option as checked.
140
- *
141
- * @remarks
142
- * Multi-selection mode only.
143
- *
144
- * @param preserveChecked - mark all options unchecked before changing the active index
145
- *
146
196
  * @internal
147
197
  */
148
- checkLastOption(preserveChecked) {
149
- if (preserveChecked) {
150
- if (this.rangeStartIndex === -1) {
151
- this.rangeStartIndex = this.activeIndex;
152
- }
153
- this.options.forEach((o, i) => {
154
- o.checked = inRange(i, this.rangeStartIndex, this.length);
155
- });
156
- } else {
157
- this.uncheckAllOptions();
158
- }
159
- this.activeIndex = this.length - 1;
160
- this.checkActiveIndex();
198
+ _currentSearchTextChanged() {
199
+ this.#updateFilteredOptions();
200
+ this.$emit("search-text-change", void 0, {
201
+ bubbles: false,
202
+ composed: false
203
+ });
161
204
  }
162
205
  /**
163
- * Increments the active index and marks the matching option as checked.
164
- *
165
- * @remarks
166
- * Multiple-selection mode only.
167
- *
168
- * @param preserveChecked - mark all options unchecked before changing the active index
169
- *
170
- * @internal
206
+ * The current search text of the component.
171
207
  */
172
- checkNextOption(preserveChecked) {
173
- if (preserveChecked) {
174
- if (this.rangeStartIndex === -1) {
175
- this.rangeStartIndex = this.activeIndex;
176
- }
177
- this.options.forEach((o, i) => {
178
- o.checked = inRange(i, this.rangeStartIndex, this.activeIndex + 1);
179
- });
180
- } else {
181
- this.uncheckAllOptions();
182
- }
183
- this.activeIndex += this.activeIndex < this.length - 1 ? 1 : 0;
184
- this.checkActiveIndex();
208
+ get searchText() {
209
+ return this._currentSearchText ?? "";
185
210
  }
186
211
  /**
187
- * Decrements the active index and marks the matching option as checked.
188
- *
189
- * @remarks
190
- * Multiple-selection mode only.
191
- *
192
- * @param preserveChecked - mark all options unchecked before changing the active index
193
- *
194
212
  * @internal
195
213
  */
196
- checkPreviousOption(preserveChecked) {
197
- if (preserveChecked) {
198
- if (this.rangeStartIndex === -1) {
199
- this.rangeStartIndex = this.activeIndex;
200
- }
201
- if (this.checkedOptions.length === 1) {
202
- this.rangeStartIndex += 1;
203
- }
204
- this.options.forEach((o, i) => {
205
- o.checked = inRange(i, this.activeIndex, this.rangeStartIndex);
206
- });
207
- } else {
208
- this.uncheckAllOptions();
209
- }
210
- this.activeIndex -= this.activeIndex > 0 ? 1 : 0;
211
- this.checkActiveIndex();
214
+ get _inputValue() {
215
+ return this._currentSearchText ?? (!this.multiple && this.value !== "" ? this.#textForValue(this.value) ?? "" : "");
212
216
  }
213
217
  /**
214
218
  * @internal
215
219
  */
216
- focusAndScrollOptionIntoView() {
217
- super.focusAndScrollOptionIntoView(this.activeOption);
220
+ _onInputInput(event) {
221
+ this._currentSearchText = event.target.value;
218
222
  }
219
223
  /**
220
- * In multiple-selection mode:
221
- * If any options are selected, the first selected option is checked when
222
- * the listbox receives focus. If no options are selected, the first
223
- * selectable option is checked.
224
- *
225
224
  * @internal
226
225
  */
227
- focusinHandler(e) {
228
- if (!this.multiple) {
229
- return super.focusinHandler(e);
230
- }
231
- if (!this.shouldSkipFocus && e.target === e.currentTarget) {
232
- this.uncheckAllOptions();
233
- if (this.activeIndex === -1) {
234
- this.activeIndex = this.firstSelectedOptionIndex !== -1 ? this.firstSelectedOptionIndex : 0;
235
- }
236
- this.checkActiveIndex();
237
- this.setSelectedOptions();
238
- this.focusAndScrollOptionIntoView();
239
- }
240
- this.shouldSkipFocus = false;
226
+ _onInputFocus(_) {
227
+ this.#updateFilteredOptions();
228
+ this.open = true;
241
229
  }
242
230
  /**
243
- * Sets an option as selected and gives it focus.
244
- *
245
- * @public
231
+ * @internal
246
232
  */
247
- setSelectedOptions() {
248
- if (!this.multiple) {
249
- super.setSelectedOptions();
250
- return;
251
- }
252
- if (this.$fastController.isConnected && this.options) {
253
- this.selectedOptions = this.options.filter((o) => o.selected);
254
- this.focusAndScrollOptionIntoView();
255
- }
233
+ _onInputBlur(_) {
234
+ this.open = false;
235
+ this._currentSearchText = null;
236
+ this._changeDescription = "";
256
237
  }
257
238
  /**
258
- * Toggles the selected state of the provided options. If any provided items
259
- * are in an unselected state, all items are set to selected. If every
260
- * provided item is selected, they are all unselected.
261
- *
262
239
  * @internal
263
240
  */
264
- toggleSelectedForAllCheckedOptions() {
265
- const enabledCheckedOptions = this.checkedOptions.filter(
266
- (o) => !o.disabled
267
- );
268
- const force = !enabledCheckedOptions.every((o) => o.selected);
269
- enabledCheckedOptions.forEach((o) => o.selected = force);
270
- this.selectedIndex = this.options.indexOf(
271
- enabledCheckedOptions[enabledCheckedOptions.length - 1]
272
- );
273
- this.setSelectedOptions();
274
- this.updateValue(true);
241
+ _onInputKeydown(e) {
242
+ if (e.ctrlKey || e.shiftKey) {
243
+ return true;
244
+ }
245
+ switch (e.key) {
246
+ case "Enter":
247
+ this.#selectHighlightedOption();
248
+ return false;
249
+ case "Escape":
250
+ this.open = false;
251
+ break;
252
+ case "Home":
253
+ if (!this.open) {
254
+ this.open = true;
255
+ break;
256
+ }
257
+ this.#highlightFirstOption();
258
+ return false;
259
+ case "End":
260
+ if (!this.open) {
261
+ this.open = true;
262
+ break;
263
+ }
264
+ this.#highlightLastOption();
265
+ return false;
266
+ case "PageUp":
267
+ if (!this.open) {
268
+ this.open = true;
269
+ break;
270
+ }
271
+ this.#highlightPrevPage();
272
+ return false;
273
+ case "PageDown":
274
+ if (!this.open) {
275
+ this.open = true;
276
+ break;
277
+ }
278
+ this.#highlightNextPage();
279
+ return false;
280
+ case "ArrowUp":
281
+ if (!this.open) {
282
+ this.open = true;
283
+ break;
284
+ }
285
+ this.#highlightPreviousOption();
286
+ return false;
287
+ case "ArrowDown":
288
+ if (!this.open) {
289
+ this.open = true;
290
+ break;
291
+ }
292
+ this.#highlightNextOption();
293
+ return false;
294
+ case "ArrowLeft":
295
+ if (this.multiple && this._inputValue === "" && this.values.length && !this.externalTags) {
296
+ this.#moveTagFocusTo(this.#nextTagIndexLeft(this.values.length));
297
+ return false;
298
+ }
299
+ return true;
300
+ case "Backspace":
301
+ if (this.multiple && this._inputValue === "" && this.values.length) {
302
+ this._onTagRemoved(this.values[this.values.length - 1]);
303
+ return false;
304
+ }
305
+ return true;
306
+ default:
307
+ if (!this.open) {
308
+ this.open = true;
309
+ }
310
+ return true;
311
+ }
312
+ return true;
275
313
  }
276
314
  /**
277
315
  * @internal
278
316
  */
279
- typeaheadBufferChanged(prev, next) {
280
- if (!this.multiple) {
281
- super.typeaheadBufferChanged(prev, next);
317
+ _slottedOptionsChanged(oldValue, newValue) {
318
+ const hasSlottedOptions = Boolean(
319
+ this.querySelectorAll(`:not([slot])`).length
320
+ );
321
+ if (!newValue.length && hasSlottedOptions) {
282
322
  return;
283
323
  }
284
- if (this.$fastController.isConnected) {
285
- const typeaheadMatches = this.getTypeaheadMatches();
286
- const activeIndex = this.options.indexOf(typeaheadMatches[0]);
287
- if (activeIndex > -1) {
288
- this.activeIndex = activeIndex;
289
- this.uncheckAllOptions();
290
- this.checkActiveIndex();
324
+ this._areOptionsInitialized = true;
325
+ if (oldValue) {
326
+ this._slottedDisabledOptions = [];
327
+ for (const option of oldValue) {
328
+ const notifier = Observable.getNotifier(option);
329
+ notifier.unsubscribe(this.#slottedOptionsChangeHandler, "selected");
330
+ }
331
+ }
332
+ if (newValue) {
333
+ for (const option of newValue) {
334
+ option._displayCheckmark = true;
335
+ const notifier = Observable.getNotifier(option);
336
+ notifier.subscribe(this.#slottedOptionsChangeHandler, "selected");
291
337
  }
292
- this.typeaheadExpired = false;
293
338
  }
339
+ const values = [];
340
+ for (const option of this._slottedOptions) {
341
+ if (option.selected || option.value === this.value || this.values.includes(option.value)) {
342
+ values.push(option.value);
343
+ }
344
+ if (option.disabled) {
345
+ this._slottedDisabledOptions.push(option);
346
+ }
347
+ }
348
+ this.#updateValuesWhileMaintainingOrder(values);
349
+ this.#updateFilteredOptions();
350
+ this.#updateSelectionLimit();
351
+ }
352
+ #slottedOptionsChangeHandler;
353
+ #updateSelectedOnSlottedOptions() {
354
+ for (const option of this._slottedOptions) {
355
+ option.selected = this.values.includes(option.value);
356
+ this.#updateClonedTagIconOfOption(option);
357
+ }
358
+ }
359
+ #handleOptionInteraction(option) {
360
+ const value = option.value;
361
+ let newValues;
362
+ let shouldClearSearchText = false;
363
+ const isSelection = !this.values.includes(value);
364
+ if (this.multiple) {
365
+ if (isSelection) {
366
+ newValues = [...this.values, value];
367
+ } else {
368
+ newValues = this.values.filter((option2) => option2 !== value);
369
+ }
370
+ shouldClearSearchText = true;
371
+ } else {
372
+ if (isSelection) {
373
+ newValues = [value];
374
+ shouldClearSearchText = true;
375
+ } else {
376
+ newValues = [];
377
+ }
378
+ this.open = false;
379
+ }
380
+ this.#updateValuesThroughUserInteraction(newValues);
381
+ const optionMessage = isSelection ? this.locale.searchableSelect.optionSelectedMessage(option.text) : this.locale.searchableSelect.optionDeselectedMessage(option.text);
382
+ const maxSelectedMessage = this.multiple && this.maxSelected && this.maxSelected >= 1 ? this.locale.searchableSelect.maxSelectedMessage(
383
+ this.values.length,
384
+ this.maxSelected
385
+ ) : "";
386
+ this._changeDescription = `${optionMessage} ${maxSelectedMessage}`;
387
+ if (shouldClearSearchText) {
388
+ this._currentSearchText = null;
389
+ }
390
+ }
391
+ #clonedTagIcons;
392
+ #tagIconOfOption(option) {
393
+ return option.querySelector('[slot="tag-icon"]');
294
394
  }
295
395
  /**
296
- * Unchecks all options.
297
- *
298
- * @remarks
299
- * Multiple-selection mode only.
300
- *
301
- * @param preserveChecked - reset the rangeStartIndex
302
- *
303
396
  * @internal
304
397
  */
305
- uncheckAllOptions(preserveChecked = false) {
306
- this.options.forEach((o) => o.checked = false);
307
- if (!preserveChecked) {
308
- this.rangeStartIndex = -1;
398
+ _tagIconSlotName(value) {
399
+ return `_tag-icon-${this.values.indexOf(value)}`;
400
+ }
401
+ #updateClonedTagIconOfOption(option) {
402
+ if (option.selected && this.#tagIconOfOption(option)) {
403
+ let clone = this.#clonedTagIcons.get(option);
404
+ if (!clone) {
405
+ clone = this.#tagIconOfOption(option).cloneNode(true);
406
+ this.#clonedTagIcons.set(option, clone);
407
+ }
408
+ clone.slot = this._tagIconSlotName(option.value);
409
+ this.appendChild(clone);
410
+ } else {
411
+ const clone = this.#clonedTagIcons.get(option);
412
+ if (clone) {
413
+ clone.remove();
414
+ this.#clonedTagIcons.delete(option);
415
+ }
309
416
  }
310
417
  }
311
418
  /**
312
- * Sets focus when the open property changes.
313
- *
314
419
  * @internal
315
420
  */
316
- openChanged(prev, next) {
317
- if (!this.collapsible) {
318
- return;
421
+ optionFilterChanged() {
422
+ this.#updateFilteredOptions();
423
+ }
424
+ #updateFilteredOptions() {
425
+ const newFilteredOptions = [];
426
+ const optionFilter = this.optionFilter ?? ((option, searchText) => option.text.toLowerCase().includes(searchText.toLowerCase()));
427
+ for (const option of this._slottedOptions ?? []) {
428
+ option._vvdSearchText = this.searchText;
429
+ const matches = !this.searchText || optionFilter(option, this.searchText);
430
+ option._isNotMatching = !matches;
431
+ if (!option.hidden && matches) {
432
+ newFilteredOptions.push(option);
433
+ }
319
434
  }
320
- if (this.open) {
321
- this.focusAndScrollOptionIntoView();
322
- this.indexWhenOpened = this.selectedIndex;
323
- DOM.queueUpdate(() => this.focus());
324
- return;
435
+ this.#transitionHighlightedOptionTo(null);
436
+ this._filteredOptions = newFilteredOptions;
437
+ this._filteredEnabledOptions = newFilteredOptions.filter(
438
+ (option) => !option.disabled
439
+ );
440
+ }
441
+ #transitionHighlightedOptionTo(index) {
442
+ if (typeof this._highlightedOptionIndex === "number") {
443
+ this._filteredEnabledOptions[this._highlightedOptionIndex]._highlighted = false;
444
+ }
445
+ if (typeof index === "number") {
446
+ if (!this._filteredEnabledOptions.length) {
447
+ index = null;
448
+ } else {
449
+ index = Math.max(
450
+ 0,
451
+ Math.min(this._filteredEnabledOptions.length - 1, index)
452
+ );
453
+ }
454
+ }
455
+ this._highlightedOptionIndex = index;
456
+ if (typeof this._highlightedOptionIndex === "number") {
457
+ const highlightedOption = this._filteredEnabledOptions[this._highlightedOptionIndex];
458
+ highlightedOption._highlighted = true;
459
+ scrollIntoView(highlightedOption, this._listbox, "nearest");
460
+ this._changeDescription = this.locale.searchableSelect.optionFocusedMessage(
461
+ highlightedOption.text,
462
+ this._highlightedOptionIndex + 1,
463
+ this._filteredEnabledOptions.length
464
+ );
325
465
  }
326
- const didClose = prev === true && next === false;
327
- const selectionChangedWhileOpen = this.indexWhenOpened !== this.selectedIndex;
328
- if (didClose && selectionChangedWhileOpen) {
329
- this.updateValue(true);
466
+ }
467
+ #selectHighlightedOption() {
468
+ if (this._highlightedOptionIndex === null) {
469
+ return;
330
470
  }
471
+ this.#handleOptionInteraction(
472
+ this._filteredEnabledOptions[this._highlightedOptionIndex]
473
+ );
474
+ }
475
+ #highlightFirstOption() {
476
+ this.#transitionHighlightedOptionTo(0);
331
477
  }
332
- get collapsible() {
333
- return !this.multiple;
478
+ #highlightLastOption() {
479
+ this.#transitionHighlightedOptionTo(
480
+ this._filteredEnabledOptions.length - 1
481
+ );
482
+ }
483
+ #highlightPrevPage() {
484
+ this.#transitionHighlightedOptionTo(
485
+ (this._highlightedOptionIndex ?? this._filteredEnabledOptions.length) - PageSize
486
+ );
334
487
  }
488
+ #highlightNextPage() {
489
+ this.#transitionHighlightedOptionTo(
490
+ (this._highlightedOptionIndex ?? -1) + PageSize
491
+ );
492
+ }
493
+ #highlightPreviousOption() {
494
+ this.#transitionHighlightedOptionTo(
495
+ (this._highlightedOptionIndex ?? this._filteredEnabledOptions.length) - 1
496
+ );
497
+ }
498
+ #highlightNextOption() {
499
+ this.#transitionHighlightedOptionTo(
500
+ (this._highlightedOptionIndex ?? -1) + 1
501
+ );
502
+ }
503
+ // --- Tags ---
335
504
  /**
336
- * The value property.
337
- *
338
- * @public
505
+ * @internal
339
506
  */
340
- get value() {
341
- Observable.track(this, "value");
342
- return this._value;
343
- }
344
- set value(next) {
345
- const prev = `${this._value}`;
346
- if (this.length) {
347
- const selectedIndex = this._options.findIndex((el) => el.value === next);
348
- const prevSelectedValue = this._options[this.selectedIndex]?.value ?? null;
349
- const nextSelectedValue = this._options[selectedIndex]?.value ?? null;
350
- if (selectedIndex === -1 || prevSelectedValue !== nextSelectedValue) {
351
- next = "";
352
- this.selectedIndex = selectedIndex;
353
- }
354
- next = this.firstSelectedOption?.value ?? next;
355
- }
356
- if (prev !== next) {
357
- this._value = next;
358
- super.valueChanged(prev, next);
359
- Observable.notify(this, "value");
360
- this.updateDisplayValue();
361
- }
507
+ _tagLabelForValue(value) {
508
+ const option = this._slottedOptions.find(
509
+ (option2) => option2.value === value
510
+ );
511
+ return option.label;
362
512
  }
363
513
  /**
364
- * Sets the value and display value to match the first selected option.
365
- *
366
- * @param shouldEmit - if true, the input and change events will be emitted
367
- *
368
514
  * @internal
369
515
  */
370
- updateValue(shouldEmit) {
371
- if (this.$fastController.isConnected) {
372
- this.value = this.firstSelectedOption?.value ?? "";
373
- }
374
- if (shouldEmit) {
375
- this.$emit("input");
376
- this.$emit("change", this, {
377
- bubbles: true,
378
- composed: void 0
379
- });
380
- }
516
+ _tagConnotationForValue(value) {
517
+ const option = this._slottedOptions.find(
518
+ (option2) => option2.value === value
519
+ );
520
+ return option.tagConnotation;
381
521
  }
382
522
  /**
383
- * Updates the proxy value when the selected index changes.
384
- *
385
- * @param prev - the previous selected index
386
- * @param next - the next selected index
387
- *
388
523
  * @internal
389
524
  */
390
- selectedIndexChanged(prev, next) {
391
- super.selectedIndexChanged(prev, next);
392
- this.updateValue();
525
+ _isTagDisabled(value) {
526
+ const option = this._slottedOptions.find(
527
+ (option2) => option2.value === value
528
+ );
529
+ return this.disabled || option.disabled;
530
+ }
531
+ #textForValue(value) {
532
+ return this._slottedOptions?.find((option) => option.value === value)?.text;
393
533
  }
394
534
  /**
395
- * Handle opening and closing the listbox when the select is clicked.
396
- *
397
- * @param e - the mouse event
398
535
  * @internal
399
536
  */
400
- clickHandler(e) {
401
- if (this.disabled) {
537
+ #measureTagWidth(label, removable, hasIcon) {
538
+ const tag = document.createElement(this._optionTagTagName);
539
+ tag.label = label;
540
+ tag.removable = removable;
541
+ tag.style.cssText = "position: absolute; visibility: hidden;";
542
+ tag.hasIconPlaceholder = hasIcon;
543
+ this.shadowRoot.appendChild(tag);
544
+ const width = tag.getBoundingClientRect().width;
545
+ tag.remove();
546
+ return width;
547
+ }
548
+ #updateTagLayout() {
549
+ if (!this.multiple) {
550
+ this._numElidedTags = 0;
551
+ this._tagRows = [];
552
+ this._lastTagRow = [];
402
553
  return;
403
554
  }
404
- const clickedOption = e.target.closest(
405
- `option,[role=option]`
406
- );
407
- if (clickedOption && clickedOption.disabled) {
555
+ if (this.externalTags) {
556
+ this._numElidedTags = this.values.length;
557
+ this._tagRows = [];
558
+ this._lastTagRow = [];
408
559
  return;
409
560
  }
410
- if (this.multiple) {
411
- this.uncheckAllOptions();
412
- this.activeIndex = this.options.indexOf(clickedOption);
413
- this.checkActiveIndex();
414
- this.toggleSelectedForAllCheckedOptions();
415
- } else {
416
- super.clickHandler(e);
561
+ const rowWidth = this._contentArea.getBoundingClientRect().width;
562
+ const rows = [[]];
563
+ let currentRowIndex = 0;
564
+ let currentRowWidth = InputMinWidthPx;
565
+ let i;
566
+ for (i = this.values.length - 1; i >= 0; i--) {
567
+ const isLastRow = this.maxLines && currentRowIndex === this.maxLines - 1;
568
+ const tagWidth = this.#measureTagWidth(
569
+ this._tagLabelForValue(this.values[i]),
570
+ true,
571
+ this.#tagIconOfOption(this.selectedOptions[i]) !== null
572
+ );
573
+ const entry = {
574
+ value: this.values[i],
575
+ width: tagWidth
576
+ };
577
+ let elidedTagCounterWidth = 0;
578
+ if (isLastRow) {
579
+ const numElidedTags = i;
580
+ if (numElidedTags) {
581
+ elidedTagCounterWidth = TagGapPx + this.#measureTagWidth(numElidedTags.toString(), false, false);
582
+ }
583
+ }
584
+ const totalWidthNeeded = currentRowWidth + TagGapPx + tagWidth + elidedTagCounterWidth;
585
+ if (totalWidthNeeded > rowWidth) {
586
+ if (isLastRow) {
587
+ if (i === this.values.length - 1) {
588
+ rows[currentRowIndex].unshift(entry);
589
+ currentRowWidth += TagGapPx + tagWidth;
590
+ } else {
591
+ break;
592
+ }
593
+ } else {
594
+ rows.push([]);
595
+ currentRowIndex++;
596
+ rows[currentRowIndex].unshift(entry);
597
+ currentRowWidth = tagWidth;
598
+ }
599
+ continue;
600
+ }
601
+ rows[currentRowIndex].unshift(entry);
602
+ currentRowWidth += TagGapPx + tagWidth;
417
603
  }
418
- if (this.collapsible) {
419
- this.open = !this.open;
604
+ this._numElidedTags = i + 1;
605
+ rows.reverse();
606
+ for (let i2 = 0; i2 < rows.length - 1; i2++) {
607
+ let lineWidth = rows[i2].map((e) => e.width).reduce((a, b) => a + b, 0) + (rows[i2].length - 1) * TagGapPx;
608
+ if (i2 === 0 && this._numElidedTags) {
609
+ lineWidth += TagGapPx + this.#measureTagWidth(this._numElidedTags.toString(), false, false);
610
+ }
611
+ while (rows[i2 + 1].length && lineWidth + TagGapPx + rows[i2 + 1][0].width <= rowWidth) {
612
+ const nextTag = rows[i2 + 1].shift();
613
+ rows[i2].push(nextTag);
614
+ lineWidth += TagGapPx + nextTag.width;
615
+ }
420
616
  }
421
- return true;
617
+ const rowValues = rows.map((line) => line.map((entry) => entry.value));
618
+ this._tagRows = rowValues.slice(0, -1);
619
+ this._lastTagRow = rowValues.slice(-1)[0];
422
620
  }
423
621
  /**
424
- * Handles focus state when the element or its children lose focus.
425
- *
426
- * @param e - The focus event
427
622
  * @internal
428
623
  */
429
- focusoutHandler(e) {
430
- if (this.multiple) {
431
- this.uncheckAllOptions();
624
+ _onTagRemoved(value) {
625
+ this.#updateValuesThroughUserInteraction(
626
+ this.values.filter((option) => option !== value)
627
+ );
628
+ this.#updateFilteredOptions();
629
+ }
630
+ /**
631
+ * @internal
632
+ */
633
+ _onTagKeydown(event) {
634
+ const tagIndex = parseInt(event.target.dataset.index);
635
+ switch (event.key) {
636
+ case "Backspace":
637
+ case "Delete":
638
+ case "Enter":
639
+ case " ": {
640
+ this._onTagRemoved(this.values[tagIndex]);
641
+ DOM.processUpdates();
642
+ this.#moveTagFocusTo(this.#nextTagIndexForRemoved(tagIndex));
643
+ break;
644
+ }
645
+ case "ArrowLeft":
646
+ this.#moveTagFocusTo(this.#nextTagIndexLeft(tagIndex) ?? tagIndex);
647
+ break;
648
+ case "ArrowRight":
649
+ this.#moveTagFocusTo(this.#nextTagIndexRight(tagIndex));
650
+ break;
432
651
  }
433
- if (!this.open) {
434
- return true;
652
+ return true;
653
+ }
654
+ #moveTagFocusTo(index) {
655
+ if (index === null) {
656
+ this._input.focus();
657
+ } else {
658
+ this.shadowRoot.querySelector(`[data-index="${index}"]`)?.focus();
435
659
  }
436
- const focusTarget = e.relatedTarget;
437
- if (this.isSameNode(focusTarget)) {
438
- this.focus();
439
- return;
660
+ }
661
+ #nextTagIndexLeft(index) {
662
+ if (!this.values.length) {
663
+ return null;
440
664
  }
441
- if (!this.options.includes(focusTarget)) {
442
- this.open = false;
443
- if (this.indexWhenOpened !== this.selectedIndex) {
444
- this.updateValue(true);
665
+ for (let i = index - 1; i >= 0; i--) {
666
+ if (!this._isTagDisabled(this.values[i])) {
667
+ return i;
445
668
  }
446
669
  }
670
+ return null;
671
+ }
672
+ #nextTagIndexRight(index) {
673
+ if (!this.values.length) {
674
+ return null;
675
+ }
676
+ for (let i = index + 1; i < this.values.length; i++) {
677
+ if (!this._isTagDisabled(this.values[i])) {
678
+ return i;
679
+ }
680
+ }
681
+ return null;
682
+ }
683
+ #nextTagIndexForRemoved(index) {
684
+ return this.#nextTagIndexRight(index - 1) ?? this.#nextTagIndexLeft(index);
447
685
  }
448
686
  /**
449
- * Updates the value when an option's value changes.
450
- *
451
- * @param source - the source object
452
- * @param propertyName - the property to evaluate
453
- *
454
687
  * @internal
455
688
  */
456
- handleChange(source, propertyName) {
457
- super.handleChange(source, propertyName);
458
- if (propertyName === "value") {
459
- this.updateValue();
689
+ _onListboxClick(e) {
690
+ if (this.disabled) {
691
+ return;
692
+ }
693
+ const capturedOption = e.target.closest(
694
+ `option,[role=option]`
695
+ );
696
+ if (capturedOption && !capturedOption.disabled) {
697
+ this.#handleOptionInteraction(capturedOption);
460
698
  }
461
699
  }
462
700
  /**
463
- * Prevents focus when a scrollbar is clicked.
464
- *
465
- * @param e - the mouse event object
466
- *
467
701
  * @internal
468
702
  */
469
- mousedownHandler(e) {
470
- if (e.offsetX >= 0 && e.offsetX <= this.listbox.scrollWidth) {
471
- return super.mousedownHandler(e);
472
- }
473
- return this.collapsible;
703
+ get _shouldShowClearButton() {
704
+ return this.clearable && this.values.length > 0;
474
705
  }
475
706
  /**
476
707
  * @internal
477
708
  */
478
- multipleChanged(_, next) {
479
- this.options.forEach((o) => {
480
- o.checked = next ? false : void 0;
481
- });
482
- this.setSelectedOptions();
483
- if (this.proxy) {
484
- this.proxy.multiple = next;
485
- }
709
+ _onClearButtonClick() {
710
+ this.#updateValuesThroughUserInteraction(
711
+ this.selectedOptions.filter((option) => option.disabled).map((option) => option.value)
712
+ );
486
713
  }
487
714
  /**
488
- * Updates the selectedness of each option when the list of selected options changes.
489
- *
490
- * @param prev - the previous list of selected options
491
- * @param next - the current list of selected options
492
- *
493
715
  * @internal
494
716
  */
495
- selectedOptionsChanged(prev, next) {
496
- super.selectedOptionsChanged(prev, next);
497
- this.options.forEach((o, i) => {
498
- const proxyOption = this.proxy.options.item(i);
499
- if (proxyOption) {
500
- proxyOption.selected = o.selected;
717
+ maxSelectedChanged() {
718
+ this.#updateSelectionLimit();
719
+ }
720
+ #updateSelectionLimit() {
721
+ if (!this.multiple || typeof this.maxSelected !== "number" || this.maxSelected <= 0) {
722
+ return;
723
+ }
724
+ const options = this._slottedOptions.filter(
725
+ (option) => !this._slottedDisabledOptions.includes(option)
726
+ );
727
+ if (this.values.length >= this.maxSelected) {
728
+ const unselectedOptions = options.filter(
729
+ (option) => !this.selectedOptions.includes(option)
730
+ );
731
+ for (const option of unselectedOptions) {
732
+ option.disabled = true;
501
733
  }
502
- });
734
+ } else {
735
+ for (const option of options) {
736
+ option.disabled = false;
737
+ }
738
+ }
739
+ }
740
+ #determineInitialValues() {
741
+ return this.initialValues.length ? this.initialValues : this.initialValue ? [this.initialValue] : [];
503
742
  }
504
743
  /**
505
- * Resets and fills the proxy to match the component's options.
506
- *
507
744
  * @internal
508
745
  */
509
- setProxyOptions() {
510
- if (this.proxy instanceof HTMLSelectElement && this.options) {
511
- this.proxy.length = 0;
512
- this.options.forEach((option) => {
513
- const proxyOption = option.proxy || (option instanceof HTMLOptionElement ? option.cloneNode() : null);
514
- if (proxyOption) {
515
- this.proxy.options.add(proxyOption);
516
- }
517
- });
746
+ nameChanged(previous, next) {
747
+ super.nameChanged(previous, next);
748
+ this.#updateFormValue();
749
+ }
750
+ #updateFormValue() {
751
+ if (!this.name) {
752
+ this.setFormValue(null);
753
+ } else {
754
+ const formData = new FormData();
755
+ for (const value of this.values) {
756
+ formData.append(this.name, value);
757
+ }
758
+ this.setFormValue(formData);
518
759
  }
519
760
  }
520
761
  /**
521
- * Handles keydown actions when the select is in multiple selection mode.
522
- *
523
762
  * @internal
524
763
  */
525
- multipleKeydownHandler(e) {
764
+ formResetCallback() {
765
+ super.formResetCallback();
766
+ this.#updateValuesThroughUserInteraction(this.#determineInitialValues());
767
+ }
768
+ #resizeObserver;
769
+ /**
770
+ * @internal
771
+ */
772
+ _onFieldsetClick(e) {
526
773
  if (this.disabled) {
527
774
  return;
528
775
  }
529
- const { key, shiftKey } = e;
530
- this.shouldSkipFocus = false;
531
- switch (key) {
532
- case keyHome: {
533
- this.checkFirstOption(shiftKey);
534
- return;
535
- }
536
- case keyArrowDown: {
537
- this.checkNextOption(shiftKey);
538
- return;
539
- }
540
- case keyArrowUp: {
541
- this.checkPreviousOption(shiftKey);
542
- return;
543
- }
544
- case keyEnd: {
545
- this.checkLastOption(shiftKey);
546
- return;
547
- }
548
- case keyTab: {
549
- this.focusAndScrollOptionIntoView();
550
- return;
551
- }
552
- case keyEscape: {
553
- this.uncheckAllOptions();
554
- this.checkActiveIndex();
555
- return;
556
- }
557
- case keySpace: {
558
- e.preventDefault();
559
- if (this.typeaheadExpired) {
560
- this.toggleSelectedForAllCheckedOptions();
561
- return;
562
- }
563
- }
564
- default: {
565
- if (key.length === 1) {
566
- this.handleTypeAhead(`${key}`);
567
- }
568
- return;
569
- }
776
+ if (!e.defaultPrevented) {
777
+ this._input.focus();
778
+ this.open = true;
570
779
  }
571
780
  }
572
781
  /**
573
- * Handle keyboard interaction for the select.
574
- *
575
- * @param e - the keyboard event
576
782
  * @internal
577
783
  */
578
- keydownHandler(e) {
579
- const selectedIndexBefore = this.selectedIndex;
580
- if (this.multiple) {
581
- this.multipleKeydownHandler(e);
582
- } else {
583
- super.keydownHandler(e);
584
- }
585
- const key = e.key;
586
- switch (key) {
587
- case keySpace: {
588
- e.preventDefault();
589
- if (this.collapsible && this.typeaheadExpired) {
590
- this.open = !this.open;
591
- }
592
- break;
593
- }
594
- case keyHome:
595
- case keyEnd: {
596
- e.preventDefault();
597
- break;
598
- }
599
- case keyEnter: {
600
- e.preventDefault();
601
- this.open = !this.open;
602
- break;
603
- }
604
- case keyEscape: {
605
- if (this.collapsible && this.open) {
606
- e.preventDefault();
607
- this.open = false;
608
- }
609
- break;
610
- }
611
- case keyTab: {
612
- if (this.collapsible && this.open) {
613
- e.preventDefault();
614
- this.open = false;
615
- }
616
- return true;
617
- }
618
- }
619
- if (this.collapsible && !this.open && this.selectedIndex !== selectedIndexBefore) {
620
- this.updateValue(true);
784
+ _onChevronClick() {
785
+ if (this.open) {
786
+ this.open = false;
787
+ return false;
621
788
  }
622
- return !(e.key === keyArrowDown || e.key === keyArrowUp);
789
+ return true;
623
790
  }
624
791
  connectedCallback() {
625
792
  super.connectedCallback();
626
- this.addEventListener("focusout", this.focusoutHandler);
627
- this.addEventListener("contentchange", this.updateDisplayValue);
793
+ if (!this.values.length) {
794
+ this.values = this.#determineInitialValues();
795
+ }
796
+ this.#resizeObserver.observe(this._contentArea);
628
797
  }
629
798
  disconnectedCallback() {
630
- this.removeEventListener("focusout", this.focusoutHandler);
631
- this.removeEventListener("contentchange", this.updateDisplayValue);
632
799
  super.disconnectedCallback();
800
+ this.#resizeObserver.disconnect();
633
801
  }
634
802
  /**
635
- *
636
803
  * @internal
637
804
  */
638
- updateDisplayValue() {
639
- if (this.collapsible) {
640
- Observable.notify(this, "displayValue");
641
- }
642
- }
643
- labelChanged() {
644
- if (!this.ariaLabel) {
645
- this.ariaLabel = this.label;
646
- }
647
- }
648
- get displayValue() {
649
- Observable.track(this, "displayValue");
650
- return this.firstSelectedOption?.getAttribute("label") ?? this.firstSelectedOption?.text ?? this.placeholder ?? "";
651
- }
652
- setDefaultSelectedOption() {
653
- const options = Array.from(this.children).filter(
654
- Listbox.slottedOptionFilter
655
- );
656
- const selectedIndex = options.findIndex(
657
- (el) => el.hasAttribute("selected") || el.selected || el.value === this.value
658
- );
659
- if (selectedIndex === -1 && !this.placeholderOption) {
660
- this.selectedIndex = 0;
661
- return;
662
- }
663
- if (selectedIndex !== -1 || this.placeholder !== "") {
664
- this.selectedIndex = selectedIndex;
665
- return;
666
- }
805
+ validate() {
806
+ super.validate(this._input ?? void 0);
667
807
  }
668
- /*
808
+ /**
669
809
  * @internal
670
810
  */
671
- slottedOptionsChanged(prev, next) {
672
- this.options.forEach((o) => {
673
- const notifier = Observable.getNotifier(o);
674
- notifier.unsubscribe(this, "value");
675
- });
676
- super.slottedOptionsChanged(prev, next);
677
- this.options.forEach((o) => {
678
- const notifier = Observable.getNotifier(o);
679
- notifier.subscribe(this, "value");
680
- });
681
- this.setProxyOptions();
682
- this.updateValue();
683
- const scale = this.getAttribute("scale") || this.scale;
684
- next.forEach((element) => {
685
- if (scale) {
686
- element.setAttribute("scale", scale);
687
- element.scale = scale;
688
- }
689
- });
690
- this.proxy.value = this.value;
691
- this.validate();
811
+ focus(options) {
812
+ this._input?.focus(options);
692
813
  }
693
- formResetCallback() {
694
- this.setProxyOptions();
695
- super.setDefaultSelectedOption();
696
- if (this.selectedIndex === -1) {
697
- this.selectedIndex = 0;
698
- }
699
- if (this.placeholder) {
700
- this.selectedIndex = -1;
814
+ /**
815
+ * @internal
816
+ */
817
+ _onMouseDown(event) {
818
+ const originalTarget = event.composedPath()[0];
819
+ if (!event.defaultPrevented && originalTarget !== this._input) {
820
+ this._input.focus();
821
+ return false;
701
822
  }
823
+ return true;
702
824
  }
703
- };
704
- __decorateClass([
825
+ }
826
+ __decorateClass$1([
827
+ attr
828
+ ], SearchableSelect.prototype, "appearance");
829
+ __decorateClass$1([
830
+ attr
831
+ ], SearchableSelect.prototype, "shape");
832
+ __decorateClass$1([
833
+ attr({ mode: "boolean", attribute: "fixed-dropdown" })
834
+ ], SearchableSelect.prototype, "fixedDropdown");
835
+ __decorateClass$1([
836
+ attr
837
+ ], SearchableSelect.prototype, "placeholder");
838
+ __decorateClass$1([
839
+ attr({ mode: "boolean" })
840
+ ], SearchableSelect.prototype, "open");
841
+ __decorateClass$1([
842
+ attr({ mode: "boolean" })
843
+ ], SearchableSelect.prototype, "multiple");
844
+ __decorateClass$1([
845
+ attr({ attribute: "external-tags", mode: "boolean" })
846
+ ], SearchableSelect.prototype, "externalTags");
847
+ __decorateClass$1([
848
+ attr({ attribute: "max-lines", converter: nullableNumberConverter })
849
+ ], SearchableSelect.prototype, "maxLines");
850
+ __decorateClass$1([
705
851
  observable
706
- ], Select.prototype, "activeIndex", 2);
707
- // @ts-expect-error Type is incorrectly non-optional
708
- __decorateClass([
852
+ ], SearchableSelect.prototype, "values");
853
+ __decorateClass$1([
854
+ observable
855
+ ], SearchableSelect.prototype, "initialValues");
856
+ __decorateClass$1([
857
+ observable
858
+ ], SearchableSelect.prototype, "_input");
859
+ __decorateClass$1([
860
+ observable
861
+ ], SearchableSelect.prototype, "_currentSearchText");
862
+ __decorateClass$1([
863
+ observable
864
+ ], SearchableSelect.prototype, "_slottedOptions");
865
+ __decorateClass$1([
866
+ observable
867
+ ], SearchableSelect.prototype, "optionFilter");
868
+ __decorateClass$1([
869
+ observable
870
+ ], SearchableSelect.prototype, "_filteredOptions");
871
+ __decorateClass$1([
872
+ observable
873
+ ], SearchableSelect.prototype, "_filteredEnabledOptions");
874
+ __decorateClass$1([
875
+ attr({
876
+ mode: "boolean"
877
+ })
878
+ ], SearchableSelect.prototype, "loading");
879
+ __decorateClass$1([
880
+ observable
881
+ ], SearchableSelect.prototype, "_highlightedOptionIndex");
882
+ __decorateClass$1([
883
+ observable
884
+ ], SearchableSelect.prototype, "_contentArea");
885
+ __decorateClass$1([
886
+ observable
887
+ ], SearchableSelect.prototype, "_numElidedTags");
888
+ __decorateClass$1([
889
+ observable
890
+ ], SearchableSelect.prototype, "_tagRows");
891
+ __decorateClass$1([
892
+ observable
893
+ ], SearchableSelect.prototype, "_lastTagRow");
894
+ __decorateClass$1([
895
+ observable
896
+ ], SearchableSelect.prototype, "_listbox");
897
+ __decorateClass$1([
709
898
  attr({ mode: "boolean" })
710
- ], Select.prototype, "multiple", 2);
711
- __decorateClass([
712
- attr({ attribute: "open", mode: "boolean" })
713
- ], Select.prototype, "open", 2);
714
- __decorateClass([
715
- volatile
716
- ], Select.prototype, "collapsible", 1);
717
- __decorateClass([
899
+ ], SearchableSelect.prototype, "clearable");
900
+ __decorateClass$1([
901
+ attr({ attribute: "max-selected", converter: nullableNumberConverter })
902
+ ], SearchableSelect.prototype, "maxSelected");
903
+ __decorateClass$1([
718
904
  observable
719
- ], Select.prototype, "control", 2);
720
- __decorateClass([
905
+ ], SearchableSelect.prototype, "_slottedDisabledOptions");
906
+ __decorateClass$1([
721
907
  observable
722
- ], Select.prototype, "maxHeight", 2);
723
- __decorateClass([
908
+ ], SearchableSelect.prototype, "_changeDescription");
909
+ __decorateClass$1([
724
910
  observable
725
- ], Select.prototype, "_anchor", 2);
726
- __decorateClass([
727
- attr()
728
- ], Select.prototype, "scale", 2);
729
- __decorateClass([
730
- attr
731
- ], Select.prototype, "appearance", 2);
911
+ ], SearchableSelect.prototype, "_anchor");
912
+
913
+ var __defProp = Object.defineProperty;
914
+ var __decorateClass = (decorators, target, key, kind) => {
915
+ var result = void 0 ;
916
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
917
+ if (decorator = decorators[i])
918
+ result = (decorator(target, key, result) ) || result;
919
+ if (result) __defProp(target, key, result);
920
+ return result;
921
+ };
922
+ class OptionTag extends Localized(VividElement) {
923
+ constructor() {
924
+ super(...arguments);
925
+ this.removable = false;
926
+ this.disabled = false;
927
+ this.hasIconPlaceholder = false;
928
+ }
929
+ _onClickRemove() {
930
+ this.$emit("remove", void 0, {
931
+ bubbles: false
932
+ });
933
+ }
934
+ }
732
935
  __decorateClass([
733
936
  attr
734
- ], Select.prototype, "shape", 2);
937
+ ], OptionTag.prototype, "shape");
735
938
  __decorateClass([
736
- attr({ mode: "boolean", attribute: "fixed-dropdown" })
737
- ], Select.prototype, "fixedDropdown", 2);
939
+ observable
940
+ ], OptionTag.prototype, "connotation");
738
941
  __decorateClass([
739
942
  attr
740
- ], Select.prototype, "placeholder", 2);
943
+ ], OptionTag.prototype, "label");
741
944
  __decorateClass([
742
- observable
743
- ], Select.prototype, "placeholderOption", 2);
945
+ attr({ mode: "boolean" })
946
+ ], OptionTag.prototype, "removable");
744
947
  __decorateClass([
745
- observable
746
- ], Select.prototype, "_feedbackWrapper", 2);
948
+ attr({ mode: "boolean" })
949
+ ], OptionTag.prototype, "disabled");
747
950
  __decorateClass([
748
951
  observable
749
- ], Select.prototype, "metaSlottedContent", 2);
750
- Select = __decorateClass([
751
- errorText,
752
- formElements
753
- ], Select);
754
- applyMixinsWithObservables(
755
- Select,
756
- FormElementHelperText,
757
- FormElementSuccessText
758
- );
952
+ ], OptionTag.prototype, "hasIconPlaceholder");
759
953
 
760
- const getStateClasses = ({
761
- shape,
762
- disabled,
763
- appearance,
764
- metaSlottedContent,
765
- errorValidationMessage,
766
- successText,
767
- placeholder,
768
- value,
769
- scale
770
- }) => classNames(
771
- ["disabled", disabled],
772
- [`appearance-${appearance}`, Boolean(appearance)],
773
- [`shape-${shape}`, Boolean(shape)],
774
- ["has-meta", Boolean(metaSlottedContent?.length)],
775
- ["error", Boolean(errorValidationMessage)],
776
- ["success", !!successText],
777
- ["shows-placeholder", Boolean(placeholder) && !value],
778
- [`size-${scale}`, Boolean(scale)]
954
+ const getStateClasses = (x) => classNames(
955
+ ["disabled", x.disabled],
956
+ [`appearance-${x.appearance}`, Boolean(x.appearance)],
957
+ [`shape-${x.shape}`, Boolean(x.shape)],
958
+ ["error", Boolean(x.errorValidationMessage)],
959
+ ["success", !!x.successText]
779
960
  );
780
961
  function renderLabel() {
781
- return html` <label
782
- for="${(x) => x.multiple ? null : "control"}"
783
- class="label"
784
- id="label"
785
- >
786
- ${(x) => x.label}
787
- </label>`;
962
+ return html`
963
+ <label for="control" class="label" id="label"> ${(x) => x.label} </label>
964
+ `;
788
965
  }
789
- function renderPlaceholder(context) {
790
- const optionTag = context.tagFor(ListboxOption);
966
+ function renderSelectionCount() {
791
967
  return html`
792
- <${optionTag} ${ref("placeholderOption")}
793
- text="${(x) => x.placeholder}" hidden disabled>
794
- </${optionTag}>`;
968
+ <span
969
+ id="selection-count"
970
+ class="selection-count"
971
+ aria-label="${(x) => x.locale.searchableSelect.maxSelectedMessage(
972
+ x.values.length,
973
+ x.maxSelected
974
+ )}"
975
+ >(${(x) => `${x.values.length}/${x.maxSelected}`})</span
976
+ >
977
+ `;
795
978
  }
796
- function selectValue(context) {
979
+ const tagTemplateFactory = (context, getComponent) => {
980
+ const optionTagTag = context.tagFor(OptionTag);
981
+ return html`
982
+ <div class="tag-wrapper">
983
+ <${optionTagTag}
984
+ class="tag"
985
+ tabindex="-1"
986
+ data-index="${(x, c) => getComponent(c).values.indexOf(x)}"
987
+ removable
988
+ :label="${(x, c) => getComponent(c)._tagLabelForValue(x)}"
989
+ :shape="${(_, c) => getComponent(c).shape}"
990
+ :connotation="${(x, c) => getComponent(c)._tagConnotationForValue(x)}"
991
+ ?disabled="${(x, c) => getComponent(c)._isTagDisabled(x)}"
992
+ @remove="${(x, c) => getComponent(c)._onTagRemoved(x)}"
993
+ @keydown="${(_, c) => getComponent(c)._onTagKeydown(c.event)}"
994
+ @mousedown="${() => false}">
995
+ <slot slot="icon" name="${(x, c) => getComponent(c)._tagIconSlotName(x)}"></slot>
996
+ </${optionTagTag}>
997
+ </div>
998
+ `;
999
+ };
1000
+ const elidedTagTemplateFactory = (context, getComponent) => {
1001
+ const optionTagTag = context.tagFor(OptionTag);
1002
+ return html`
1003
+ <${optionTagTag}
1004
+ class="tag"
1005
+ tabindex="-1"
1006
+ :label="${(x, c) => getComponent(x, c)._numElidedTags.toString()}"
1007
+ :shape="${(x, c) => getComponent(x, c).shape}"
1008
+ ?disabled="${(x, c) => getComponent(x, c).disabled}"
1009
+ @mousedown="${() => false}">
1010
+ </${optionTagTag}>
1011
+ `;
1012
+ };
1013
+ function renderFieldset(context) {
1014
+ const buttonTag = context.tagFor(Button);
1015
+ const progressRingTag = context.tagFor(ProgressRing);
797
1016
  const affixIconTemplate = affixIconTemplateFactory(context);
798
1017
  const chevronTemplate = chevronTemplateFactory(context);
799
- return html` <div
800
- class="control ${getStateClasses}"
801
- ${ref("_anchor")}
802
- id="control"
803
- ?disabled="${(x) => x.disabled}"
804
- >
805
- <div class="selected-value">
1018
+ const tagTemplate = tagTemplateFactory(context, (c) => c.parent);
1019
+ const nestedTagTemplate = tagTemplateFactory(
1020
+ context,
1021
+ (c) => c.parentContext.parent
1022
+ );
1023
+ const elidedTagTemplate = elidedTagTemplateFactory(context, (x, _) => x);
1024
+ const nestedElidedTagTemplate = elidedTagTemplateFactory(
1025
+ context,
1026
+ (_, c) => c.parent
1027
+ );
1028
+ return html`
1029
+ <div
1030
+ class="fieldset ${getStateClasses}"
1031
+ @click="${(x, c) => x._onFieldsetClick(c.event)}"
1032
+ ${ref("_anchor")}
1033
+ >
806
1034
  ${(x) => affixIconTemplate(x.icon, IconWrapper.Slot)}
807
- <span class="text">${(x) => x.displayValue}</span>
808
- <slot name="meta" ${slotted("metaSlottedContent")}></slot>
1035
+ <div class="content-area" ${ref("_contentArea")}>
1036
+ ${repeat(
1037
+ (x) => x._tagRows,
1038
+ html`
1039
+ <div class="tag-row">
1040
+ ${when(
1041
+ (_, c) => c.isFirst && c.parent._numElidedTags,
1042
+ nestedElidedTagTemplate
1043
+ )}
1044
+ ${repeat((x) => x, nestedTagTemplate)}
1045
+ </div>
1046
+ `,
1047
+ { positioning: true }
1048
+ )}
1049
+ <div
1050
+ class="tag-row ${(x) => classNames([
1051
+ "contains-only-input",
1052
+ x._tagRows.length > 0 && x._lastTagRow.length === 0
1053
+ ])}"
1054
+ >
1055
+ ${when(
1056
+ (x) => x._tagRows.length === 0 && x._numElidedTags,
1057
+ elidedTagTemplate
1058
+ )}
1059
+ ${repeat((x) => x._lastTagRow, tagTemplate)}
1060
+ <input
1061
+ id="control"
1062
+ class="control"
1063
+ autocomplete="off"
1064
+ aria-controls="listbox"
1065
+ aria-describedby="${(x) => x._feedbackDescribedBy} ${(x) => x.multiple && x.maxSelected && x.maxSelected >= 1 ? "selection-count" : null}"
1066
+ ${delegateAria({
1067
+ role: "combobox",
1068
+ ariaAutoComplete: "list",
1069
+ ariaHasPopup: "listbox",
1070
+ ariaExpanded: (x) => x.open
1071
+ })}
1072
+ placeholder="${(x) => x.multiple && x.values.length ? "" : x.placeholder}"
1073
+ type="text"
1074
+ ?disabled="${(x) => x.disabled}"
1075
+ :value="${(x) => x._inputValue}"
1076
+ @input="${(x, c) => {
1077
+ x._onInputInput(c.event);
1078
+ c.event.stopPropagation();
1079
+ }}"
1080
+ @change="${(_, c) => {
1081
+ c.event.stopPropagation();
1082
+ }}"
1083
+ @focus="${(x, c) => x._onInputFocus(c.event)}"
1084
+ @blur="${(x, c) => x._onInputBlur(c.event)}"
1085
+ @keydown="${(x, c) => x._onInputKeydown(c.event)}"
1086
+ ${ref("_input")}
1087
+ />
1088
+ </div>
1089
+ </div>
1090
+ <slot name="meta"></slot>
1091
+ ${when(
1092
+ (x) => x._shouldShowClearButton,
1093
+ html`<${buttonTag}
1094
+ aria-label="${(x) => x.locale.searchableSelect.clearButtonLabel}"
1095
+ @click="${(x) => x._onClearButtonClick()}"
1096
+ @mousedown="${() => false}"
1097
+ ?disabled="${(x) => x.disabled}"
1098
+ :shape="${(x) => x.shape}"
1099
+ size="super-condensed"
1100
+ icon="close-line"
1101
+ appearance="ghost-light"
1102
+ tabindex="-1"
1103
+ ></${buttonTag}>`
1104
+ )}
1105
+ <div @mousedown="${() => false}" @click="${(x) => x._onChevronClick()}">
1106
+ ${when(
1107
+ (x) => x.loading,
1108
+ html`<${progressRingTag} indeterminate size="-6"></${progressRingTag}>`
1109
+ )}
1110
+ ${when((x) => !x.loading, chevronTemplate)}
1111
+ </div>
809
1112
  </div>
810
- ${chevronTemplate}
811
- </div>`;
1113
+ `;
812
1114
  }
813
1115
  function setFixedDropdownVarWidth(x) {
814
- return x.open && x.fixedDropdown ? `--_select-fixed-width: ${Math.round(x.getBoundingClientRect().width)}px` : null;
1116
+ return x.open && x.fixedDropdown ? `--_searchable-select-fixed-width: ${Math.round(
1117
+ x.getBoundingClientRect().width
1118
+ )}px` : null;
815
1119
  }
816
1120
  function renderControl(context) {
817
1121
  const popupTag = context.tagFor(Popup);
818
1122
  return html`
819
- ${when((x) => x.label, renderLabel())}
820
- <div class="control-wrapper">
821
- ${when((x) => !x.multiple, selectValue(context))}
822
- <${popupTag} class="popup"
823
- style="${setFixedDropdownVarWidth}"
824
- ?open="${(x) => x.collapsible ? x.open : true}"
1123
+ ${when(
1124
+ (x) => x.label || x.multiple && x.maxSelected && x.maxSelected >= 1,
1125
+ html`
1126
+ <div>
1127
+ ${when((x) => x.label, renderLabel())}
1128
+ ${when(
1129
+ (x) => x.multiple && x.maxSelected && x.maxSelected >= 1,
1130
+ renderSelectionCount()
1131
+ )}
1132
+ </div>
1133
+ `
1134
+ )}
1135
+ <span aria-live="assertive" aria-relevant="text" class="visually-hidden">
1136
+ ${(x) => x._changeDescription}
1137
+ </span>
1138
+ <div>
1139
+ ${renderFieldset(context)}
1140
+ <div class="popup-wrapper">
1141
+ <${popupTag}
825
1142
  :anchor="${(x) => x._anchor}"
1143
+ :open="${(x) => x.open}"
1144
+ class="popup"
826
1145
  placement="bottom-start"
827
- strategy="${(x) => x.fixedDropdown ? null : "absolute"}">
828
- <div class="listbox"
829
- id="${(x) => x.listboxId}"
1146
+ style="${setFixedDropdownVarWidth}"
1147
+ strategy="${(x) => x.fixedDropdown ? "fixed" : "absolute"}">
1148
+ <div
1149
+ class="listbox"
830
1150
  role="listbox"
831
1151
  aria-multiselectable="${(x) => x.multiple}"
832
- aria-label="${(x) => x.multiple && !x.label && x.ariaLabel ? x.ariaLabel : null}"
833
- aria-labelledby="${(x) => x.multiple && x.label ? "label" : null}"
834
- ?disabled="${(x) => x.disabled}"
835
- ?hidden="${(x) => x.collapsible ? !x.open : false}"
836
- ${ref("listbox")}>
837
- ${when((x) => x.placeholder, renderPlaceholder(context))}
1152
+ aria-required="${(x) => x.required}"
1153
+ ${ref("_listbox")}
1154
+ @click="${(x, c) => x._onListboxClick(c.event)}"
1155
+ @mousedown="${() => false}"
1156
+ >
838
1157
  <slot
839
1158
  ${slotted({
840
- filter: Listbox.slottedOptionFilter,
1159
+ filter: isListboxOption,
841
1160
  flatten: true,
842
- property: "slottedOptions"
1161
+ property: "_slottedOptions"
843
1162
  })}>
844
1163
  </slot>
845
- </div>
1164
+ ${when(
1165
+ (x) => x._filteredOptions.length === 0,
1166
+ html`<div class="empty-message">
1167
+ ${when(
1168
+ (x) => x.loading,
1169
+ html`<slot name="loading-options">
1170
+ ${(x) => x.locale.searchableSelect.loadingOptionsMessage}
1171
+ </slot>`
1172
+ )}
1173
+ ${when(
1174
+ (x) => !x.loading && x.searchText === "",
1175
+ html`<slot name="no-options">
1176
+ ${(x) => x.locale.searchableSelect.noOptionsMessage}
1177
+ </slot>`
1178
+ )}
1179
+ ${when(
1180
+ (x) => !x.loading && x.searchText !== "",
1181
+ html`<slot name="no-matches">
1182
+ ${(x) => x.locale.searchableSelect.noMatchesMessage}
1183
+ </slot>`
1184
+ )}
1185
+ </div>`
1186
+ )}
1187
+ </div>
846
1188
  </${popupTag}>
847
1189
  </div>
848
- `;
849
- }
850
- function ifNotFromFeedback(handler) {
851
- return (x, c) => {
852
- if (!c.event.composedPath().includes(x._feedbackWrapper)) {
853
- return handler(x, c.event);
854
- }
855
- return true;
856
- };
1190
+ </div>
1191
+ `;
857
1192
  }
858
- const SelectTemplate = (context) => {
1193
+ const SearchableSelectTemplate = (context) => {
1194
+ const optionTagTag = context.tagFor(OptionTag);
859
1195
  return html`
860
1196
  <template
861
- class="base"
862
- ${applyHostSemantics({
863
- role: "combobox",
864
- ariaLabel: (x) => x.ariaLabel ?? x.label,
865
- ariaHasPopup: (x) => x.collapsible ? "listbox" : "false",
866
- ariaExpanded: (x) => x.open,
867
- ariaDisabled: (x) => x.disabled
868
- })}
869
- aria-controls="${(x) => x.listboxId}"
870
- aria-activedescendant="${(x) => x._activeDescendant}"
871
- tabindex="${(x) => !x.disabled ? "0" : null}"
872
- @click="${ifNotFromFeedback((x, e) => x.clickHandler(e))}"
873
- @focusin="${ifNotFromFeedback((x, e) => x.focusinHandler(e))}"
874
- @focusout="${ifNotFromFeedback(
875
- (x, e) => x.focusoutHandler(e)
876
- )}"
877
- @keydown="${ifNotFromFeedback((x, e) => {
878
- x.open && handleEscapeKeyAndStopPropogation(e);
879
- return x.keydownHandler(e);
880
- })}"
881
- @mousedown="${ifNotFromFeedback(
882
- (x, e) => x.mousedownHandler(e)
883
- )}"
1197
+ :_optionTagTagName="${() => optionTagTag}"
1198
+ @mousedown="${(x, c) => x._onMouseDown(c.event)}"
884
1199
  >
885
- ${renderControl(context)}
886
- <div class="feedback-wrapper" ${ref("_feedbackWrapper")}>
887
- ${getFeedbackTemplate(context)}
1200
+ <div class="control-wrapper">
1201
+ ${renderControl(context)} ${(x) => x._getFeedbackTemplate(context)}
888
1202
  </div>
889
1203
  </template>
890
1204
  `;
891
1205
  };
892
1206
 
893
- const selectDefinition = defineVividComponent(
894
- "select",
895
- Select,
896
- SelectTemplate,
897
- [popupDefinition, iconDefinition, listboxOptionDefinition],
1207
+ const getClasses = ({ shape, connotation, disabled, removable }) => classNames(
1208
+ "base",
1209
+ ["disabled", disabled],
1210
+ ["removable", removable],
1211
+ [`shape-${shape}`, Boolean(shape)],
1212
+ [`connotation-${connotation}`, Boolean(connotation)]
1213
+ );
1214
+ function renderRemoveButton(iconTag) {
1215
+ return html`
1216
+ <span
1217
+ class="remove-button"
1218
+ aria-label="${(x) => x.locale.searchableSelect.removeTagButtonLabel(x.label)}"
1219
+ role="button"
1220
+ tabindex="${(x) => x.disabled ? null : 0}"
1221
+ @click="${(x) => x._onClickRemove()}"
1222
+ >
1223
+ <${iconTag} name="close-line"></${iconTag}>
1224
+ </span>
1225
+ `;
1226
+ }
1227
+ const optionTagTemplate = (context) => {
1228
+ const iconTag = context.tagFor(Icon);
1229
+ return html`<span class="${getClasses}" aria-disabled="${(x) => x.disabled}">
1230
+ <slot name="icon" aria-hidden="true">
1231
+ ${when(
1232
+ (x) => x.hasIconPlaceholder,
1233
+ html`<div class="icon-placeholder"></div>`
1234
+ )}
1235
+ </slot>
1236
+ ${when(
1237
+ (x) => x.label,
1238
+ (x) => html`<span class="label">${x.label}</span>`
1239
+ )}
1240
+ ${when((x) => x.removable, renderRemoveButton(iconTag))}
1241
+ </span>`;
1242
+ };
1243
+
1244
+ const optionTagDefinition = defineVividComponent(
1245
+ "option-tag",
1246
+ OptionTag,
1247
+ optionTagTemplate,
1248
+ [iconDefinition],
1249
+ {
1250
+ styles: [optionTagStyles],
1251
+ shadowOptions: {
1252
+ delegatesFocus: true
1253
+ }
1254
+ }
1255
+ );
1256
+ const searchableSelectDefinition = defineVividComponent(
1257
+ "searchable-select",
1258
+ SearchableSelect,
1259
+ SearchableSelectTemplate,
1260
+ [
1261
+ buttonDefinition,
1262
+ popupDefinition,
1263
+ iconDefinition,
1264
+ optionTagDefinition,
1265
+ progressRingDefinition,
1266
+ feedbackMessageDefinition
1267
+ ],
898
1268
  {
899
1269
  styles
900
1270
  }
901
1271
  );
902
- const registerSelect = createRegisterFunction(selectDefinition);
1272
+ const registerSearchableSelect = createRegisterFunction(
1273
+ searchableSelectDefinition
1274
+ );
903
1275
 
904
- export { Select as S, registerSelect as r, selectDefinition as s };
1276
+ export { SearchableSelect as S, registerSearchableSelect as r };