@fusion.dev/helix-headless 0.145.0 → 0.147.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/custom-elements.json +12286 -4281
  2. package/dist/accordion/item/accordion-item.d.ts +51 -18
  3. package/dist/accordion/item/accordion-item.js +45 -13
  4. package/dist/accordion/root/accordion-root.d.ts +16 -7
  5. package/dist/accordion/root/accordion-root.js +3 -3
  6. package/dist/announcement/root/announcement-root.d.ts +16 -6
  7. package/dist/announcement/root/announcement-root.js +37 -7
  8. package/dist/app-header/root/app-header-root.d.ts +7 -2
  9. package/dist/app-header/root/app-header-root.js +9 -1
  10. package/dist/app-header/sectioning.d.ts +3 -1
  11. package/dist/app-header/sectioning.js +3 -2
  12. package/dist/area-chart/root/area-chart-root.d.ts +18 -1
  13. package/dist/area-chart/root/area-chart-root.js +11 -3
  14. package/dist/avatar/group/avatar-group.d.ts +20 -14
  15. package/dist/avatar/group/avatar-group.js +17 -19
  16. package/dist/avatar/root/avatar-root.d.ts +31 -1
  17. package/dist/avatar/root/avatar-root.js +11 -1
  18. package/dist/badge/root/badge-root.d.ts +60 -7
  19. package/dist/badge/root/badge-root.js +89 -6
  20. package/dist/bar-chart/root/bar-chart-root.d.ts +18 -1
  21. package/dist/bar-chart/root/bar-chart-root.js +48 -20
  22. package/dist/bar-chart/stack.d.ts +25 -0
  23. package/dist/bar-chart/stack.js +27 -0
  24. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +14 -0
  25. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +12 -2
  26. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +25 -0
  27. package/dist/breadcrumbs/root/breadcrumbs-root.js +51 -4
  28. package/dist/button/root/button-root.d.ts +50 -38
  29. package/dist/button/root/button-root.js +42 -53
  30. package/dist/button/submitter.d.ts +27 -0
  31. package/dist/button/submitter.js +25 -0
  32. package/dist/button-group/root/button-group-root.d.ts +2 -1
  33. package/dist/button-group/root/button-group-root.js +5 -1
  34. package/dist/canvas/node/canvas-node.d.ts +4 -3
  35. package/dist/canvas/node/canvas-node.js +3 -2
  36. package/dist/canvas/root/canvas-root.js +2 -2
  37. package/dist/card/root/card-root.d.ts +36 -4
  38. package/dist/card/root/card-root.js +29 -10
  39. package/dist/carousel/root/carousel-root.d.ts +43 -4
  40. package/dist/carousel/root/carousel-root.js +40 -12
  41. package/dist/carousel/stops.d.ts +20 -0
  42. package/dist/carousel/stops.js +30 -0
  43. package/dist/chat/message/chat-message.js +5 -4
  44. package/dist/chat/naming.d.ts +5 -1
  45. package/dist/chat/naming.js +4 -2
  46. package/dist/chat/root/chat-root.d.ts +40 -4
  47. package/dist/chat/root/chat-root.js +32 -6
  48. package/dist/checkbox/root/checkbox-root.d.ts +20 -0
  49. package/dist/checkbox/root/checkbox-root.js +18 -7
  50. package/dist/checkbox-group/root/checkbox-group-root.d.ts +8 -2
  51. package/dist/checkbox-group/root/checkbox-group-root.js +14 -11
  52. package/dist/checkbox-group/select-all.d.ts +21 -0
  53. package/dist/checkbox-group/select-all.js +7 -0
  54. package/dist/code-block/highlight.d.ts +15 -0
  55. package/dist/code-block/highlight.js +4 -0
  56. package/dist/code-block/root/code-block-root.d.ts +79 -10
  57. package/dist/code-block/root/code-block-root.js +74 -5
  58. package/dist/color-input/root/color-input-root.d.ts +14 -0
  59. package/dist/color-input/root/color-input-root.js +17 -7
  60. package/dist/color-picker/channels.js +1 -2
  61. package/dist/color-picker/root/color-picker-root.d.ts +53 -16
  62. package/dist/color-picker/root/color-picker-root.js +155 -46
  63. package/dist/color-picker/wheel.d.ts +17 -0
  64. package/dist/color-picker/wheel.js +6 -0
  65. package/dist/combobox/root/combobox-root.d.ts +56 -0
  66. package/dist/combobox/root/combobox-root.js +92 -15
  67. package/dist/command/root/command-root.d.ts +19 -2
  68. package/dist/command/root/command-root.js +24 -23
  69. package/dist/copy-button/root/copy-button-root.d.ts +21 -1
  70. package/dist/copy-button/root/copy-button-root.js +16 -4
  71. package/dist/data-table/index.d.ts +1 -1
  72. package/dist/data-table/index.js +1 -1
  73. package/dist/data-table/keys.d.ts +6 -0
  74. package/dist/data-table/keys.js +14 -0
  75. package/dist/data-table/root/data-table-root.d.ts +27 -18
  76. package/dist/data-table/root/data-table-root.js +31 -57
  77. package/dist/data-table/tree.d.ts +16 -1
  78. package/dist/data-table/tree.js +2 -3
  79. package/dist/date-input/root/date-input-root.d.ts +11 -2
  80. package/dist/date-input/root/date-input-root.js +26 -12
  81. package/dist/date-picker/root/date-picker-root.d.ts +14 -9
  82. package/dist/date-picker/root/date-picker-root.js +33 -22
  83. package/dist/description-list/item/description-list-item.d.ts +18 -4
  84. package/dist/description-list/item/description-list-item.js +8 -4
  85. package/dist/dialog/description.js +15 -1
  86. package/dist/dialog/root/dialog-root.d.ts +8 -2
  87. package/dist/dialog/root/dialog-root.js +16 -1
  88. package/dist/divider/root/divider-root.d.ts +3 -0
  89. package/dist/divider/root/divider-root.js +2 -1
  90. package/dist/donut-chart/root/donut-chart-root.d.ts +31 -0
  91. package/dist/donut-chart/root/donut-chart-root.js +55 -1
  92. package/dist/donut-chart/slice-label.d.ts +35 -0
  93. package/dist/donut-chart/slice-label.js +11 -0
  94. package/dist/drawer/reveal.d.ts +8 -0
  95. package/dist/drawer/reveal.js +1 -0
  96. package/dist/drawer/root/drawer-root.d.ts +63 -2
  97. package/dist/drawer/root/drawer-root.js +75 -4
  98. package/dist/drilldown/item/drilldown-item.d.ts +21 -1
  99. package/dist/drilldown/item/drilldown-item.js +40 -5
  100. package/dist/drilldown/root/drilldown-root.d.ts +21 -3
  101. package/dist/drilldown/root/drilldown-root.js +22 -3
  102. package/dist/element-events.generated.js +35 -6
  103. package/dist/empty/root/empty-root.d.ts +34 -2
  104. package/dist/empty/root/empty-root.js +26 -4
  105. package/dist/file-input/root/file-input-root.d.ts +99 -8
  106. package/dist/file-input/root/file-input-root.js +120 -22
  107. package/dist/file-input/screen.d.ts +2 -1
  108. package/dist/file-input/status.d.ts +24 -0
  109. package/dist/file-input/status.js +10 -0
  110. package/dist/filter-input/chip/filter-chip.d.ts +23 -0
  111. package/dist/filter-input/chip/filter-chip.js +11 -2
  112. package/dist/filter-input/menu/filter-menu.d.ts +2 -0
  113. package/dist/filter-input/menu/filter-menu.js +12 -4
  114. package/dist/filter-input/root/filter-input-root.d.ts +38 -6
  115. package/dist/filter-input/root/filter-input-root.js +18 -28
  116. package/dist/filter-input/suggest.d.ts +28 -3
  117. package/dist/filter-input/suggest.js +18 -8
  118. package/dist/format-date/options.d.ts +3 -4
  119. package/dist/format-date/options.js +2 -2
  120. package/dist/format-date/root/format-date-root.d.ts +27 -17
  121. package/dist/format-date/root/format-date-root.js +27 -48
  122. package/dist/heatmap/root/buckets.d.ts +21 -0
  123. package/dist/heatmap/root/buckets.js +34 -0
  124. package/dist/heatmap/root/heatmap-root.d.ts +83 -11
  125. package/dist/heatmap/root/heatmap-root.js +104 -12
  126. package/dist/icon/index.d.ts +1 -1
  127. package/dist/icon/index.js +1 -1
  128. package/dist/icon/registry.d.ts +26 -1
  129. package/dist/icon/registry.js +12 -1
  130. package/dist/icon/root/icon-root.d.ts +25 -1
  131. package/dist/icon/root/icon-root.js +44 -12
  132. package/dist/icon/root/index.d.ts +1 -1
  133. package/dist/icon/root/index.js +1 -1
  134. package/dist/icon/settle.d.ts +7 -0
  135. package/dist/icon/settle.js +10 -0
  136. package/dist/icon-button/root/icon-button-root.d.ts +2 -0
  137. package/dist/image/root/image-root.d.ts +14 -1
  138. package/dist/image/root/image-root.js +7 -2
  139. package/dist/input/root/input-root.d.ts +34 -3
  140. package/dist/input/root/input-root.js +17 -4
  141. package/dist/kbd/keys.d.ts +29 -0
  142. package/dist/kbd/keys.js +52 -0
  143. package/dist/kbd/root/kbd-root.d.ts +52 -1
  144. package/dist/kbd/root/kbd-root.js +40 -47
  145. package/dist/legend/hidden-series.d.ts +10 -2
  146. package/dist/legend/hidden-series.js +5 -2
  147. package/dist/legend/root/legend-root.d.ts +77 -12
  148. package/dist/legend/root/legend-root.js +71 -17
  149. package/dist/lightbox/root/lightbox-root.d.ts +98 -10
  150. package/dist/lightbox/root/lightbox-root.js +153 -18
  151. package/dist/lightbox/zoom-controller.d.ts +59 -0
  152. package/dist/lightbox/zoom-controller.js +128 -0
  153. package/dist/lightbox/zoom.d.ts +24 -0
  154. package/dist/lightbox/zoom.js +18 -0
  155. package/dist/line-chart/root/line-chart-root.d.ts +38 -1
  156. package/dist/line-chart/root/line-chart-root.js +31 -3
  157. package/dist/list/item/list-item.d.ts +2 -0
  158. package/dist/list/item/list-item.js +11 -2
  159. package/dist/list/root/list-root.d.ts +70 -13
  160. package/dist/list/root/list-root.js +91 -13
  161. package/dist/list/selection.d.ts +54 -7
  162. package/dist/list/selection.js +32 -7
  163. package/dist/listbox/root/listbox-root.d.ts +44 -4
  164. package/dist/listbox/root/listbox-root.js +72 -34
  165. package/dist/loader/busy.d.ts +10 -0
  166. package/dist/loader/busy.js +11 -0
  167. package/dist/loader/root/loader-root.d.ts +9 -2
  168. package/dist/loader/root/loader-root.js +23 -3
  169. package/dist/megamenu/hand-over.d.ts +75 -0
  170. package/dist/megamenu/hand-over.js +67 -0
  171. package/dist/megamenu/root/megamenu-root.d.ts +80 -15
  172. package/dist/megamenu/root/megamenu-root.js +76 -19
  173. package/dist/menu/item/menu-item.d.ts +51 -2
  174. package/dist/menu/item/menu-item.js +50 -26
  175. package/dist/menu/root/menu-root.d.ts +17 -0
  176. package/dist/menu/root/menu-root.js +2 -2
  177. package/dist/meter/root/meter-root.d.ts +62 -1
  178. package/dist/meter/root/meter-root.js +38 -16
  179. package/dist/navigation/root/navigation-root.d.ts +2 -1
  180. package/dist/navigation/root/navigation-root.js +2 -2
  181. package/dist/notifications/root/notifications-root.d.ts +20 -4
  182. package/dist/notifications/root/notifications-root.js +30 -8
  183. package/dist/number-input/grain.d.ts +28 -0
  184. package/dist/number-input/grain.js +14 -0
  185. package/dist/number-input/root/number-input-root.d.ts +34 -9
  186. package/dist/number-input/root/number-input-root.js +31 -25
  187. package/dist/object-inspector/root/object-inspector-root.d.ts +88 -33
  188. package/dist/object-inspector/root/object-inspector-root.js +134 -92
  189. package/dist/object-inspector/rows.d.ts +18 -0
  190. package/dist/object-inspector/rows.js +33 -0
  191. package/dist/option/index.d.ts +1 -0
  192. package/dist/option/index.js +1 -0
  193. package/dist/option/root/option-root.d.ts +5 -3
  194. package/dist/option/separator/option-separator.d.ts +30 -0
  195. package/dist/option/separator/option-separator.js +21 -0
  196. package/dist/outline/root/outline-root.d.ts +30 -0
  197. package/dist/outline/root/outline-root.js +17 -13
  198. package/dist/pagination/size/pagination-size.d.ts +6 -0
  199. package/dist/pagination/size/pagination-size.js +5 -3
  200. package/dist/pin-input/code.d.ts +37 -0
  201. package/dist/pin-input/code.js +25 -0
  202. package/dist/pin-input/root/pin-input-root.d.ts +116 -115
  203. package/dist/pin-input/root/pin-input-root.js +163 -155
  204. package/dist/popover/hide-outside.d.ts +14 -0
  205. package/dist/popover/hide-outside.js +34 -0
  206. package/dist/popover/root/popover-root.d.ts +64 -8
  207. package/dist/popover/root/popover-root.js +67 -4
  208. package/dist/progress/root/progress-root.d.ts +47 -0
  209. package/dist/progress/root/progress-root.js +34 -16
  210. package/dist/qr-code/export.d.ts +45 -1
  211. package/dist/qr-code/export.js +28 -0
  212. package/dist/qr-code/name.d.ts +11 -0
  213. package/dist/qr-code/name.js +7 -0
  214. package/dist/qr-code/root/qr-code-root.d.ts +36 -11
  215. package/dist/qr-code/root/qr-code-root.js +58 -11
  216. package/dist/quote/marks.d.ts +8 -0
  217. package/dist/quote/marks.js +5 -0
  218. package/dist/quote/root/quote-root.d.ts +14 -2
  219. package/dist/quote/root/quote-root.js +23 -1
  220. package/dist/radio/group/radio-group.d.ts +15 -0
  221. package/dist/radio/root/radio-root.d.ts +6 -2
  222. package/dist/radio/root/radio-root.js +7 -2
  223. package/dist/rating/root/rating-root.d.ts +30 -12
  224. package/dist/rating/root/rating-root.js +27 -27
  225. package/dist/scatter-chart/fit.d.ts +15 -0
  226. package/dist/scatter-chart/fit.js +35 -0
  227. package/dist/scatter-chart/root/scatter-chart-root.d.ts +51 -1
  228. package/dist/scatter-chart/root/scatter-chart-root.js +64 -24
  229. package/dist/scroll-area/root/scroll-area-root.d.ts +22 -51
  230. package/dist/scroll-area/root/scroll-area-root.js +57 -173
  231. package/dist/scroll-area/scrollbar-controller.d.ts +111 -0
  232. package/dist/scroll-area/scrollbar-controller.js +172 -0
  233. package/dist/scroll-area/scrollbar.d.ts +27 -8
  234. package/dist/scroll-area/scrollbar.js +10 -1
  235. package/dist/select/align.d.ts +47 -0
  236. package/dist/select/align.js +7 -0
  237. package/dist/select/root/select-root.d.ts +71 -0
  238. package/dist/select/root/select-root.js +78 -6
  239. package/dist/skeleton/root/skeleton-root.d.ts +3 -0
  240. package/dist/skeleton/root/skeleton-root.js +4 -2
  241. package/dist/skip-link/root/skip-link-root.js +2 -2
  242. package/dist/slider/root/slider-root.d.ts +59 -5
  243. package/dist/slider/root/slider-root.js +45 -3
  244. package/dist/sparkline/readings.d.ts +36 -0
  245. package/dist/sparkline/readings.js +36 -0
  246. package/dist/sparkline/root/sparkline-root.d.ts +121 -7
  247. package/dist/sparkline/root/sparkline-root.js +160 -26
  248. package/dist/speed-dial/item/speed-dial-item.d.ts +1 -1
  249. package/dist/speed-dial/item/speed-dial-item.js +2 -2
  250. package/dist/speed-dial/root/speed-dial-root.d.ts +6 -0
  251. package/dist/speed-dial/root/speed-dial-root.js +11 -3
  252. package/dist/splitter/root/splitter-root.d.ts +10 -12
  253. package/dist/splitter/root/splitter-root.js +211 -204
  254. package/dist/stat/root/stat-root.d.ts +14 -5
  255. package/dist/stat/root/stat-root.js +21 -5
  256. package/dist/steps/reach.d.ts +5 -0
  257. package/dist/steps/reach.js +3 -0
  258. package/dist/steps/root/steps-root.d.ts +56 -3
  259. package/dist/steps/root/steps-root.js +50 -6
  260. package/dist/steps/step/step.d.ts +8 -0
  261. package/dist/steps/step/step.js +8 -3
  262. package/dist/switch/root/switch-root.d.ts +10 -0
  263. package/dist/table/column/table-column.d.ts +6 -10
  264. package/dist/table/column/table-column.js +8 -20
  265. package/dist/table/root/table-root.d.ts +31 -10
  266. package/dist/table/root/table-root.js +14 -18
  267. package/dist/table/row/table-row.d.ts +24 -0
  268. package/dist/table/row/table-row.js +37 -1
  269. package/dist/tabs/panel/tab-panel.d.ts +16 -3
  270. package/dist/tabs/panel/tab-panel.js +34 -5
  271. package/dist/tabs/root/tabs-root.d.ts +17 -3
  272. package/dist/tabs/root/tabs-root.js +1 -1
  273. package/dist/tabs/tab/tab.d.ts +5 -0
  274. package/dist/tabs/tab/tab.js +5 -2
  275. package/dist/tag/group/tag-group.d.ts +44 -5
  276. package/dist/tag/group/tag-group.js +60 -9
  277. package/dist/tag/root/tag-root.js +9 -2
  278. package/dist/textarea/root/textarea-root.d.ts +41 -2
  279. package/dist/textarea/root/textarea-root.js +39 -1
  280. package/dist/time-input/root/time-input-root.d.ts +10 -2
  281. package/dist/time-input/root/time-input-root.js +24 -13
  282. package/dist/time-picker/root/time-picker-root.d.ts +28 -17
  283. package/dist/time-picker/root/time-picker-root.js +18 -29
  284. package/dist/time-picker/wheel-name.d.ts +9 -0
  285. package/dist/time-picker/wheel-name.js +15 -0
  286. package/dist/timeline/group/timeline-group.js +2 -2
  287. package/dist/toast/item/toast-item.d.ts +16 -3
  288. package/dist/toast/item/toast-item.js +22 -6
  289. package/dist/toast/root/toast-root.d.ts +34 -4
  290. package/dist/toast/root/toast-root.js +74 -13
  291. package/dist/toast/words.d.ts +12 -0
  292. package/dist/toast/words.js +25 -0
  293. package/dist/toggle/group/toggle-group.d.ts +52 -7
  294. package/dist/toggle/group/toggle-group.js +58 -44
  295. package/dist/toggle/root/toggle-root.js +2 -4
  296. package/dist/toolbar/root/toolbar-root.d.ts +21 -5
  297. package/dist/toolbar/root/toolbar-root.js +52 -8
  298. package/dist/tooltip/root/tooltip-root.d.ts +20 -3
  299. package/dist/tooltip/root/tooltip-root.js +18 -1
  300. package/dist/tour/back.d.ts +10 -0
  301. package/dist/tour/back.js +8 -0
  302. package/dist/tour/root/tour-root.d.ts +45 -2
  303. package/dist/tour/root/tour-root.js +53 -8
  304. package/dist/tour/stop/tour-stop.d.ts +3 -0
  305. package/dist/tour/stop/tour-stop.js +3 -3
  306. package/dist/tree/item/tree-item.d.ts +7 -3
  307. package/dist/tree/item/tree-item.js +3 -0
  308. package/dist/tree/root/tree-root.d.ts +24 -3
  309. package/dist/tree/root/tree-root.js +32 -13
  310. package/dist/tree/selection.js +2 -15
  311. package/dist/trend/figure.d.ts +16 -0
  312. package/dist/trend/figure.js +8 -0
  313. package/dist/trend/root/trend-root.d.ts +83 -19
  314. package/dist/trend/root/trend-root.js +59 -8
  315. package/dist/video/media-events.d.ts +13 -18
  316. package/dist/video/media-events.js +1 -5
  317. package/dist/video/root/extra-controls.d.ts +12 -0
  318. package/dist/video/root/extra-controls.js +6 -2
  319. package/dist/video/root/video-root.d.ts +23 -6
  320. package/dist/video/root/video-root.js +19 -10
  321. package/dist/video-playlist/menu/video-playlist-menu.d.ts +2 -0
  322. package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
  323. package/dist/video-playlist/root/video-playlist-root.d.ts +17 -1
  324. package/dist/video-playlist/root/video-playlist-root.js +3 -0
  325. package/dist/virtual-list/fill.d.ts +11 -8
  326. package/dist/virtual-list/fill.js +10 -17
  327. package/dist/virtual-list/keys.d.ts +16 -7
  328. package/dist/virtual-list/keys.js +17 -6
  329. package/dist/virtual-list/plan.d.ts +65 -0
  330. package/dist/virtual-list/plan.js +27 -0
  331. package/dist/virtual-list/root/virtual-list-root.d.ts +136 -13
  332. package/dist/virtual-list/root/virtual-list-root.js +218 -51
  333. package/lint.json +1 -1
  334. package/package.json +8 -8
  335. package/types/jsx.d.ts +7441 -1458
  336. package/types/svelte.d.ts +1 -0
  337. package/types/vue.d.ts +1501 -326
  338. package/dist/chat/relative-clock.d.ts +0 -39
  339. package/dist/chat/relative-clock.js +0 -31
  340. package/dist/data-table/root/order.d.ts +0 -141
  341. package/dist/data-table/root/order.js +0 -76
  342. package/dist/tag/selection.d.ts +0 -10
  343. package/dist/tag/selection.js +0 -12
