@supersoniks/concorde 4.9.98-visual-stack.1 → 4.9.98-visual-stack.3

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 (340) hide show
  1. package/.gitlab-ci.yml +0 -13
  2. package/build-infos.json +1 -1
  3. package/concorde-3d.bundle.js +1 -1
  4. package/concorde-3d.es.js +2 -2
  5. package/concorde-core.bundle.js +557 -622
  6. package/concorde-core.es.js +6025 -5575
  7. package/concorde-hugging-face-infer.bundle.js +1 -1
  8. package/concorde-hugging-face-infer.es.js +1 -1
  9. package/concorde-interactive.bundle.js +1162 -0
  10. package/concorde-interactive.es.js +10723 -0
  11. package/concorde-shader.bundle.js +1 -1
  12. package/concorde-shader.es.js +2 -2
  13. package/concorde-webgpu.bundle.js +1 -1
  14. package/concorde-webgpu.es.js +2 -2
  15. package/dist/concorde-3d.bundle.js +1 -1
  16. package/dist/concorde-3d.es.js +2 -2
  17. package/dist/concorde-core.bundle.js +557 -622
  18. package/dist/concorde-core.es.js +6025 -5575
  19. package/dist/concorde-hugging-face-infer.bundle.js +1 -1
  20. package/dist/concorde-hugging-face-infer.es.js +1 -1
  21. package/dist/concorde-interactive.bundle.js +1162 -0
  22. package/dist/concorde-interactive.es.js +10723 -0
  23. package/dist/concorde-shader.bundle.js +1 -1
  24. package/dist/concorde-shader.es.js +2 -2
  25. package/dist/concorde-webgpu.bundle.js +1 -1
  26. package/dist/concorde-webgpu.es.js +2 -2
  27. package/dist/docs-mock-api-sw.js +5 -1
  28. package/dist/docs-mock-api-sw.js.map +2 -2
  29. package/dist/js/addons/interactive.js +6 -0
  30. package/dist/js/addons/interactive.js.map +1 -0
  31. package/dist/js/core/components/functional/if/if.js +65 -7
  32. package/dist/js/core/components/functional/if/if.js.map +1 -1
  33. package/dist/js/core/components/functional/interactive/action.js +96 -0
  34. package/dist/js/core/components/functional/interactive/action.js.map +1 -0
  35. package/dist/js/core/components/functional/interactive/gamepad.js +206 -0
  36. package/dist/js/core/components/functional/interactive/gamepad.js.map +1 -0
  37. package/dist/js/core/components/functional/interactive/gesture.js +148 -0
  38. package/dist/js/core/components/functional/interactive/gesture.js.map +1 -0
  39. package/dist/js/core/components/functional/interactive/helpers.js +82 -0
  40. package/dist/js/core/components/functional/interactive/helpers.js.map +1 -0
  41. package/dist/js/core/components/functional/interactive/index.js +20 -0
  42. package/dist/js/core/components/functional/interactive/index.js.map +1 -0
  43. package/dist/js/core/components/functional/interactive/keyboard.js +183 -0
  44. package/dist/js/core/components/functional/interactive/keyboard.js.map +1 -0
  45. package/dist/js/core/components/functional/interactive/matrix.js +201 -0
  46. package/dist/js/core/components/functional/interactive/matrix.js.map +1 -0
  47. package/dist/js/core/components/functional/interactive/registry.js +28 -0
  48. package/dist/js/core/components/functional/interactive/registry.js.map +1 -0
  49. package/dist/js/core/components/functional/interactive/store.js +264 -0
  50. package/dist/js/core/components/functional/interactive/store.js.map +1 -0
  51. package/dist/js/core/components/functional/interactive/ticker.js +182 -0
  52. package/dist/js/core/components/functional/interactive/ticker.js.map +1 -0
  53. package/dist/js/core/components/functional/value/value.js +49 -4
  54. package/dist/js/core/components/functional/value/value.js.map +1 -1
  55. package/dist/js/core/components/ui/_css/rounded.js +34 -0
  56. package/dist/js/core/components/ui/_css/rounded.js.map +1 -0
  57. package/dist/js/core/components/ui/_css/type.js +44 -0
  58. package/dist/js/core/components/ui/_css/type.js.map +1 -1
  59. package/dist/js/core/components/ui/_i18n/ui-aria-labels.js +95 -0
  60. package/dist/js/core/components/ui/_i18n/ui-aria-labels.js.map +1 -0
  61. package/dist/js/core/components/ui/alert/alert.js +72 -87
  62. package/dist/js/core/components/ui/alert/alert.js.map +1 -1
  63. package/dist/js/core/components/ui/alert-messages/alert-messages.js +1 -0
  64. package/dist/js/core/components/ui/alert-messages/alert-messages.js.map +1 -1
  65. package/dist/js/core/components/ui/badge/badge.js +29 -80
  66. package/dist/js/core/components/ui/badge/badge.js.map +1 -1
  67. package/dist/js/core/components/ui/button/button.js +8 -7
  68. package/dist/js/core/components/ui/button/button.js.map +1 -1
  69. package/dist/js/core/components/ui/card/card-header.js +7 -2
  70. package/dist/js/core/components/ui/card/card-header.js.map +1 -1
  71. package/dist/js/core/components/ui/divider/divider.js +1 -1
  72. package/dist/js/core/components/ui/divider/divider.js.map +1 -1
  73. package/dist/js/core/components/ui/form/checkbox/checkbox.js +4 -20
  74. package/dist/js/core/components/ui/form/checkbox/checkbox.js.map +1 -1
  75. package/dist/js/core/components/ui/form/fieldset/legend-description.js +1 -1
  76. package/dist/js/core/components/ui/form/fieldset/legend.js +1 -1
  77. package/dist/js/core/components/ui/form/input/input.js +12 -15
  78. package/dist/js/core/components/ui/form/input/input.js.map +1 -1
  79. package/dist/js/core/components/ui/form/input-autocomplete/input-autocomplete.js +363 -158
  80. package/dist/js/core/components/ui/form/input-autocomplete/input-autocomplete.js.map +1 -1
  81. package/dist/js/core/components/ui/form/radio/radio.js +1 -1
  82. package/dist/js/core/components/ui/form/select/select.js +3 -6
  83. package/dist/js/core/components/ui/form/select/select.js.map +1 -1
  84. package/dist/js/core/components/ui/form/textarea/textarea.js +1 -2
  85. package/dist/js/core/components/ui/form/textarea/textarea.js.map +1 -1
  86. package/dist/js/core/components/ui/image/image.js +9 -37
  87. package/dist/js/core/components/ui/image/image.js.map +1 -1
  88. package/dist/js/core/components/ui/link/link.js +25 -26
  89. package/dist/js/core/components/ui/link/link.js.map +1 -1
  90. package/dist/js/core/components/ui/modal/modal-close.js +3 -12
  91. package/dist/js/core/components/ui/modal/modal-close.js.map +1 -1
  92. package/dist/js/core/components/ui/modal/modal.js +18 -18
  93. package/dist/js/core/components/ui/modal/modal.js.map +1 -1
  94. package/dist/js/core/components/ui/pop/pop.js +131 -112
  95. package/dist/js/core/components/ui/pop/pop.js.map +1 -1
  96. package/dist/js/core/components/ui/table/table.js.map +1 -1
  97. package/dist/js/core/components/ui/theme/theme.js +1 -1
  98. package/dist/js/core/components/ui/toast/dismissed.js +9 -0
  99. package/dist/js/core/components/ui/toast/dismissed.js.map +1 -0
  100. package/dist/js/core/components/ui/toast/render-content.js +14 -0
  101. package/dist/js/core/components/ui/toast/render-content.js.map +1 -0
  102. package/dist/js/core/components/ui/toast/toast-item.js +150 -96
  103. package/dist/js/core/components/ui/toast/toast-item.js.map +1 -1
  104. package/dist/js/core/components/ui/toast/toast.js +41 -13
  105. package/dist/js/core/components/ui/toast/toast.js.map +1 -1
  106. package/dist/js/core/components/ui/tooltip/tooltip.js +144 -143
  107. package/dist/js/core/components/ui/tooltip/tooltip.js.map +1 -1
  108. package/dist/js/core/decorators/subscriber/ancestorAttribute.js.map +1 -1
  109. package/dist/js/core/decorators/subscriber/derive.js +3 -1
  110. package/dist/js/core/decorators/subscriber/derive.js.map +1 -1
  111. package/dist/js/core/mixins/Fetcher.js.map +1 -1
  112. package/dist/js/core/mixins/FormCheckable.js +1 -8
  113. package/dist/js/core/mixins/FormCheckable.js.map +1 -1
  114. package/dist/js/core/mixins/FormElement.js +26 -53
  115. package/dist/js/core/mixins/FormElement.js.map +1 -1
  116. package/dist/js/core/utils/AncestorAttributeObserver.js +130 -55
  117. package/dist/js/core/utils/AncestorAttributeObserver.js.map +1 -1
  118. package/dist/js/core/utils/LocationHandler.js +1 -1
  119. package/dist/js/core/utils/LocationHandler.js.map +1 -1
  120. package/dist/js/core/utils/dismissed.js +23 -0
  121. package/dist/js/core/utils/dismissed.js.map +1 -0
  122. package/dist/js/core/utils/positioning.js +110 -0
  123. package/dist/js/core/utils/positioning.js.map +1 -0
  124. package/dist/js/directives.js +1 -2
  125. package/dist/js/directives.js.map +1 -1
  126. package/dist/types/addons/interactive.d.ts +6 -0
  127. package/dist/types/addons/interactive.d.ts.map +1 -0
  128. package/dist/types/core/components/functional/if/if.d.ts +14 -5
  129. package/dist/types/core/components/functional/if/if.d.ts.map +1 -1
  130. package/dist/types/core/components/functional/interactive/action.d.ts +22 -0
  131. package/dist/types/core/components/functional/interactive/action.d.ts.map +1 -0
  132. package/dist/types/core/components/functional/interactive/gamepad.d.ts +37 -0
  133. package/dist/types/core/components/functional/interactive/gamepad.d.ts.map +1 -0
  134. package/dist/types/core/components/functional/interactive/gesture.d.ts +26 -0
  135. package/dist/types/core/components/functional/interactive/gesture.d.ts.map +1 -0
  136. package/dist/types/core/components/functional/interactive/helpers.d.ts +23 -0
  137. package/dist/types/core/components/functional/interactive/helpers.d.ts.map +1 -0
  138. package/dist/types/core/components/functional/interactive/index.d.ts +20 -0
  139. package/dist/types/core/components/functional/interactive/index.d.ts.map +1 -0
  140. package/dist/types/core/components/functional/interactive/keyboard.d.ts +37 -0
  141. package/dist/types/core/components/functional/interactive/keyboard.d.ts.map +1 -0
  142. package/dist/types/core/components/functional/interactive/matrix.d.ts +31 -0
  143. package/dist/types/core/components/functional/interactive/matrix.d.ts.map +1 -0
  144. package/dist/types/core/components/functional/interactive/registry.d.ts +23 -0
  145. package/dist/types/core/components/functional/interactive/registry.d.ts.map +1 -0
  146. package/dist/types/core/components/functional/interactive/store.d.ts +46 -0
  147. package/dist/types/core/components/functional/interactive/store.d.ts.map +1 -0
  148. package/dist/types/core/components/functional/interactive/ticker.d.ts +33 -0
  149. package/dist/types/core/components/functional/interactive/ticker.d.ts.map +1 -0
  150. package/dist/types/core/components/functional/value/value.d.ts +6 -0
  151. package/dist/types/core/components/functional/value/value.d.ts.map +1 -1
  152. package/dist/types/core/components/ui/_css/rounded.d.ts +3 -0
  153. package/dist/types/core/components/ui/_css/rounded.d.ts.map +1 -0
  154. package/dist/types/core/components/ui/_css/type.d.ts +13 -0
  155. package/dist/types/core/components/ui/_css/type.d.ts.map +1 -1
  156. package/dist/types/core/components/ui/_i18n/ui-aria-labels.d.ts +83 -0
  157. package/dist/types/core/components/ui/_i18n/ui-aria-labels.d.ts.map +1 -0
  158. package/dist/types/core/components/ui/alert/alert.d.ts +14 -12
  159. package/dist/types/core/components/ui/alert/alert.d.ts.map +1 -1
  160. package/dist/types/core/components/ui/alert-messages/alert-messages.d.ts.map +1 -1
  161. package/dist/types/core/components/ui/badge/badge.d.ts +1 -1
  162. package/dist/types/core/components/ui/badge/badge.d.ts.map +1 -1
  163. package/dist/types/core/components/ui/button/button.d.ts +13 -29
  164. package/dist/types/core/components/ui/button/button.d.ts.map +1 -1
  165. package/dist/types/core/components/ui/card/card-header.d.ts.map +1 -1
  166. package/dist/types/core/components/ui/divider/divider.d.ts +2 -1
  167. package/dist/types/core/components/ui/divider/divider.d.ts.map +1 -1
  168. package/dist/types/core/components/ui/form/checkbox/checkbox.d.ts +25 -73
  169. package/dist/types/core/components/ui/form/checkbox/checkbox.d.ts.map +1 -1
  170. package/dist/types/core/components/ui/form/input/input.d.ts +14 -29
  171. package/dist/types/core/components/ui/form/input/input.d.ts.map +1 -1
  172. package/dist/types/core/components/ui/form/input-autocomplete/input-autocomplete.d.ts +63 -65
  173. package/dist/types/core/components/ui/form/input-autocomplete/input-autocomplete.d.ts.map +1 -1
  174. package/dist/types/core/components/ui/form/radio/radio.d.ts +1 -1
  175. package/dist/types/core/components/ui/form/select/select.d.ts +3 -5
  176. package/dist/types/core/components/ui/form/select/select.d.ts.map +1 -1
  177. package/dist/types/core/components/ui/form/textarea/textarea.d.ts +12 -28
  178. package/dist/types/core/components/ui/form/textarea/textarea.d.ts.map +1 -1
  179. package/dist/types/core/components/ui/image/image.d.ts +2 -1
  180. package/dist/types/core/components/ui/image/image.d.ts.map +1 -1
  181. package/dist/types/core/components/ui/link/link.d.ts +10 -9
  182. package/dist/types/core/components/ui/link/link.d.ts.map +1 -1
  183. package/dist/types/core/components/ui/modal/modal-close.d.ts +2 -2
  184. package/dist/types/core/components/ui/modal/modal-close.d.ts.map +1 -1
  185. package/dist/types/core/components/ui/pop/pop.d.ts +12 -4
  186. package/dist/types/core/components/ui/pop/pop.d.ts.map +1 -1
  187. package/dist/types/core/components/ui/table/table.d.ts +2 -1
  188. package/dist/types/core/components/ui/table/table.d.ts.map +1 -1
  189. package/dist/types/core/components/ui/toast/dismissed.d.ts +3 -0
  190. package/dist/types/core/components/ui/toast/dismissed.d.ts.map +1 -0
  191. package/dist/types/core/components/ui/toast/render-content.d.ts +7 -0
  192. package/dist/types/core/components/ui/toast/render-content.d.ts.map +1 -0
  193. package/dist/types/core/components/ui/toast/toast-item.d.ts +26 -9
  194. package/dist/types/core/components/ui/toast/toast-item.d.ts.map +1 -1
  195. package/dist/types/core/components/ui/toast/toast.d.ts +6 -3
  196. package/dist/types/core/components/ui/toast/toast.d.ts.map +1 -1
  197. package/dist/types/core/components/ui/toast/types.d.ts +12 -2
  198. package/dist/types/core/components/ui/toast/types.d.ts.map +1 -1
  199. package/dist/types/core/components/ui/tooltip/tooltip.d.ts +19 -11
  200. package/dist/types/core/components/ui/tooltip/tooltip.d.ts.map +1 -1
  201. package/dist/types/core/decorators/subscriber/ancestorAttribute.d.ts +3 -4
  202. package/dist/types/core/decorators/subscriber/ancestorAttribute.d.ts.map +1 -1
  203. package/dist/types/core/decorators/subscriber/derive.d.ts.map +1 -1
  204. package/dist/types/core/mixins/FormCheckable.d.ts +10 -27
  205. package/dist/types/core/mixins/FormCheckable.d.ts.map +1 -1
  206. package/dist/types/core/mixins/FormElement.d.ts +21 -10
  207. package/dist/types/core/mixins/FormElement.d.ts.map +1 -1
  208. package/dist/types/core/mixins/FormInput.d.ts +11 -27
  209. package/dist/types/core/mixins/FormInput.d.ts.map +1 -1
  210. package/dist/types/core/utils/AncestorAttributeObserver.d.ts +17 -9
  211. package/dist/types/core/utils/AncestorAttributeObserver.d.ts.map +1 -1
  212. package/dist/types/core/utils/LocationHandler.d.ts +3 -2
  213. package/dist/types/core/utils/LocationHandler.d.ts.map +1 -1
  214. package/dist/types/core/utils/dismissed.d.ts +3 -0
  215. package/dist/types/core/utils/dismissed.d.ts.map +1 -0
  216. package/dist/types/core/utils/positioning.d.ts +44 -0
  217. package/dist/types/core/utils/positioning.d.ts.map +1 -0
  218. package/dist/types/directives.d.ts +1 -3
  219. package/dist/types/directives.d.ts.map +1 -1
  220. package/dist/types/mixins.d.ts +22 -55
  221. package/dist/types/mixins.d.ts.map +1 -1
  222. package/docs/assets/{index-FmK4JaIM.js → index-D8MVWqY6.js} +946 -687
  223. package/docs/assets/index-DiD0qYvE.css +1 -0
  224. package/docs/crawl/core/components/ui/form/input-autocomplete/input-autocomplete.html +17 -85
  225. package/docs/crawl/docs/_misc/formelement-subscriber-removal.html +1 -1
  226. package/docs/crawl/hubs/form-components.html +13 -11
  227. package/docs/docs-mock-api-sw.js +5 -1
  228. package/docs/docs-mock-api-sw.js.map +2 -2
  229. package/docs/index.html +2 -2
  230. package/docs/llms-full.txt +12 -38
  231. package/docs/llms.txt +1 -1
  232. package/docs/src/core/components/functional/sdui/sdui.md +0 -0
  233. package/docs/src/core/components/ui/form/checkbox/checkbox.md +0 -0
  234. package/docs/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +14 -118
  235. package/docs/src/docs/_getting-started/create-a-component.md +0 -0
  236. package/docs/src/docs/_misc/formelement-subscriber-removal.md +1 -1
  237. package/docs/src/docs/search/docs-search.json +195 -200
  238. package/docs/src/tsconfig.json +324 -0
  239. package/gitlab/job_tests.sh +25 -12
  240. package/index.html +3 -3
  241. package/package.json +182 -10
  242. package/public/docs-mock-api-sw.js +5 -1
  243. package/public/docs-mock-api-sw.js.map +2 -2
  244. package/scripts/build-addons.mjs +2 -2
  245. package/scripts/post-build.mjs +9 -0
  246. package/src/addons/interactive.ts +5 -0
  247. package/src/core/components/functional/if/if.ts +54 -7
  248. package/src/core/components/functional/interactive/action.ts +84 -0
  249. package/src/core/components/functional/interactive/gamepad.ts +197 -0
  250. package/src/core/components/functional/interactive/gesture.ts +136 -0
  251. package/src/core/components/functional/interactive/helpers.spec.ts +50 -0
  252. package/src/core/components/functional/interactive/helpers.ts +97 -0
  253. package/src/core/components/functional/interactive/index.ts +25 -0
  254. package/src/core/components/functional/interactive/interactive.md +33 -0
  255. package/src/core/components/functional/interactive/keyboard.ts +171 -0
  256. package/src/core/components/functional/interactive/matrix.ts +185 -0
  257. package/src/core/components/functional/interactive/registry.ts +46 -0
  258. package/src/core/components/functional/interactive/store.ts +270 -0
  259. package/src/core/components/functional/interactive/ticker.ts +171 -0
  260. package/src/core/components/functional/list/list.ts +0 -0
  261. package/src/core/components/functional/subscriber/subscriber.ts +0 -0
  262. package/src/core/components/functional/value/value.ts +44 -4
  263. package/src/core/components/ui/_css/rounded.ts +36 -0
  264. package/src/core/components/ui/_css/type.ts +49 -0
  265. package/src/core/components/ui/_i18n/ui-aria-labels.ts +104 -0
  266. package/src/core/components/ui/alert/alert.md +128 -72
  267. package/src/core/components/ui/alert/alert.spec.ts +145 -0
  268. package/src/core/components/ui/alert/alert.ts +86 -91
  269. package/src/core/components/ui/alert-messages/alert-messages.ts +1 -1
  270. package/src/core/components/ui/badge/badge.md +126 -78
  271. package/src/core/components/ui/badge/badge.ts +29 -81
  272. package/src/core/components/ui/button/button.md +1 -0
  273. package/src/core/components/ui/button/button.ts +12 -10
  274. package/src/core/components/ui/card/card-header.ts +7 -2
  275. package/src/core/components/ui/card/card.md +120 -65
  276. package/src/core/components/ui/divider/divider.ts +4 -9
  277. package/src/core/components/ui/form/checkbox/checkbox.md +1 -1
  278. package/src/core/components/ui/form/checkbox/checkbox.ts +4 -20
  279. package/src/core/components/ui/form/fieldset/legend-description.ts +1 -1
  280. package/src/core/components/ui/form/fieldset/legend.ts +1 -1
  281. package/src/core/components/ui/form/input/input.md +1 -1
  282. package/src/core/components/ui/form/input/input.ts +25 -27
  283. package/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +14 -118
  284. package/src/core/components/ui/form/input-autocomplete/input-autocomplete.ts +419 -187
  285. package/src/core/components/ui/form/radio/radio.ts +1 -1
  286. package/src/core/components/ui/form/select/select.ts +3 -6
  287. package/src/core/components/ui/form/textarea/textarea.md +1 -1
  288. package/src/core/components/ui/form/textarea/textarea.ts +1 -2
  289. package/src/core/components/ui/image/image.md +147 -53
  290. package/src/core/components/ui/image/image.ts +10 -44
  291. package/src/core/components/ui/link/link.md +2 -2
  292. package/src/core/components/ui/link/link.ts +42 -37
  293. package/src/core/components/ui/modal/modal-close.ts +8 -12
  294. package/src/core/components/ui/modal/modal.ts +18 -18
  295. package/src/core/components/ui/pop/pop.md +105 -1
  296. package/src/core/components/ui/pop/pop.ts +145 -141
  297. package/src/core/components/ui/table/table.ts +2 -8
  298. package/src/core/components/ui/theme/theme.ts +1 -1
  299. package/src/core/components/ui/toast/dismissed.ts +14 -0
  300. package/src/core/components/ui/toast/render-content.ts +13 -0
  301. package/src/core/components/ui/toast/toast-item.ts +179 -103
  302. package/src/core/components/ui/toast/toast.ts +58 -21
  303. package/src/core/components/ui/toast/types.ts +14 -2
  304. package/src/core/components/ui/tooltip/tooltip.ts +176 -154
  305. package/src/core/decorators/subscriber/ancestorAttribute.spec.ts +26 -0
  306. package/src/core/decorators/subscriber/ancestorAttribute.ts +3 -4
  307. package/src/core/decorators/subscriber/derive.ts +3 -3
  308. package/src/core/mixins/Fetcher.ts +1 -1
  309. package/src/core/mixins/FormCheckable.ts +1 -8
  310. package/src/core/mixins/FormElement.ts +74 -76
  311. package/src/core/mixins/Subscriber.ts +5 -1
  312. package/src/core/utils/AncestorAttributeObserver.ts +183 -70
  313. package/src/core/utils/DataBindObserver.ts +0 -0
  314. package/src/core/utils/LocationHandler.ts +12 -5
  315. package/src/core/utils/Objects.ts +0 -0
  316. package/src/core/utils/dismissed.ts +22 -0
  317. package/src/core/utils/positioning.spec.ts +208 -0
  318. package/src/core/utils/positioning.ts +183 -0
  319. package/src/directives.ts +1 -3
  320. package/src/docs/_core-concept/dataFlow.md +1 -1
  321. package/src/docs/_decorators/ancestor-attribute.md +3 -2
  322. package/src/docs/_getting-started/cdn-addons.md +1 -0
  323. package/src/docs/_getting-started/concorde-outside.md +3 -3
  324. package/src/docs/_misc/formelement-subscriber-removal.md +115 -0
  325. package/src/docs/_misc/html-integration.md +2 -0
  326. package/src/docs/code.ts +3 -1
  327. package/src/docs/components/docs-demo-sources.ts +60 -0
  328. package/src/docs/docs.ts +1 -0
  329. package/src/docs/example/docs-input-autocomplete-demos.ts +110 -0
  330. package/src/docs/mock-api/router.ts +17 -1
  331. package/src/docs/navigation/navigation.ts +8 -0
  332. package/src/docs/search/docs-search.json +956 -243
  333. package/src/docs/tailwind/css/tailwind.css +13 -6
  334. package/src/tsconfig.json +135 -6
  335. package/src/tsconfig.tsbuildinfo +1 -1
  336. package/tailwind.config.js +50 -3
  337. package/vite/config.js +23 -35
  338. package/vite.config.mts +5 -0
  339. package/vitest.setup.ts +49 -13
  340. package/docs/assets/index-BZiZXMU-.css +0 -1
