q2-tecton-elements 1.69.0 → 1.70.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 (275) hide show
  1. package/dist/bundle-report.json +2249 -358
  2. package/dist/cjs/click-elsewhere_3.cjs.entry.js +2 -2
  3. package/dist/cjs/{index-D2LFCsNP.js → index-SpDCZABA.js} +2 -2
  4. package/dist/cjs/loader.cjs.js +1 -1
  5. package/dist/cjs/q2-action-group_2.cjs.entry.js +1 -1
  6. package/dist/cjs/q2-action-sheet.cjs.entry.js +2 -2
  7. package/dist/cjs/q2-badge_2.cjs.entry.js +11 -4
  8. package/dist/cjs/q2-btn_2.cjs.entry.js +6 -2
  9. package/dist/cjs/q2-calendar.cjs.entry.js +5 -3
  10. package/dist/cjs/q2-card-image.cjs.entry.js +8 -6
  11. package/dist/cjs/q2-card.cjs.entry.js +2 -2
  12. package/dist/cjs/q2-carousel-pane.cjs.entry.js +1 -1
  13. package/dist/cjs/q2-carousel.cjs.entry.js +1 -1
  14. package/dist/cjs/q2-chart-area.cjs.entry.js +1 -1
  15. package/dist/cjs/q2-chart-bar.cjs.entry.js +1 -1
  16. package/dist/cjs/q2-chart-donut.cjs.entry.js +2 -2
  17. package/dist/cjs/q2-checkbox-group.cjs.entry.js +1 -1
  18. package/dist/cjs/q2-checkbox_2.cjs.entry.js +4 -3
  19. package/dist/cjs/q2-context.cjs.entry.js +1 -1
  20. package/dist/cjs/q2-currency.cjs.entry.js +1 -1
  21. package/dist/cjs/q2-data-table.cjs.entry.js +6 -3
  22. package/dist/cjs/q2-detail.cjs.entry.js +2 -2
  23. package/dist/cjs/q2-dropdown-item.cjs.entry.js +1 -1
  24. package/dist/cjs/q2-dropdown.cjs.entry.js +3 -3
  25. package/dist/cjs/q2-editable-field.cjs.entry.js +12 -4
  26. package/dist/cjs/q2-file-picker.cjs.entry.js +1 -1
  27. package/dist/cjs/q2-icon.cjs.entry.js +1 -1
  28. package/dist/cjs/q2-item.cjs.entry.js +2 -2
  29. package/dist/cjs/q2-legend.cjs.entry.js +1 -1
  30. package/dist/cjs/q2-link_2.cjs.entry.js +2 -2
  31. package/dist/cjs/q2-loc.cjs.entry.js +20 -2
  32. package/dist/cjs/q2-message.cjs.entry.js +1 -1
  33. package/dist/cjs/q2-meter.cjs.entry.js +2 -2
  34. package/dist/cjs/q2-modal.cjs.entry.js +3 -3
  35. package/dist/cjs/q2-month-picker.cjs.entry.js +1 -1
  36. package/dist/cjs/q2-optgroup.cjs.entry.js +1 -1
  37. package/dist/cjs/q2-option.cjs.entry.js +1 -1
  38. package/dist/cjs/q2-otp.cjs.entry.js +1 -1
  39. package/dist/cjs/q2-pagination.cjs.entry.js +10 -6
  40. package/dist/cjs/q2-pill.cjs.entry.js +1 -1
  41. package/dist/cjs/q2-radio-group.cjs.entry.js +1 -1
  42. package/dist/cjs/q2-radio.cjs.entry.js +1 -1
  43. package/dist/cjs/q2-relative-time.cjs.entry.js +1 -1
  44. package/dist/cjs/q2-search.cjs.entry.js +109 -16
  45. package/dist/cjs/q2-section.cjs.entry.js +2 -2
  46. package/dist/cjs/q2-select.cjs.entry.js +36 -6
  47. package/dist/cjs/q2-stepper-pane.cjs.entry.js +1 -1
  48. package/dist/cjs/q2-stepper-vertical.cjs.entry.js +24 -18
  49. package/dist/cjs/q2-stepper.cjs.entry.js +5 -4
  50. package/dist/cjs/q2-surface.cjs.entry.js +95 -0
  51. package/dist/cjs/q2-tab-container.cjs.entry.js +2 -2
  52. package/dist/cjs/q2-tab-pane.cjs.entry.js +1 -1
  53. package/dist/cjs/q2-tecton-elements.cjs.js +1 -1
  54. package/dist/cjs/q2-textarea.cjs.entry.js +1 -1
  55. package/dist/cjs/q2-toast.cjs.entry.js +1 -1
  56. package/dist/cjs/q2-tooltip.cjs.entry.js +1 -1
  57. package/dist/cjs/tecton-tab-pane.cjs.entry.js +2 -2
  58. package/dist/collection/collection-manifest.json +1 -0
  59. package/dist/collection/components/q2-action-sheet/q2-action-sheet.css +1 -1
  60. package/dist/collection/components/q2-btn/q2-btn.css +32 -17
  61. package/dist/collection/components/q2-btn/q2-btn.js +6 -1
  62. package/dist/collection/components/q2-calendar/q2-calendar.js +24 -2
  63. package/dist/collection/components/q2-calendar/test/q2-calendar-test.vr.js +4 -0
  64. package/dist/collection/components/q2-card/q2-card.css +2 -2
  65. package/dist/collection/components/q2-card-image/q2-card-image.css +3 -0
  66. package/dist/collection/components/q2-card-image/q2-card-image.js +25 -23
  67. package/dist/collection/components/q2-chart-area/q2-chart-area.css +4 -0
  68. package/dist/collection/components/q2-chart-bar/q2-chart-bar.css +4 -0
  69. package/dist/collection/components/q2-chart-donut/q2-chart-donut.css +1 -1
  70. package/dist/collection/components/q2-checkbox/q2-checkbox.js +2 -1
  71. package/dist/collection/components/q2-currency/q2-currency.css +1 -1
  72. package/dist/collection/components/q2-data-table/q2-data-table.js +5 -2
  73. package/dist/collection/components/q2-detail/q2-detail.css +12 -12
  74. package/dist/collection/components/q2-dropdown/q2-dropdown.js +2 -2
  75. package/dist/collection/components/q2-editable-field/q2-editable-field.js +11 -3
  76. package/dist/collection/components/q2-input/q2-input.css +11 -1
  77. package/dist/collection/components/q2-input/q2-input.js +29 -2
  78. package/dist/collection/components/q2-input/test/q2-input-test.vr.js +8 -0
  79. package/dist/collection/components/q2-item/q2-item.css +10 -5
  80. package/dist/collection/components/q2-item/test/q2-item-test.vr.js +132 -0
  81. package/dist/collection/components/q2-legend/q2-legend.css +2 -2
  82. package/dist/collection/components/q2-list/q2-list.css +1 -1
  83. package/dist/collection/components/q2-loc/q2-loc.js +42 -1
  84. package/dist/collection/components/q2-message/q2-message.css +4 -0
  85. package/dist/collection/components/q2-meter/q2-meter.css +3 -3
  86. package/dist/collection/components/q2-modal/q2-modal.css +1 -1
  87. package/dist/collection/components/q2-modal/q2-modal.js +2 -2
  88. package/dist/collection/components/q2-otp/q2-otp.css +33 -0
  89. package/dist/collection/components/q2-otp/test/q2-otp-test.vr.js +83 -0
  90. package/dist/collection/components/q2-pagination/q2-pagination.js +9 -5
  91. package/dist/collection/components/q2-popover/q2-popover.css +2 -1
  92. package/dist/collection/components/q2-radio/q2-radio.css +2 -0
  93. package/dist/collection/components/q2-search/q2-search.css +30 -0
  94. package/dist/collection/components/q2-search/q2-search.js +108 -15
  95. package/dist/collection/components/q2-search/test/q2-search-test.vr.js +145 -0
  96. package/dist/collection/components/q2-section/q2-section.css +1 -1
  97. package/dist/collection/components/q2-select/q2-select.css +4 -2
  98. package/dist/collection/components/q2-select/q2-select.js +54 -4
  99. package/dist/collection/components/q2-select/test/q2-select-test.vr.js +5 -0
  100. package/dist/collection/components/q2-stepper/q2-stepper.css +2 -2
  101. package/dist/collection/components/q2-stepper/q2-stepper.js +3 -2
  102. package/dist/collection/components/q2-stepper-vertical/q2-stepper-vertical.css +2 -2
  103. package/dist/collection/components/q2-stepper-vertical/q2-stepper-vertical.js +22 -16
  104. package/dist/collection/components/q2-surface/q2-surface.css +348 -0
  105. package/dist/collection/components/q2-surface/q2-surface.js +381 -0
  106. package/dist/collection/components/q2-surface/test/q2-surface-test.vr.js +108 -0
  107. package/dist/collection/components/q2-tab-container/q2-tab-container.js +1 -1
  108. package/dist/collection/components/q2-tab-pane/q2-tab-pane.js +1 -1
  109. package/dist/collection/components/q2-tag/q2-tag.js +1 -1
  110. package/dist/collection/components/q2-toast/q2-toast.js +3 -1
  111. package/dist/collection/components/tecton-tab-pane/tecton-tab-pane.js +2 -2
  112. package/dist/collection/utils/index.js +2 -2
  113. package/dist/components/index.js +12 -10
  114. package/dist/components/index2.js +3 -3
  115. package/dist/components/q2-action-sheet.js +1 -1
  116. package/dist/components/q2-btn2.js +5 -1
  117. package/dist/components/q2-calendar.js +294 -290
  118. package/dist/components/q2-card-image.js +9 -6
  119. package/dist/components/q2-card.js +14 -14
  120. package/dist/components/q2-chart-donut.js +1 -1
  121. package/dist/components/q2-checkbox2.js +12 -12
  122. package/dist/components/q2-currency.js +1 -1
  123. package/dist/components/q2-data-table.js +4 -4
  124. package/dist/components/q2-detail.js +1 -1
  125. package/dist/components/q2-dropdown.js +8 -8
  126. package/dist/components/q2-editable-field.js +15 -5
  127. package/dist/components/q2-input2.js +46 -26
  128. package/dist/components/q2-item2.js +1 -1
  129. package/dist/components/q2-legend2.js +51 -51
  130. package/dist/components/q2-list2.js +1 -1
  131. package/dist/components/q2-loc.js +23 -8
  132. package/dist/components/q2-meter.js +10 -10
  133. package/dist/components/q2-modal.js +54 -54
  134. package/dist/components/q2-pagination.js +28 -27
  135. package/dist/components/q2-popover2.js +1 -1
  136. package/dist/components/q2-search.js +128 -71
  137. package/dist/components/q2-section.js +1 -1
  138. package/dist/components/q2-select2.js +25 -9
  139. package/dist/components/q2-stepper-vertical.js +72 -63
  140. package/dist/components/q2-stepper.js +73 -71
  141. package/dist/components/q2-surface.d.ts +11 -0
  142. package/dist/components/q2-surface.js +109 -0
  143. package/dist/components/q2-tab-container.js +12 -12
  144. package/dist/components/q2-tab-pane.js +6 -6
  145. package/dist/components/q2-tag2.js +5 -5
  146. package/dist/components/tecton-tab-pane.js +10 -10
  147. package/dist/esm/click-elsewhere_3.entry.js +2 -2
  148. package/dist/esm/{index-Bx4m6N0g.js → index-Cps4jSQM.js} +2 -2
  149. package/dist/esm/loader.js +1 -1
  150. package/dist/esm/q2-action-group_2.entry.js +1 -1
  151. package/dist/esm/q2-action-sheet.entry.js +2 -2
  152. package/dist/esm/q2-badge_2.entry.js +11 -4
  153. package/dist/esm/q2-btn_2.entry.js +6 -2
  154. package/dist/esm/q2-calendar.entry.js +5 -3
  155. package/dist/esm/q2-card-image.entry.js +8 -6
  156. package/dist/esm/q2-card.entry.js +2 -2
  157. package/dist/esm/q2-carousel-pane.entry.js +1 -1
  158. package/dist/esm/q2-carousel.entry.js +1 -1
  159. package/dist/esm/q2-chart-area.entry.js +1 -1
  160. package/dist/esm/q2-chart-bar.entry.js +1 -1
  161. package/dist/esm/q2-chart-donut.entry.js +2 -2
  162. package/dist/esm/q2-checkbox-group.entry.js +1 -1
  163. package/dist/esm/q2-checkbox_2.entry.js +4 -3
  164. package/dist/esm/q2-context.entry.js +1 -1
  165. package/dist/esm/q2-currency.entry.js +1 -1
  166. package/dist/esm/q2-data-table.entry.js +6 -3
  167. package/dist/esm/q2-detail.entry.js +2 -2
  168. package/dist/esm/q2-dropdown-item.entry.js +1 -1
  169. package/dist/esm/q2-dropdown.entry.js +3 -3
  170. package/dist/esm/q2-editable-field.entry.js +12 -4
  171. package/dist/esm/q2-file-picker.entry.js +1 -1
  172. package/dist/esm/q2-icon.entry.js +1 -1
  173. package/dist/esm/q2-item.entry.js +2 -2
  174. package/dist/esm/q2-legend.entry.js +1 -1
  175. package/dist/esm/q2-link_2.entry.js +2 -2
  176. package/dist/esm/q2-loc.entry.js +20 -2
  177. package/dist/esm/q2-message.entry.js +1 -1
  178. package/dist/esm/q2-meter.entry.js +2 -2
  179. package/dist/esm/q2-modal.entry.js +3 -3
  180. package/dist/esm/q2-month-picker.entry.js +1 -1
  181. package/dist/esm/q2-optgroup.entry.js +1 -1
  182. package/dist/esm/q2-option.entry.js +1 -1
  183. package/dist/esm/q2-otp.entry.js +1 -1
  184. package/dist/esm/q2-pagination.entry.js +10 -6
  185. package/dist/esm/q2-pill.entry.js +1 -1
  186. package/dist/esm/q2-radio-group.entry.js +1 -1
  187. package/dist/esm/q2-radio.entry.js +1 -1
  188. package/dist/esm/q2-relative-time.entry.js +1 -1
  189. package/dist/esm/q2-search.entry.js +109 -16
  190. package/dist/esm/q2-section.entry.js +2 -2
  191. package/dist/esm/q2-select.entry.js +36 -6
  192. package/dist/esm/q2-stepper-pane.entry.js +1 -1
  193. package/dist/esm/q2-stepper-vertical.entry.js +24 -18
  194. package/dist/esm/q2-stepper.entry.js +5 -4
  195. package/dist/esm/q2-surface.entry.js +93 -0
  196. package/dist/esm/q2-tab-container.entry.js +2 -2
  197. package/dist/esm/q2-tab-pane.entry.js +1 -1
  198. package/dist/esm/q2-tecton-elements.js +1 -1
  199. package/dist/esm/q2-textarea.entry.js +1 -1
  200. package/dist/esm/q2-toast.entry.js +1 -1
  201. package/dist/esm/q2-tooltip.entry.js +1 -1
  202. package/dist/esm/tecton-tab-pane.entry.js +2 -2
  203. package/dist/q2-tecton-elements/click-elsewhere_3.entry.js +2 -2
  204. package/dist/q2-tecton-elements/{index-Bx4m6N0g.js → index-Cps4jSQM.js} +3 -3
  205. package/dist/q2-tecton-elements/q2-action-group_2.entry.js +1 -1
  206. package/dist/q2-tecton-elements/q2-action-sheet.entry.js +2 -2
  207. package/dist/q2-tecton-elements/q2-badge_2.entry.js +51 -32
  208. package/dist/q2-tecton-elements/q2-btn_2.entry.js +6 -2
  209. package/dist/q2-tecton-elements/q2-calendar.entry.js +7 -4
  210. package/dist/q2-tecton-elements/q2-card-image.entry.js +65 -62
  211. package/dist/q2-tecton-elements/q2-card.entry.js +2 -2
  212. package/dist/q2-tecton-elements/q2-carousel-pane.entry.js +1 -1
  213. package/dist/q2-tecton-elements/q2-carousel.entry.js +1 -1
  214. package/dist/q2-tecton-elements/q2-chart-area.entry.js +54 -54
  215. package/dist/q2-tecton-elements/q2-chart-bar.entry.js +3 -3
  216. package/dist/q2-tecton-elements/q2-chart-donut.entry.js +7 -7
  217. package/dist/q2-tecton-elements/q2-checkbox-group.entry.js +1 -1
  218. package/dist/q2-tecton-elements/q2-checkbox_2.entry.js +18 -18
  219. package/dist/q2-tecton-elements/q2-context.entry.js +1 -1
  220. package/dist/q2-tecton-elements/q2-currency.entry.js +1 -1
  221. package/dist/q2-tecton-elements/q2-data-table.entry.js +50 -50
  222. package/dist/q2-tecton-elements/q2-detail.entry.js +2 -2
  223. package/dist/q2-tecton-elements/q2-dropdown-item.entry.js +1 -1
  224. package/dist/q2-tecton-elements/q2-dropdown.entry.js +16 -16
  225. package/dist/q2-tecton-elements/q2-editable-field.entry.js +15 -8
  226. package/dist/q2-tecton-elements/q2-file-picker.entry.js +1 -1
  227. package/dist/q2-tecton-elements/q2-icon.entry.js +1 -1
  228. package/dist/q2-tecton-elements/q2-item.entry.js +2 -2
  229. package/dist/q2-tecton-elements/q2-legend.entry.js +1 -1
  230. package/dist/q2-tecton-elements/q2-link_2.entry.js +2 -2
  231. package/dist/q2-tecton-elements/q2-loc.entry.js +20 -6
  232. package/dist/q2-tecton-elements/q2-message.entry.js +1 -1
  233. package/dist/q2-tecton-elements/q2-meter.entry.js +2 -2
  234. package/dist/q2-tecton-elements/q2-modal.entry.js +55 -55
  235. package/dist/q2-tecton-elements/q2-month-picker.entry.js +1 -1
  236. package/dist/q2-tecton-elements/q2-optgroup.entry.js +1 -1
  237. package/dist/q2-tecton-elements/q2-option.entry.js +7 -7
  238. package/dist/q2-tecton-elements/q2-otp.entry.js +1 -1
  239. package/dist/q2-tecton-elements/q2-pagination.entry.js +25 -24
  240. package/dist/q2-tecton-elements/q2-pill.entry.js +1 -1
  241. package/dist/q2-tecton-elements/q2-radio-group.entry.js +1 -1
  242. package/dist/q2-tecton-elements/q2-radio.entry.js +1 -1
  243. package/dist/q2-tecton-elements/q2-relative-time.entry.js +1 -1
  244. package/dist/q2-tecton-elements/q2-search.entry.js +118 -61
  245. package/dist/q2-tecton-elements/q2-section.entry.js +2 -2
  246. package/dist/q2-tecton-elements/q2-select.entry.js +25 -10
  247. package/dist/q2-tecton-elements/q2-stepper-pane.entry.js +1 -1
  248. package/dist/q2-tecton-elements/q2-stepper-vertical.entry.js +75 -66
  249. package/dist/q2-tecton-elements/q2-stepper.entry.js +47 -45
  250. package/dist/q2-tecton-elements/q2-surface.entry.js +89 -0
  251. package/dist/q2-tecton-elements/q2-tab-container.entry.js +13 -13
  252. package/dist/q2-tecton-elements/q2-tab-pane.entry.js +2 -2
  253. package/dist/q2-tecton-elements/q2-tecton-elements.esm.js +1 -1
  254. package/dist/q2-tecton-elements/q2-textarea.entry.js +1 -1
  255. package/dist/q2-tecton-elements/q2-toast.entry.js +5 -5
  256. package/dist/q2-tecton-elements/q2-tooltip.entry.js +8 -8
  257. package/dist/q2-tecton-elements/tecton-tab-pane.entry.js +6 -6
  258. package/dist/types/components/q2-calendar/q2-calendar.d.ts +2 -0
  259. package/dist/types/components/q2-card-image/q2-card-image.d.ts +5 -5
  260. package/dist/types/components/q2-editable-field/q2-editable-field.d.ts +1 -0
  261. package/dist/types/components/q2-input/q2-input.d.ts +3 -0
  262. package/dist/types/components/q2-item/test/q2-item-test.vr.d.ts +1 -0
  263. package/dist/types/components/q2-loc/q2-loc.d.ts +6 -0
  264. package/dist/types/components/q2-modal/q2-modal.d.ts +1 -1
  265. package/dist/types/components/q2-otp/test/q2-otp-test.vr.d.ts +1 -0
  266. package/dist/types/components/q2-search/q2-search.d.ts +7 -0
  267. package/dist/types/components/q2-search/test/q2-search-test.vr.d.ts +1 -0
  268. package/dist/types/components/q2-select/q2-select.d.ts +4 -0
  269. package/dist/types/components/q2-stepper-vertical/q2-stepper-vertical.d.ts +2 -1
  270. package/dist/types/components/q2-surface/q2-surface.d.ts +54 -0
  271. package/dist/types/components/q2-surface/test/q2-surface-test.vr.d.ts +1 -0
  272. package/dist/types/components/q2-toast/q2-toast.d.ts +3 -1
  273. package/dist/types/components.d.ts +259 -13
  274. package/dist/types/utils/index.d.ts +3 -1
  275. package/package.json +4 -3