@@ -4,9 +4,10 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { AriaForwardController, anchorElement, arrowReach, CommandController, customElement, deepActiveElement, FocusScopeController, HelixElement, headingIn, MixinControllerOpen, PopoverController, reflectElements, sideOf, tellDisclosure, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, anchorElement, arrowReach, CommandController, customElement, deepActiveElement, FocusScopeController, HelixElement, headingIn, MixinControllerOpen, OpenPhaseController, PopoverController, reflectElements, sideOf, tellDisclosure, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
+ import { hideOutside } from '../hide-outside.js';
10
11
  let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpen(HelixElement) {
11
12
  constructor() {
12
13
  super(...arguments);
@@ -18,6 +19,14 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
18
19
  });
19
20
  this.offset = 4;
20
21
  this.placement = 'bottom-start';
22
+ this.phases = new OpenPhaseController(this, {
23
+ mark: {
24
+ entering: (on) => this.toggleState('entering', on),
25
+ exiting: (on) => this.toggleState('exiting', on),
26
+ },
27
+ open: () => this.open,
28
+ surface: () => this.partNamed('popover'),
29
+ });
21
30
  this.panel = new PopoverController(this, {
22
31
  anchor: () => this.trigger,
23
32
  arrow: () => this.partNamed('arrow'),
@@ -60,6 +69,7 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
60
69
  },
61
70
  },
62
71
  });
