@supersoniks/concorde 5.0.0 → 5.0.2

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 (314) hide show
  1. package/.gitlab-ci.yml +0 -13
  2. package/build-infos.json +1 -1
  3. package/concorde-core.bundle.js +606 -689
  4. package/concorde-core.es.js +4511 -4329
  5. package/dist/concorde-core.bundle.js +606 -689
  6. package/dist/concorde-core.es.js +4511 -4329
  7. package/gitlab/job_tests.sh +25 -12
  8. package/index.html +3 -3
  9. package/package.json +16 -1
  10. package/scripts/post-build-docs.js +0 -0
  11. package/scripts/post-build.mjs +0 -0
  12. package/src/core/components/functional/fetch/fetch.md +0 -0
  13. package/src/core/components/functional/if/if.md +0 -0
  14. package/src/core/components/functional/list/list.demo.ts +0 -0
  15. package/src/core/components/functional/list/list.md +0 -0
  16. package/src/core/components/functional/list/list.ts +0 -0
  17. package/src/core/components/functional/router/router.md +0 -0
  18. package/src/core/components/functional/router/router.ts +0 -0
  19. package/src/core/components/functional/sdui/sdui.md +0 -0
  20. package/src/core/components/functional/states/states.md +0 -0
  21. package/src/core/components/functional/submit/submit.md +0 -0
  22. package/src/core/components/ui/_css/rounded.ts +36 -0
  23. package/src/core/components/ui/_css/size.ts +0 -0
  24. package/src/core/components/ui/_css/type.ts +49 -0
  25. package/src/core/components/ui/_i18n/ui-aria-labels.ts +104 -0
  26. package/src/core/components/ui/alert/alert.md +128 -72
  27. package/src/core/components/ui/alert/alert.spec.ts +145 -0
  28. package/src/core/components/ui/alert/alert.ts +86 -91
  29. package/src/core/components/ui/alert-messages/alert-messages.ts +1 -1
  30. package/src/core/components/ui/badge/badge.md +126 -78
  31. package/src/core/components/ui/badge/badge.ts +29 -81
  32. package/src/core/components/ui/button/button.md +1 -0
  33. package/src/core/components/ui/button/button.ts +11 -8
  34. package/src/core/components/ui/captcha/altchaStyles.ts +0 -0
  35. package/src/core/components/ui/captcha/captcha.md +0 -0
  36. package/src/core/components/ui/card/card-header.ts +7 -2
  37. package/src/core/components/ui/card/card.md +120 -65
  38. package/src/core/components/ui/divider/divider.ts +4 -9
  39. package/src/core/components/ui/form/fieldset/legend-description.ts +1 -1
  40. package/src/core/components/ui/form/fieldset/legend.ts +1 -1
  41. package/src/core/components/ui/form/input/input.md +1 -1
  42. package/src/core/components/ui/form/input/input.ts +19 -15
  43. package/src/core/components/ui/form/radio/radio.md +0 -0
  44. package/src/core/components/ui/form/select/select.md +0 -0
  45. package/src/core/components/ui/form/switch/switch.md +0 -0
  46. package/src/core/components/ui/form/textarea/textarea.md +1 -1
  47. package/src/core/components/ui/image/image.md +147 -53
  48. package/src/core/components/ui/image/image.ts +10 -44
  49. package/src/core/components/ui/link/link.md +2 -2
  50. package/src/core/components/ui/link/link.ts +42 -37
  51. package/src/core/components/ui/loader/loader.md +0 -0
  52. package/src/core/components/ui/menu/menu.md +0 -0
  53. package/src/core/components/ui/modal/modal-close.ts +8 -12
  54. package/src/core/components/ui/modal/modal.md +0 -0
  55. package/src/core/components/ui/modal/modal.ts +18 -18
  56. package/src/core/components/ui/pop/pop.md +105 -1
  57. package/src/core/components/ui/pop/pop.ts +143 -148
  58. package/src/core/components/ui/table/table-caption.ts +0 -0
  59. package/src/core/components/ui/table/table.md +0 -0
  60. package/src/core/components/ui/table/table.ts +2 -8
  61. package/src/core/components/ui/theme/theme.ts +1 -1
  62. package/src/core/components/ui/toast/dismissed.ts +14 -0
  63. package/src/core/components/ui/toast/toast-item.ts +139 -91
  64. package/src/core/components/ui/toast/toast.ts +4 -7
  65. package/src/core/components/ui/tooltip/tooltip.ts +176 -154
  66. package/src/core/utils/LocationHandler.ts +12 -5
  67. package/src/core/utils/dismissed.ts +22 -0
  68. package/src/core/utils/positioning.spec.ts +208 -0
  69. package/src/core/utils/positioning.ts +183 -0
  70. package/src/core/utils/route.ts +0 -0
  71. package/src/directives.ts +2 -3
  72. package/src/docs/_core-concept/subscriber.md +0 -0
  73. package/src/docs/_getting-started/concorde-outside.md +3 -3
  74. package/src/docs/_getting-started/my-first-subscriber.md +0 -0
  75. package/src/docs/_getting-started/pubsub.md +0 -0
  76. package/src/docs/_getting-started/start.md +0 -0
  77. package/src/docs/code.ts +3 -1
  78. package/src/docs/example/users.ts +0 -0
  79. package/src/docs/search/docs-search.json +651 -21
  80. package/src/docs/search/page.ts +0 -0
  81. package/src/docs/tailwind/css/tailwind.css +13 -6
  82. package/src/tsconfig.json +45 -0
  83. package/src/tsconfig.tsbuildinfo +1 -1
  84. package/tailwind.config.js +50 -3
  85. package/vitest.setup.ts +49 -13
  86. package/.github/workflows/ci.yml +0 -21
  87. package/.github-export-ignore +0 -30
  88. package/CONTRIBUTING.md +0 -59
  89. package/DEV.md +0 -54
  90. package/LICENSE +0 -21
  91. package/OPEN_SOURCE_CHECKPOINT.md +0 -133
  92. package/concorde-3d.bundle.js +0 -3960
  93. package/concorde-3d.es.js +0 -33433
  94. package/concorde-hugging-face-infer.bundle.js +0 -32
  95. package/concorde-hugging-face-infer.es.js +0 -2952
  96. package/concorde-shader.bundle.js +0 -116
  97. package/concorde-shader.es.js +0 -8353
  98. package/concorde-webgpu.bundle.js +0 -307
  99. package/concorde-webgpu.es.js +0 -8627
  100. package/docs/altcha-widget.js +0 -2662
  101. package/docs/assets/index-BZiZXMU-.css +0 -1
  102. package/docs/assets/index-FmK4JaIM.js +0 -11992
  103. package/docs/crawl/core/components/functional/date/date.html +0 -190
  104. package/docs/crawl/core/components/functional/fetch/fetch.html +0 -109
  105. package/docs/crawl/core/components/functional/if/if.html +0 -27
  106. package/docs/crawl/core/components/functional/jsonata/jsonata.html +0 -79
  107. package/docs/crawl/core/components/functional/list/list.html +0 -70
  108. package/docs/crawl/core/components/functional/mix/mix.html +0 -45
  109. package/docs/crawl/core/components/functional/queue/queue.html +0 -96
  110. package/docs/crawl/core/components/functional/router/router.html +0 -106
  111. package/docs/crawl/core/components/functional/sdui/sdui.html +0 -269
  112. package/docs/crawl/core/components/functional/states/states.html +0 -66
  113. package/docs/crawl/core/components/functional/submit/submit.html +0 -160
  114. package/docs/crawl/core/components/functional/subscriber/subscriber.html +0 -67
  115. package/docs/crawl/core/components/functional/value/value.html +0 -51
  116. package/docs/crawl/core/components/ui/alert/alert.html +0 -110
  117. package/docs/crawl/core/components/ui/alert-messages/alert-messages.html +0 -23
  118. package/docs/crawl/core/components/ui/badge/badge.html +0 -119
  119. package/docs/crawl/core/components/ui/button/button.html +0 -149
  120. package/docs/crawl/core/components/ui/captcha/captcha.html +0 -30
  121. package/docs/crawl/core/components/ui/card/card.html +0 -99
  122. package/docs/crawl/core/components/ui/divider/divider.html +0 -58
  123. package/docs/crawl/core/components/ui/form/checkbox/checkbox.html +0 -73
  124. package/docs/crawl/core/components/ui/form/fieldset/fieldset.html +0 -135
  125. package/docs/crawl/core/components/ui/form/form-actions/form-actions.html +0 -79
  126. package/docs/crawl/core/components/ui/form/form-layout/form-layout.html +0 -62
  127. package/docs/crawl/core/components/ui/form/input/input.html +0 -109
  128. package/docs/crawl/core/components/ui/form/input-autocomplete/input-autocomplete.html +0 -106
  129. package/docs/crawl/core/components/ui/form/radio/radio.html +0 -56
  130. package/docs/crawl/core/components/ui/form/select/select.html +0 -73
  131. package/docs/crawl/core/components/ui/form/switch/switch.html +0 -56
  132. package/docs/crawl/core/components/ui/form/textarea/textarea.html +0 -59
  133. package/docs/crawl/core/components/ui/group/group.html +0 -82
  134. package/docs/crawl/core/components/ui/icon/icon.html +0 -133
  135. package/docs/crawl/core/components/ui/image/image.html +0 -110
  136. package/docs/crawl/core/components/ui/link/link.html +0 -42
  137. package/docs/crawl/core/components/ui/loader/loader.html +0 -50
  138. package/docs/crawl/core/components/ui/menu/menu.html +0 -322
  139. package/docs/crawl/core/components/ui/modal/modal.html +0 -117
  140. package/docs/crawl/core/components/ui/pop/pop.html +0 -102
  141. package/docs/crawl/core/components/ui/progress/progress.html +0 -58
  142. package/docs/crawl/core/components/ui/table/table.html +0 -413
  143. package/docs/crawl/core/components/ui/toast/toast.html +0 -122
  144. package/docs/crawl/core/components/ui/tooltip/tooltip.html +0 -79
  145. package/docs/crawl/docs/_core-concept/dataFlow.html +0 -177
  146. package/docs/crawl/docs/_core-concept/overview.html +0 -66
  147. package/docs/crawl/docs/_core-concept/subscriber.html +0 -75
  148. package/docs/crawl/docs/_decorators/ancestor-attribute.html +0 -116
  149. package/docs/crawl/docs/_decorators/auto-subscribe.html +0 -202
  150. package/docs/crawl/docs/_decorators/bind.html +0 -141
  151. package/docs/crawl/docs/_decorators/get.html +0 -149
  152. package/docs/crawl/docs/_decorators/handle.html +0 -165
  153. package/docs/crawl/docs/_decorators/on-assign.html +0 -355
  154. package/docs/crawl/docs/_decorators/patch.html +0 -78
  155. package/docs/crawl/docs/_decorators/post.html +0 -139
  156. package/docs/crawl/docs/_decorators/publish.html +0 -61
  157. package/docs/crawl/docs/_decorators/put.html +0 -78
  158. package/docs/crawl/docs/_decorators/subscribe.html +0 -109
  159. package/docs/crawl/docs/_decorators/wait-for-ancestors.html +0 -137
  160. package/docs/crawl/docs/_directives/sub.html +0 -93
  161. package/docs/crawl/docs/_getting-started/ai-agents.html +0 -173
  162. package/docs/crawl/docs/_getting-started/concorde-manual-install.html +0 -100
  163. package/docs/crawl/docs/_getting-started/concorde-outside.html +0 -58
  164. package/docs/crawl/docs/_getting-started/create-a-component.html +0 -111
  165. package/docs/crawl/docs/_getting-started/my-first-component.html +0 -189
  166. package/docs/crawl/docs/_getting-started/my-first-subscriber.html +0 -110
  167. package/docs/crawl/docs/_getting-started/pubsub.html +0 -62
  168. package/docs/crawl/docs/_getting-started/start.html +0 -76
  169. package/docs/crawl/docs/_getting-started/theming.html +0 -85
  170. package/docs/crawl/docs/_misc/api-configuration.html +0 -149
  171. package/docs/crawl/docs/_misc/dataProviderKey.html +0 -135
  172. package/docs/crawl/docs/_misc/docs-mock-api.html +0 -158
  173. package/docs/crawl/docs/_misc/dynamic-path.html +0 -180
  174. package/docs/crawl/docs/_misc/endpoint.html +0 -50
  175. package/docs/crawl/docs/_misc/formelement-subscriber-removal.html +0 -138
  176. package/docs/crawl/docs/_misc/html-integration.html +0 -48
  177. package/docs/crawl/hubs/decorators.html +0 -547
  178. package/docs/crawl/hubs/form-components.html +0 -357
  179. package/docs/crawl/hubs/remote-data.html +0 -452
  180. package/docs/crawl/index.html +0 -101
  181. package/docs/css/docs.css +0 -0
  182. package/docs/docs-mock-api-sw.js +0 -720
  183. package/docs/docs-mock-api-sw.js.map +0 -7
  184. package/docs/fonts/ClashGrotesk-Bold.eot +0 -0
  185. package/docs/fonts/ClashGrotesk-Bold.ttf +0 -0
  186. package/docs/fonts/ClashGrotesk-Bold.woff +0 -0
  187. package/docs/fonts/ClashGrotesk-Bold.woff2 +0 -0
  188. package/docs/fonts/ClashGrotesk-Extralight.eot +0 -0
  189. package/docs/fonts/ClashGrotesk-Extralight.ttf +0 -0
  190. package/docs/fonts/ClashGrotesk-Extralight.woff +0 -0
  191. package/docs/fonts/ClashGrotesk-Extralight.woff2 +0 -0
  192. package/docs/fonts/ClashGrotesk-Light.eot +0 -0
  193. package/docs/fonts/ClashGrotesk-Light.ttf +0 -0
  194. package/docs/fonts/ClashGrotesk-Light.woff +0 -0
  195. package/docs/fonts/ClashGrotesk-Light.woff2 +0 -0
  196. package/docs/fonts/ClashGrotesk-Medium.eot +0 -0
  197. package/docs/fonts/ClashGrotesk-Medium.ttf +0 -0
  198. package/docs/fonts/ClashGrotesk-Medium.woff +0 -0
  199. package/docs/fonts/ClashGrotesk-Medium.woff2 +0 -0
  200. package/docs/fonts/ClashGrotesk-Regular.eot +0 -0
  201. package/docs/fonts/ClashGrotesk-Regular.ttf +0 -0
  202. package/docs/fonts/ClashGrotesk-Regular.woff +0 -0
  203. package/docs/fonts/ClashGrotesk-Regular.woff2 +0 -0
  204. package/docs/fonts/ClashGrotesk-Semibold.eot +0 -0
  205. package/docs/fonts/ClashGrotesk-Semibold.ttf +0 -0
  206. package/docs/fonts/ClashGrotesk-Semibold.woff +0 -0
  207. package/docs/fonts/ClashGrotesk-Semibold.woff2 +0 -0
  208. package/docs/fonts/ClashGrotesk-Variable.eot +0 -0
  209. package/docs/fonts/ClashGrotesk-Variable.ttf +0 -0
  210. package/docs/fonts/ClashGrotesk-Variable.woff +0 -0
  211. package/docs/fonts/ClashGrotesk-Variable.woff2 +0 -0
  212. package/docs/img/concorde-icon.svg +0 -5
  213. package/docs/img/concorde-logo.svg +0 -1
  214. package/docs/img/concorde.png +0 -0
  215. package/docs/img/concorde_def.png +0 -0
  216. package/docs/img/concorde_seuil.png.webp +0 -0
  217. package/docs/img/concorde_seuil_invert.png +0 -0
  218. package/docs/img/paul_metrand.jpg +0 -0
  219. package/docs/img/paul_metrand_xs.jpg +0 -0
  220. package/docs/index.html +0 -93
  221. package/docs/llms-full.txt +0 -5787
  222. package/docs/llms.txt +0 -104
  223. package/docs/robots.txt +0 -16
  224. package/docs/sitemap.xml +0 -237
  225. package/docs/src/core/components/functional/date/date.md +0 -290
  226. package/docs/src/core/components/functional/fetch/fetch.md +0 -125
  227. package/docs/src/core/components/functional/if/if.md +0 -9
  228. package/docs/src/core/components/functional/jsonata/jsonata.md +0 -56
  229. package/docs/src/core/components/functional/list/list.md +0 -65
  230. package/docs/src/core/components/functional/mix/mix.md +0 -41
  231. package/docs/src/core/components/functional/queue/queue.md +0 -72
  232. package/docs/src/core/components/functional/router/router.md +0 -94
  233. package/docs/src/core/components/functional/sdui/default-library.json +0 -108
  234. package/docs/src/core/components/functional/sdui/example.json +0 -99
  235. package/docs/src/core/components/functional/sdui/sdui.md +0 -356
  236. package/docs/src/core/components/functional/states/states.md +0 -87
  237. package/docs/src/core/components/functional/submit/submit.md +0 -114
  238. package/docs/src/core/components/functional/subscriber/subscriber.md +0 -91
  239. package/docs/src/core/components/functional/value/value.md +0 -35
  240. package/docs/src/core/components/ui/alert/alert.md +0 -121
  241. package/docs/src/core/components/ui/alert-messages/alert-messages.md +0 -0
  242. package/docs/src/core/components/ui/badge/badge.md +0 -127
  243. package/docs/src/core/components/ui/button/button.md +0 -182
  244. package/docs/src/core/components/ui/captcha/captcha.md +0 -12
  245. package/docs/src/core/components/ui/card/card.md +0 -97
  246. package/docs/src/core/components/ui/divider/divider.md +0 -35
  247. package/docs/src/core/components/ui/form/checkbox/checkbox.md +0 -77
  248. package/docs/src/core/components/ui/form/fieldset/fieldset.md +0 -129
  249. package/docs/src/core/components/ui/form/form-actions/form-actions.md +0 -77
  250. package/docs/src/core/components/ui/form/form-layout/form-layout.md +0 -44
  251. package/docs/src/core/components/ui/form/input/input.md +0 -142
  252. package/docs/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +0 -133
  253. package/docs/src/core/components/ui/form/radio/radio.md +0 -57
  254. package/docs/src/core/components/ui/form/select/select.md +0 -71
  255. package/docs/src/core/components/ui/form/switch/switch.md +0 -57
  256. package/docs/src/core/components/ui/form/textarea/textarea.md +0 -65
  257. package/docs/src/core/components/ui/group/group.md +0 -75
  258. package/docs/src/core/components/ui/icon/icon.md +0 -125
  259. package/docs/src/core/components/ui/icon/icons.json +0 -1
  260. package/docs/src/core/components/ui/image/image.md +0 -107
  261. package/docs/src/core/components/ui/link/link.md +0 -43
  262. package/docs/src/core/components/ui/loader/loader.md +0 -55
  263. package/docs/src/core/components/ui/menu/menu.md +0 -329
  264. package/docs/src/core/components/ui/modal/modal.md +0 -119
  265. package/docs/src/core/components/ui/pop/pop.md +0 -96
  266. package/docs/src/core/components/ui/progress/progress.md +0 -63
  267. package/docs/src/core/components/ui/table/table.md +0 -455
  268. package/docs/src/core/components/ui/toast/toast.md +0 -166
  269. package/docs/src/core/components/ui/tooltip/tooltip.md +0 -82
  270. package/docs/src/docs/_core-concept/dataFlow.md +0 -88
  271. package/docs/src/docs/_core-concept/overview.md +0 -57
  272. package/docs/src/docs/_core-concept/subscriber.md +0 -75
  273. package/docs/src/docs/_decorators/ancestor-attribute.md +0 -127
  274. package/docs/src/docs/_decorators/auto-subscribe.md +0 -202
  275. package/docs/src/docs/_decorators/bind.md +0 -167
  276. package/docs/src/docs/_decorators/get.md +0 -162
  277. package/docs/src/docs/_decorators/handle.md +0 -175
  278. package/docs/src/docs/_decorators/on-assign.md +0 -390
  279. package/docs/src/docs/_decorators/patch.md +0 -70
  280. package/docs/src/docs/_decorators/post.md +0 -146
  281. package/docs/src/docs/_decorators/publish.md +0 -55
  282. package/docs/src/docs/_decorators/put.md +0 -70
  283. package/docs/src/docs/_decorators/subscribe.md +0 -100
  284. package/docs/src/docs/_decorators/wait-for-ancestors.md +0 -163
  285. package/docs/src/docs/_directives/sub.md +0 -91
  286. package/docs/src/docs/_getting-started/ai-agents.md +0 -76
  287. package/docs/src/docs/_getting-started/concorde-manual-install.md +0 -133
  288. package/docs/src/docs/_getting-started/concorde-outside.md +0 -33
  289. package/docs/src/docs/_getting-started/create-a-component.md +0 -139
  290. package/docs/src/docs/_getting-started/my-first-component.md +0 -236
  291. package/docs/src/docs/_getting-started/my-first-subscriber.md +0 -120
  292. package/docs/src/docs/_getting-started/pubsub.md +0 -37
  293. package/docs/src/docs/_getting-started/start.md +0 -47
  294. package/docs/src/docs/_getting-started/theming.md +0 -91
  295. package/docs/src/docs/_misc/api-configuration.md +0 -81
  296. package/docs/src/docs/_misc/dataProviderKey.md +0 -168
  297. package/docs/src/docs/_misc/docs-mock-api.md +0 -63
  298. package/docs/src/docs/_misc/dynamic-path.md +0 -71
  299. package/docs/src/docs/_misc/endpoint.md +0 -45
  300. package/docs/src/docs/_misc/formelement-subscriber-removal.md +0 -115
  301. package/docs/src/docs/_misc/html-integration.md +0 -15
  302. package/docs/src/docs/search/docs-search.json +0 -9512
  303. package/docs/src/tag-list.json +0 -1
  304. package/docs/src/tsconfig-model.json +0 -23
  305. package/docs/src/tsconfig.declarations.json +0 -22
  306. package/docs/src/tsconfig.emit.json +0 -23
  307. package/docs/src/tsconfig.json +0 -858
  308. package/docs/svg/regular/plane.svg +0 -1
  309. package/docs/svg/solid/plane.svg +0 -1
  310. package/gitlab/job_github_mirror.sh +0 -39
  311. package/scripts/github-export.mjs +0 -150
  312. package/src/css.d.ts +0 -19
  313. package/src/tsconfig.declarations.json +0 -22
  314. package/src/tsconfig.emit.json +0 -23