@@ -9,7 +9,6 @@ import {
9
9
  import {
10
10
  FormInput,
11
11
  FormElement,
12
- Subscriber,
13
12
  TemplatesContainer,
14
13
  } from "@supersoniks/concorde/mixins";
15
14
  import "@supersoniks/concorde/core/components/ui/form/input/input";
@@ -25,17 +24,20 @@ import { customScroll } from "@supersoniks/concorde/core/components/ui/_css/scro
25
24
  import { ResizeController } from "@lit-labs/observers/resize-controller.js";
26
25
  import { traverseDotNotation } from "@supersoniks/concorde/core/utils/Objects";
27
26
  import { Pop } from "../../pop/pop";
27
+ import type { ListItems } from "@supersoniks/concorde/core/components/functional/list/list";
28
28
 
29
29
  type ListItem = Record<string, string>;
30
30
 
31
+ const SUGGESTION_VALUE_ATTR = "data-autocomplete-value";
32
+ const SUGGESTION_LABEL_ATTR = "data-autocomplete-label";
33
+
31
34
  /**
32
- * Input avec autocomplete. Propose des valeurs à partir d'un sonic-queue.
33
- * L'input permet de filtrer les choix de valeurs dans le sonic-pop.
34
- * La valeur de cet input est ensuite retransmit au premier via un dataProvider.
35
+ * Input avec suggestions (queue). Valeur formulaire via formDataProvider + name ;
36
+ * texte de recherche sur un store interne dédié.
35
37
  */
36
38
  @customElement("sonic-input-autocomplete")
37
39
  export class InputAutocomplete extends TemplatesContainer(
38
- FormInput(FormElement(Subscriber(LitElement)))
40
+ FormInput(FormElement(LitElement))
39
41
  ) {
40
42
  static styles = [
41
43
  customScroll,
@@ -43,7 +45,6 @@ export class InputAutocomplete extends TemplatesContainer(
43
45
  :host {
44
46
  display: block;
45
47
  }
46
- /**disable mouse if there is an attribute readonly */
47
48
  :host([readonly]),
48
49
  :host([disabled]) {
49
50
  pointer-events: none;
@@ -60,26 +61,34 @@ export class InputAutocomplete extends TemplatesContainer(
60
61
  min-width: 14rem;
61
62
  width: 100%;
62
63
  }
64
+
65
+ .init-queue {
66
+ display: none !important;
67
+ position: absolute;
68
+ width: 0;
69
+ height: 0;
70
+ overflow: hidden;
71
+ pointer-events: none;
72
+ }
63
73
  `,
64
74
  ];
65
75
 
66
- /**
67
- * Possibles mutualisation avec text
68
- */
69
76
  @property({ type: String }) size: Size = "md";
70
77
  @property({ type: String }) placeholder = "";
71
78
  @property() filteredFields = "";
72
79
  @property({ type: Boolean, reflect: true }) readonly: boolean | null = null;
73
80
 
74
81
  @property({ type: String }) dataProviderExpression = "";
75
- @property({ type: Number }) minSearchLength = 0;
82
+ @property({ type: String }) serviceURL = "";
83
+ @property({ type: Number }) minSearchLength = 3;
76
84
  @property({ type: Boolean }) select?: boolean;
77
85
  @property({ type: String }) key = "";
78
- /** The parameter name to use in dataProviderExpression route */
79
86
  @property({ type: String }) searchParameter = "";
80
- /** The property name to search in the dataProviderExpression result, use "_self" if result is a string list */
81
87
  @property({ type: String }) propertyName = "";
82
88
 
89
+ @property({ type: Function, attribute: false }) items: ListItems | null =
90
+ null;
91
+
83
92
  @queryAssignedNodes({ slot: "prefix", flatten: true })
84
93
  slotInputPrefixNodes!: Array<Node>;
85
94
 
@@ -87,58 +96,292 @@ export class InputAutocomplete extends TemplatesContainer(
87
96
  @query("sonic-pop") popElement!: Pop;
88
97
  _resizeController = new ResizeController(this, {});
89
98
 
90
- @state() isPopVisible = false;
91
-
92
- /**
93
- * Les dataProviders
94
- */
95
- private searchDataProvider = "";
99
+ @state() private searchText = "";
100
+ @state() private searchDataProvider = "";
101
+ /** Filtre queue : ne reçoit la saisie qu'à partir de minSearchLength. */
102
+ private queueFilterDataProvider = "";
96
103
  private initSearchDataProvider = "";
97
104
  private queueDataProvider = "";
98
105
  private initQueueDataProvider = "";
99
- @state()
100
- private lastValidSearch: ListItem | string = "";
101
- private searchPublisher?: DataProvider;
102
- private countPublisher?: DataProvider;
106
+
107
+ /** Public — cptypeahead. */
108
+ lastValidSearch: ListItem | string = "";
109
+ /** Public — cptypeahead (saisie live). */
110
+ searchPublisher?: DataProvider;
111
+
112
+ private queueFilterPublisher?: DataProvider;
103
113
  private initCountPublisher?: DataProvider;
104
114
  private formValuePublisher?: DataProvider;
115
+ private providersReady = false;
116
+ /** Évite de rouvrir le pop juste après une sélection. */
117
+ private suppressPopOpen = false;
118
+ private filterArmed = false;
119
+
120
+ private static instanceCounter = 0;
105
121
 
106
- /**
107
- * Les fonctions de gestion
108
- */
109
122
  hasSlotOrProps() {
110
123
  this.hasInputPrefix = !!this.slotInputPrefixNodes?.length;
111
124
  }
112
125
 
113
- /**
114
- * Run when the formDataProvider's field is updated
115
- *
116
- * @param value
117
- */
126
+ private searchFieldName() {
127
+ return this.searchParameter || this.name;
128
+ }
129
+
130
+ private isSearchParameter() {
131
+ return !this.searchParameter || this.searchParameter === this.name;
132
+ }
133
+
134
+ private shouldShowSuggestions(text: string) {
135
+ return (
136
+ (text?.length || 0) >= this.minSearchLength || this.minSearchLength === 0
137
+ );
138
+ }
139
+
140
+ private isSearchFocused() {
141
+ const active = document.activeElement;
142
+ if (!active) return false;
143
+ const input = this.renderRoot?.querySelector(
144
+ "sonic-input.form-element"
145
+ ) as HTMLElement | null;
146
+ return !!(
147
+ input &&
148
+ (active === input ||
149
+ input.contains(active) ||
150
+ input.shadowRoot?.contains(active))
151
+ );
152
+ }
153
+
154
+ private openPopIfNeeded(text: string, fromUserInput = false) {
155
+ if (this.suppressPopOpen) return;
156
+ if (!this.shouldShowSuggestions(text)) {
157
+ this.popElement?.hide();
158
+ return;
159
+ }
160
+ if (fromUserInput || this.isSearchFocused() || this.popElement?.open) {
161
+ this.popElement?.show();
162
+ // 2e show après le render Lit du pop (remettait display:none)
163
+ requestAnimationFrame(() => {
164
+ if (this.suppressPopOpen) return;
165
+ const current = String(
166
+ this.searchPublisher?.get() ?? this.searchText ?? ""
167
+ );
168
+ if (this.shouldShowSuggestions(current)) {
169
+ this.popElement?.show();
170
+ }
171
+ });
172
+ }
173
+ }
174
+
175
+ private setSearchText(value: string | ListItem | null | undefined) {
176
+ const next =
177
+ value == null || value === ""
178
+ ? ""
179
+ : typeof value === "string"
180
+ ? value
181
+ : String(value);
182
+ this.searchText = next;
183
+ if (this.searchPublisher && this.searchPublisher.get() !== next) {
184
+ this.searchPublisher.set(next);
185
+ }
186
+ this.syncQueueFilter(next);
187
+ }
188
+
189
+ /** Pousse vers la queue seulement si assez de caractères. */
190
+ private syncQueueFilter(text: string) {
191
+ if (!this.queueFilterPublisher) return;
192
+ const ready =
193
+ this.minSearchLength === 0 || text.length >= this.minSearchLength;
194
+ if (ready) {
195
+ if (!this.filterArmed || this.queueFilterPublisher.get() !== text) {
196
+ this.queueFilterPublisher.set(text);
197
+ }
198
+ this.filterArmed = true;
199
+ return;
200
+ }
201
+ if (this.filterArmed) {
202
+ this.filterArmed = false;
203
+ this.clearSearchQueueResults();
204
+ }
205
+ }
206
+
207
+ private clearSearchQueueResults() {
208
+ if (!this.queueDataProvider) return;
209
+ const queuePub = PublisherManager.get(this.queueDataProvider);
210
+ const descriptors = queuePub.get();
211
+ if (Array.isArray(descriptors)) {
212
+ for (const d of descriptors) {
213
+ if (d?.dataProvider) {
214
+ PublisherManager.getInstance().delete(d.dataProvider);
215
+ }
216
+ }
217
+ }
218
+ queuePub.set([]);
219
+ }
220
+
221
+ private applySuggestion(value: unknown, label: string) {
222
+ this.suppressPopOpen = true;
223
+ const formValue = value as string;
224
+ const searchLabel =
225
+ label ||
226
+ (typeof formValue === "string" ? formValue : String(formValue ?? ""));
227
+
228
+ this.formValuePublisher?.set(formValue);
229
+ this.lastValidSearch = this.isSearchParameter() ? formValue : searchLabel;
230
+ this.setSearchText(
231
+ this.isSearchParameter() ? String(formValue ?? "") : searchLabel
232
+ );
233
+ this.popElement?.hide();
234
+
235
+ // Laisse le cycle hide/focus se terminer avant de réautoriser l'ouverture.
236
+ requestAnimationFrame(() => {
237
+ this.suppressPopOpen = false;
238
+ });
239
+ }
240
+
118
241
  private updateSearchParameter = (value: string) => {
119
- if (value == "" && this.isSearchParameter()) {
120
- this.lastValidSearch = "";
242
+ if (value == "" || value == null) {
243
+ if (this.isSearchParameter()) {
244
+ this.lastValidSearch = "";
245
+ this.setSearchText("");
246
+ }
121
247
  return;
122
248
  }
123
249
  const found = this.queryQueueListItem(
124
250
  this.queueDataProvider,
125
- this.findSelection,
251
+ (listItem) => {
252
+ const itemVal =
253
+ this.propertyName === "_self"
254
+ ? listItem
255
+ : listItem[this.propertyName || this.name];
256
+ return itemVal == value;
257
+ },
126
258
  this.setSearchFromSelection
127
259
  );
128
- // Si la liste de this.queueDataProvider est bien initialisée, mais qu'aucun item ne correspond à la recherche
129
- // On affiche quand même la valeur dans le champ de recherche, car il provient directement du formDataProvider
130
- if (found === false && this.isSearchParameter()) {
131
- this.lastValidSearch = value;
132
- this.searchPublisher?.set(this.lastValidSearch);
260
+ if (!found) {
261
+ const foundInit = this.queryQueueListItem(
262
+ this.initQueueDataProvider,
263
+ (listItem) => {
264
+ const itemVal =
265
+ this.propertyName === "_self"
266
+ ? listItem
267
+ : listItem[this.propertyName || this.name];
268
+ return itemVal == value;
269
+ },
270
+ this.setSearchFromSelection
271
+ );
272
+ if (!foundInit && this.isSearchParameter()) {
273
+ this.lastValidSearch = value;
274
+ this.setSearchText(String(value));
275
+ }
133
276
  }
134
277
  };
135
278
 
136
- private isSearchParameter() {
137
- return !this.searchParameter || this.searchParameter === this.name;
279
+ private onSearchPublisherAssign = (value: string) => {
280
+ const next = value == null ? "" : String(value);
281
+ if (this.searchText !== next) {
282
+ this.searchText = next;
283
+ }
284
+ this.syncQueueFilter(next);
285
+ this.openPopIfNeeded(next);
286
+ };
287
+
288
+ private handleSearchActivity = () => {
289
+ const text = String(this.searchPublisher?.get() ?? this.searchText ?? "");
290
+ this.openPopIfNeeded(text, true);
291
+ };
292
+
293
+ /** keyup composé depuis l'input shadow — ouvre le pop dès qu'il y a assez de caractères. */
294
+ private handleSearchKeyUp = (e: KeyboardEvent) => {
295
+ if (e.ctrlKey || e.metaKey || e.altKey) return;
296
+ if (["Escape", "Tab", "Enter", "ArrowUp", "ArrowDown"].includes(e.key))
297
+ return;
298
+ this.handleSearchActivity();
299
+ };
300
+
301
+ /**
302
+ * Capture : empêche la nav FormElement (data-keyboard-nav) de sortir du champ
303
+ * et de fermer le pop. ArrowDown/Up gardent le pop ouvert et entrent dans la liste.
304
+ * Enter depuis le champ sélectionne la 1re suggestion.
305
+ */
306
+ private handleSearchKeyDown = (e: KeyboardEvent) => {
307
+ const path = e.composedPath() as HTMLElement[];
308
+ const fromMenuItem = path.some((n) => n?.tagName === "SONIC-MENU-ITEM");
309
+
310
+ // Enter / Espace sur une suggestion → sélection
311
+ if (fromMenuItem && (e.key === "Enter" || e.key === " ")) {
312
+ const item = path.find((n) => n?.tagName === "SONIC-MENU-ITEM") as
313
+ | (HTMLElement & { value?: string | null })
314
+ | undefined;
315
+ if (!item) return;
316
+ e.preventDefault();
317
+ e.stopPropagation();
318
+ this.selectSuggestionItem(item);
319
+ return;
320
+ }
321
+
322
+ if (fromMenuItem) return;
323
+
324
+ const text = String(this.searchPublisher?.get() ?? this.searchText ?? "");
325
+ if (!this.shouldShowSuggestions(text)) return;
326
+
327
+ // Enter dans le champ → 1re suggestion
328
+ if (e.key === "Enter") {
329
+ const items = this.getSuggestionItems();
330
+ if (!items.length) return;
331
+ e.preventDefault();
332
+ e.stopPropagation();
333
+ this.selectSuggestionItem(items[0]);
334
+ return;
335
+ }
336
+
337
+ if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
338
+
339
+ e.preventDefault();
340
+ e.stopPropagation();
341
+
342
+ this.suppressPopOpen = false;
343
+ this.popElement?.show();
344
+ requestAnimationFrame(() => this.popElement?.show());
345
+
346
+ const items = this.getSuggestionItems();
347
+ if (!items.length) return;
348
+
349
+ const target = e.key === "ArrowDown" ? items[0] : items[items.length - 1];
350
+ target.focus?.();
351
+ };
352
+
353
+ private getSuggestionItems() {
354
+ return [
355
+ ...(this.renderRoot?.querySelectorAll("sonic-menu-item") ?? []),
356
+ ] as Array<HTMLElement & { value?: string | null; focus?: () => void }>;
357
+ }
358
+
359
+ private selectSuggestionItem(
360
+ item: HTMLElement & { value?: string | null }
361
+ ) {
362
+ const value =
363
+ item.getAttribute(SUGGESTION_VALUE_ATTR) ?? item.value ?? null;
364
+ const label =
365
+ item.getAttribute(SUGGESTION_LABEL_ATTR) ??
366
+ (item.textContent || "").trim();
367
+
368
+ if (value == null && !label) return;
369
+ this.applySuggestion(value ?? label, label);
138
370
  }
139
371
 
140
- private updatePopContentVisibility = (value: string) => {
141
- this.isPopVisible = (value?.length || 0) >= this.minSearchLength;
372
+ /**
373
+ * pointerdown : avant le click document qui ferme le pop.
374
+ * data-autocomplete-* (démos .items) ou value + textContent (templates legacy).
375
+ */
376
+ private handleSuggestionPointerDown = (e: Event) => {
377
+ const item = (e.composedPath() as HTMLElement[]).find(
378
+ (n) => n?.tagName === "SONIC-MENU-ITEM"
379
+ ) as (HTMLElement & { value?: string | null }) | undefined;
380
+ if (!item) return;
381
+
382
+ e.preventDefault();
383
+ e.stopPropagation();
384
+ this.selectSuggestionItem(item);
142
385
  };
143
386
 
144
387
  private initSearchParameter = () => {
@@ -149,22 +392,6 @@ export class InputAutocomplete extends TemplatesContainer(
149
392
  );
150
393
  };
151
394
 
152
- private selectListItem = (listItem: ListItem) => {
153
- const value =
154
- this.propertyName === "_self"
155
- ? listItem
156
- : listItem[this.propertyName || this.name];
157
- this.formValuePublisher?.set(value);
158
- };
159
-
160
- private findSearchedItem = (listItem: ListItem) => {
161
- const value =
162
- this.propertyName === "_self"
163
- ? listItem
164
- : listItem[this.propertyName || this.searchParameter || this.name];
165
- return value == this.searchPublisher?.get();
166
- };
167
-
168
395
  private findSelection = (listItem: ListItem) => {
169
396
  const value =
170
397
  this.propertyName === "_self"
@@ -178,115 +405,97 @@ export class InputAutocomplete extends TemplatesContainer(
178
405
  this.propertyName === "_self"
179
406
  ? listItem
180
407
  : listItem[this.searchParameter || this.propertyName || this.name];
181
- if (this.isSearchParameter()) {
182
- this.searchPublisher?.set(this.lastValidSearch);
183
- }
184
- };
185
- private updateActiveSelection = () => {
186
- this.queryQueueListItem(
187
- this.queueDataProvider,
188
- this.findSearchedItem,
189
- this.selectListItem
190
- );
191
- if (
192
- !this.select &&
193
- this.lastValidSearch &&
194
- this.lastValidSearch != this.searchPublisher?.get() &&
195
- this.formValuePublisher?.get()
196
- ) {
197
- this.formValuePublisher?.set("");
198
- }
408
+ this.setSearchText(this.lastValidSearch as string);
199
409
  };
200
410
 
201
- connectedCallback() {
202
- super.connectedCallback();
203
-
204
- /**
205
- * Nom de la valeur de recherche
206
- * Si non défini, on utilise name
207
- **/
208
- const searchParameter = this.searchParameter || this.name;
209
-
210
- /**
211
- * Nommage des différents dataProviders utilisés
212
- */
213
- const formDataProvider = this.getAncestorAttributeValue("formDataProvider");
214
- // Create unique provider name with formProvider name + input name
215
- const dpPrefix = formDataProvider + "__" + this.name + "__autocomplete";
411
+ private setupProviders() {
412
+ const searchParameter = this.searchFieldName();
413
+ const formDataProvider =
414
+ this.formDataProvider ||
415
+ this.getAncestorAttributeValue("formDataProvider") ||
416
+ "";
417
+
418
+ const instanceId = InputAutocomplete.instanceCounter++;
419
+ const dpPrefix =
420
+ (formDataProvider || "form") +
421
+ "__" +
422
+ (this.name || "field") +
423
+ "__autocomplete__" +
424
+ instanceId;
216
425
  this.initSearchDataProvider = `${dpPrefix}_init_search__`;
217
426
  this.initQueueDataProvider = `${dpPrefix}_init_queue__`;
218
427
  this.searchDataProvider = `${dpPrefix}_search__`;
428
+ this.queueFilterDataProvider = `${dpPrefix}_queue_filter__`;
219
429
  this.queueDataProvider = `${dpPrefix}_queue__`;
220
430
 
221
- /**
222
- * Les publishers utilisés plusieurs fois dans la classe son miss en propriétés privées de la classe
223
- */
224
431
  const getPublisher = PublisherManager.get;
225
- this.searchPublisher = getPublisher(this.searchDataProvider)[
226
- searchParameter
227
- ];
432
+ this.searchPublisher =
433
+ getPublisher(this.searchDataProvider)[searchParameter];
434
+ this.queueFilterPublisher =
435
+ getPublisher(this.queueFilterDataProvider)[searchParameter];
228
436
  this.formValuePublisher = traverseDotNotation(
229
437
  getPublisher(formDataProvider),
230
438
  this.name
231
439
  );
232
- this.countPublisher = getPublisher(this.queueDataProvider).resultCount;
233
440
  this.initCountPublisher = getPublisher(
234
441
  this.initQueueDataProvider
235
442
  ).resultCount;
236
443
 
237
- /**
238
- * Si une valeur est fourrnie a l'initialisation, un queue spécifique appelle le service avec le name founi en paramètre
239
- * Pour récupérer la valeur de searchParameter correspondante et renseigner l'input
240
- **/
241
444
  if (this.value) {
242
- PublisherManager.get(this.initSearchDataProvider)[this.name].set(this.value);
445
+ getPublisher(this.initSearchDataProvider)[this.name].set(this.value);
243
446
  }
244
- this.initCountPublisher?.onAssign(this.initSearchParameter);
245
447
 
246
- /**
247
- * Lorsque la sélection change, on met à jour la valeur de recherche
248
- */
448
+ this.initCountPublisher?.onAssign(this.initSearchParameter);
249
449
  this.formValuePublisher?.onAssign(this.updateSearchParameter);
450
+ this.searchPublisher?.onAssign(this.onSearchPublisherAssign);
250
451
 
251
- /**
252
- * Lorsque La valeur de recherche change, que le composant n'est pas en mode select
253
- * et qu'elle est différente de la derniere recherche valide on désactive la selection
254
- */
255
- this.countPublisher?.onAssign(this.updateActiveSelection);
452
+ const existing = this.searchPublisher?.get();
453
+ if (existing != null && existing !== "") {
454
+ this.searchText = String(existing);
455
+ this.syncQueueFilter(this.searchText);
456
+ }
256
457
 
257
- this.searchPublisher?.onAssign(this.updatePopContentVisibility);
458
+ this.providersReady = true;
459
+ }
460
+
461
+ private teardownProviders() {
462
+ this.initCountPublisher?.offAssign(this.initSearchParameter);
463
+ this.formValuePublisher?.offAssign(this.updateSearchParameter);
464
+ this.searchPublisher?.offAssign(this.onSearchPublisherAssign);
465
+
466
+ const getPublisher = PublisherManager.get;
467
+ if (this.initSearchDataProvider)
468
+ getPublisher(this.initSearchDataProvider).delete();
469
+ if (this.initQueueDataProvider)
470
+ getPublisher(this.initQueueDataProvider).delete();
471
+ if (this.searchDataProvider) getPublisher(this.searchDataProvider).delete();
472
+ if (this.queueFilterDataProvider)
473
+ getPublisher(this.queueFilterDataProvider).delete();
474
+ if (this.queueDataProvider) getPublisher(this.queueDataProvider).delete();
475
+
476
+ this.providersReady = false;
477
+ }
478
+
479
+ connectedCallback() {
480
+ super.connectedCallback();
481
+ this.setupProviders();
482
+ this.addEventListener("keyup", this.handleSearchKeyUp);
483
+ this.addEventListener("keydown", this.handleSearchKeyDown, true);
258
484
  }
259
485
 
260
486
  protected firstUpdated(_changedProperties: PropertyValues): void {
261
487
  super.firstUpdated(_changedProperties);
262
- // Écouter le focusout sur le popElement pour fermer le pop quand le focus sort
263
488
  this.popElement.addEventListener("focusout", this.handleFocusOut);
264
489
  }
265
490
 
266
491
  disconnectedCallback() {
492
+ this.removeEventListener("keyup", this.handleSearchKeyUp);
493
+ this.removeEventListener("keydown", this.handleSearchKeyDown, true);
494
+ this.teardownProviders();
495
+ this.popElement?.removeEventListener("focusout", this.handleFocusOut);
267
496
  super.disconnectedCallback();
268
- this.initCountPublisher?.offAssign(this.initSearchParameter);
269
- this.formValuePublisher?.offAssign(this.updateSearchParameter);
270
- this.countPublisher?.offAssign(this.updateActiveSelection);
271
- const getPublisher = PublisherManager.get;
272
- getPublisher(this.initSearchDataProvider).delete();
273
- getPublisher(this.initQueueDataProvider).delete();
274
- getPublisher(this.searchDataProvider).delete();
275
- getPublisher(this.queueDataProvider).delete();
276
-
277
- this.searchPublisher?.offAssign(this.updatePopContentVisibility);
278
-
279
- // Retirer l'écouteur de focusout
280
- this.popElement.removeEventListener("focusout", this.handleFocusOut);
281
497
  }
282
498
 
283
- /**
284
- * @param queueDataProvider
285
- * @param itemFinder
286
- * @param itemMutator
287
- *
288
- * @return bool|undefined True if item found, false otherwise, undefined if result is not a list
289
- */
290
499
  queryQueueListItem(
291
500
  queueDataProvider: string,
292
501
  itemFinder: (listItem: ListItem) => boolean,
@@ -294,19 +503,17 @@ export class InputAutocomplete extends TemplatesContainer(
294
503
  ) {
295
504
  const queuePublisher = PublisherManager.get(queueDataProvider);
296
505
  let listItem: ListItem | undefined;
297
- let listsDescriptors = queuePublisher.get();
506
+ const listsDescriptors = queuePublisher.get();
298
507
 
299
508
  if (!listsDescriptors || !Array.isArray(listsDescriptors)) {
300
509
  return false;
301
510
  }
302
511
  if (!listsDescriptors.length) return false;
303
512
  for (const listDescriptor of listsDescriptors) {
304
- let list = PublisherManager.get(listDescriptor.dataProvider).get();
513
+ const list = PublisherManager.get(listDescriptor.dataProvider).get();
305
514
  if (!Array.isArray(list)) continue;
306
515
  listItem = list.find(itemFinder);
307
- if (listItem) {
308
- break;
309
- }
516
+ if (listItem) break;
310
517
  }
311
518
  if (listItem) {
312
519
  itemMutator(listItem);
@@ -316,68 +523,86 @@ export class InputAutocomplete extends TemplatesContainer(
316
523
  }
317
524
 
318
525
  setSelectionRange(start: number, end: number) {
319
- (this.querySelector("sonic-input") as Input)?.setSelectionRange(start, end);
526
+ (
527
+ this.renderRoot?.querySelector("sonic-input.form-element") as Input | null
528
+ )?.setSelectionRange(start, end);
320
529
  }
321
530
 
322
531
  handleHide(): void {
323
532
  if (!this.select) return;
324
- if (this.searchPublisher?.get() == "") {
533
+ if (!this.searchText) {
325
534
  this.lastValidSearch = "";
326
535
  this.formValuePublisher?.set("");
327
536
  return;
328
537
  }
329
- this.searchPublisher?.set(this.lastValidSearch);
538
+ this.setSearchText(this.lastValidSearch as string);
330
539
  }
331
540
 
332
- getInputValue() {
333
- if (!this.isSearchParameter()) {
334
- return this.lastValidSearch;
541
+ /** Contient `node` même à travers des shadow roots imbriqués. */
542
+ private deepContains(container: Element | null | undefined, node: Node | null) {
543
+ if (!container || !node) return false;
544
+ let current: Node | null = node;
545
+ while (current) {
546
+ if (current === container || container.contains(current)) return true;
547
+ const root = current.getRootNode();
548
+ if (root instanceof ShadowRoot) {
549
+ current = root.host;
550
+ } else {
551
+ break;
552
+ }
335
553
  }
336
- return this.searchPublisher?.get();
554
+ return false;
337
555
  }
338
556
 
339
557
  handleFocus(): void {
340
- this.popElement.show();
558
+ const text = String(this.searchText ?? "");
559
+ if (this.shouldShowSuggestions(text)) {
560
+ this.suppressPopOpen = false;
561
+ this.popElement?.show();
562
+ }
341
563
  }
342
564
 
343
- handleFocusOut = (_e: FocusEvent): void => {
344
- // Utiliser setTimeout pour vérifier où va le focus après le focusout
345
- // Cela permet de ne pas fermer le pop si le focus va vers un élément du pop
565
+ handleFocusOut = (e: FocusEvent): void => {
566
+ const related = e.relatedTarget as Node | null;
567
+ if (
568
+ this.deepContains(this.popElement, related) ||
569
+ this.deepContains(this, related)
570
+ ) {
571
+ return;
572
+ }
573
+
346
574
  setTimeout(() => {
575
+ if (this.suppressPopOpen) return;
347
576
  const activeElement = document.activeElement;
348
- if (!activeElement) {
349
- this.popElement.hide();
577
+ if (
578
+ !activeElement ||
579
+ this.deepContains(this.popElement, activeElement) ||
580
+ this.deepContains(this, activeElement)
581
+ ) {
350
582
  return;
351
583
  }
352
-
353
- // Vérifier si le focus est dans le popElement ou dans ce composant
354
- const isInPop =
355
- this.popElement.contains(activeElement) ||
356
- this.popElement.shadowRoot?.contains(activeElement);
357
- const isInComponent =
358
- this.contains(activeElement) ||
359
- this.shadowRoot?.contains(activeElement);
360
-
361
- // Si le focus n'est ni dans le pop ni dans ce composant, fermer le pop
362
- if (!isInPop && !isInComponent) {
363
- this.popElement.hide();
364
- }
584
+ this.popElement?.hide();
365
585
  }, 0);
366
586
  };
367
587
 
368
588
  render() {
589
+ if (!this.providersReady || !this.searchDataProvider) {
590
+ return nothing;
591
+ }
592
+
593
+ const templates = this.templateList.concat(this.templatePartsList);
594
+
369
595
  return html`
370
596
  <sonic-pop manual style="display:block;" @hide=${this.handleHide}>
371
597
  <sonic-input
372
598
  class="form-element"
373
- dataProvider="${this.initSearchDataProvider + Math.random()}"
374
- noAutofill
375
- formDataProvider="${this.searchDataProvider}"
599
+ .formDataProvider=${this.searchDataProvider}
600
+ .value=${this.searchText}
376
601
  type="search"
377
- data-keyboard-nav="${this.getAttribute("data-keyboard-nav") || ""}"
602
+ data-keyboard-nav=""
378
603
  label="${ifDefined(this.label)}"
379
604
  description="${ifDefined(this.description)}"
380
- name="${ifDefined(this.searchParameter || this.name)}"
605
+ name="${ifDefined(this.searchFieldName())}"
381
606
  ?required=${this.required}
382
607
  placeholder="${ifDefined(this.placeholder)}"
383
608
  ?readonly="${this.readonly}"
@@ -386,7 +611,7 @@ export class InputAutocomplete extends TemplatesContainer(
386
611
  inlineContent
387
612
  size=${this.size}
388
613
  @focus=${this.handleFocus}
389
- value=${ifDefined(this.getInputValue())}
614
+ @change=${this.handleSearchActivity}
390
615
  >
391
616
  <slot
392
617
  name="prefix"
@@ -408,33 +633,40 @@ export class InputAutocomplete extends TemplatesContainer(
408
633
  <sonic-menu
409
634
  slot="content"
410
635
  class="custom-scroll"
411
- style="${this.offsetWidth
412
- ? `width: ${this.offsetWidth}px`
413
- : ""}; display:${this.isPopVisible ? "block" : "none"};"
636
+ data-on-select="keep"
637
+ style="${this.offsetWidth ? `width: ${this.offsetWidth}px` : ""}"
638
+ @pointerdown=${this.handleSuggestionPointerDown}
414
639
  >
415
640
  <sonic-queue
416
641
  dataProvider="${this.queueDataProvider}"
417
642
  filteredFields=${this.filteredFields}
418
643
  dataProviderExpression="${this.dataProviderExpression}"
419
- dataFilterProvider="${this.searchDataProvider}"
420
- key="${this.key}"
421
- .templates=${this.templateList.concat(this.templatePartsList)}
422
- displayContents
423
- >
424
- </sonic-queue>
425
- <sonic-queue
426
- noLazyload
427
- loader="no-loader"
428
- dataProvider="${this.initQueueDataProvider}"
429
- filteredFields=${this.filteredFields}
430
- dataProviderExpression="${this.dataProviderExpression}"
431
- dataFilterProvider="${this.initSearchDataProvider}"
644
+ dataFilterProvider="${this.queueFilterDataProvider}"
645
+ serviceURL=${ifDefined(this.serviceURL || undefined)}
432
646
  key="${this.key}"
647
+ .templates=${templates}
648
+ .items=${this.items}
433
649
  displayContents
434
650
  >
435
651
  </sonic-queue>
436
652
  </sonic-menu>
437
653
  </sonic-pop>
654
+ <!-- Lookup valeur initiale : hors menu, jamais displayContents (sinon visible). -->
655
+ <sonic-queue
656
+ class="init-queue"
657
+ hidden
658
+ noLazyload
659
+ loader="no-loader"
660
+ dataProvider="${this.initQueueDataProvider}"
661
+ filteredFields=${this.filteredFields}
662
+ dataProviderExpression="${this.dataProviderExpression}"
663
+ dataFilterProvider="${this.initSearchDataProvider}"
664
+ serviceURL=${ifDefined(this.serviceURL || undefined)}
665
+ key="${this.key}"
666
+ .templates=${templates}
667
+ .items=${this.items}
668
+ >
669
+ </sonic-queue>
438
670
  `;
439
671
  }
440
672
  }