72
+ this.closingFromInside = false;
63
73
  this.handleFocusOut = (event) => {
64
74
  const next = event.relatedTarget;
65
75
  if (!this.open || !(next instanceof Node)) {
@@ -68,7 +78,15 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
68
78
  if (this.contains(next) || this.renderRoot.contains(next) || this.invoker?.contains(next)) {
69
79
  return;
70
80
  }
71
- this.hide();
81
+ this.close(false);
82
+ };
83
+ this.notePress = (event) => {
84
+ const trigger = this.renderRoot
85
+ ?.querySelector('slot[name="trigger"]')
86
+ ?.assignedElements({ flatten: false })[0];
87
+ if (trigger && event.composedPath().includes(trigger)) {
88
+ this.pressed = true;
89
+ }
72
90
  };
73
91
  this.handleTriggerClick = () => {
74
92
  if (this.open) {
@@ -78,6 +96,8 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
78
96
  this.show();
79
97
  }
80
98
  };
99
+ this.pressed = false;
100
+ this.settled = false;
81
101
  }
82
102
  get slotted() {
83
103
  return (this.renderRoot
@@ -99,33 +119,76 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
99
119
  }
100
120
  }
101
121
  hide() {
122
+ this.close(true);
123
+ }
124
+ close(giveBack) {
102
125
  if (!this.open || !this.requestOpen(false)) {
103
126
  return;
104
127
  }
105
128
  this.panel.hide();
106
- if (this.contains(deepActiveElement())) {
129
+ if (giveBack && this.focusInside) {
107
130
  this.trigger.focus();
108
131
  }
109
132
  }
133
+ get focusInside() {
134
+ return this.scope.holdsFocus || this.contains(deepActiveElement());
135
+ }
136
+ willUpdate(changed) {
137
+ super.willUpdate(changed);
138
+ this.closingFromInside =
139
+ changed.has('open') && !this.open && changed.get('open') === true && this.focusInside;
140
+ }
110
141
  connectedCallback() {
111
142
  super.connectedCallback();
112
143
  this.addEventListener('focusout', this.handleFocusOut);
144
+ this.addEventListener('click', this.notePress, { capture: true });
113
145
  }
114
146
  disconnectedCallback() {
115
147
  super.disconnectedCallback();
116
148
  this.removeEventListener('focusout', this.handleFocusOut);
149
+ this.removeEventListener('click', this.notePress, { capture: true });
150
+ this.hideAround();
117
151
  }
118
152
  tellTrigger() {
119
153
  const slot = this.renderRoot?.querySelector('slot[name="trigger"]');
120
- tellDisclosure(slot?.assignedElements({ flatten: false })[0], this.open);
154
+ const trigger = slot?.assignedElements({ flatten: false })[0];
155
+ if (!trigger || trigger.localName.includes('-')) {
156
+ tellDisclosure(trigger, this.open);
157
+ return;
158
+ }
159
+ if (trigger !== this.told) {
160
+ if (!this.pressed || trigger.hasAttribute('aria-expanded')) {
161
+ return;
162
+ }
163
+ this.told = trigger;
164
+ }
165
+ trigger.setAttribute('aria-expanded', String(this.open));
166
+ }
167
+ hideAround() {
168
+ const hide = this.open && this.modal && this.isConnected;
169
+ if (hide && !this.showOutside) {
170
+ this.showOutside = hideOutside([this, ...(this.invoker ? [this.invoker] : [])]);
171
+ }
172
+ else if (!hide && this.showOutside) {
173
+ this.showOutside();
174
+ this.showOutside = undefined;
175
+ }
121
176
  }
122
177
  updated(changed) {
123
178
  super.updated(changed);
124
179
  this.tellTrigger();
125
180
  this.nameByHeading();
181
+ if (this.settled) {
182
+ this.hideAround();
183
+ }
184
+ this.settled = true;
126
185
  if (changed.has('open') && this.open && changed.get('open') === false) {
127
186
  this.scope.enter();
128
187
  }
188
+ if (this.closingFromInside) {
189
+ this.closingFromInside = false;
190
+ this.trigger.focus();
191
+ }
129
192
  }
130
193
  render() {
131
194
  return html `<slot name="trigger" @click=${this.handleTriggerClick}></slot>
@@ -15,6 +15,13 @@ import { type PropertyValues, type TemplateResult } from 'lit';
15
15
  *
16
16
  * The fill is a fraction of the track, written as a custom property.
17
17
  *
18
+ * **Reaching `max` is said and sent.** When the value arrives at `max` from
19
+ * below, the bar says `complete-label`, "Complete" by default, in a polite
20
+ * `live` region and sends `helix-complete`, so a reader hears that the work is
21
+ * done and a page can move on. A bar that starts full says nothing, and an
22
+ * empty `complete-label` keeps it quiet. The `complete` custom state is set
23
+ * while the bar is full.
24
+ *
18
25
  * @since 0.1.0
19
26
  * @status stable
20
27
  * @category feedback
@@ -23,14 +30,41 @@ import { type PropertyValues, type TemplateResult } from 'lit';
23
30
  *
24
31
  * @slot label - A visible label. Its words name the progress bar in place of `label`.
25
32
  *
33
+ * @event {HelixCompleteEvent} helix-complete - The value reached `max` from below. Its `value` is `max`. Not sent for a bar that starts full.
34
+ *
26
35
  * @csspart groove - The unfilled circle under the ring's filled arc.
27
36
  * @csspart indicator - The filled part that shows the work done.
37
+ * @csspart live - A polite region that says `complete-label` when the work is done, rendered from that moment. Hide it visually.
28
38
  * @csspart track - The full length the work is measured against.
29
39
  * @csspart value - The value shown beside the bar when `show-value` is set.
30
40
  *
31
41
  * @cssproperty --progress-fraction - The value as a fraction of the range, 0 to 1. Pinned to 1 while indeterminate.
42
+ *
43
+ * @cssstate complete - The value is at `max` and the bar is not indeterminate.
32
44
  */
33
45
  export declare class HelixHeadlessProgress extends HelixElement {
46
+ /**
47
+ * The words a screen reader hears when the value reaches `max`, such as
48
+ * "Upload complete".
49
+ *
50
+ * Said once, politely, as the work finishes. Empty says nothing, for a bar
51
+ * whose page announces the result itself.
52
+ */
53
+ completeLabel: string;
54
+ /**
55
+ * `Intl.NumberFormat` options the reading is written with, as React Aria's
56
+ * Meter and ProgressBar take them: a unit, a currency, or a number of
57
+ * decimals. Written as JSON in the attribute.
58
+ *
59
+ * ```html
60
+ * <helix-progress value="18" max="32" format-options='{"style":"unit","unit":"gigabyte"}'></helix-progress>
61
+ * ```
62
+ *
63
+ * A `percent` style writes the share of the range, and any other the value
64
+ * itself, so the example says "18 GB". Absent is a whole percentage. The
65
+ * words are announced as well as shown. `value-text` still wins.
66
+ */
67
+ formatOptions?: Intl.NumberFormatOptions;
34
68
  /**
35
69
  * Whether the length of the task is unknown. The bar then shows activity
36
70
  * without a value.
@@ -114,11 +148,24 @@ export declare class HelixHeadlessProgress extends HelixElement {
114
148
  * report progress.
115
149
  */
116
150
  valueText: string;
151
+ /** The region the bar says `complete-label` in, rendered from the first time it does. */
152
+ private readonly status;
153
+ /** Whether the bar was full at the last update, or unknown before the first. */
154
+ private wasComplete;
155
+ /** Whether the work is done: determinate and at `max`. */
156
+ private get complete();
117
157
  /** The range the value is measured in, with a top above its bottom. */
118
158
  private get range();
119
159
  /** What is done, as a fraction of the whole, between 0 and 1. */
120
160
  get fraction(): number;
121
161
  protected willUpdate(changed: PropertyValues): void;
162
+ protected updated(changed: PropertyValues): void;
163
+ /**
164
+ * What the bar says: the consumer's words, or the reading in
165
+ * `format-options`, a share of the range for a percent and the value for
166
+ * anything else.
167
+ */
168
+ private get text();
122
169
  /** A label's words changed, so the name is written again. */
123
170
  private readonly renamed;
124
171
  protected render(): TemplateResult;
@@ -4,24 +4,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixElement, hostAria, slottedWords } from '@fusion.dev/helix-core';
7
+ import { customElement, drawStatus, HelixCompleteEvent, HelixElement, hostAria, readingText, StatusController, slottedWords, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
- const percents = new Map();
11
- const percent = (share, locale) => {
12
- let format = percents.get(locale);
13
- if (!format) {
14
- format = new Intl.NumberFormat(locale || undefined, {
15
- maximumFractionDigits: 0,
16
- style: 'percent',
17
- });
18
- percents.set(locale, format);
19
- }
20
- return format.format(share);
21
- };
22
10
  let HelixHeadlessProgress = class HelixHeadlessProgress extends HelixElement {
23
11
  constructor() {
24
12
  super(...arguments);
13
+ this.completeLabel = 'Complete';
25
14
  this.indeterminate = false;
26
15
  this.label = 'Progress';
27
16
  this.locale = '';
@@ -30,10 +19,14 @@ let HelixHeadlessProgress = class HelixHeadlessProgress extends HelixElement {
30
19
  this.showValue = false;
31
20
  this.value = 0;
32
21
  this.valueText = '';
22
+ this.status = new StatusController(this);
33
23
  this.renamed = () => {
34
24
  this.requestUpdate();
35
25
  };
36
26
  }
27
+ get complete() {
28
+ return !this.indeterminate && this.fraction >= 1;
29
+ }
37
30
  get range() {
38
31
  const min = Number.isFinite(this.min) ? this.min : 0;
39
32
  return { max: this.max > min ? this.max : min + 100, min };
@@ -54,7 +47,26 @@ let HelixHeadlessProgress = class HelixHeadlessProgress extends HelixElement {
54
47
  this.internals.ariaValueNow = this.indeterminate
55
48
  ? null
56
49
  : String(Math.min(Math.max(this.value || min, min), max));
57
- this.internals.ariaValueText = this.valueText || null;
50
+ this.internals.ariaValueText =
51
+ this.valueText || (this.formatOptions !== undefined && !this.indeterminate ? this.text : null);
52
+ this.toggleState('complete', this.complete);
53
+ }
54
+ updated(changed) {
55
+ super.updated(changed);
56
+ const complete = this.complete;
57
+ const arrived = complete && this.wasComplete === false;
58
+ this.wasComplete = complete;
59
+ if (!arrived) {
60
+ return;
61
+ }
62
+ if (this.completeLabel !== '') {
63
+ this.status.say(this.completeLabel);
64
+ }
65
+ this.dispatchEvent(new HelixCompleteEvent({ value: String(this.range.max) }));
66
+ }
67
+ get text() {
68
+ return (this.valueText ||
69
+ readingText(Math.min(Math.max(this.value, this.range.min), this.range.max), this.fraction, this.locale, this.formatOptions));
58
70
  }
59
71
  render() {
60
72
  const fraction = this.indeterminate ? 1 : this.fraction;
@@ -69,11 +81,17 @@ let HelixHeadlessProgress = class HelixHeadlessProgress extends HelixElement {
69
81
  </div>`}
70
82
  ${this.showValue && !this.indeterminate
71
83
  ? html `<span part="value" aria-hidden="true"
72
- >${this.valueText || percent(this.fraction, this.locale)}</span
84
+ >${this.text}</span
73
85
  >`
74
- : ''}`;
86
+ : ''}${drawStatus(this.status)}`;
75
87
  }