@@ -1,5 +1,5 @@
1
1
  import { Fragment, h, } from "@stencil/core";
2
- import { loc, overrideFocus, waitForNextPaint } from "../../utils";
2
+ import { isEventFromElement, loc, overrideFocus, waitForNextPaint } from "../../utils";
3
3
  /**
4
4
  * @name Search
5
5
  * @category Forms
@@ -83,6 +83,7 @@ export class Q2Search {
83
83
  */
84
84
  this.showSelectedToggle = false;
85
85
  this.clearAll = () => {
86
+ this.clearSelectedDisplay();
86
87
  const { multiple } = this;
87
88
  this.value = '';
88
89
  this.selectedOptions = [];
@@ -101,7 +102,7 @@ export class Q2Search {
101
102
  this.tctInput.emit({ query: '' });
102
103
  };
103
104
  this.handleSelectionChanges = (detail) => {
104
- var _a, _b, _c, _d, _e, _f, _g;
105
+ var _a, _b, _c, _d, _e;
105
106
  const { multiple } = this;
106
107
  if (multiple) {
107
108
  const values = (_a = detail.values) !== null && _a !== void 0 ? _a : [];
@@ -123,6 +124,10 @@ export class Q2Search {
123
124
  // Re-clicking the already-selected single-select option closes the popover
124
125
  // with no state change and no `tctChange`.
125
126
  if (value === this.value && value !== '') {
127
+ const reSelected = this.optionElements.find(o => o.value === value);
128
+ this.searchText = this.optionDisplayText(reSelected, value);
129
+ this.isShowingSelectionDisplay = true;
130
+ this.checkSelectedDisplay();
126
131
  // Focus before close so the focus event fires while open===true, which
127
132
  // causes onFocus to skip openDropdown (it's already open at that point).
128
133
  (_d = this.innerInputField) === null || _d === void 0 ? void 0 : _d.focus();
@@ -131,12 +136,13 @@ export class Q2Search {
131
136
  }
132
137
  this.value = value;
133
138
  const selected = this.optionElements.find(o => o.value === value);
134
- const display = (selected === null || selected === void 0 ? void 0 : selected.display) ? loc(selected.display) : (_f = (_e = selected === null || selected === void 0 ? void 0 : selected.textContent) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f : value;
139
+ const display = this.optionDisplayText(selected, value);
135
140
  this.searchText = display;
136
141
  this.isShowingSelectionDisplay = true;
142
+ this.checkSelectedDisplay();
137
143
  // Focus before close: the focus event fires while open===true so onFocus's
138
144
  // openDropdown call is a no-op, then closeDropdown brings us to closed+focused.
139
- (_g = this.innerInputField) === null || _g === void 0 ? void 0 : _g.focus();
145
+ (_e = this.innerInputField) === null || _e === void 0 ? void 0 : _e.focus();
140
146
  this.closeDropdown();
141
147
  this.tctChange.emit({
142
148
  value,
@@ -154,6 +160,8 @@ export class Q2Search {
154
160
  this.inputInputHandler = (event) => {
155
161
  event.stopPropagation();
156
162
  const next = event.detail.value;
163
+ if (this.isShowingSelectionDisplay)
164
+ this.clearSelectedDisplay();
157
165
  // Preserve `value` while the user types — they're searching, not deselecting.
158
166
  // `handleSelectionChanges` updates value if they pick a different option,
159
167
  // and `revertToSelectedDisplay` restores the display on blur.
@@ -208,6 +216,17 @@ export class Q2Search {
208
216
  event.stopPropagation();
209
217
  this.handleSelectionChanges(event.detail);
210
218
  };
219
+ this.onSelectedDisplayMousedown = (event) => {
220
+ var _a;
221
+ event.preventDefault();
222
+ event.stopPropagation();
223
+ this.isShowingSelectionDisplay = false;
224
+ this.clearSelectedDisplay();
225
+ if (this.value)
226
+ (_a = this.innerInputField) === null || _a === void 0 ? void 0 : _a.select();
227
+ if (!this.open)
228
+ this.openDropdown();
229
+ };
211
230
  this.onViewportResize = () => {
212
231
  if (!this.open || !this.popoverAlignment)
213
232
  return;
@@ -248,19 +267,27 @@ export class Q2Search {
248
267
  // #endregion
249
268
  // #region Listeners
250
269
  onFocus(event) {
251
- var _a, _b, _c;
270
+ var _a, _b, _c, _d;
271
+ // Delegate programmatic focus from the host to the inner input
272
+ if (event && isEventFromElement(event, this.hostElement)) {
273
+ (_a = this.innerInputField) === null || _a === void 0 ? void 0 : _a.focus();
274
+ }
252
275
  // Ignore focus that originates inside the popover (e.g., tabbing into a slotted
253
276
  // popover-bottom action button). Without this gate, the focus event bubbles up and
254
277
  // re-opens a just-closed popover as the user is trying to leave it.
255
278
  const path = typeof (event === null || event === void 0 ? void 0 : event.composedPath) === 'function' ? event.composedPath() : [];
256
- const target = ((_a = path[0]) !== null && _a !== void 0 ? _a : event === null || event === void 0 ? void 0 : event.target);
257
- if (target && ((_b = this.popoverElement) === null || _b === void 0 ? void 0 : _b.contains(target)))
279
+ const target = ((_b = path[0]) !== null && _b !== void 0 ? _b : event === null || event === void 0 ? void 0 : event.target);
280
+ if (target && ((_c = this.popoverElement) === null || _c === void 0 ? void 0 : _c.contains(target)))
258
281
  return;
259
282
  this.inputFocused = true;
260
- if (this.isShowingSelectionDisplay) {
283
+ // Only switch away from the selection display when the user actively re-engages the
284
+ // field (popover is closed). During the post-selection focus that closes the popover,
285
+ // `open` is still true — clearing here would immediately discard the rich display.
286
+ if (this.isShowingSelectionDisplay && !this.open) {
261
287
  // Clear the flag before mutating searchText so the filter watcher runs against the
262
288
  // new query, not the stale "showing a selection" guard.
263
289
  this.isShowingSelectionDisplay = false;
290
+ this.clearSelectedDisplay();
264
291
  if (this.multiple) {
265
292
  // Multi-select always opens to a fresh search after a selection.
266
293
  this.searchText = '';
@@ -268,7 +295,7 @@ export class Q2Search {
268
295
  else if (this.value) {
269
296
  // Single-select: pre-select the displayed text so the next keystroke replaces it
270
297
  // — typeahead pattern used by Material Autocomplete, Headless UI Combobox, etc.
271
- (_c = this.innerInputField) === null || _c === void 0 ? void 0 : _c.select();
298
+ (_d = this.innerInputField) === null || _d === void 0 ? void 0 : _d.select();
272
299
  }
273
300
  }
274
301
  if (!this.open)
@@ -357,6 +384,7 @@ export class Q2Search {
357
384
  * Does not emit `tctChange`.
358
385
  */
359
386
  async reset() {
387
+ this.clearSelectedDisplay();
360
388
  this.value = '';
361
389
  this.selectedOptions = [];
362
390
  this.searchText = '';
@@ -473,6 +501,11 @@ export class Q2Search {
473
501
  return count > 1 ? `+${count - 1}` : null;
474
502
  return `${count}`;
475
503
  }
504
+ get firstSelectedOptionElement() {
505
+ var _a, _b;
506
+ const v = this.multiple ? (_a = this.selectedOptions) === null || _a === void 0 ? void 0 : _a[0] : this.value;
507
+ return v ? (_b = this.optionElements.find(o => o.value === v)) !== null && _b !== void 0 ? _b : null : null;
508
+ }
476
509
  get innerInputContainer() {
477
510
  var _a, _b, _c;
478
511
  return (_c = (_b = (_a = this.inputField) === null || _a === void 0 ? void 0 : _a.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('.input-container')) !== null && _c !== void 0 ? _c : null;
@@ -509,6 +542,9 @@ export class Q2Search {
509
542
  const rowHeight = Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
510
543
  return this.minRows * rowHeight;
511
544
  }
545
+ get selectedDisplaySlot() {
546
+ return this.hostElement.querySelector('[slot="_selected-display"]');
547
+ }
512
548
  get visibleOptionElements() {
513
549
  return this.optionElements.filter(opt => !opt.hidden);
514
550
  }
@@ -542,12 +578,50 @@ export class Q2Search {
542
578
  const { multilineOptions } = this;
543
579
  this.optionElements.forEach(option => (option.multiline = multilineOptions));
544
580
  }
581
+ checkSelectedDisplay() {
582
+ var _a;
583
+ const { multilineOptions, isShowingSelectionDisplay, value, multiple, selectedOptions, firstSelectedOptionElement, } = this;
584
+ const hasNoValue = multiple ? !(selectedOptions === null || selectedOptions === void 0 ? void 0 : selectedOptions.length) : !value;
585
+ if (!multilineOptions || !isShowingSelectionDisplay || hasNoValue)
586
+ return this.clearSelectedDisplay();
587
+ if (!firstSelectedOptionElement || firstSelectedOptionElement.display)
588
+ return this.clearSelectedDisplay();
589
+ const child = firstSelectedOptionElement.firstElementChild;
590
+ if (!child)
591
+ return this.clearSelectedDisplay();
592
+ const selectionClone = child.cloneNode(true);
593
+ selectionClone.querySelectorAll('[hide-on-select]').forEach(el => el.remove());
594
+ let namedSlot = this.selectedDisplaySlot;
595
+ if (namedSlot) {
596
+ if (((_a = namedSlot.firstElementChild) === null || _a === void 0 ? void 0 : _a.outerHTML) !== selectionClone.outerHTML) {
597
+ namedSlot.replaceChildren(selectionClone);
598
+ }
599
+ }
600
+ else {
601
+ namedSlot = document.createElement('div');
602
+ namedSlot.slot = '_selected-display';
603
+ // Prevent the non-focusable overlay from blurring the hidden input when clicked.
604
+ // Same pattern used by q2-input's clear button (onMouseDown preventDefault).
605
+ namedSlot.addEventListener('mousedown', this.onSelectedDisplayMousedown);
606
+ namedSlot.appendChild(selectionClone);
607
+ this.hostElement.appendChild(namedSlot);
608
+ }
609
+ return namedSlot;
610
+ }
545
611
  checkSelectedOptions() {
546
612
  const { multiple, selectedOptions, value } = this;
547
613
  this.optionElements.forEach(option => {
548
614
  option.selected = multiple ? selectedOptions.includes(option.value) : option.value === value;
549
615
  });
550
616
  }
617
+ clearSelectedDisplay() {
618
+ const slot = this.selectedDisplaySlot;
619
+ if (slot) {
620
+ slot.removeEventListener('mousedown', this.onSelectedDisplayMousedown);
621
+ slot.remove();
622
+ }
623
+ return null;
624
+ }
551
625
  closeDropdown() {
552
626
  if (!this.open)
553
627
  return;
@@ -574,8 +648,16 @@ export class Q2Search {
574
648
  this.open = true;
575
649
  this.tctOpen.emit();
576
650
  }
651
+ optionDisplayText(option, fallback = '') {
652
+ var _a, _b;
653
+ if (option === null || option === void 0 ? void 0 : option.display)
654
+ return loc(option.display);
655
+ if (this.multilineOptions)
656
+ return '';
657
+ return (_b = (_a = option === null || option === void 0 ? void 0 : option.textContent) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : fallback;
658
+ }
577
659
  revertToSelectedDisplay() {
578
- var _a, _b, _c, _d, _e;
660
+ var _a, _b, _c;
579
661
  if (this.multiple) {
580
662
  // Preserve an in-progress query — user blurred without selecting and may resume.
581
663
  if (this.searchText)
@@ -589,7 +671,7 @@ export class Q2Search {
589
671
  // re-populated yet) — leave the query alone rather than blanking it.
590
672
  if (!selected)
591
673
  return;
592
- const display = selected.display ? loc(selected.display) : (_c = (_b = selected.textContent) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : '';
674
+ const display = this.optionDisplayText(selected);
593
675
  this.searchText = display;
594
676
  this.isShowingSelectionDisplay = true;
595
677
  return;
@@ -602,10 +684,16 @@ export class Q2Search {
602
684
  // Same guard: don't clobber an in-progress query when the matching option isn't in the DOM.
603
685
  if (!selected)
604
686
  return;
605
- const display = selected.display ? loc(selected.display) : (_e = (_d = selected.textContent) === null || _d === void 0 ? void 0 : _d.trim()) !== null && _e !== void 0 ? _e : '';
687
+ const display = selected.display
688
+ ? loc(selected.display)
689
+ : this.multilineOptions
690
+ ? ''
691
+ : (_c = (_b = selected.textContent) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : '';
606
692
  this.isShowingSelectionDisplay = true;
607
- if (this.searchText === display)
693
+ if (this.searchText === display) {
694
+ this.checkSelectedDisplay();
608
695
  return;
696
+ }
609
697
  this.searchText = display;
610
698
  }
611
699
  setCustomLabel() {
@@ -640,6 +728,11 @@ export class Q2Search {
640
728
  }
641
729
  // #endregion
642
730
  // #region Render Methods
731
+ renderCustomDisplay() {
732
+ if (!this.checkSelectedDisplay())
733
+ return null;
734
+ return (h("slot", { name: "_selected-display", slot: "custom-display" }));
735
+ }
643
736
  renderEmpty() {
644
737
  if (this.hasEmptySlot) {
645
738
  return h("slot", { name: "empty" });
@@ -669,9 +762,9 @@ export class Q2Search {
669
762
  return (h("q2-popover", { ref: el => (this.popoverElement = el), controlElement: (_a = this.innerInputContainer) !== null && _a !== void 0 ? _a : undefined, open: this.open, maxHeight: this.popoverMaxHeight, minHeight: this.popoverMinHeight, align: useBlock ? undefined : this.popoverAlignment, block: useBlock }, h("div", { class: "popover-content", "data-loading": this.loading ? 'true' : 'false', "aria-busy": this.loading ? 'true' : null }, h("div", { class: "popover-top-container", hidden: !this.hasPopoverTop && !(this.multiple && this.showSelectedToggle), tabindex: "-1" }, h("slot", { name: "popover-top" }), !this.loading && this.renderMultiSelectHeader()), h("q2-option-list", { ref: el => (this.optionList = el), type: "listbox", id: this.optionListId, label: loc(this.listLabel), multiple: this.multiple, selectedOptions: this.structuredSelectedOptions, showSelected: this.showSelected, customSearch: true, hidden: this.loading, onChange: (e) => e.stopPropagation(), onTctChange: this.onOptionListChange }, h("slot", null)), showEmpty && this.renderEmpty(), h("div", { class: "popover-bottom-container", hidden: !this.hasPopoverBottom, tabindex: "-1" }, h("slot", { name: "popover-bottom" })), this.loading && (h("div", { class: "loading-overlay", "aria-hidden": "true" }, h("q2-loading", { type: "spinner" }))))));
670
763
  }
671
764
  render() {
672
- return (h(Fragment, { key: '25d659528e22b848146b76d76f5bdb94da246d02' }, h("div", { key: '6a46b01c7d9598bbf887a1c579dd500588303ad6', "aria-live": "polite", "aria-atomic": "true", role: "status", class: "sr" }, this.statusMessage), h("click-elsewhere", { key: '22a8cbf8de37dfe302ca63904cc8fc22841018a6', class: this.wrapperClasses, role: "presentation", onChange: this.onClickElsewhere }, h("q2-input", { key: 'e96361aec59e427a3a5b59d95b7c4aefd3b9d169', ref: el => (this.inputField = el), class: "q2-search-input", label: (this.label && loc(this.label)) || '', value: this.searchText, iconLeft: "search", clearable: this.clearable && !this.loading && !!this.searchText, disabled: this.disabled, optional: this.optional, readonly: !!this.readonly, placeholder: this.placeholder ? loc(this.placeholder) : undefined, hideLabel: this.hideLabel, errors: (Array.isArray(this.errors) && this.errors.length > 0 && this.errors.map(e => loc(e))) ||
765
+ return (h(Fragment, { key: '0686476c153375c6c3cca61cdeaf1267c5ca6b20' }, h("div", { key: 'f5ed583b8da30b2222dd4a466a37e1c10b589fa7', "aria-live": "polite", "aria-atomic": "true", role: "status", class: "sr" }, this.statusMessage), h("click-elsewhere", { key: 'f1172a06706ea799f6020d21610004450e051d9a', class: this.wrapperClasses, role: "presentation", onChange: this.onClickElsewhere }, h("q2-input", { key: '75675f105bd09279650e6aae6b869e0f3f99c270', ref: el => (this.inputField = el), class: "q2-search-input", label: (this.label && loc(this.label)) || '', value: this.searchText, iconLeft: "search", clearable: this.clearable && !this.loading && !!this.searchText, disabled: this.disabled, optional: this.optional, readonly: !!this.readonly, placeholder: this.placeholder ? loc(this.placeholder) : undefined, hideLabel: this.hideLabel, errors: (Array.isArray(this.errors) && this.errors.length > 0 && this.errors.map(e => loc(e))) ||
673
766
  (this.invalid && ['tecton.element.search.invalid']) ||
674
- [], ariaExpanded: this.isInputOnlyMode ? undefined : `${this.open}`, ariaControls: this.isInputOnlyMode ? undefined : this.optionListId, ariaHaspopup: this.isInputOnlyMode ? undefined : 'listbox', hideMessages: true, _role: this.isInputOnlyMode ? undefined : 'combobox', onTctInput: this.inputInputHandler, onInput: (e) => e.stopImmediatePropagation(), onChange: (e) => e.stopPropagation(), onTctChange: (e) => e.stopPropagation(), badgeValue: this.badgeValue }), this.renderOptionsDropdown())));
767
+ [], ariaExpanded: this.isInputOnlyMode ? undefined : `${this.open}`, ariaControls: this.isInputOnlyMode ? undefined : this.optionListId, ariaHaspopup: this.isInputOnlyMode ? undefined : 'listbox', hideMessages: true, _role: this.isInputOnlyMode ? undefined : 'combobox', onTctInput: this.inputInputHandler, onInput: (e) => e.stopImmediatePropagation(), onChange: (e) => e.stopPropagation(), onTctChange: (e) => e.stopPropagation(), badgeValue: this.badgeValue }, this.renderCustomDisplay()), this.renderOptionsDropdown())));
675
768
  }
676
769
  static get is() { return "q2-search"; }
677
770
  static get encapsulation() { return "shadow"; }
@@ -0,0 +1,145 @@
1
+ import { expect } from "@playwright/test";
2
+ import { test } from "@stencil/playwright";
3
+ import { setupPage, THEMES } from "../../../utils/test/vr-helpers";
4
+ const OPTIONS = `
5
+ <q2-option value="1" display="Apothecary"></q2-option>
6
+ <q2-option value="2" display="Borant"></q2-option>
7
+ <q2-option value="3" display="Valtay"></q2-option>
8
+ `;
9
+ const ERRORS = ['This field is required.', 'The value entered is invalid.'];
10
+ const VARIANTS = [
11
+ {
12
+ name: 'default',
13
+ html: '<q2-search label="Search" placeholder="Type to search"></q2-search>',
14
+ states: ['resting', 'focus'],
15
+ },
16
+ {
17
+ name: 'clearable-with-value',
18
+ html: `<q2-search label="Search" clearable value="1">${OPTIONS}</q2-search>`,
19
+ states: ['resting'],
20
+ },
21
+ {
22
+ name: 'optional',
23
+ html: '<q2-search label="Search" optional placeholder="Type to search"></q2-search>',
24
+ states: ['resting'],
25
+ },
26
+ {
27
+ name: 'readonly',
28
+ html: `<q2-search label="Search" readonly value="1">${OPTIONS}</q2-search>`,
29
+ states: ['resting'],
30
+ },
31
+ {
32
+ name: 'disabled',
33
+ html: '<q2-search label="Search" disabled placeholder="Type to search"></q2-search>',
34
+ states: ['resting'],
35
+ },
36
+ {
37
+ name: 'errored',
38
+ html: '<q2-search label="Search" placeholder="Type to search"></q2-search>',
39
+ states: ['resting'],
40
+ setup: 'errors',
41
+ },
42
+ {
43
+ name: 'expanded',
44
+ html: `<q2-search label="Search">${OPTIONS}</q2-search>`,
45
+ states: ['expanded', 'expanded-option-hover', 'expanded-option-focus'],
46
+ },
47
+ {
48
+ name: 'loading',
49
+ html: '<q2-search label="Search" loading placeholder="Type to search"></q2-search>',
50
+ states: ['expanded'],
51
+ },
52
+ {
53
+ name: 'no-results',
54
+ html: '<q2-search label="Search"></q2-search>',
55
+ states: ['expanded'],
56
+ },
57
+ {
58
+ name: 'multiple',
59
+ html: `<q2-search label="Search" multiple filter>${OPTIONS}</q2-search>`,
60
+ states: ['expanded', 'expanded-option-hover', 'expanded-option-focus'],
61
+ },
62
+ {
63
+ name: 'multiple-show-selected-toggle',
64
+ html: `<q2-search label="Search" multiple filter show-selected-toggle>${OPTIONS}</q2-search>`,
65
+ states: ['expanded'],
66
+ setup: 'multiple-selected',
67
+ },
68
+ ];
69
+ const applySetup = async (page, setup) => {
70
+ if (!setup)
71
+ return;
72
+ await page.evaluate(({ setup, errors }) => {
73
+ const el = document.querySelector('q2-search');
74
+ if (setup === 'errors')
75
+ el.errors = errors;
76
+ if (setup === 'multiple-selected')
77
+ el.selectedOptions = ['1'];
78
+ }, { setup, errors: ERRORS });
79
+ };
80
+ const openSearch = async (page, hasOptions) => {
81
+ await page.evaluate(() => {
82
+ const el = document.querySelector('q2-search');
83
+ return el.openPopover();
84
+ });
85
+ if (hasOptions) {
86
+ await page.locator('q2-option').first().waitFor({ state: 'visible' });
87
+ }
88
+ // Wait for the popover open transition (box-shadow, opacity) to fully settle.
89
+ await page.waitForTimeout(300);
90
+ };
91
+ const setup = (page, variant, theme) => setupPage(page, variant.html, { theme, setupLoc: true }).then(() => applySetup(page, variant.setup));
92
+ const restingCase = (variant, theme) => {
93
+ test(`q2-search--${variant.name}--resting — ${theme}`, async ({ page }) => {
94
+ await setup(page, variant, theme);
95
+ await expect(page).toHaveScreenshot(`q2-search--${variant.name}--resting--${theme}.png`);
96
+ });
97
+ };
98
+ const focusCase = (variant, theme) => {
99
+ test(`q2-search--${variant.name}--focus — ${theme}`, async ({ page }) => {
100
+ await setup(page, variant, theme);
101
+ await page.keyboard.press('Tab');
102
+ await expect(page).toHaveScreenshot(`q2-search--${variant.name}--focus--${theme}.png`);
103
+ });
104
+ };
105
+ const expandedCase = (variant, theme) => {
106
+ const hasOptions = !variant.name.includes('loading') && !variant.name.includes('no-results');
107
+ test(`q2-search--${variant.name}--expanded — ${theme}`, async ({ page }) => {
108
+ await setup(page, variant, theme);
109
+ await openSearch(page, hasOptions);
110
+ await expect(page).toHaveScreenshot(`q2-search--${variant.name}--expanded--${theme}.png`);
111
+ });
112
+ };
113
+ const expandedOptionHoverCase = (variant, theme) => {
114
+ test(`q2-search--${variant.name}--expanded-option-hover — ${theme}`, async ({ page }) => {
115
+ await setup(page, variant, theme);
116
+ await openSearch(page, true);
117
+ await page.locator('q2-option[value="1"]').hover();
118
+ await expect(page).toHaveScreenshot(`q2-search--${variant.name}--expanded-option-hover--${theme}.png`);
119
+ });
120
+ };
121
+ // ArrowDown after open moves focus to the first option, triggering :focus-visible.
122
+ const expandedOptionFocusCase = (variant, theme) => {
123
+ test(`q2-search--${variant.name}--expanded-option-focus — ${theme}`, async ({ page }) => {
124
+ await setup(page, variant, theme);
125
+ await openSearch(page, true);
126
+ await page.keyboard.press('ArrowDown');
127
+ await expect(page).toHaveScreenshot(`q2-search--${variant.name}--expanded-option-focus--${theme}.png`);
128
+ });
129
+ };
130
+ const CASE_MAP = {
131
+ resting: restingCase,
132
+ focus: focusCase,
133
+ expanded: expandedCase,
134
+ 'expanded-option-hover': expandedOptionHoverCase,
135
+ 'expanded-option-focus': expandedOptionFocusCase,
136
+ };
137
+ test.describe('q2-search — variants × themes', () => {
138
+ for (const variant of VARIANTS) {
139
+ for (const theme of THEMES) {
140
+ for (const state of variant.states) {
141
+ CASE_MAP[state](variant, theme);
142
+ }
143
+ }
144
+ }
145
+ });
@@ -94,7 +94,7 @@ button {
94
94
  :host {
95
95
  display: block;
96
96
  background: var(--tct-section-background, var(--tct-section-background-color, var(--t-section-background-color, var(--tct-section-bg, var(--t-section-bg, var(--app-white, #ffffff))))));
97
- color: var(--tct-section-font-color, var(--t-section-font-color, var(--t-text, #4d4d4d)));
97
+ color: var(--tct-section-font-color, var(--t-section-font-color, inherit));
98
98
  border-radius: var(--tct-section-border-radius, var(--t-section-border-radius, var(--app-border-radius-1, 4px)));
99
99
  margin: var(--tct-section-margin, var(--t-section-margin, var(--app-scale-3x, 15px)));
100
100
  border-width: var(--tct-section-border-width, 0);
@@ -128,8 +128,8 @@ button {
128
128
  * @prop --tct-select-margin: (<length-percentage>+) Controls the outer margin of the select host.
129
129
  * @prop --tct-select-margin-top: (<length-percentage>) Controls the top margin of the select host when --tct-select-margin is not set.
130
130
  * @prop --tct-select-margin-bottom: (<length-percentage>) Controls the bottom margin of the select host when --tct-select-margin is not set.
131
- * @prop --tct-select-input-min-height?: (<length-percentage>) Controls the optional minimum height of the select input.
132
- * @prop --tct-select-input-max-height?: (<length-percentage>) Controls the optional maximum height of the select input.
131
+ * @prop --tct-select-input-min-height?: (<length-percentage>) Controls the optional minimum height of the select input. Use this to control the height of the select in the empty state, especially when using multiline-options with tall content.
132
+ * @prop --tct-select-input-max-height?: (<length-percentage>) Controls the optional maximum height of the select input. Use this to constrain the height when displaying multiline selected content.
133
133
  * @prop --tct-select-multi-select-option-background?: (<color>) Controls the optional background color of multi-select option chips in their default state.
134
134
  * @prop --tct-select-multi-select-option-color?: (<color>) Controls the optional text color of multi-select option chips in their default state.
135
135
  * @prop --tct-select-multi-select-option-active-background: (<color>) Controls the background color of multi-select option chips when active (checked).
@@ -161,6 +161,8 @@ button {
161
161
  ::slotted([slot=_selected-display]) {
162
162
  width: 100%;
163
163
  min-height: var(--comp-selected-display-height, 44px);
164
+ padding-top: var(--tct-select-selected-display-padding, var(--app-scale-2x, 10px));
165
+ padding-bottom: var(--tct-select-selected-display-padding, var(--app-scale-2x, 10px));
164
166
  }
165
167
 
166
168
  .custom-display-content {
@@ -82,6 +82,8 @@ export class Q2Select {
82
82
  * This property is only relevant for `multiple` (i.e., multi-select) implementations.
83
83
  */
84
84
  this.selectedOptions = [];
85
+ /** Determines whether to show a success state. The error state takes precedence when both are set. */
86
+ this.valid = false;
85
87
  this.addTouchEventListeners = () => {
86
88
  if (!isMobile() || !this.popoverElement)
87
89
  return;
@@ -271,6 +273,22 @@ export class Q2Select {
271
273
  return;
272
274
  this.open = open;
273
275
  };
276
+ this.onSelectedDisplayClick = (event) => {
277
+ // Stop click from bubbling to q2-input's onClick (which would toggle the dropdown)
278
+ event.stopPropagation();
279
+ if (this.open) {
280
+ this.focusInput();
281
+ return;
282
+ }
283
+ this.prioritizeSearch = this.searchable;
284
+ this.clearSelectedDisplay();
285
+ this.openDropdownWithoutActiveElement();
286
+ this.focusInput();
287
+ };
288
+ this.onSelectedDisplayMousedown = (event) => {
289
+ // Prevent focus shift away from the hidden input
290
+ event.preventDefault();
291
+ };
274
292
  this.removeTouchEventListeners = () => {
275
293
  if (!isMobile() || !this.popoverElement)
276
294
  return;
@@ -495,6 +513,8 @@ export class Q2Select {
495
513
  }
496
514
  else {
497
515
  this.removeTouchEventListeners();
516
+ // Reset prioritizeSearch when dropdown closes to ensure selected display can be restored
517
+ this.prioritizeSearch = false;
498
518
  }
499
519
  this.scheduledAfterRender.push(async () => {
500
520
  await waitForNextPaint();
@@ -657,10 +677,16 @@ export class Q2Select {
657
677
  if (namedSlot.firstElementChild.outerHTML !== selectionClone.outerHTML) {
658
678
  namedSlot.replaceChild(selectionClone, namedSlot.firstElementChild);
659
679
  }
680
+ namedSlot.removeEventListener('mousedown', this.onSelectedDisplayMousedown);
681
+ namedSlot.addEventListener('mousedown', this.onSelectedDisplayMousedown);
682
+ namedSlot.removeEventListener('click', this.onSelectedDisplayClick);
683
+ namedSlot.addEventListener('click', this.onSelectedDisplayClick);
660
684
  }
661
685
  else {
662
686
  namedSlot = document.createElement('div');
663
687
  namedSlot.slot = '_selected-display';
688
+ namedSlot.addEventListener('mousedown', this.onSelectedDisplayMousedown);
689
+ namedSlot.addEventListener('click', this.onSelectedDisplayClick);
664
690
  namedSlot.appendChild(selectionClone);
665
691
  this.hostElement.appendChild(namedSlot);
666
692
  }
@@ -690,8 +716,12 @@ export class Q2Select {
690
716
  mirrorEmit(this, ['input', 'tctInput'], { query: '' });
691
717
  }
692
718
  clearSelectedDisplay() {
693
- var _a;
694
- (_a = this.selectedDisplaySlot) === null || _a === void 0 ? void 0 : _a.remove();
719
+ const slot = this.selectedDisplaySlot;
720
+ if (slot) {
721
+ slot.removeEventListener('mousedown', this.onSelectedDisplayMousedown);
722
+ slot.removeEventListener('click', this.onSelectedDisplayClick);
723
+ slot.remove();
724
+ }
695
725
  }
696
726
  clearValue() {
697
727
  const { multiple } = this;
@@ -786,7 +816,7 @@ export class Q2Select {
786
816
  this.errors.length > 0 &&
787
817
  this.errors.map(error => loc(error))) ||
788
818
  (this.invalid && ['tecton.element.select.invalid']) ||
789
- [], disabled: this.disabled, optional: this.optional, readonly: !!this.readonly, placeholder: this.placeholder || undefined, hideLabel: this.hideLabel, ariaExpanded: `${this.open}`, ariaControls: "option-list", ariaHaspopup: "listbox", "test-id": "toggleDropdown", "hide-messages": true, iconRight: "chevron-down", onClick: this.inputClickHandler, onInput: e => e.stopImmediatePropagation(), onTctInput: this.inputInputHandler, onKeyDown: this.inputKeydownHandler, onFocus: this.inputFocusHandler, onBlur: this.inputBlurHandler, onChange: e => e.stopPropagation(), onTctChange: this.inputChangeHandler, badgeValue: this.badgeValue, badgeTheme: this.inputFocused ? 'primary' : undefined, _role: "combobox", _preventEntry: !this.searchable }, this.renderCustomDisplay()), h("div", { class: "custom-display-content", hidden: !this.hasCustomDisplay || !!this.searchText, onClick: this.onCustomDisplayClick }, h("slot", { name: "q2-select-display" })), this.renderOptionsDropdown()));
819
+ [], disabled: this.disabled, optional: this.optional, readonly: !!this.readonly, placeholder: this.placeholder || undefined, hideLabel: this.hideLabel, valid: this.valid, ariaExpanded: `${this.open}`, ariaControls: "option-list", ariaHaspopup: "listbox", "test-id": "toggleDropdown", "hide-messages": true, iconRight: "chevron-down", onClick: this.inputClickHandler, onInput: e => e.stopImmediatePropagation(), onTctInput: this.inputInputHandler, onKeyDown: this.inputKeydownHandler, onFocus: this.inputFocusHandler, onBlur: this.inputBlurHandler, onChange: e => e.stopPropagation(), onTctChange: this.inputChangeHandler, badgeValue: this.badgeValue, badgeTheme: this.inputFocused ? 'primary' : undefined, _role: "combobox", _preventEntry: !this.searchable }, this.renderCustomDisplay()), h("div", { class: "custom-display-content", hidden: !this.hasCustomDisplay || !!this.searchText, onClick: this.onCustomDisplayClick }, h("slot", { name: "q2-select-display" })), this.renderOptionsDropdown()));
790
820
  }
791
821
  renderVisibilityToggle() {
792
822
  var _a, _b;
@@ -797,7 +827,7 @@ export class Q2Select {
797
827
  ]), checked: showSelected, "test-id": "selectedOptionsButton", onClick: this.showSelectedOptions, onKeyDown: this.visibilityToggleKeyDown }), h("label", { htmlFor: "selected" }, loc('tecton.element.select.multiHeader.selected', [selectedOptionsCount]))))));
798
828
  }
799
829
  render() {
800
- return (h("click-elsewhere", { key: 'd83fb08f6af3a98edc661170f83d89af991275ec', class: this.wrapperClasses, role: "presentation", onChange: this.clickedElsewhere }, this.renderSelectField()));
830
+ return (h("click-elsewhere", { key: 'a08e10cf8bd4e2d3f210d36003417f2a1dec4b21', class: this.wrapperClasses, role: "presentation", onChange: this.clickedElsewhere }, this.renderSelectField()));
801
831
  }
802
832
  static get is() { return "q2-select"; }
803
833
  static get encapsulation() { return "shadow"; }
@@ -1227,6 +1257,26 @@ export class Q2Select {
1227
1257
  "setter": false,
1228
1258
  "defaultValue": "[]"
1229
1259
  },
1260
+ "valid": {
1261
+ "type": "boolean",
1262
+ "mutable": false,
1263
+ "complexType": {
1264
+ "original": "boolean",
1265
+ "resolved": "boolean",
1266
+ "references": {}
1267
+ },
1268
+ "required": false,
1269
+ "optional": false,
1270
+ "docs": {
1271
+ "tags": [],
1272
+ "text": "Determines whether to show a success state. The error state takes precedence when both are set."
1273
+ },
1274
+ "getter": false,
1275
+ "setter": false,
1276
+ "reflect": true,
1277
+ "attribute": "valid",
1278
+ "defaultValue": "false"
1279
+ },
1230
1280
  "value": {
1231
1281
  "type": "string",
1232
1282
  "mutable": true,
@@ -44,6 +44,11 @@ const VARIANTS = [
44
44
  states: ['resting'],
45
45
  setup: 'errors',
46
46
  },
47
+ {
48
+ name: 'valid',
49
+ html: `<q2-select label="My select" placeholder="Select an option" valid value="2">${OPTIONS}</q2-select>`,
50
+ states: ['resting'],
51
+ },
47
52
  ];
48
53
  const applySelectSetup = async (page, setup) => {
49
54
  if (!setup)
@@ -275,7 +275,7 @@ li {
275
275
  }
276
276
 
277
277
  .step-label {
278
- color: var(--tct-stepper-label-color, var(--tct-stepper-title-color, var(--t-stepper-title-color, var(--t-text, #4d4d4d))));
278
+ color: var(--tct-stepper-label-color, var(--tct-stepper-title-color, var(--t-stepper-title-color, inherit)));
279
279
  font-size: var(--tct-stepper-label-font-size, var(--tct-stepper-title-font-size, var(--t-stepper-title-font-size, var(--app-font-size-small, 12px))));
280
280
  line-height: var(--tct-stepper-label-line-height, 1.5em);
281
281
  text-overflow: ellipsis;
@@ -290,7 +290,7 @@ li {
290
290
  }
291
291
 
292
292
  .step-description {
293
- color: var(--tct-stepper-description-color, var(--t-stepper-description-color, var(--t-textA, rgba(77, 77, 77, 0.77))));
293
+ color: var(--tct-stepper-description-color, var(--t-stepper-description-color, inherit));
294
294
  font-size: var(--tct-stepper-description-font-size, var(--t-stepper-description-font-size, var(--app-font-size-small, 12px)));
295
295
  text-overflow: ellipsis;
296
296
  overflow: hidden;
@@ -1,6 +1,7 @@
1
1
  import { h, Fragment, } from "@stencil/core";
2
2
  import { loc, overrideFocus, isEventFromElement, createGuid } from "../../utils/index";
3
3
  import mirrorEmit from "../../utils/mirror-emit";
4
+ import sanitizeHTMLString from "../../utils/sanitize-html-string";
4
5
  /**
5
6
  * @name Stepper
6
7
  * @category Display
@@ -273,14 +274,14 @@ export class Q2Stepper {
273
274
  if (isPaneDisabled)
274
275
  stepClasses.push('pane-disabled');
275
276
  const disabled = isLocked || stepNumber > lastEnabledStep;
276
- return (h("li", { role: "presentation" }, h("button", { class: stepClasses.join(' '), type: "button", "aria-labelledby": labelId, "aria-describedBy": descriptionId, "aria-label": btnLabel, "aria-selected": `${isCurrentStep}`, "aria-disabled": disabled ? 'true' : undefined, role: "tab", tabIndex: isCurrentStep ? 0 : -1, onKeyDown: ev => this.onStepKeyDown(ev, stepNumber), onClick: ev => !isPaneDisabled && !disabled && this.onStepClick(ev, stepNumber) }, h(Fragment, null, statusIcon ? (h("div", { class: "step-icon" }, h("q2-icon", { type: statusIcon }))) : (h("div", { class: "step-bubble" }, stepNumber)), labelId && (h("div", { class: "step-label", id: labelId, innerHTML: label })), labelId && descriptionId && (h("div", { class: "step-description", id: descriptionId, innerHTML: description })))), stepIndex ? h("div", { class: "step-divider" }) : ''));
277
+ return (h("li", { role: "presentation" }, h("button", { class: stepClasses.join(' '), type: "button", "aria-labelledby": labelId, "aria-describedBy": descriptionId, "aria-label": btnLabel, "aria-selected": `${isCurrentStep}`, "aria-disabled": disabled ? 'true' : undefined, role: "tab", tabIndex: isCurrentStep ? 0 : -1, onKeyDown: ev => this.onStepKeyDown(ev, stepNumber), onClick: ev => !isPaneDisabled && !disabled && this.onStepClick(ev, stepNumber) }, h(Fragment, null, statusIcon ? (h("div", { class: "step-icon" }, h("q2-icon", { type: statusIcon }))) : (h("div", { class: "step-bubble" }, stepNumber)), labelId && (h("div", { class: "step-label", id: labelId, innerHTML: sanitizeHTMLString(label) })), labelId && descriptionId && (h("div", { class: "step-description", id: descriptionId, innerHTML: sanitizeHTMLString(description) })))), stepIndex ? h("div", { class: "step-divider" }) : ''));
277
278
  }
278
279
  render() {
279
280
  const { stepCount, scrollEnabled, showScrollLeft, showScrollRight } = this;
280
281
  const containerClasses = ['step-container'];
281
282
  if (scrollEnabled)
282
283
  containerClasses.push('has-scroll');
283
- return (h(Fragment, { key: 'dd38d7922ef9b468399b57f4e1d656b2213d6b32' }, h("div", { key: '92dc67bc25e5a5275241b91ec6cb968202e91af8', class: containerClasses.join(' ') }, scrollEnabled && (h(Fragment, { key: 'ef83e893c3dfe020cb1c561c1b0619f364c7f08f' }, h("div", { key: '9cf06f95042064daa7564a9927cf3f1ff9a227be', class: "gradient-left", hidden: !showScrollLeft }), h("div", { key: 'd9c58f971a8b2fd545c09e698d57baf356c3947b', class: "gradient-right", hidden: !showScrollRight }), h("q2-btn", { key: '38c601f9fe74886e912e983b9b52e7c1e1a0694e', class: "btn-left", hideLabel: true, hidden: !this.showScrollLeft, label: loc('tecton.element.stepper.scrollLeft'), onTctClick: () => this.onScrollBtnClick('left') }, h("q2-icon", { key: 'a53ac3dc6ebd84e3d221136868df0861bc6acf4f', type: "chevron-left" })), h("q2-btn", { key: '38363d0a8a2d9fa96a919bf08d243ddb42d65552', class: "btn-right", hideLabel: true, hidden: !this.showScrollRight, label: loc('tecton.element.stepper.scrollRight'), onTctClick: () => this.onScrollBtnClick('right') }, h("q2-icon", { key: '6da3f9a38d4b1bb375b25257634058c869f9478f', type: "chevron-right" })))), h("ul", { key: '2c5e3d0e0aebd5fa05ecce735232dd42d1aec470', onScroll: this.checkScrollState, ref: el => (this.listElement = el), role: "tablist" }, stepCount > 0 && [...Array(stepCount).keys()].map(stepIndex => this.renderStepBtn(stepIndex)))), h("div", { key: '21e4f4c96ebcef94c73a29f468c820c9d4c22a57', role: "list" }, h("slot", { key: '84d871317ccc4471f1d954d469047bc6f97399cf', onSlotchange: () => this.onSlotChange() }))));
284
+ return (h(Fragment, { key: 'c1e21a36e0b2f684f4520b0f75f78affd73359bd' }, h("div", { key: '3791e857d27aa3fea928f663268f21270d499023', class: containerClasses.join(' ') }, scrollEnabled && (h(Fragment, { key: '19b689b2b56f52099f22f859264e33e16fd918bf' }, h("div", { key: '5b1b1e00034fa009c555924c2067fed6369740cb', class: "gradient-left", hidden: !showScrollLeft }), h("div", { key: 'b5de1b413f6c8ce006207926f26ae845594228eb', class: "gradient-right", hidden: !showScrollRight }), h("q2-btn", { key: '832a97c5619cadb1167f6e1e7ecd94ab4f5f3cb1', class: "btn-left", hideLabel: true, hidden: !this.showScrollLeft, label: loc('tecton.element.stepper.scrollLeft'), onTctClick: () => this.onScrollBtnClick('left') }, h("q2-icon", { key: '3e53b2b2f19bbba59a37016b9cdf1a302b030b63', type: "chevron-left" })), h("q2-btn", { key: 'ffacbb0d2f9c15a503221a2dbc4662ba6f3c3272', class: "btn-right", hideLabel: true, hidden: !this.showScrollRight, label: loc('tecton.element.stepper.scrollRight'), onTctClick: () => this.onScrollBtnClick('right') }, h("q2-icon", { key: '2c60dc5392c9c9eec99dd24b229bbd1fa5e9587f', type: "chevron-right" })))), h("ul", { key: '25806a72f9f0cda2e032a81dbaac9f808be385c9', onScroll: this.checkScrollState, ref: el => (this.listElement = el), role: "tablist" }, stepCount > 0 && [...Array(stepCount).keys()].map(stepIndex => this.renderStepBtn(stepIndex)))), h("div", { key: '622b3ed56e3f66adc33bf197b7874a7b9bf8667f', role: "list" }, h("slot", { key: '73c4321743596227054e317c271bcc044179363b', onSlotchange: () => this.onSlotChange() }))));
284
285
  }
285
286
  static get is() { return "q2-stepper"; }
286
287
  static get encapsulation() { return "shadow"; }