@@ -1,221 +1,243 @@
1
1
  import { html, LitElement, css, PropertyValues } from "lit";
2
- import { customElement, property } from "lit/decorators.js";
2
+ import { customElement, property, query } from "lit/decorators.js";
3
+ import {
4
+ computeFloatingPosition,
5
+ FloatingPlacement,
6
+ getFloatingBounds,
7
+ parsePlacement,
8
+ startFrameLoop,
9
+ supportsPopover,
10
+ } from "@supersoniks/concorde/core/utils/positioning";
11
+
3
12
  const tagName = "sonic-tooltip";
13
+
14
+ type TooltipPlacement = FloatingPlacement;
15
+
16
+ const GAP = 4;
17
+
4
18
  @customElement(tagName)
5
19
  export class Tooltip extends LitElement {
20
+ static readonly supportsPopover = supportsPopover;
21
+
6
22
  static styles = [
7
23
  css`
8
24
  :host {
9
- position: relative;
10
25
  display: inline-flex;
11
26
  align-items: center;
12
27
  text-align: center;
13
28
  --sc-tooltip-fw: var(--sc-font-weight-base, 400);
14
- --sc-tooltip-position: calc(100% + 0.25rem);
15
29
  }
16
30
 
17
- .tooltip:before {
18
- position: absolute;
19
- content: attr(data-tooltip-text);
20
- font-size: 0.85rem;
21
- display: block;
22
- opacity: 0;
31
+ .tooltip-bubble {
32
+ position: fixed;
33
+ inset: auto;
34
+ margin: 0;
35
+ border: none;
23
36
  pointer-events: none;
37
+ font-size: 0.85rem;
24
38
  background: var(--sc-base-content, #000);
25
39
  padding: 0.32rem 0.55rem;
26
40
  border-radius: var(--sc-rounded);
27
41
  color: var(--sc-base, #fff);
28
- z-index: 999;
29
42
  line-height: 1.1;
30
43
  width: max-content;
31
44
  max-width: 18rem;
32
45
  white-space: pre-line;
33
46
  font-weight: var(--sc-tooltip-fw);
34
- transition: 0.2s;
35
- scale: 0.9;
36
- will-change: opacity, transform;
47
+ opacity: 0;
48
+ transition:
49
+ opacity 0.2s,
50
+ display 0.2s allow-discrete,
51
+ overlay 0.2s allow-discrete;
37
52
  }
38
53
 
39
- :host(:focus-visible) .tooltip:before,
40
- :host(.keyboard-focus) .tooltip:before,
41
- .tooltip:hover:before {
54
+ .tooltip-bubble:popover-open {
42
55
  opacity: 1;
43
- scale: 1;
44
56
  }
45
57
 
46
- .disabled:before {
47
- opacity: 0 !important;
48
- scale: 0.9 !important;
49
- }
50
-
51
- :host(:not([placement])) .tooltip:before,
52
- :host([placement="top"]) .tooltip:before {
53
- bottom: var(--sc-tooltip-position);
54
- left: 50%;
55
- transform: translateX(-50%);
56
- }
57
-
58
- :host([placement="top-end"]) .tooltip:before {
59
- bottom: var(--sc-tooltip-position);
60
- right: 0;
61
- }
62
- :host([placement="top-start"]) .tooltip:before {
63
- bottom: var(--sc-tooltip-position);
64
- left: 0;
65
- }
66
-
67
- :host([placement="bottom"]) .tooltip:before {
68
- top: var(--sc-tooltip-position);
69
- left: 50%;
70
- transform: translateX(-50%);
71
- }
72
-
73
- :host([placement="bottom-start"]) .tooltip:before {
74
- top: var(--sc-tooltip-position);
75
- left: 0;
76
- }
77
- :host([placement="bottom-end"]) .tooltip:before {
78
- top: var(--sc-tooltip-position);
79
- right: 0;
80
- }
81
-
82
- :host([placement="left"]) .tooltip:before {
83
- top: 50%;
84
- right: var(--sc-tooltip-position);
85
- transform: translateY(-50%);
86
- }
87
-
88
- :host([placement="left-start"]) .tooltip:before {
89
- top: 0;
90
- right: var(--sc-tooltip-position);
91
- }
92
-
93
- :host([placement="left-end"]) .tooltip:before {
94
- bottom: 0;
95
- right: var(--sc-tooltip-position);
96
- }
97
-
98
- :host([placement="right"]) .tooltip:before {
99
- top: 50%;
100
- transform: translateY(-50%);
101
- left: var(--sc-tooltip-position);
58
+ @starting-style {
59
+ .tooltip-bubble:popover-open {
60
+ opacity: 0;
61
+ }
102
62
  }
103
63
 
104
- :host([placement="right-start"]) .tooltip:before {
105
- top: 0;
106
- left: var(--sc-tooltip-position);
64
+ .tooltip-bubble.disabled {
65
+ display: none !important;
107
66
  }
108
67
 
109
- :host([placement="right-end"]) .tooltip:before {
110
- bottom: 0;
111
- left: var(--sc-tooltip-position);
68
+ @media (prefers-reduced-motion: reduce) {
69
+ .tooltip-bubble {
70
+ transition: none;
71
+ }
112
72
  }
113
73
  `,
114
74
  ];
115
75
 
116
- @property({ type: String }) label = "";
117
- @property({ type: String, reflect: true }) placement?:
118
- | "top"
119
- | "top-start"
120
- | "top-end"
121
- | "right"
122
- | "right-start"
123
- | "right-end"
124
- | "bottom"
125
- | "bottom-start"
126
- | "bottom-end"
127
- | "left"
128
- | "left-start"
129
- | "left-end";
76
+ @property({ type: String }) label?: string;
77
+ @property({ type: String, reflect: true }) placement?: TooltipPlacement;
130
78
 
131
79
  @property({ type: Boolean }) disabled = false;
132
80
  @property({ type: Boolean }) focusable = false;
133
81
 
134
- private lastKeyWasTab = false;
135
- private boundHandleKeyDown: (e: KeyboardEvent) => void;
136
- private boundHandleMouseDown: () => void;
82
+ @query(".tooltip-bubble") private bubbleEl?: HTMLElement;
137
83
 
138
- constructor() {
139
- super();
140
- this.boundHandleKeyDown = this.handleKeyDown.bind(this);
141
- this.boundHandleMouseDown = this.handleMouseDown.bind(this);
84
+ private stopPositioning?: () => void;
85
+
86
+ private get isEffectivelyDisabled() {
87
+ return this.disabled || !this.formatLabel();
88
+ }
89
+
90
+ private formatLabel() {
91
+ return (this.label ?? "")
92
+ .replaceAll(" ", " ")
93
+ .trim()
94
+ .split("\n")
95
+ .map((line) => line.trim())
96
+ .join("\n");
142
97
  }
143
98
 
144
- private handleKeyDown = (e: KeyboardEvent) => {
145
- // Détecter si Tab ou Shift+Tab a été pressé
146
- if (e.key === "Tab") {
147
- this.lastKeyWasTab = true;
99
+ private computePosition() {
100
+ const bubble = this.bubbleEl;
101
+ if (!bubble) {
102
+ return;
148
103
  }
149
- };
150
104
 
151
- private handleMouseDown = () => {
152
- // Réinitialiser le flag si la souris est utilisée
153
- this.lastKeyWasTab = false;
154
- // Retirer la classe si elle existe (au cas où)
155
- this.classList.remove("keyboard-focus");
105
+ const { side, align } = parsePlacement(this.placement, "top", "center");
106
+
107
+ const { x, y } = computeFloatingPosition({
108
+ anchor: this.getBoundingClientRect(),
109
+ floating: bubble.getBoundingClientRect(),
110
+ side,
111
+ align,
112
+ gap: GAP,
113
+ bounds: getFloatingBounds(this, GAP),
114
+ });
115
+
116
+ Object.assign(bubble.style, {
117
+ left: `${Math.round(x)}px`,
118
+ top: `${Math.round(y)}px`,
119
+ });
120
+ }
121
+
122
+ private showTooltip = () => {
123
+ if (this.isEffectivelyDisabled || !this.bubbleEl) {
124
+ return;
125
+ }
126
+
127
+ if (!this.bubbleEl.matches(":popover-open")) {
128
+ this.bubbleEl.showPopover();
129
+ }
156
130
  };
157
131
 
158
- private handleFocusIn = () => {
159
- // Ajouter la classe seulement si le focus est venu du clavier (Tab)
160
- if (this.lastKeyWasTab) {
161
- this.classList.add("keyboard-focus");
132
+ private hideTooltip = () => {
133
+ if (this.bubbleEl?.matches(":popover-open")) {
134
+ this.bubbleEl.hidePopover();
162
135
  }
163
- this.lastKeyWasTab = false;
164
136
  };
165
137
 
166
- private handleFocusOut = () => {
167
- this.classList.remove("keyboard-focus");
168
- this.lastKeyWasTab = false;
138
+ private handleBeforeToggle = (e: ToggleEvent) => {
139
+ if (e.newState === "open") {
140
+ this.stopPositioning ??= startFrameLoop(() => this.computePosition());
141
+ return;
142
+ }
143
+
144
+ this.endPositioning();
169
145
  };
170
146
 
147
+ private endPositioning() {
148
+ this.stopPositioning?.();
149
+ this.stopPositioning = undefined;
150
+ }
151
+
152
+ private syncTabIndex() {
153
+ if (this.disabled) {
154
+ this.setAttribute("tabindex", "-1");
155
+ } else if (this.focusable) {
156
+ this.setAttribute("tabindex", "0");
157
+ } else {
158
+ this.removeAttribute("tabindex");
159
+ }
160
+ }
161
+
162
+ /** Fallback natif quand Popover API indisponible */
163
+ private syncNativeTitle() {
164
+ if (Tooltip.supportsPopover || this.isEffectivelyDisabled) {
165
+ this.removeAttribute("title");
166
+ return;
167
+ }
168
+
169
+ this.setAttribute("title", this.formatLabel().replaceAll("\n", " "));
170
+ }
171
+
171
172
  connectedCallback() {
172
173
  super.connectedCallback();
173
- this.addEventListener("focusin", this.handleFocusIn);
174
- this.addEventListener("focusout", this.handleFocusOut);
175
- // Écouter les événements au niveau du document avec capture pour détecter Tab
176
- document.addEventListener("keydown", this.boundHandleKeyDown, true);
177
- document.addEventListener("mousedown", this.boundHandleMouseDown, true);
174
+
175
+ if (!Tooltip.supportsPopover) {
176
+ return;
177
+ }
178
+
179
+ this.addEventListener("focusin", this.showTooltip);
180
+ this.addEventListener("focusout", this.hideTooltip);
178
181
  }
179
182
 
180
183
  disconnectedCallback() {
184
+ this.endPositioning();
185
+
186
+ if (Tooltip.supportsPopover) {
187
+ this.removeEventListener("focusin", this.showTooltip);
188
+ this.removeEventListener("focusout", this.hideTooltip);
189
+ }
190
+
181
191
  super.disconnectedCallback();
182
- this.removeEventListener("focusin", this.handleFocusIn);
183
- this.removeEventListener("focusout", this.handleFocusOut);
184
- document.removeEventListener("keydown", this.boundHandleKeyDown, true);
185
- document.removeEventListener("mousedown", this.boundHandleMouseDown, true);
186
192
  }
187
193
 
188
- protected willUpdate(_changedProperties: PropertyValues): void {
189
- if (_changedProperties.has("disabled")) {
190
- const wasDisabledBefore = _changedProperties.get("disabled") === true;
191
- if (this.disabled) {
192
- this.setAttribute("tabindex", "-1");
193
- this.classList.remove("keyboard-focus");
194
- } else if (!this.disabled && wasDisabledBefore) {
195
- if (this.focusable) {
196
- this.setAttribute("tabindex", "0");
197
- } else {
198
- this.removeAttribute("tabindex");
199
- }
200
- }
194
+ protected willUpdate(changedProperties: PropertyValues): void {
195
+ if (
196
+ !this.hasUpdated ||
197
+ changedProperties.has("disabled") ||
198
+ changedProperties.has("focusable")
199
+ ) {
200
+ this.syncTabIndex();
201
201
  }
202
- if (_changedProperties.has("focusable")) {
203
- if (this.focusable && !this.disabled) {
204
- this.setAttribute("tabindex", "0");
205
- } else if (!this.focusable && !this.disabled) {
206
- this.removeAttribute("tabindex");
207
- }
202
+
203
+ if (
204
+ !this.hasUpdated ||
205
+ changedProperties.has("label") ||
206
+ changedProperties.has("disabled")
207
+ ) {
208
+ this.syncNativeTitle();
209
+ }
210
+
211
+ if (changedProperties.has("disabled") && this.disabled) {
212
+ this.hideTooltip();
213
+ }
214
+
215
+ if (changedProperties.has("label") && this.isEffectivelyDisabled) {
216
+ this.hideTooltip();
208
217
  }
209
- super.willUpdate(_changedProperties);
218
+
219
+ super.willUpdate(changedProperties);
210
220
  }
211
221
 
212
222
  render() {
213
- const disabledClass = this.disabled || this.label == "" ? "disabled" : "";
214
- return html`<div
215
- data-tooltip-text="${this.label.trim().replace("&nbsp;", " ")}"
216
- class="tooltip ${disabledClass}"
217
- >
218
- <slot></slot>
219
- </div>`;
223
+ if (!Tooltip.supportsPopover) {
224
+ return html`<slot></slot>`;
225
+ }
226
+
227
+ const disabledClass = this.isEffectivelyDisabled ? "disabled" : "";
228
+ const label = this.formatLabel();
229
+
230
+ return html`
231
+ <slot
232
+ @mouseenter=${this.showTooltip}
233
+ @mouseleave=${this.hideTooltip}
234
+ ></slot>
235
+ <span
236
+ popover="hint"
237
+ class="tooltip-bubble ${disabledClass}"
238
+ @beforetoggle=${this.handleBeforeToggle}
239
+ >${label}</span
240
+ >
241
+ `;
220
242
  }
221
243
  }
@@ -1,13 +1,20 @@
1
1
  import { Route } from "./route";
2
2
 
3
+ export type AutoActiveMode =
4
+ | "strict"
5
+ | "partial"
6
+ | "disabled"
7
+ | "strict-path-only";
8
+
3
9
  declare type Listener = {
4
10
  location: string;
5
11
  };
6
- declare type ActivableLink = HTMLElement & {
12
+
13
+ export type ActivableLink = HTMLElement & {
7
14
  location?: string;
8
15
  href?: Route | string;
9
16
  goBack?: Route | string | null;
10
- autoActive?: "strict" | "partial" | "disabled" | "strict-path-only";
17
+ autoActive?: AutoActiveMode;
11
18
  setAttribute(name: string, value: string): void;
12
19
  removeAttribute(name: string): void;
13
20
  };
@@ -22,7 +29,7 @@ export default class LocationHandler {
22
29
  static listening = false;
23
30
  static prevURL: string = document.location?.href.replace(
24
31
  document.location.origin,
25
- ""
32
+ "",
26
33
  );
27
34
  static listen() {
28
35
  if (!LocationHandler.listening) {
@@ -30,7 +37,7 @@ export default class LocationHandler {
30
37
  }
31
38
  const newURL = document.location?.href.replace(
32
39
  document.location.origin,
33
- ""
40
+ "",
34
41
  );
35
42
  if (LocationHandler.prevURL && LocationHandler.prevURL != newURL) {
36
43
  LocationHandler.prevURL = newURL;
@@ -116,7 +123,7 @@ export default class LocationHandler {
116
123
  prevSp = sp;
117
124
  }
118
125
 
119
- to = "/" + newPathName.join("/") + url.search + (url.hash ? +url.hash : "");
126
+ to = "/" + newPathName.join("/") + url.search + (url.hash ? url.hash : "");
120
127
  if (component.hasAttribute("pushState")) {
121
128
  history.pushState(null, "", to);
122
129
  } else if (component.hasAttribute("replaceState")) {
@@ -0,0 +1,22 @@
1
+ function readDismissed(storageKey: string): Record<string, boolean> {
2
+ try {
3
+ const parsed = JSON.parse(localStorage.getItem(storageKey) || "{}");
4
+ return parsed && typeof parsed === "object" ? parsed : {};
5
+ } catch {
6
+ return {};
7
+ }
8
+ }
9
+
10
+ export function isDismissed(storageKey: string, id: string | number): boolean {
11
+ return !!readDismissed(storageKey)[id];
12
+ }
13
+
14
+ export function markDismissed(storageKey: string, id: string | number): void {
15
+ const dismissed = readDismissed(storageKey);
16
+ dismissed[id] = true;
17
+ try {
18
+ localStorage.setItem(storageKey, JSON.stringify(dismissed));
19
+ } catch {
20
+ // stockage indisponible (mode privé, quota)
21
+ }
22
+ }
@@ -0,0 +1,208 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ computeFloatingPosition,
4
+ FloatingBounds,
5
+ FloatingSide,
6
+ parsePlacement,
7
+ } from "./positioning";
8
+
9
+ type Rect = {
10
+ left: number;
11
+ top: number;
12
+ right: number;
13
+ bottom: number;
14
+ width: number;
15
+ height: number;
16
+ };
17
+ type Size = { width: number; height: number };
18
+
19
+ const rect = (left: number, top: number, width: number, height: number) => ({
20
+ left,
21
+ top,
22
+ width,
23
+ height,
24
+ right: left + width,
25
+ bottom: top + height,
26
+ });
27
+
28
+ /**
29
+ * Ancien Pop.computePosition, étape par étape, sur un contenu simulé dont
30
+ * chaque déplacement appliqué se retrouve tel quel dans la mesure suivante.
31
+ */
32
+ function legacyPopPosition(
33
+ thisRect: Rect,
34
+ size: Size,
35
+ placement: string,
36
+ { minX, minY, maxX, maxY }: FloatingBounds,
37
+ ) {
38
+ const [placementBase, placementSec] = placement.split(" ");
39
+ const x0 = thisRect.left;
40
+ const y0 = thisRect.top;
41
+ let x = x0,
42
+ y = y0;
43
+ const yTop = y0 - size.height;
44
+ const xLeft = x0 - size.width;
45
+ const xRight = x0 + thisRect.width;
46
+ const yBottom = y0 + thisRect.height;
47
+ const xCenter = x0 + (thisRect.width - size.width) * 0.5;
48
+ const yCenter = y0 + (thisRect.height - size.height) * 0.5;
49
+ switch (placementBase) {
50
+ case "bottom":
51
+ y = yBottom;
52
+ if (placementSec == "center") x = xCenter;
53
+ break;
54
+ case "top":
55
+ y = yTop;
56
+ if (placementSec == "center") x = xCenter;
57
+ break;
58
+ case "left":
59
+ x = xLeft;
60
+ if (placementSec == "center") y = yCenter;
61
+ break;
62
+ case "right":
63
+ x = xRight;
64
+ if (placementSec == "center") y = yCenter;
65
+ break;
66
+ }
67
+
68
+ let contentX = x;
69
+ let contentY = y;
70
+ if (contentX < minX && placementBase == "left") x = xRight;
71
+ if (contentY < minY && placementBase == "top") y = yBottom;
72
+ if (contentX + size.width > maxX && placementBase == "right") x = xLeft;
73
+ if (contentY + size.height > maxY && placementBase == "bottom") y = yTop;
74
+
75
+ contentX = x;
76
+ contentY = y;
77
+ if (contentX < minX) contentX = minX;
78
+ if (contentY < minY) contentY = minY;
79
+ if (contentX + size.width > maxX) contentX = maxX - size.width;
80
+ if (contentY + size.height > maxY) contentY = maxY - size.height;
81
+
82
+ return { x: contentX, y: contentY };
83
+ }
84
+
85
+ function popPosition(
86
+ anchor: Rect,
87
+ floating: Size,
88
+ placement: string,
89
+ bounds: FloatingBounds,
90
+ ) {
91
+ const [side, secondary] = placement.split(" ");
92
+ const { x, y } = computeFloatingPosition({
93
+ anchor,
94
+ floating,
95
+ side: side as FloatingSide,
96
+ align: secondary == "center" ? "center" : "start",
97
+ bounds,
98
+ });
99
+ return { x, y };
100
+ }
101
+
102
+ const bounds: FloatingBounds = { minX: 5, minY: 5, maxX: 795, maxY: 595 };
103
+
104
+ describe("computeFloatingPosition", () => {
105
+ describe("reproduit l'ancien positionnement de sonic-pop", () => {
106
+ const placements = [
107
+ "bottom",
108
+ "top",
109
+ "right",
110
+ "left",
111
+ "bottom center",
112
+ "top center",
113
+ "right center",
114
+ "left center",
115
+ "unknown",
116
+ ];
117
+ const anchors = {
118
+ "au centre": rect(380, 280, 40, 30),
119
+ "coin haut gauche": rect(0, 0, 40, 30),
120
+ "coin bas droit": rect(760, 570, 40, 30),
121
+ "bord gauche": rect(2, 300, 40, 30),
122
+ "bord droit": rect(780, 300, 40, 30),
123
+ "bord haut": rect(400, 2, 40, 30),
124
+ "bord bas": rect(400, 590, 40, 30),
125
+ };
126
+ const sizes = {
127
+ petit: { width: 120, height: 80 },
128
+ "plus grand que la zone": { width: 900, height: 700 },
129
+ };
130
+
131
+ for (const placement of placements) {
132
+ for (const [anchorName, anchor] of Object.entries(anchors)) {
133
+ for (const [sizeName, size] of Object.entries(sizes)) {
134
+ it(`${placement} / ${anchorName} / contenu ${sizeName}`, () => {
135
+ expect(popPosition(anchor, size, placement, bounds)).toEqual(
136
+ legacyPopPosition(anchor, size, placement, bounds),
137
+ );
138
+ });
139
+ }
140
+ }
141
+ }
142
+ });
143
+
144
+ describe("placements du tooltip", () => {
145
+ const anchor = rect(380, 280, 40, 30);
146
+ const floating = { width: 100, height: 20 };
147
+ const place = (side: FloatingSide, align?: "start" | "center" | "end") =>
148
+ computeFloatingPosition({
149
+ anchor,
150
+ floating,
151
+ side,
152
+ align,
153
+ gap: 4,
154
+ bounds,
155
+ });
156
+
157
+ it("centre par défaut", () => {
158
+ expect(place("top")).toEqual({ side: "top", x: 350, y: 256 });
159
+ expect(place("right")).toEqual({ side: "right", x: 424, y: 285 });
160
+ });
161
+
162
+ it("aligne start / end", () => {
163
+ expect(place("bottom", "start")).toMatchObject({ x: 380, y: 314 });
164
+ expect(place("bottom", "end")).toMatchObject({ x: 320, y: 314 });
165
+ expect(place("left", "end")).toMatchObject({ x: 276, y: 290 });
166
+ });
167
+
168
+ it("bascule sur le côté opposé quand il déborde", () => {
169
+ const nearTop = rect(380, 10, 40, 30);
170
+ expect(
171
+ computeFloatingPosition({
172
+ anchor: nearTop,
173
+ floating,
174
+ side: "top",
175
+ gap: 4,
176
+ bounds,
177
+ }),
178
+ ).toEqual({ side: "bottom", x: 350, y: 44 });
179
+ });
180
+ });
181
+ });
182
+
183
+ describe("parsePlacement", () => {
184
+ it("lit côté et alignement", () => {
185
+ expect(parsePlacement("left-end", "bottom", "start")).toEqual({
186
+ side: "left",
187
+ align: "end",
188
+ });
189
+ });
190
+
191
+ it("applique l'alignement par défaut quand il est absent", () => {
192
+ expect(parsePlacement("top", "bottom", "start")).toEqual({
193
+ side: "top",
194
+ align: "start",
195
+ });
196
+ });
197
+
198
+ it("retombe sur les défauts pour une valeur inconnue", () => {
199
+ expect(parsePlacement("foo-bar", "top", "center")).toEqual({
200
+ side: "top",
201
+ align: "center",
202
+ });
203
+ expect(parsePlacement(undefined, "top", "center")).toEqual({
204
+ side: "top",
205
+ align: "center",
206
+ });
207
+ });
208
+ });