76
88
  };
89
+ __decorate([
90
+ property({ attribute: 'complete-label' })
91
+ ], HelixHeadlessProgress.prototype, "completeLabel", void 0);
92
+ __decorate([
93
+ property({ attribute: 'format-options', type: Object })
94
+ ], HelixHeadlessProgress.prototype, "formatOptions", void 0);
77
95
  __decorate([
78
96
  property({ reflect: true, type: Boolean })
79
97
  ], HelixHeadlessProgress.prototype, "indeterminate", void 0);
@@ -21,11 +21,55 @@ export interface QrDrawing {
21
21
  modules: string;
22
22
  /** The quiet zone, in modules, that both paths are moved in by. */
23
23
  quiet: number;
24
+ /** The logo in the middle, where one is slotted and can be written into a file. */
25
+ logo?: QrLogo;
24
26
  }
27
+ /** A place in the drawing, in `viewBox` units: x, y, width and height. */
28
+ export type QrBox = readonly [number, number, number, number];
29
+ /** The slotted logo, read from the element, as a file can carry it. */
30
+ export interface QrLogo {
31
+ /** The box around the mark, which clears the modules under it. */
32
+ frame: QrBox;
33
+ /** The fill of that box. */
34
+ ground: string;
35
+ /** An image's address, written as an `<image>`. */
36
+ href?: string;
37
+ /** Where the mark sits inside the box. */
38
+ mark: QrBox;
39
+ /** An inline SVG's markup, written as a nested `<svg>` at the mark's place. */
40
+ svg?: string;
41
+ }
42
+ /** A rectangle the page measured, as `getBoundingClientRect` gives one. */
43
+ export interface QrRect {
44
+ readonly height: number;
45
+ readonly left: number;
46
+ readonly top: number;
47
+ readonly width: number;
48
+ }
49
+ /**
50
+ * A rectangle measured on the page as a place in the drawing: `area` is the
51
+ * drawn code's own rectangle and `box` the side of its `viewBox`, so a logo
52
+ * set over the code by a stylesheet lands where it was drawn.
53
+ */
54
+ export declare const boxIn: (rect: QrRect, area: QrRect, box: number) => QrBox;
55
+ /**
56
+ * The logo's box in the drawing, or nothing where any of it falls outside the
57
+ * code, such as a logo the headless tier sets beside the code rather than
58
+ * over it, which a file of the code alone has no place for.
59
+ */
60
+ export declare const logoBox: (rect: QrRect, area: QrRect, box: number) => QrBox | undefined;
61
+ /**
62
+ * The logo as the file writes it: its box, then the mark, as an `<image>` or as
63
+ * the slotted SVG moved to the mark's place. Nothing for a mark that is
64
+ * neither, such as text, which a file of its own cannot carry in the page's
65
+ * fonts.
66
+ */
67
+ export declare const qrLogo: (logo: QrLogo) => string;
25
68
  /**
26
69
  * The code as standalone SVG markup, sized `size` pixels square.
27
70
  *
28
71
  * The background is a rectangle under the code, since a scanner needs the
29
- * light quiet zone and a transparent file put on a dark page has none.
72
+ * light quiet zone and a transparent file put on a dark page has none. A
73
+ * logo goes over the code, as it is drawn on the page.
30
74
  */
31
75
  export declare const qrSvg: (drawing: QrDrawing, size: number) => string;
@@ -1,4 +1,31 @@
1
+ export const boxIn = (rect, area, box) => {
2
+ const scale = box / area.width;
3
+ return [
4
+ (rect.left - area.left) * scale,
5
+ (rect.top - area.top) * scale,
6
+ rect.width * scale,
7
+ rect.height * scale,
8
+ ];
9
+ };
10
+ export const logoBox = (rect, area, box) => {
11
+ const frame = boxIn(rect, area, box);
12
+ const [x, y, width, height] = frame;
13
+ return x < 0 || y < 0 || x + width > box || y + height > box ? undefined : frame;
14
+ };
1
15
  const attribute = (value) => value.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;');
16
+ const unit = (value) => String(Math.round(value * 100) / 100);
17
+ const placed = ([x, y, width, height]) => `x="${unit(x)}" y="${unit(y)}" width="${unit(width)}" height="${unit(height)}"`;
18
+ export const qrLogo = (logo) => {
19
+ const ground = `<rect ${placed(logo.frame)} fill="${attribute(logo.ground)}"/>`;
20
+ if (logo.href) {
21
+ return `${ground}<image ${placed(logo.mark)} href="${attribute(logo.href)}" preserveAspectRatio="xMidYMid meet"/>`;
22
+ }
23
+ if (logo.svg) {
24
+ const mark = logo.svg.replace(/^\s*<svg\b([^>]*)>/, (_, rest) => `<svg${rest.replace(/\s(?:x|y|width|height|slot)="[^"]*"/g, '')} ${placed(logo.mark)}>`);
25
+ return `${ground}${mark}`;
26
+ }
27
+ return '';
28
+ };
2
29
  export const qrSvg = (drawing, size) => {
3
30
  const shift = `translate(${drawing.quiet} ${drawing.quiet})`;
4
31
  return (`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${drawing.box} ${drawing.box}"` +
@@ -6,5 +33,6 @@ export const qrSvg = (drawing, size) => {
6
33
  `<rect width="${drawing.box}" height="${drawing.box}" fill="${attribute(drawing.background)}"/>` +
7
34
  `<path d="${attribute(drawing.modules)}" transform="${shift}" fill="${attribute(drawing.moduleColor)}"/>` +
8
35
  `<path d="${attribute(drawing.finders)}" transform="${shift}" fill="${attribute(drawing.finderColor)}"/>` +
36
+ (drawing.logo ? qrLogo(drawing.logo) : '') +
9
37
  '</svg>');
10
38
  };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The name a code is read by: its `label`, or with none, the word for a code,
3
+ * followed by the value where the value is a web address.
4
+ *
5
+ * **A value that is no web address is never read out.** A code that sets up
6
+ * an authenticator (`otpauth://…?secret=…`) or joins a network
7
+ * (`WIFI:…;P:…;;`) carries a secret in its value, and the value as the name
8
+ * read that secret aloud to anyone in earshot. A web address is what a
9
+ * sighted reader gets by scanning, so it is said after the word.
10
+ */
11
+ export declare const qrName: (label: string, value: string, word: string) => string;
@@ -0,0 +1,7 @@
1
+ export const qrName = (label, value, word) => {
2
+ if (label.trim() !== '') {
3
+ return label;
4
+ }
5
+ const address = value.trim();
6
+ return /^https?:\/\/\S+$/iu.test(address) ? `${word}, ${address}` : word;
7
+ };
@@ -13,9 +13,12 @@ export type QrStyle = 'dots' | 'rounded' | 'square';
13
13
  * thousand modules, and an element for each would be a thousand nodes for one
14
14
  * image.
15
15
  *
16
- * The element has `role="img"` and an accessible name. Set the name to what the
17
- * code is for. When you pass no name, the value is the name. Most codes hold a
18
- * URL, and a URL read aloud gives the destination.
16
+ * The element has `role="img"` and an accessible name. Set `label` to what
17
+ * the code is for, such as "Open the menu". With no `label`, a code is named
18
+ * by `code-label`, "QR code", followed by its value where the value is a web
19
+ * address, which a URL read aloud makes a destination. Any other value is
20
+ * never read out: a code that sets up an authenticator or joins a network
21
+ * carries a secret in it.
19
22
  *
20
23
  * The quiet zone of four modules is required by the specification, and a
21
24
  * scanner rejects a code cropped to its edge. It is drawn inside the `viewBox`,
@@ -28,11 +31,13 @@ export type QrStyle = 'dots' | 'rounded' | 'square';
28
31
  *
29
32
  * A higher level holds fewer characters, so a value that fits at `M` can be too
30
33
  * long once a logo raises the level to `H`. A value too long for the level
31
- * draws no code, and the element fires `error`.
34
+ * draws no code: the element fires `error`, carries `:state(overflow)`, and
35
+ * shows what is in the `fallback` slot, such as the link written out.
32
36
  *
33
37
  * `toSvg()`, `toPng()` and `download()` give the drawn code as a file of its
34
- * own, in the colors it is drawn in, for a page that offers it to save or
35
- * print, as Zag's download trigger does.
38
+ * own, in the colors it is drawn in, with a slotted image or SVG logo where
39
+ * it sits, for a page that offers it to save or print, as Zag's download
40
+ * trigger does.
36
41
  *
37
42
  * @since 0.1.0
38
43
  * @status stable
@@ -40,16 +45,25 @@ export type QrStyle = 'dots' | 'rounded' | 'square';
40
45
  * @aliases QR, Barcode, Scan code
41
46
  * @documentation https://fusion.dev/helix/components/qr-code
42
47
  *
48
+ * @slot fallback - Shown in place of the code when the value is too long for it, such as the link as text.
43
49
  * @slot logo - A logo for the center. A code with a logo scans only at a high correction level.
44
50
  *
45
51
  * @event {Event} error - The value is too long for a code at the correction level, so no code is drawn.
46
52
  *
47
53
  * @csspart code - The square code image.
54
+ * @csspart fallback - The box around the `fallback` slot, drawn only when the value is too long for a code.
48
55
  * @csspart finders - The three corner squares, which can take their own color.
49
56
  * @csspart logo - The box that holds the center logo.
50
57
  * @csspart modules - Every module except the three corner squares.
58
+ *
59
+ * @cssstate overflow - The value is too long for a code at the correction level, so the fallback shows.
51
60
  */
52
61
  export declare class HelixHeadlessQrCode extends HelixElement {
62
+ /**
63
+ * The words a code with no `label` is named by, followed by its value where
64
+ * the value is a web address.
65
+ */
66
+ codeLabel: string;
53
67
  /**
54
68
  * The error correction level, which sets how much of the code can be lost
55
69
  * and still scan.
@@ -62,10 +76,13 @@ export declare class HelixHeadlessQrCode extends HelixElement {
62
76
  */
63
77
  correction?: QrCorrection;
64
78
  /**
65
- * The image's accessible name.
79
+ * The image's accessible name: what the code is for, such as "Open the
80
+ * menu".
66
81
  *
67
- * Falling back to the value is deliberate: a code almost always holds a URL,
68
- * and a URL is a better name than "QR code" for a reader who cannot see it.
82
+ * Absent names it by `code-label` and, for a web address, the address,
83
+ * which is what a reader who cannot scan it needs. Any other value is left
84
+ * out of the name, since it may hold a secret, such as an authenticator's
85
+ * key or a network's password.
69
86
  */
70
87
  label: string;
71
88
  /**
@@ -112,10 +129,18 @@ export declare class HelixHeadlessQrCode extends HelixElement {
112
129
  * is drawn in, with its background under the quiet zone.
113
130
  *
114
131
  * `''` where no code is drawn, before the first render or for a value too
115
- * long for the level. A slotted logo is not in the file, being the page's
116
- * own markup; save the file at `H` and the code still scans without it.
132
+ * long for the level. A slotted `<img>` or `<svg>` logo is written in where
133
+ * it is drawn: an image as its address, or as its pixels where the page may
134
+ * read them, which a PNG needs. A logo of text is left out, a file having
135
+ * none of the page's fonts; the code still scans without it.
117
136
  */
118
137
  toSvg(size?: number): string;
138
+ /**
139
+ * The slotted logo as the file draws it, measured against the drawn code, or
140
+ * nothing where none is slotted, where it is not over the code (the headless
141
+ * tier sets it beside it), or where it is neither an image nor an SVG.
142
+ */
143
+ private logoDrawing;
119
144
  /**
120
145
  * The code as a PNG, `size` pixels square, drawn from `toSvg()`.
121
146
  *
@@ -8,8 +8,21 @@ import { customElement, HelixElement, rgbOf } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg, } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { encode } from 'uqr';
11
- import { qrSvg } from '../export.js';
11
+ import { boxIn, logoBox, qrSvg } from '../export.js';
12
+ import { qrName } from '../name.js';
12
13
  const QUIET = 4;
14
+ const pixelsOf = (image) => {
15
+ try {
16
+ const canvas = document.createElement('canvas');
17
+ canvas.width = image.naturalWidth;
18
+ canvas.height = image.naturalHeight;
19
+ canvas.getContext('2d')?.drawImage(image, 0, 0);
20
+ return canvas.toDataURL('image/png');
21
+ }
22
+ catch {
23
+ return image.currentSrc || image.src;
24
+ }
25
+ };
13
26
  const isFinder = (x, y, size) => {
14
27
  const near = (a, b) => a < 8 && b < 8;
15
28
  return near(x, y) || near(size - 1 - x, y) || near(x, size - 1 - y);
@@ -64,6 +77,7 @@ const pathFor = (matrix, style, only = 'all') => {
64
77
  let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
65
78
  constructor() {
66
79
  super(...arguments);
80
+ this.codeLabel = 'QR code';
67
81
  this.label = '';
68
82
  this.value = '';
69
83
  this.logo = false;
@@ -96,7 +110,9 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
96
110
  updated(changed) {
97
111
  super.updated(changed);
98
112
  const key = `${this.level}:${this.value}`;
99
- if (this.value !== '' && this.encoded() === undefined) {
113
+ const overflow = this.value !== '' && this.encoded() === undefined;
114
+ this.toggleState('overflow', overflow);
115
+ if (overflow) {
100
116
  if (this.overflowed !== key) {
101
117
  this.overflowed = key;
102
118
  this.dispatchEvent(new Event('error', { bubbles: false, cancelable: false, composed: false }));
@@ -115,16 +131,46 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
115
131
  }
116
132
  const ground = getComputedStyle(this).backgroundColor;
117
133
  const clear = ground === '' || ground === 'transparent' || /^rgba\(.*,\s*0\)$/.test(ground);
134
+ const background = clear ? '#ffffff' : rgbOf(ground);
135
+ const box = Number(code.getAttribute('viewBox')?.split(' ')[2] ?? 0);
136
+ const logo = this.logoDrawing(code, box, background);
118
137
  return qrSvg({
119
- background: clear ? '#ffffff' : rgbOf(ground),
120
- box: Number(code.getAttribute('viewBox')?.split(' ')[2] ?? 0),
138
+ background,
139
+ box,
121
140
  finderColor: rgbOf(getComputedStyle(finders).fill || '#000000'),
122
141
  finders: finders.getAttribute('d') ?? '',
123
142
  moduleColor: rgbOf(getComputedStyle(modules).fill || '#000000'),
124
143
  modules: modules.getAttribute('d') ?? '',
125
144
  quiet: QUIET,
145
+ ...(logo ? { logo } : {}),
126
146
  }, size);
127
147
  }
148
+ logoDrawing(code, box, background) {
149
+ const holder = this.renderRoot.querySelector('[part="logo"]');
150
+ const mark = this.knownChildren.find((child) => child.slot === 'logo');
151
+ const area = code.getBoundingClientRect();
152
+ if (!holder || holder.hidden || !mark || area.width === 0) {
153
+ return undefined;
154
+ }
155
+ const frame = logoBox(holder.getBoundingClientRect(), area, box);
156
+ if (!frame) {
157
+ return undefined;
158
+ }
159
+ const fill = getComputedStyle(holder).backgroundColor;
160
+ const ground = fill === '' || fill === 'transparent' || /^rgba\(.*,\s*0\)$/.test(fill);
161
+ const logo = {
162
+ frame,
163
+ ground: ground ? background : rgbOf(fill),
164
+ mark: boxIn(mark.getBoundingClientRect(), area, box),
165
+ };
166
+ if (mark instanceof HTMLImageElement) {
167
+ return { ...logo, href: pixelsOf(mark) };
168
+ }
169
+ if (mark instanceof SVGSVGElement) {
170
+ return { ...logo, svg: new XMLSerializer().serializeToString(mark) };
171
+ }
172
+ return undefined;
173
+ }
128
174
  async toPng(size = 512) {
129
175
  const markup = this.toSvg(size);
130
176
  if (markup === '') {
@@ -151,11 +197,7 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
151
197
  link.click();
152
198
  setTimeout(() => URL.revokeObjectURL(url));
153
199
  }
154
- renderCode() {
155
- if (this.value === '') {
156
- return nothing;
157
- }
158
- const result = this.encoded();
200
+ renderCode(result) {
159
201
  if (result === undefined) {
160
202
  return nothing;
161
203
  }
@@ -168,7 +210,7 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
168
210
  viewBox="0 0 ${box} ${box}"
169
211
  xmlns="http://www.w3.org/2000/svg"
170
212
  role="img"
171
- aria-label=${this.label || this.value}
213
+ aria-label=${qrName(this.label, this.value, this.codeLabel)}
172
214
  shape-rendering="crispEdges"
173
215
  >
174
216
  <path
@@ -186,7 +228,9 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
186
228
  </svg>`;
187
229
  }
188
230
  render() {
189
- return html `${this.renderCode()}
231
+ const result = this.value === '' ? undefined : this.encoded();
232
+ const overflow = this.value !== '' && result === undefined;
233
+ return html `${this.renderCode(result)}${overflow ? html `<span part="fallback"><slot name="fallback"></slot></span>` : nothing}
190
234
  <span ?hidden=${!this.logo} part="logo">
191
235
  <slot
192
236
  name="logo"
@@ -201,6 +245,9 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
201
245
  </span>`;
202
246
  }
203
247
  };
248
+ __decorate([
249
+ property({ attribute: 'code-label' })
250
+ ], HelixHeadlessQrCode.prototype, "codeLabel", void 0);
204
251
  __decorate([
205
252
  property({ attribute: 'correction' })
206
253
  ], HelixHeadlessQrCode.prototype, "correction", void 0);
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Whether quoted text starts or ends with a quotation mark someone typed.
3
+ *
4
+ * A Quote renders a `<q>`, which sets the marks itself, so typed ones show
5
+ * twice: “"like this"”. Only the ends are read, since a mark inside the text
6
+ * is a quotation within it, which a nested Quote should set.
7
+ */
8
+ export declare const typedMarks: (text: string) => boolean;