@vielzeug/refine 26.10.0 → 26.10.1

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 (463) hide show
  1. package/README.md +1 -1
  2. package/dist/accordion-item.cjs.map +1 -1
  3. package/dist/accordion-item.js.map +1 -1
  4. package/dist/accordion.cjs.map +1 -1
  5. package/dist/accordion.js.map +1 -1
  6. package/dist/async.cjs.map +1 -1
  7. package/dist/async.js.map +1 -1
  8. package/dist/badge.cjs.map +1 -1
  9. package/dist/badge.js.map +1 -1
  10. package/dist/box.cjs.map +1 -1
  11. package/dist/box.js.map +1 -1
  12. package/dist/button.cjs +3 -3
  13. package/dist/button.cjs.map +1 -1
  14. package/dist/button.js +3 -3
  15. package/dist/button.js.map +1 -1
  16. package/dist/calendar.cjs.map +1 -1
  17. package/dist/calendar.js.map +1 -1
  18. package/dist/carousel.cjs.map +1 -1
  19. package/dist/carousel.js.map +1 -1
  20. package/dist/chat-message.cjs.map +1 -1
  21. package/dist/chat-message.js.map +1 -1
  22. package/dist/chat-panel.cjs.map +1 -1
  23. package/dist/chat-panel.js.map +1 -1
  24. package/dist/checkbox-group.cjs.map +1 -1
  25. package/dist/checkbox-group.js.map +1 -1
  26. package/dist/chip.cjs.map +1 -1
  27. package/dist/chip.js.map +1 -1
  28. package/dist/code-window.cjs.map +1 -1
  29. package/dist/code-window.js.map +1 -1
  30. package/dist/combobox.cjs.map +1 -1
  31. package/dist/combobox.js.map +1 -1
  32. package/dist/command-palette.cjs.map +1 -1
  33. package/dist/command-palette.js.map +1 -1
  34. package/dist/content/card/card.css.cjs.map +1 -1
  35. package/dist/content/card/card.css.js.map +1 -1
  36. package/dist/content/carousel/carousel-slide.css.cjs.map +1 -1
  37. package/dist/content/carousel/carousel-slide.css.js.map +1 -1
  38. package/dist/content/carousel/carousel.css.cjs.map +1 -1
  39. package/dist/content/carousel/carousel.css.js.map +1 -1
  40. package/dist/content/carousel/carousel.d.ts +5 -5
  41. package/dist/content/chat-message/chat-message.css.cjs +1 -1
  42. package/dist/content/chat-message/chat-message.css.cjs.map +1 -1
  43. package/dist/content/chat-message/chat-message.css.js +1 -1
  44. package/dist/content/chat-message/chat-message.css.js.map +1 -1
  45. package/dist/content/chat-message/chat-message.d.ts +4 -4
  46. package/dist/content/chat-message/chat-message.d.ts.map +1 -1
  47. package/dist/content/chat-panel/chat-panel.d.ts +1 -1
  48. package/dist/content/code-window/code-window.css.cjs.map +1 -1
  49. package/dist/content/code-window/code-window.css.js.map +1 -1
  50. package/dist/content/code-window/code-window.d.ts +4 -4
  51. package/dist/content/index.d.ts +4 -0
  52. package/dist/content/index.d.ts.map +1 -1
  53. package/dist/content/list/_list-item-reveal.cjs.map +1 -1
  54. package/dist/content/list/_list-item-reveal.d.ts +4 -4
  55. package/dist/content/list/_list-item-reveal.d.ts.map +1 -1
  56. package/dist/content/list/_list-item-reveal.js.map +1 -1
  57. package/dist/content/list/list-item.css.cjs.map +1 -1
  58. package/dist/content/list/list-item.css.js.map +1 -1
  59. package/dist/content/list/list-item.d.ts +8 -8
  60. package/dist/content/list/list-item.d.ts.map +1 -1
  61. package/dist/content/list/list.d.ts +8 -8
  62. package/dist/content/list/list.d.ts.map +1 -1
  63. package/dist/content/qr-code/qr-code.d.ts +1 -1
  64. package/dist/content/rank-list/rank-item.css.cjs +2 -0
  65. package/dist/content/rank-list/rank-item.css.cjs.map +1 -0
  66. package/dist/content/rank-list/rank-item.css.js +2 -0
  67. package/dist/content/rank-list/rank-item.css.js.map +1 -0
  68. package/dist/content/rank-list/rank-item.d.ts +68 -0
  69. package/dist/content/rank-list/rank-item.d.ts.map +1 -0
  70. package/dist/content/rank-list/rank-list.css.cjs +2 -0
  71. package/dist/content/rank-list/rank-list.css.cjs.map +1 -0
  72. package/dist/content/rank-list/rank-list.css.js +2 -0
  73. package/dist/content/rank-list/rank-list.css.js.map +1 -0
  74. package/dist/content/rank-list/rank-list.d.ts +44 -0
  75. package/dist/content/rank-list/rank-list.d.ts.map +1 -0
  76. package/dist/content/separator/separator.d.ts +1 -1
  77. package/dist/content/separator/separator.d.ts.map +1 -1
  78. package/dist/content/speech-player/speech-player.css.cjs.map +1 -1
  79. package/dist/content/speech-player/speech-player.css.js.map +1 -1
  80. package/dist/content/speech-player/speech-player.d.ts +12 -12
  81. package/dist/content/speech-player/speech-player.d.ts.map +1 -1
  82. package/dist/content/stepper/step.css.cjs.map +1 -1
  83. package/dist/content/stepper/step.css.js.map +1 -1
  84. package/dist/content/stepper/step.d.ts +3 -3
  85. package/dist/content/stepper/step.d.ts.map +1 -1
  86. package/dist/content/stepper/stepper.css.cjs.map +1 -1
  87. package/dist/content/stepper/stepper.css.js.map +1 -1
  88. package/dist/content/stepper/stepper.d.ts +3 -3
  89. package/dist/content/stepper/stepper.d.ts.map +1 -1
  90. package/dist/content/table/table.css.cjs.map +1 -1
  91. package/dist/content/table/table.css.js.map +1 -1
  92. package/dist/content/table/table.d.ts +3 -3
  93. package/dist/content/table/table.d.ts.map +1 -1
  94. package/dist/content/text/text.css.cjs.map +1 -1
  95. package/dist/content/text/text.css.js.map +1 -1
  96. package/dist/content/text/text.d.ts +2 -2
  97. package/dist/content/text/text.d.ts.map +1 -1
  98. package/dist/cookie-banner.cjs.map +1 -1
  99. package/dist/cookie-banner.js.map +1 -1
  100. package/dist/copy-command.cjs +1 -1
  101. package/dist/copy-command.cjs.map +1 -1
  102. package/dist/copy-command.js +1 -1
  103. package/dist/copy-command.js.map +1 -1
  104. package/dist/core/announcer.cjs.map +1 -1
  105. package/dist/core/announcer.d.ts +2 -2
  106. package/dist/core/announcer.js.map +1 -1
  107. package/dist/core/aria-reflection.cjs.map +1 -1
  108. package/dist/core/aria-reflection.d.ts +3 -3
  109. package/dist/core/aria-reflection.js.map +1 -1
  110. package/dist/core/auto-resize.cjs.map +1 -1
  111. package/dist/core/auto-resize.d.ts +1 -1
  112. package/dist/core/auto-resize.js.map +1 -1
  113. package/dist/core/checkable.cjs.map +1 -1
  114. package/dist/core/checkable.d.ts +2 -2
  115. package/dist/core/checkable.d.ts.map +1 -1
  116. package/dist/core/checkable.js.map +1 -1
  117. package/dist/core/choice-field.cjs.map +1 -1
  118. package/dist/core/choice-field.d.ts +3 -3
  119. package/dist/core/choice-field.d.ts.map +1 -1
  120. package/dist/core/choice-field.js.map +1 -1
  121. package/dist/core/composer.cjs.map +1 -1
  122. package/dist/core/composer.d.ts +4 -4
  123. package/dist/core/composer.d.ts.map +1 -1
  124. package/dist/core/composer.js.map +1 -1
  125. package/dist/core/date-picker.cjs.map +1 -1
  126. package/dist/core/date-picker.d.ts +5 -5
  127. package/dist/core/date-picker.d.ts.map +1 -1
  128. package/dist/core/date-picker.js.map +1 -1
  129. package/dist/core/direction.cjs.map +1 -1
  130. package/dist/core/direction.d.ts +1 -1
  131. package/dist/core/direction.js.map +1 -1
  132. package/dist/core/field-base.cjs.map +1 -1
  133. package/dist/core/field-base.d.ts +9 -9
  134. package/dist/core/field-base.d.ts.map +1 -1
  135. package/dist/core/field-base.js.map +1 -1
  136. package/dist/core/focus.cjs.map +1 -1
  137. package/dist/core/focus.d.ts +1 -1
  138. package/dist/core/focus.js.map +1 -1
  139. package/dist/core/icon-registry.cjs.map +1 -1
  140. package/dist/core/icon-registry.d.ts +1 -1
  141. package/dist/core/icon-registry.js.map +1 -1
  142. package/dist/core/icons.cjs.map +1 -1
  143. package/dist/core/icons.js.map +1 -1
  144. package/dist/core/keyboard.cjs.map +1 -1
  145. package/dist/core/keyboard.d.ts +1 -1
  146. package/dist/core/keyboard.js.map +1 -1
  147. package/dist/core/numbers.cjs.map +1 -1
  148. package/dist/core/numbers.d.ts +1 -1
  149. package/dist/core/numbers.js.map +1 -1
  150. package/dist/core/overlay.cjs.map +1 -1
  151. package/dist/core/overlay.d.ts +6 -6
  152. package/dist/core/overlay.js.map +1 -1
  153. package/dist/core/parse.cjs.map +1 -1
  154. package/dist/core/parse.d.ts +1 -1
  155. package/dist/core/parse.js.map +1 -1
  156. package/dist/core/positioner.cjs.map +1 -1
  157. package/dist/core/positioner.d.ts +3 -3
  158. package/dist/core/positioner.js.map +1 -1
  159. package/dist/core/ref-callback.cjs.map +1 -1
  160. package/dist/core/ref-callback.d.ts +2 -2
  161. package/dist/core/ref-callback.d.ts.map +1 -1
  162. package/dist/core/ref-callback.js.map +1 -1
  163. package/dist/core/text-field.cjs.map +1 -1
  164. package/dist/core/text-field.d.ts +6 -6
  165. package/dist/core/text-field.d.ts.map +1 -1
  166. package/dist/core/text-field.js.map +1 -1
  167. package/dist/counter.cjs +1 -0
  168. package/dist/counter.cjs.map +1 -1
  169. package/dist/counter.js +1 -0
  170. package/dist/counter.js.map +1 -1
  171. package/dist/custom-elements.json +315 -87
  172. package/dist/datagrid.cjs.map +1 -1
  173. package/dist/datagrid.js.map +1 -1
  174. package/dist/date-picker.cjs +1 -1
  175. package/dist/date-picker.cjs.map +1 -1
  176. package/dist/date-picker.js +1 -1
  177. package/dist/date-picker.js.map +1 -1
  178. package/dist/dialog.cjs.map +1 -1
  179. package/dist/dialog.js.map +1 -1
  180. package/dist/disclosure/accordion-item/accordion-item.css.cjs +1 -1
  181. package/dist/disclosure/accordion-item/accordion-item.css.cjs.map +1 -1
  182. package/dist/disclosure/accordion-item/accordion-item.css.js +1 -1
  183. package/dist/disclosure/accordion-item/accordion-item.css.js.map +1 -1
  184. package/dist/disclosure/tab-item/tab-item.d.ts +1 -1
  185. package/dist/disclosure/tab-item/tab-item.d.ts.map +1 -1
  186. package/dist/drawer.cjs.map +1 -1
  187. package/dist/drawer.js.map +1 -1
  188. package/dist/feedback/alert/alert.css.cjs.map +1 -1
  189. package/dist/feedback/alert/alert.css.js.map +1 -1
  190. package/dist/feedback/badge/badge.css.cjs.map +1 -1
  191. package/dist/feedback/badge/badge.css.js.map +1 -1
  192. package/dist/feedback/badge/badge.d.ts +1 -1
  193. package/dist/feedback/badge/badge.d.ts.map +1 -1
  194. package/dist/feedback/chip/chip.css.cjs.map +1 -1
  195. package/dist/feedback/chip/chip.css.js.map +1 -1
  196. package/dist/feedback/chip/chip.d.ts +3 -3
  197. package/dist/feedback/chip/chip.d.ts.map +1 -1
  198. package/dist/feedback/cookie-banner/cookie-banner.css.cjs.map +1 -1
  199. package/dist/feedback/cookie-banner/cookie-banner.css.js.map +1 -1
  200. package/dist/feedback/cookie-banner/cookie-banner.d.ts +8 -8
  201. package/dist/feedback/cookie-banner/cookie-banner.d.ts.map +1 -1
  202. package/dist/feedback/progress/progress.css.cjs.map +1 -1
  203. package/dist/feedback/progress/progress.css.js.map +1 -1
  204. package/dist/feedback/progress/progress.d.ts +1 -1
  205. package/dist/feedback/progress/progress.d.ts.map +1 -1
  206. package/dist/feedback/skeleton/skeleton.css.cjs.map +1 -1
  207. package/dist/feedback/skeleton/skeleton.css.js.map +1 -1
  208. package/dist/feedback/skeleton/skeleton.d.ts +1 -1
  209. package/dist/feedback/skeleton/skeleton.d.ts.map +1 -1
  210. package/dist/feedback/toast/toast.css.cjs +1 -1
  211. package/dist/feedback/toast/toast.css.cjs.map +1 -1
  212. package/dist/feedback/toast/toast.css.js +1 -1
  213. package/dist/feedback/toast/toast.css.js.map +1 -1
  214. package/dist/feedback/toast/toast.d.ts +1 -1
  215. package/dist/feedback/toast/toast.d.ts.map +1 -1
  216. package/dist/feedback/typing-indicator/typing-indicator.d.ts +2 -2
  217. package/dist/file-input.cjs +1 -1
  218. package/dist/file-input.cjs.map +1 -1
  219. package/dist/file-input.js +1 -1
  220. package/dist/file-input.js.map +1 -1
  221. package/dist/frameworks/elements.d.ts +1 -1
  222. package/dist/grid-item.cjs.map +1 -1
  223. package/dist/grid-item.js.map +1 -1
  224. package/dist/grid.cjs.map +1 -1
  225. package/dist/grid.js.map +1 -1
  226. package/dist/icon.cjs.map +1 -1
  227. package/dist/icon.js.map +1 -1
  228. package/dist/input.cjs.map +1 -1
  229. package/dist/input.js.map +1 -1
  230. package/dist/inputs/button/button.css.cjs.map +1 -1
  231. package/dist/inputs/button/button.css.js.map +1 -1
  232. package/dist/inputs/button/button.d.ts +3 -3
  233. package/dist/inputs/button/button.d.ts.map +1 -1
  234. package/dist/inputs/calendar/calendar.css.cjs.map +1 -1
  235. package/dist/inputs/calendar/calendar.css.js.map +1 -1
  236. package/dist/inputs/calendar/calendar.d.ts +2 -2
  237. package/dist/inputs/checkbox-group/checkbox-group.d.ts +2 -2
  238. package/dist/inputs/checkbox-group/checkbox-group.d.ts.map +1 -1
  239. package/dist/inputs/combobox/combobox.css.cjs.map +1 -1
  240. package/dist/inputs/combobox/combobox.css.js.map +1 -1
  241. package/dist/inputs/counter/counter.css.cjs +1 -1
  242. package/dist/inputs/counter/counter.css.cjs.map +1 -1
  243. package/dist/inputs/counter/counter.css.js +1 -1
  244. package/dist/inputs/counter/counter.css.js.map +1 -1
  245. package/dist/inputs/counter/counter.d.ts +2 -1
  246. package/dist/inputs/counter/counter.d.ts.map +1 -1
  247. package/dist/inputs/datagrid/datagrid.css.cjs.map +1 -1
  248. package/dist/inputs/datagrid/datagrid.css.js.map +1 -1
  249. package/dist/inputs/datagrid/datagrid.d.ts +5 -5
  250. package/dist/inputs/date-picker/date-picker.css.cjs.map +1 -1
  251. package/dist/inputs/date-picker/date-picker.css.js.map +1 -1
  252. package/dist/inputs/date-picker/date-picker.d.ts +1 -1
  253. package/dist/inputs/file-input/file-input-upload.cjs.map +1 -1
  254. package/dist/inputs/file-input/file-input-upload.d.ts +6 -6
  255. package/dist/inputs/file-input/file-input-upload.js.map +1 -1
  256. package/dist/inputs/file-input/file-input.css.cjs.map +1 -1
  257. package/dist/inputs/file-input/file-input.css.js.map +1 -1
  258. package/dist/inputs/file-input/file-input.d.ts +6 -6
  259. package/dist/inputs/input/input.css.cjs +1 -1
  260. package/dist/inputs/input/input.css.cjs.map +1 -1
  261. package/dist/inputs/input/input.css.js +1 -1
  262. package/dist/inputs/input/input.css.js.map +1 -1
  263. package/dist/inputs/input/input.d.ts +6 -6
  264. package/dist/inputs/input/input.d.ts.map +1 -1
  265. package/dist/inputs/message-composer/message-composer.css.cjs.map +1 -1
  266. package/dist/inputs/message-composer/message-composer.css.js.map +1 -1
  267. package/dist/inputs/message-composer/message-composer.d.ts +7 -7
  268. package/dist/inputs/message-composer/message-composer.d.ts.map +1 -1
  269. package/dist/inputs/number-input/number-input.css.cjs.map +1 -1
  270. package/dist/inputs/number-input/number-input.css.js.map +1 -1
  271. package/dist/inputs/number-input/number-input.d.ts +2 -2
  272. package/dist/inputs/otp-input/otp-input.css.cjs.map +1 -1
  273. package/dist/inputs/otp-input/otp-input.css.js.map +1 -1
  274. package/dist/inputs/qr-scanner/qr-scanner.d.ts +1 -1
  275. package/dist/inputs/qr-scanner/qr-scanner.d.ts.map +1 -1
  276. package/dist/inputs/rating/rating.d.ts +1 -1
  277. package/dist/inputs/rating/rating.d.ts.map +1 -1
  278. package/dist/inputs/select/select.css.cjs +1 -1
  279. package/dist/inputs/select/select.css.cjs.map +1 -1
  280. package/dist/inputs/select/select.css.js +1 -1
  281. package/dist/inputs/select/select.css.js.map +1 -1
  282. package/dist/inputs/select/select.d.ts +1 -1
  283. package/dist/inputs/select/select.d.ts.map +1 -1
  284. package/dist/inputs/shared/field-binding.cjs.map +1 -1
  285. package/dist/inputs/shared/field-binding.d.ts +2 -2
  286. package/dist/inputs/shared/field-binding.d.ts.map +1 -1
  287. package/dist/inputs/shared/field-binding.js.map +1 -1
  288. package/dist/inputs/shared/native-field-event.cjs.map +1 -1
  289. package/dist/inputs/shared/native-field-event.d.ts +1 -1
  290. package/dist/inputs/shared/native-field-event.js.map +1 -1
  291. package/dist/inputs/shared/templates.cjs.map +1 -1
  292. package/dist/inputs/shared/templates.d.ts +3 -3
  293. package/dist/inputs/shared/templates.js.map +1 -1
  294. package/dist/inputs/slider/slider.css.cjs.map +1 -1
  295. package/dist/inputs/slider/slider.css.js.map +1 -1
  296. package/dist/inputs/slider/slider.d.ts +1 -1
  297. package/dist/inputs/slider/slider.d.ts.map +1 -1
  298. package/dist/inputs/textarea/textarea.css.cjs.map +1 -1
  299. package/dist/inputs/textarea/textarea.css.js.map +1 -1
  300. package/dist/inputs/textarea/textarea.d.ts +1 -1
  301. package/dist/inputs/time-picker/time-picker.css.cjs.map +1 -1
  302. package/dist/inputs/time-picker/time-picker.css.js.map +1 -1
  303. package/dist/inputs/time-picker/time-picker.d.ts +1 -1
  304. package/dist/layout/box/box.css.cjs.map +1 -1
  305. package/dist/layout/box/box.css.js.map +1 -1
  306. package/dist/layout/box/box.d.ts +1 -1
  307. package/dist/layout/grid/grid.d.ts +1 -1
  308. package/dist/layout/grid-item/grid-item.d.ts +3 -3
  309. package/dist/layout/grid-item/grid-item.d.ts.map +1 -1
  310. package/dist/layout/navbar/navbar.css.cjs +1 -1
  311. package/dist/layout/navbar/navbar.css.cjs.map +1 -1
  312. package/dist/layout/navbar/navbar.css.js +1 -1
  313. package/dist/layout/navbar/navbar.css.js.map +1 -1
  314. package/dist/layout/navbar/navbar.d.ts +3 -3
  315. package/dist/layout/sidebar/sidebar-item.css.cjs.map +1 -1
  316. package/dist/layout/sidebar/sidebar-item.css.js.map +1 -1
  317. package/dist/layout/sidebar/sidebar.d.ts +5 -5
  318. package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
  319. package/dist/list-item.cjs.map +1 -1
  320. package/dist/list-item.js.map +1 -1
  321. package/dist/list.cjs.map +1 -1
  322. package/dist/list.js.map +1 -1
  323. package/dist/menu.cjs +6 -5
  324. package/dist/menu.cjs.map +1 -1
  325. package/dist/menu.js +6 -5
  326. package/dist/menu.js.map +1 -1
  327. package/dist/message-composer.cjs.map +1 -1
  328. package/dist/message-composer.js.map +1 -1
  329. package/dist/navbar.cjs.map +1 -1
  330. package/dist/navbar.js.map +1 -1
  331. package/dist/number-input.cjs.map +1 -1
  332. package/dist/number-input.js.map +1 -1
  333. package/dist/otp-input.cjs.map +1 -1
  334. package/dist/otp-input.js.map +1 -1
  335. package/dist/overlay/command-palette/command-palette-items.cjs.map +1 -1
  336. package/dist/overlay/command-palette/command-palette-items.d.ts +1 -1
  337. package/dist/overlay/command-palette/command-palette-items.js.map +1 -1
  338. package/dist/overlay/command-palette/command-palette.css.cjs.map +1 -1
  339. package/dist/overlay/command-palette/command-palette.css.js.map +1 -1
  340. package/dist/overlay/command-palette/command-palette.d.ts +3 -3
  341. package/dist/overlay/command-palette/command-palette.types.d.ts +6 -6
  342. package/dist/overlay/command-palette/command-palette.types.d.ts.map +1 -1
  343. package/dist/overlay/dialog/dialog.css.cjs +1 -1
  344. package/dist/overlay/dialog/dialog.css.cjs.map +1 -1
  345. package/dist/overlay/dialog/dialog.css.js +1 -1
  346. package/dist/overlay/dialog/dialog.css.js.map +1 -1
  347. package/dist/overlay/dialog/dialog.d.ts +2 -2
  348. package/dist/overlay/dialog/dialog.d.ts.map +1 -1
  349. package/dist/overlay/drawer/drawer.css.cjs.map +1 -1
  350. package/dist/overlay/drawer/drawer.css.js.map +1 -1
  351. package/dist/overlay/drawer/drawer.d.ts +1 -1
  352. package/dist/overlay/drawer/drawer.d.ts.map +1 -1
  353. package/dist/overlay/menu/menu-item.css.cjs.map +1 -1
  354. package/dist/overlay/menu/menu-item.css.js.map +1 -1
  355. package/dist/overlay/menu/menu.d.ts.map +1 -1
  356. package/dist/overlay/shared/use-dialog.cjs.map +1 -1
  357. package/dist/overlay/shared/use-dialog.d.ts +6 -6
  358. package/dist/overlay/shared/use-dialog.d.ts.map +1 -1
  359. package/dist/overlay/shared/use-dialog.js.map +1 -1
  360. package/dist/overlay/shared/use-floating-trigger.cjs.map +1 -1
  361. package/dist/overlay/shared/use-floating-trigger.d.ts +1 -1
  362. package/dist/overlay/shared/use-floating-trigger.d.ts.map +1 -1
  363. package/dist/overlay/shared/use-floating-trigger.js.map +1 -1
  364. package/dist/overlay/tooltip/tooltip.css.cjs.map +1 -1
  365. package/dist/overlay/tooltip/tooltip.css.js.map +1 -1
  366. package/dist/progress.cjs.map +1 -1
  367. package/dist/progress.js.map +1 -1
  368. package/dist/qr-code.cjs.map +1 -1
  369. package/dist/qr-code.js.map +1 -1
  370. package/dist/qr-scanner.cjs.map +1 -1
  371. package/dist/qr-scanner.js.map +1 -1
  372. package/dist/radio-group.cjs.map +1 -1
  373. package/dist/radio-group.js.map +1 -1
  374. package/dist/radio.cjs.map +1 -1
  375. package/dist/radio.js.map +1 -1
  376. package/dist/rank-item.cjs +21 -0
  377. package/dist/rank-item.cjs.map +1 -0
  378. package/dist/rank-item.js +21 -0
  379. package/dist/rank-item.js.map +1 -0
  380. package/dist/rank-list.cjs +4 -0
  381. package/dist/rank-list.cjs.map +1 -0
  382. package/dist/rank-list.js +4 -0
  383. package/dist/rank-list.js.map +1 -0
  384. package/dist/rating.cjs.map +1 -1
  385. package/dist/rating.js.map +1 -1
  386. package/dist/refine.cjs +103 -80
  387. package/dist/refine.cjs.map +1 -1
  388. package/dist/refine.iife.js +103 -80
  389. package/dist/refine.iife.js.map +1 -1
  390. package/dist/refine.js +104 -81
  391. package/dist/refine.js.map +1 -1
  392. package/dist/select.cjs.map +1 -1
  393. package/dist/select.js.map +1 -1
  394. package/dist/separator.cjs.map +1 -1
  395. package/dist/separator.js.map +1 -1
  396. package/dist/shared/index.d.ts +3 -3
  397. package/dist/shared/size-presets.cjs.map +1 -1
  398. package/dist/shared/size-presets.d.ts +4 -4
  399. package/dist/shared/size-presets.js.map +1 -1
  400. package/dist/shared/types.d.ts +1 -1
  401. package/dist/sidebar.cjs.map +1 -1
  402. package/dist/sidebar.js.map +1 -1
  403. package/dist/skeleton.cjs.map +1 -1
  404. package/dist/skeleton.js.map +1 -1
  405. package/dist/slider.cjs.map +1 -1
  406. package/dist/slider.js.map +1 -1
  407. package/dist/speech-player.cjs.map +1 -1
  408. package/dist/speech-player.js.map +1 -1
  409. package/dist/step.cjs.map +1 -1
  410. package/dist/step.js.map +1 -1
  411. package/dist/stepper.cjs.map +1 -1
  412. package/dist/stepper.js.map +1 -1
  413. package/dist/styles/fouc.css +2 -2
  414. package/dist/styles/mixins/accessibility.css.cjs.map +1 -1
  415. package/dist/styles/mixins/accessibility.css.d.ts +4 -4
  416. package/dist/styles/mixins/accessibility.css.js.map +1 -1
  417. package/dist/styles/mixins/animation.css.cjs +2 -2
  418. package/dist/styles/mixins/animation.css.cjs.map +1 -1
  419. package/dist/styles/mixins/animation.css.d.ts +4 -4
  420. package/dist/styles/mixins/animation.css.js +2 -2
  421. package/dist/styles/mixins/animation.css.js.map +1 -1
  422. package/dist/styles/mixins/field-variants.css.cjs +2 -2
  423. package/dist/styles/mixins/field-variants.css.cjs.map +1 -1
  424. package/dist/styles/mixins/field-variants.css.d.ts +6 -6
  425. package/dist/styles/mixins/field-variants.css.d.ts.map +1 -1
  426. package/dist/styles/mixins/field-variants.css.js +2 -2
  427. package/dist/styles/mixins/field-variants.css.js.map +1 -1
  428. package/dist/styles/mixins/shape.css.cjs.map +1 -1
  429. package/dist/styles/mixins/shape.css.d.ts +9 -9
  430. package/dist/styles/mixins/shape.css.d.ts.map +1 -1
  431. package/dist/styles/mixins/shape.css.js.map +1 -1
  432. package/dist/styles/mixins/variants.css.cjs.map +1 -1
  433. package/dist/styles/mixins/variants.css.d.ts +1 -1
  434. package/dist/styles/mixins/variants.css.js.map +1 -1
  435. package/dist/styles/theme.css +36 -28
  436. package/dist/tab-item.cjs.map +1 -1
  437. package/dist/tab-item.js.map +1 -1
  438. package/dist/tab-panel.cjs.map +1 -1
  439. package/dist/tab-panel.js.map +1 -1
  440. package/dist/table.cjs.map +1 -1
  441. package/dist/table.js.map +1 -1
  442. package/dist/text.cjs.map +1 -1
  443. package/dist/text.js.map +1 -1
  444. package/dist/textarea.cjs.map +1 -1
  445. package/dist/textarea.js.map +1 -1
  446. package/dist/theme.cjs.map +1 -1
  447. package/dist/theme.d.ts +2 -2
  448. package/dist/theme.js.map +1 -1
  449. package/dist/time-picker.cjs +1 -1
  450. package/dist/time-picker.cjs.map +1 -1
  451. package/dist/time-picker.js +1 -1
  452. package/dist/time-picker.js.map +1 -1
  453. package/dist/toast.cjs.map +1 -1
  454. package/dist/toast.js.map +1 -1
  455. package/dist/types/elements.d.ts +6 -2
  456. package/dist/typing-indicator.cjs.map +1 -1
  457. package/dist/typing-indicator.js.map +1 -1
  458. package/dist/utils/css.cjs.map +1 -1
  459. package/dist/utils/css.js.map +1 -1
  460. package/dist/utils/link.cjs.map +1 -1
  461. package/dist/utils/link.d.ts +2 -2
  462. package/dist/utils/link.js.map +1 -1
  463. package/package.json +31 -14
@@ -1 +1 @@
1
- {"version":3,"file":"table.css.cjs","names":[],"sources":["../../../src/content/table/table.css?inline"],"sourcesContent":["/* ore-table — shadow DOM (proxy/mirror).\n *\n * Shadow tree (managed by table.ts):\n * :host display:block — token host (tableBaseMixin)\n * └─ .scroll-container overflow:auto — sticky / max-height context\n * └─ table display:table — native table semantics\n * ├─ caption — optional caption bar\n * ├─ thead — rows built from ore-tr[head]\n * ├─ tbody — rows built from plain ore-tr\n * └─ tfoot — rows built from ore-tr[foot]\n * └─ tr > th / td — mirrored from ore-th / ore-td\n *\n * Light DOM (consumer):\n * <ore-tr head><ore-th>…</ore-th></ore-tr>\n * <ore-tr><ore-td>…</ore-td></ore-tr>\n * <ore-tr foot><ore-td>…</ore-td></ore-tr>\n *\n * tableBaseMixin('table') handles: :host tokens, .scroll-container,\n * table width/collapse, loading state, size variants, fullwidth, bordered,\n * and sticky scroll-container activation.\n * This file handles: caption, section backgrounds, body tr/th/td, and variants.\n * No ::slotted() needed — the shadow tree owns all rendered th/td elements.\n */\n\n@layer refine.base {\n :host {\n border: none;\n border-radius: 0;\n box-shadow: var(--table-shadow, none);\n }\n\n /* ── Caption ─────────────────────────────────────────────────────────── */\n\n caption {\n padding: var(--size-3);\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-align: start;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n caption-side: top;\n background: var(--_header-bg);\n }\n\n /* ── Section backgrounds ─────────────────────────────────────────────── */\n\n thead,\n tfoot {\n background: var(--_header-bg);\n }\n\n thead {\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n tfoot {\n border-block-start: var(--border) solid var(--_border-color);\n }\n\n /* ── Body rows ───────────────────────────────────────────────────────── */\n\n tbody tr {\n border-block-end: var(--border) solid var(--_border-color);\n transition: background var(--transition-fast);\n }\n\n tbody tr:hover {\n background: var(--_row-hover-bg);\n }\n\n /* ── Cells ───────────────────────────────────────────────────────────── */\n\n th,\n td {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n font-size: var(--_font-size);\n font-weight: normal;\n vertical-align: middle;\n color: var(--color-contrast-800);\n text-align: start;\n }\n\n /* ── Header cells ────────────────────────────────────────────────────── */\n\n thead th,\n thead td,\n tfoot th,\n tfoot td {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n white-space: nowrap;\n user-select: none;\n }\n}\n\n@layer refine.variants {\n /* ── Bordered: vertical cell separators ─────────────────────────────── */\n\n :host([bordered]) th,\n :host([bordered]) td {\n border-inline-end: var(--border) solid var(--_border-color);\n }\n\n :host([bordered]) th:last-child,\n :host([bordered]) td:last-child {\n border-inline-end: none;\n }\n\n /* ── Striped ─────────────────────────────────────────────────────────── */\n\n :host([striped]) tbody tr:nth-child(even) {\n background: var(--_stripe-bg);\n }\n\n /* ── Sticky header ───────────────────────────────────────────────────── */\n\n /* tableBaseMixin activates overflow-y:auto on .scroll-container when\n [sticky] is present. position:sticky on thead is relative to that context. */\n\n :host([sticky]) thead {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--table-sticky-header-bg, color-mix(in oklch, var(--_header-bg) 94%, transparent));\n\n /* border-collapse merges the bottom border into the cell below, which\n scrolls away under the sticky header. Use box-shadow instead — it\n travels with the element regardless of border-collapse. */\n box-shadow: inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n backdrop-filter: blur(var(--table-sticky-blur, var(--blur-md))) saturate(140%);\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"table.css.cjs","names":[],"sources":["../../../src/content/table/table.css?inline"],"sourcesContent":["/* ore-table: shadow DOM (proxy/mirror).\n *\n * Shadow tree (managed by table.ts):\n * :host (display:block): token host (tableBaseMixin)\n * └─ .scroll-container (overflow:auto): sticky / max-height context\n * └─ table (display:table): native table semantics\n * ├─ caption: optional caption bar\n * ├─ thead: rows built from ore-tr[head]\n * ├─ tbody: rows built from plain ore-tr\n * └─ tfoot: rows built from ore-tr[foot]\n * └─ tr > th / td: mirrored from ore-th / ore-td\n *\n * Light DOM (consumer):\n * <ore-tr head><ore-th>…</ore-th></ore-tr>\n * <ore-tr><ore-td>…</ore-td></ore-tr>\n * <ore-tr foot><ore-td>…</ore-td></ore-tr>\n *\n * tableBaseMixin('table') handles: :host tokens, .scroll-container,\n * table width/collapse, loading state, size variants, fullwidth, bordered,\n * and sticky scroll-container activation.\n * This file handles: caption, section backgrounds, body tr/th/td, and variants.\n * No ::slotted() needed: the shadow tree owns all rendered th/td elements.\n */\n\n@layer refine.base {\n :host {\n border: none;\n border-radius: 0;\n box-shadow: var(--table-shadow, none);\n }\n\n /* ── Caption ─────────────────────────────────────────────────────────── */\n\n caption {\n padding: var(--size-3);\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-align: start;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n caption-side: top;\n background: var(--_header-bg);\n }\n\n /* ── Section backgrounds ─────────────────────────────────────────────── */\n\n thead,\n tfoot {\n background: var(--_header-bg);\n }\n\n thead {\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n tfoot {\n border-block-start: var(--border) solid var(--_border-color);\n }\n\n /* ── Body rows ───────────────────────────────────────────────────────── */\n\n tbody tr {\n border-block-end: var(--border) solid var(--_border-color);\n transition: background var(--transition-fast);\n }\n\n tbody tr:hover {\n background: var(--_row-hover-bg);\n }\n\n /* ── Cells ───────────────────────────────────────────────────────────── */\n\n th,\n td {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n font-size: var(--_font-size);\n font-weight: normal;\n vertical-align: middle;\n color: var(--color-contrast-800);\n text-align: start;\n }\n\n /* ── Header cells ────────────────────────────────────────────────────── */\n\n thead th,\n thead td,\n tfoot th,\n tfoot td {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n white-space: nowrap;\n user-select: none;\n }\n}\n\n@layer refine.variants {\n /* ── Bordered: vertical cell separators ─────────────────────────────── */\n\n :host([bordered]) th,\n :host([bordered]) td {\n border-inline-end: var(--border) solid var(--_border-color);\n }\n\n :host([bordered]) th:last-child,\n :host([bordered]) td:last-child {\n border-inline-end: none;\n }\n\n /* ── Striped ─────────────────────────────────────────────────────────── */\n\n :host([striped]) tbody tr:nth-child(even) {\n background: var(--_stripe-bg);\n }\n\n /* ── Sticky header ───────────────────────────────────────────────────── */\n\n /* tableBaseMixin activates overflow-y:auto on .scroll-container when\n [sticky] is present. position:sticky on thead is relative to that context. */\n\n :host([sticky]) thead {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--table-sticky-header-bg, color-mix(in oklch, var(--_header-bg) 94%, transparent));\n\n /* border-collapse merges the bottom border into the cell below, which\n scrolls away under the sticky header. Use box-shadow instead: it\n travels with the element regardless of border-collapse. */\n box-shadow: inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n backdrop-filter: blur(var(--table-sticky-blur, var(--blur-md))) saturate(140%);\n }\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"table.css.js","names":[],"sources":["../../../src/content/table/table.css?inline"],"sourcesContent":["/* ore-table — shadow DOM (proxy/mirror).\n *\n * Shadow tree (managed by table.ts):\n * :host display:block — token host (tableBaseMixin)\n * └─ .scroll-container overflow:auto — sticky / max-height context\n * └─ table display:table — native table semantics\n * ├─ caption — optional caption bar\n * ├─ thead — rows built from ore-tr[head]\n * ├─ tbody — rows built from plain ore-tr\n * └─ tfoot — rows built from ore-tr[foot]\n * └─ tr > th / td — mirrored from ore-th / ore-td\n *\n * Light DOM (consumer):\n * <ore-tr head><ore-th>…</ore-th></ore-tr>\n * <ore-tr><ore-td>…</ore-td></ore-tr>\n * <ore-tr foot><ore-td>…</ore-td></ore-tr>\n *\n * tableBaseMixin('table') handles: :host tokens, .scroll-container,\n * table width/collapse, loading state, size variants, fullwidth, bordered,\n * and sticky scroll-container activation.\n * This file handles: caption, section backgrounds, body tr/th/td, and variants.\n * No ::slotted() needed — the shadow tree owns all rendered th/td elements.\n */\n\n@layer refine.base {\n :host {\n border: none;\n border-radius: 0;\n box-shadow: var(--table-shadow, none);\n }\n\n /* ── Caption ─────────────────────────────────────────────────────────── */\n\n caption {\n padding: var(--size-3);\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-align: start;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n caption-side: top;\n background: var(--_header-bg);\n }\n\n /* ── Section backgrounds ─────────────────────────────────────────────── */\n\n thead,\n tfoot {\n background: var(--_header-bg);\n }\n\n thead {\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n tfoot {\n border-block-start: var(--border) solid var(--_border-color);\n }\n\n /* ── Body rows ───────────────────────────────────────────────────────── */\n\n tbody tr {\n border-block-end: var(--border) solid var(--_border-color);\n transition: background var(--transition-fast);\n }\n\n tbody tr:hover {\n background: var(--_row-hover-bg);\n }\n\n /* ── Cells ───────────────────────────────────────────────────────────── */\n\n th,\n td {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n font-size: var(--_font-size);\n font-weight: normal;\n vertical-align: middle;\n color: var(--color-contrast-800);\n text-align: start;\n }\n\n /* ── Header cells ────────────────────────────────────────────────────── */\n\n thead th,\n thead td,\n tfoot th,\n tfoot td {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n white-space: nowrap;\n user-select: none;\n }\n}\n\n@layer refine.variants {\n /* ── Bordered: vertical cell separators ─────────────────────────────── */\n\n :host([bordered]) th,\n :host([bordered]) td {\n border-inline-end: var(--border) solid var(--_border-color);\n }\n\n :host([bordered]) th:last-child,\n :host([bordered]) td:last-child {\n border-inline-end: none;\n }\n\n /* ── Striped ─────────────────────────────────────────────────────────── */\n\n :host([striped]) tbody tr:nth-child(even) {\n background: var(--_stripe-bg);\n }\n\n /* ── Sticky header ───────────────────────────────────────────────────── */\n\n /* tableBaseMixin activates overflow-y:auto on .scroll-container when\n [sticky] is present. position:sticky on thead is relative to that context. */\n\n :host([sticky]) thead {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--table-sticky-header-bg, color-mix(in oklch, var(--_header-bg) 94%, transparent));\n\n /* border-collapse merges the bottom border into the cell below, which\n scrolls away under the sticky header. Use box-shadow instead — it\n travels with the element regardless of border-collapse. */\n box-shadow: inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n backdrop-filter: blur(var(--table-sticky-blur, var(--blur-md))) saturate(140%);\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"table.css.js","names":[],"sources":["../../../src/content/table/table.css?inline"],"sourcesContent":["/* ore-table: shadow DOM (proxy/mirror).\n *\n * Shadow tree (managed by table.ts):\n * :host (display:block): token host (tableBaseMixin)\n * └─ .scroll-container (overflow:auto): sticky / max-height context\n * └─ table (display:table): native table semantics\n * ├─ caption: optional caption bar\n * ├─ thead: rows built from ore-tr[head]\n * ├─ tbody: rows built from plain ore-tr\n * └─ tfoot: rows built from ore-tr[foot]\n * └─ tr > th / td: mirrored from ore-th / ore-td\n *\n * Light DOM (consumer):\n * <ore-tr head><ore-th>…</ore-th></ore-tr>\n * <ore-tr><ore-td>…</ore-td></ore-tr>\n * <ore-tr foot><ore-td>…</ore-td></ore-tr>\n *\n * tableBaseMixin('table') handles: :host tokens, .scroll-container,\n * table width/collapse, loading state, size variants, fullwidth, bordered,\n * and sticky scroll-container activation.\n * This file handles: caption, section backgrounds, body tr/th/td, and variants.\n * No ::slotted() needed: the shadow tree owns all rendered th/td elements.\n */\n\n@layer refine.base {\n :host {\n border: none;\n border-radius: 0;\n box-shadow: var(--table-shadow, none);\n }\n\n /* ── Caption ─────────────────────────────────────────────────────────── */\n\n caption {\n padding: var(--size-3);\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-align: start;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n caption-side: top;\n background: var(--_header-bg);\n }\n\n /* ── Section backgrounds ─────────────────────────────────────────────── */\n\n thead,\n tfoot {\n background: var(--_header-bg);\n }\n\n thead {\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n tfoot {\n border-block-start: var(--border) solid var(--_border-color);\n }\n\n /* ── Body rows ───────────────────────────────────────────────────────── */\n\n tbody tr {\n border-block-end: var(--border) solid var(--_border-color);\n transition: background var(--transition-fast);\n }\n\n tbody tr:hover {\n background: var(--_row-hover-bg);\n }\n\n /* ── Cells ───────────────────────────────────────────────────────────── */\n\n th,\n td {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n font-size: var(--_font-size);\n font-weight: normal;\n vertical-align: middle;\n color: var(--color-contrast-800);\n text-align: start;\n }\n\n /* ── Header cells ────────────────────────────────────────────────────── */\n\n thead th,\n thead td,\n tfoot th,\n tfoot td {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n white-space: nowrap;\n user-select: none;\n }\n}\n\n@layer refine.variants {\n /* ── Bordered: vertical cell separators ─────────────────────────────── */\n\n :host([bordered]) th,\n :host([bordered]) td {\n border-inline-end: var(--border) solid var(--_border-color);\n }\n\n :host([bordered]) th:last-child,\n :host([bordered]) td:last-child {\n border-inline-end: none;\n }\n\n /* ── Striped ─────────────────────────────────────────────────────────── */\n\n :host([striped]) tbody tr:nth-child(even) {\n background: var(--_stripe-bg);\n }\n\n /* ── Sticky header ───────────────────────────────────────────────────── */\n\n /* tableBaseMixin activates overflow-y:auto on .scroll-container when\n [sticky] is present. position:sticky on thead is relative to that context. */\n\n :host([sticky]) thead {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--table-sticky-header-bg, color-mix(in oklch, var(--_header-bg) 94%, transparent));\n\n /* border-collapse merges the bottom border into the cell below, which\n scrolls away under the sticky header. Use box-shadow instead: it\n travels with the element regardless of border-collapse. */\n box-shadow: inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n backdrop-filter: blur(var(--table-sticky-blur, var(--blur-md))) saturate(140%);\n }\n}\n"],"mappings":""}
@@ -2,7 +2,7 @@
2
2
  export type OreTableProps = {
3
3
  /** Adds a thicker outer border */
4
4
  bordered?: boolean;
5
- /** Visible caption text — also used as the accessible table label via `aria-label` */
5
+ /** Visible caption text: also used as the accessible table label via `aria-label` */
6
6
  caption?: string;
7
7
  /** Cell density: `'compact'` | `'cozy'` (default) | `'comfortable'` */
8
8
  density?: 'compact' | 'cozy' | 'comfortable';
@@ -22,8 +22,8 @@ export type OreTableProps = {
22
22
  * into a fully-native shadow `<table>`. Cell attributes (`colspan`, `rowspan`,
23
23
  * `scope`, etc.) are mirrored. Changes are observed and synced incrementally.
24
24
  *
25
- * Native table features — sticky headers, colspan/rowspan, the table layout
26
- * algorithm — all work because the shadow tree contains real table elements.
25
+ * Native table features: sticky headers, colspan/rowspan, the table layout
26
+ * algorithm: all work because the shadow tree contains real table elements.
27
27
  *
28
28
  * @element ore-table
29
29
  *
@@ -1 +1 @@
1
- {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../src/content/table/table.ts"],"names":[],"mappings":"AAOA,iCAAiC;AACjC,MAAM,MAAM,aAAa,GAAG;IAC1B,kCAAkC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,aAAa,CAAC;IAC7C,uDAAuD;IACvD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAkKF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,eAAO,MAAM,SAAS,EAAG,WAAoB,CAAC"}
1
+ {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../src/content/table/table.ts"],"names":[],"mappings":"AAOA,iCAAiC;AACjC,MAAM,MAAM,aAAa,GAAG;IAC1B,kCAAkC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,aAAa,CAAC;IAC7C,uDAAuD;IACvD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAkKF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,eAAO,MAAM,SAAS,EAAG,WAAoB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"text.css.cjs","names":[],"sources":["../../../src/content/text/text.css?inline"],"sourcesContent":["/* ========================================\n Base Styles & Defaults\n ======================================== */\n\n:host {\n /* Internal variables with CSS custom property fallbacks */\n --_size: var(--text-size, var(--text-base));\n --_weight: var(--text-weight, var(--font-normal));\n --_color: var(--text-color, var(--text-color-body));\n --_line-height: var(--text-line-height, var(--leading-normal));\n --_letter-spacing: var(--text-letter-spacing, normal);\n\n display: block;\n font-size: var(--_size);\n font-weight: var(--_weight);\n line-height: var(--_line-height);\n color: var(--_color);\n letter-spacing: var(--_letter-spacing);\n}\n\n:host([hidden]) {\n display: none;\n}\n\n/* ========================================\n Unlayered display overrides\n These must live outside any @layer so they can beat the unlayered\n display: block on :host. Same cascade rule that was fixed for [lines].\n ======================================== */\n\n/* Multi-line truncation */\n:host([lines]) {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n -webkit-line-clamp: var(--_lines);\n}\n\n/* Inline semantic tags — but not when align/truncate force block layout */\n:host(:is([as='span'], [as='label'], [as='code']):not([align], [truncate])) {\n display: inline;\n}\n\n/* ========================================\n Variant Styles\n ======================================== */\n\n/* Body (default) — uses base defaults above */\n\n/* Heading\n Default size: --heading-md (24px)\n Letter-spacing: --tracking-header (-0.05em) per theme guidelines\n Line-height: --leading-tight (1.15) for display headings */\n:host([variant='heading']) {\n --_size: var(--text-size, var(--heading-md));\n --_weight: var(--text-weight, var(--font-semibold));\n --_color: var(--text-color, var(--text-color-heading));\n --_line-height: var(--text-line-height, var(--leading-tight));\n --_letter-spacing: var(--text-letter-spacing, var(--tracking-header));\n}\n\n/* Label — slightly tighter line-height for UI labels */\n:host([variant='label']) {\n --_size: var(--text-size, var(--text-sm));\n --_weight: var(--text-weight, var(--font-medium));\n --_color: var(--text-color, var(--text-color-heading));\n --_line-height: var(--text-line-height, var(--leading-snug));\n}\n\n/* Caption */\n:host([variant='caption']) {\n --_size: var(--text-size, var(--text-sm));\n --_color: var(--text-color, var(--text-color-secondary));\n}\n\n/* Overline */\n:host([variant='overline']) {\n --_size: var(--text-size, var(--text-xs));\n --_weight: var(--text-weight, var(--font-semibold));\n --_line-height: var(--text-line-height, var(--leading-none));\n --_letter-spacing: var(--text-letter-spacing, 0.08em);\n\n text-transform: uppercase;\n}\n\n/* Code */\n:host([variant='code']) {\n --_size: var(--text-size, var(--text-sm));\n\n font-family: var(--font-mono, monospace);\n}\n\n/* ========================================\n Size Variants — body scale (--text-*)\n Used by all variants except heading.\n ======================================== */\n\n:host([size='2xs']) {\n --_size: var(--text-size, var(--text-2xs));\n}\n\n:host([size='xs']) {\n --_size: var(--text-size, var(--text-xs));\n}\n\n:host([size='sm']) {\n --_size: var(--text-size, var(--text-sm));\n}\n\n:host([size='md']) {\n --_size: var(--text-size, var(--text-base));\n}\n\n:host([size='lg']) {\n --_size: var(--text-size, var(--text-lg));\n}\n\n:host([size='xl']) {\n --_size: var(--text-size, var(--text-xl));\n}\n\n:host([size='2xl']) {\n --_size: var(--text-size, var(--text-2xl));\n}\n\n/* ========================================\n Size Variants — heading scale (--heading-*)\n Compound selectors override the body-scale rules above when\n variant=\"heading\" and a size is also provided.\n ======================================== */\n\n:host([variant='heading'][size='xs']) {\n --_size: var(--text-size, var(--heading-xs));\n}\n\n:host([variant='heading'][size='sm']) {\n --_size: var(--text-size, var(--heading-sm));\n}\n\n:host([variant='heading'][size='md']) {\n --_size: var(--text-size, var(--heading-md));\n}\n\n:host([variant='heading'][size='lg']) {\n --_size: var(--text-size, var(--heading-lg));\n}\n\n:host([variant='heading'][size='xl']) {\n --_size: var(--text-size, var(--heading-xl));\n}\n\n:host([variant='heading'][size='2xl']) {\n --_size: var(--text-size, var(--heading-2xl));\n}\n\n/* ========================================\n Weight Variants\n ======================================== */\n\n:host([weight='normal']) {\n --_weight: var(--text-weight, var(--font-normal));\n}\n\n:host([weight='medium']) {\n --_weight: var(--text-weight, var(--font-medium));\n}\n\n:host([weight='semibold']) {\n --_weight: var(--text-weight, var(--font-semibold));\n}\n\n:host([weight='bold']) {\n --_weight: var(--text-weight, var(--font-bold));\n}\n\n/* ========================================\n Color Variants\n Semantic colors map to --text-color-* tokens.\n Theme colors map to --color-* base tokens.\n ======================================== */\n\n:host([color='primary']) {\n --_color: var(--text-color, var(--color-primary));\n}\n\n:host([color='secondary']) {\n --_color: var(--text-color, var(--color-secondary));\n}\n\n:host([color='info']) {\n --_color: var(--text-color, var(--color-info));\n}\n\n:host([color='success']) {\n --_color: var(--text-color, var(--color-success));\n}\n\n:host([color='warning']) {\n --_color: var(--text-color, var(--color-warning));\n}\n\n:host([color='error']) {\n --_color: var(--text-color, var(--color-error));\n}\n\n/* Semantic text colors */\n:host([color='heading']) {\n --_color: var(--text-color, var(--text-color-heading)); /* --color-contrast-900 */\n}\n\n:host([color='body']) {\n --_color: var(--text-color, var(--text-color-body)); /* --color-contrast-800 */\n}\n\n:host([color='muted']) {\n --_color: var(--text-color, var(--text-color-secondary)); /* --color-contrast-600 — AA compliant */\n}\n\n:host([color='tertiary']) {\n --_color: var(--text-color, var(--text-color-tertiary)); /* --color-contrast-500 — AA for large text */\n}\n\n:host([color='disabled']) {\n --_color: var(--text-color, var(--text-color-disabled)); /* --color-contrast-400 */\n}\n\n:host([color='contrast']) {\n --_color: var(--text-color, var(--text-color-contrast)); /* --color-contrast-100 */\n}\n\n@layer refine.utilities {\n /* ========================================\n Alignment\n ======================================== */\n\n :host([align]) {\n display: block;\n }\n\n :host([align='left']) {\n text-align: start;\n }\n\n :host([align='center']) {\n text-align: center;\n }\n\n :host([align='right']) {\n text-align: end;\n }\n\n :host([align='justify']) {\n text-align: justify;\n }\n\n /* ========================================\n Truncate (single-line)\n ======================================== */\n\n :host([truncate]) {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ========================================\n Italic\n ======================================== */\n\n :host([italic]) {\n font-style: italic;\n }\n\n /* ========================================\n Block Display for Certain Tags\n ======================================== */\n\n :host([as='p']),\n :host([as='div']),\n :host(:is([as='h1'], [as='h2'], [as='h3'], [as='h4'], [as='h5'], [as='h6'])) {\n display: block;\n }\n\n /* Note: display: inline for span/label/code is handled outside this @layer\n (above) so it can override the unlayered :host { display: block }. */\n}\n"],"mappings":""}
1
+ {"version":3,"file":"text.css.cjs","names":[],"sources":["../../../src/content/text/text.css?inline"],"sourcesContent":["/* ========================================\n Base Styles & Defaults\n ======================================== */\n\n:host {\n /* Internal variables with CSS custom property fallbacks */\n --_size: var(--text-size, var(--text-base));\n --_weight: var(--text-weight, var(--font-normal));\n --_color: var(--text-color, var(--text-color-body));\n --_line-height: var(--text-line-height, var(--leading-normal));\n --_letter-spacing: var(--text-letter-spacing, normal);\n\n display: block;\n font-size: var(--_size);\n font-weight: var(--_weight);\n line-height: var(--_line-height);\n color: var(--_color);\n letter-spacing: var(--_letter-spacing);\n}\n\n:host([hidden]) {\n display: none;\n}\n\n/* ========================================\n Unlayered display overrides\n These must live outside any @layer so they can beat the unlayered\n display: block on :host. Same cascade rule that was fixed for [lines].\n ======================================== */\n\n/* Multi-line truncation */\n:host([lines]) {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n -webkit-line-clamp: var(--_lines);\n}\n\n/* Inline semantic tags, but not when align/truncate force block layout */\n:host(:is([as='span'], [as='label'], [as='code']):not([align], [truncate])) {\n display: inline;\n}\n\n/* ========================================\n Variant Styles\n ======================================== */\n\n/* Body (default): uses base defaults above */\n\n/* Heading\n Default size: --heading-md (24px)\n Letter-spacing: --tracking-header (-0.05em) per theme guidelines\n Line-height: --leading-tight (1.15) for display headings */\n:host([variant='heading']) {\n --_size: var(--text-size, var(--heading-md));\n --_weight: var(--text-weight, var(--font-semibold));\n --_color: var(--text-color, var(--text-color-heading));\n --_line-height: var(--text-line-height, var(--leading-tight));\n --_letter-spacing: var(--text-letter-spacing, var(--tracking-header));\n}\n\n/* Label: slightly tighter line-height for UI labels */\n:host([variant='label']) {\n --_size: var(--text-size, var(--text-sm));\n --_weight: var(--text-weight, var(--font-medium));\n --_color: var(--text-color, var(--text-color-heading));\n --_line-height: var(--text-line-height, var(--leading-snug));\n}\n\n/* Caption */\n:host([variant='caption']) {\n --_size: var(--text-size, var(--text-sm));\n --_color: var(--text-color, var(--text-color-secondary));\n}\n\n/* Overline */\n:host([variant='overline']) {\n --_size: var(--text-size, var(--text-xs));\n --_weight: var(--text-weight, var(--font-semibold));\n --_line-height: var(--text-line-height, var(--leading-none));\n --_letter-spacing: var(--text-letter-spacing, 0.08em);\n\n text-transform: uppercase;\n}\n\n/* Code */\n:host([variant='code']) {\n --_size: var(--text-size, var(--text-sm));\n\n font-family: var(--font-mono, monospace);\n}\n\n/* ========================================\n Size Variants: body scale (--text-*)\n Used by all variants except heading.\n ======================================== */\n\n:host([size='2xs']) {\n --_size: var(--text-size, var(--text-2xs));\n}\n\n:host([size='xs']) {\n --_size: var(--text-size, var(--text-xs));\n}\n\n:host([size='sm']) {\n --_size: var(--text-size, var(--text-sm));\n}\n\n:host([size='md']) {\n --_size: var(--text-size, var(--text-base));\n}\n\n:host([size='lg']) {\n --_size: var(--text-size, var(--text-lg));\n}\n\n:host([size='xl']) {\n --_size: var(--text-size, var(--text-xl));\n}\n\n:host([size='2xl']) {\n --_size: var(--text-size, var(--text-2xl));\n}\n\n/* ========================================\n Size Variants: heading scale (--heading-*)\n Compound selectors override the body-scale rules above when\n variant=\"heading\" and a size is also provided.\n ======================================== */\n\n:host([variant='heading'][size='xs']) {\n --_size: var(--text-size, var(--heading-xs));\n}\n\n:host([variant='heading'][size='sm']) {\n --_size: var(--text-size, var(--heading-sm));\n}\n\n:host([variant='heading'][size='md']) {\n --_size: var(--text-size, var(--heading-md));\n}\n\n:host([variant='heading'][size='lg']) {\n --_size: var(--text-size, var(--heading-lg));\n}\n\n:host([variant='heading'][size='xl']) {\n --_size: var(--text-size, var(--heading-xl));\n}\n\n:host([variant='heading'][size='2xl']) {\n --_size: var(--text-size, var(--heading-2xl));\n}\n\n/* ========================================\n Weight Variants\n ======================================== */\n\n:host([weight='normal']) {\n --_weight: var(--text-weight, var(--font-normal));\n}\n\n:host([weight='medium']) {\n --_weight: var(--text-weight, var(--font-medium));\n}\n\n:host([weight='semibold']) {\n --_weight: var(--text-weight, var(--font-semibold));\n}\n\n:host([weight='bold']) {\n --_weight: var(--text-weight, var(--font-bold));\n}\n\n/* ========================================\n Color Variants\n Semantic colors map to --text-color-* tokens.\n Theme colors map to --color-* base tokens.\n ======================================== */\n\n:host([color='primary']) {\n --_color: var(--text-color, var(--color-primary));\n}\n\n:host([color='secondary']) {\n --_color: var(--text-color, var(--color-secondary));\n}\n\n:host([color='info']) {\n --_color: var(--text-color, var(--color-info));\n}\n\n:host([color='success']) {\n --_color: var(--text-color, var(--color-success));\n}\n\n:host([color='warning']) {\n --_color: var(--text-color, var(--color-warning));\n}\n\n:host([color='error']) {\n --_color: var(--text-color, var(--color-error));\n}\n\n/* Semantic text colors */\n:host([color='heading']) {\n --_color: var(--text-color, var(--text-color-heading)); /* --color-contrast-900 */\n}\n\n:host([color='body']) {\n --_color: var(--text-color, var(--text-color-body)); /* --color-contrast-800 */\n}\n\n:host([color='muted']) {\n --_color: var(--text-color, var(--text-color-secondary)); /* --color-contrast-600: AA compliant */\n}\n\n:host([color='tertiary']) {\n --_color: var(--text-color, var(--text-color-tertiary)); /* --color-contrast-500: AA for large text */\n}\n\n:host([color='disabled']) {\n --_color: var(--text-color, var(--text-color-disabled)); /* --color-contrast-400 */\n}\n\n:host([color='contrast']) {\n --_color: var(--text-color, var(--text-color-contrast)); /* --color-contrast-100 */\n}\n\n@layer refine.utilities {\n /* ========================================\n Alignment\n ======================================== */\n\n :host([align]) {\n display: block;\n }\n\n :host([align='left']) {\n text-align: start;\n }\n\n :host([align='center']) {\n text-align: center;\n }\n\n :host([align='right']) {\n text-align: end;\n }\n\n :host([align='justify']) {\n text-align: justify;\n }\n\n /* ========================================\n Truncate (single-line)\n ======================================== */\n\n :host([truncate]) {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ========================================\n Italic\n ======================================== */\n\n :host([italic]) {\n font-style: italic;\n }\n\n /* ========================================\n Block Display for Certain Tags\n ======================================== */\n\n :host([as='p']),\n :host([as='div']),\n :host(:is([as='h1'], [as='h2'], [as='h3'], [as='h4'], [as='h5'], [as='h6'])) {\n display: block;\n }\n\n /* Note: display: inline for span/label/code is handled outside this @layer\n (above) so it can override the unlayered :host { display: block }. */\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"text.css.js","names":[],"sources":["../../../src/content/text/text.css?inline"],"sourcesContent":["/* ========================================\n Base Styles & Defaults\n ======================================== */\n\n:host {\n /* Internal variables with CSS custom property fallbacks */\n --_size: var(--text-size, var(--text-base));\n --_weight: var(--text-weight, var(--font-normal));\n --_color: var(--text-color, var(--text-color-body));\n --_line-height: var(--text-line-height, var(--leading-normal));\n --_letter-spacing: var(--text-letter-spacing, normal);\n\n display: block;\n font-size: var(--_size);\n font-weight: var(--_weight);\n line-height: var(--_line-height);\n color: var(--_color);\n letter-spacing: var(--_letter-spacing);\n}\n\n:host([hidden]) {\n display: none;\n}\n\n/* ========================================\n Unlayered display overrides\n These must live outside any @layer so they can beat the unlayered\n display: block on :host. Same cascade rule that was fixed for [lines].\n ======================================== */\n\n/* Multi-line truncation */\n:host([lines]) {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n -webkit-line-clamp: var(--_lines);\n}\n\n/* Inline semantic tags — but not when align/truncate force block layout */\n:host(:is([as='span'], [as='label'], [as='code']):not([align], [truncate])) {\n display: inline;\n}\n\n/* ========================================\n Variant Styles\n ======================================== */\n\n/* Body (default) — uses base defaults above */\n\n/* Heading\n Default size: --heading-md (24px)\n Letter-spacing: --tracking-header (-0.05em) per theme guidelines\n Line-height: --leading-tight (1.15) for display headings */\n:host([variant='heading']) {\n --_size: var(--text-size, var(--heading-md));\n --_weight: var(--text-weight, var(--font-semibold));\n --_color: var(--text-color, var(--text-color-heading));\n --_line-height: var(--text-line-height, var(--leading-tight));\n --_letter-spacing: var(--text-letter-spacing, var(--tracking-header));\n}\n\n/* Label — slightly tighter line-height for UI labels */\n:host([variant='label']) {\n --_size: var(--text-size, var(--text-sm));\n --_weight: var(--text-weight, var(--font-medium));\n --_color: var(--text-color, var(--text-color-heading));\n --_line-height: var(--text-line-height, var(--leading-snug));\n}\n\n/* Caption */\n:host([variant='caption']) {\n --_size: var(--text-size, var(--text-sm));\n --_color: var(--text-color, var(--text-color-secondary));\n}\n\n/* Overline */\n:host([variant='overline']) {\n --_size: var(--text-size, var(--text-xs));\n --_weight: var(--text-weight, var(--font-semibold));\n --_line-height: var(--text-line-height, var(--leading-none));\n --_letter-spacing: var(--text-letter-spacing, 0.08em);\n\n text-transform: uppercase;\n}\n\n/* Code */\n:host([variant='code']) {\n --_size: var(--text-size, var(--text-sm));\n\n font-family: var(--font-mono, monospace);\n}\n\n/* ========================================\n Size Variants — body scale (--text-*)\n Used by all variants except heading.\n ======================================== */\n\n:host([size='2xs']) {\n --_size: var(--text-size, var(--text-2xs));\n}\n\n:host([size='xs']) {\n --_size: var(--text-size, var(--text-xs));\n}\n\n:host([size='sm']) {\n --_size: var(--text-size, var(--text-sm));\n}\n\n:host([size='md']) {\n --_size: var(--text-size, var(--text-base));\n}\n\n:host([size='lg']) {\n --_size: var(--text-size, var(--text-lg));\n}\n\n:host([size='xl']) {\n --_size: var(--text-size, var(--text-xl));\n}\n\n:host([size='2xl']) {\n --_size: var(--text-size, var(--text-2xl));\n}\n\n/* ========================================\n Size Variants — heading scale (--heading-*)\n Compound selectors override the body-scale rules above when\n variant=\"heading\" and a size is also provided.\n ======================================== */\n\n:host([variant='heading'][size='xs']) {\n --_size: var(--text-size, var(--heading-xs));\n}\n\n:host([variant='heading'][size='sm']) {\n --_size: var(--text-size, var(--heading-sm));\n}\n\n:host([variant='heading'][size='md']) {\n --_size: var(--text-size, var(--heading-md));\n}\n\n:host([variant='heading'][size='lg']) {\n --_size: var(--text-size, var(--heading-lg));\n}\n\n:host([variant='heading'][size='xl']) {\n --_size: var(--text-size, var(--heading-xl));\n}\n\n:host([variant='heading'][size='2xl']) {\n --_size: var(--text-size, var(--heading-2xl));\n}\n\n/* ========================================\n Weight Variants\n ======================================== */\n\n:host([weight='normal']) {\n --_weight: var(--text-weight, var(--font-normal));\n}\n\n:host([weight='medium']) {\n --_weight: var(--text-weight, var(--font-medium));\n}\n\n:host([weight='semibold']) {\n --_weight: var(--text-weight, var(--font-semibold));\n}\n\n:host([weight='bold']) {\n --_weight: var(--text-weight, var(--font-bold));\n}\n\n/* ========================================\n Color Variants\n Semantic colors map to --text-color-* tokens.\n Theme colors map to --color-* base tokens.\n ======================================== */\n\n:host([color='primary']) {\n --_color: var(--text-color, var(--color-primary));\n}\n\n:host([color='secondary']) {\n --_color: var(--text-color, var(--color-secondary));\n}\n\n:host([color='info']) {\n --_color: var(--text-color, var(--color-info));\n}\n\n:host([color='success']) {\n --_color: var(--text-color, var(--color-success));\n}\n\n:host([color='warning']) {\n --_color: var(--text-color, var(--color-warning));\n}\n\n:host([color='error']) {\n --_color: var(--text-color, var(--color-error));\n}\n\n/* Semantic text colors */\n:host([color='heading']) {\n --_color: var(--text-color, var(--text-color-heading)); /* --color-contrast-900 */\n}\n\n:host([color='body']) {\n --_color: var(--text-color, var(--text-color-body)); /* --color-contrast-800 */\n}\n\n:host([color='muted']) {\n --_color: var(--text-color, var(--text-color-secondary)); /* --color-contrast-600 — AA compliant */\n}\n\n:host([color='tertiary']) {\n --_color: var(--text-color, var(--text-color-tertiary)); /* --color-contrast-500 — AA for large text */\n}\n\n:host([color='disabled']) {\n --_color: var(--text-color, var(--text-color-disabled)); /* --color-contrast-400 */\n}\n\n:host([color='contrast']) {\n --_color: var(--text-color, var(--text-color-contrast)); /* --color-contrast-100 */\n}\n\n@layer refine.utilities {\n /* ========================================\n Alignment\n ======================================== */\n\n :host([align]) {\n display: block;\n }\n\n :host([align='left']) {\n text-align: start;\n }\n\n :host([align='center']) {\n text-align: center;\n }\n\n :host([align='right']) {\n text-align: end;\n }\n\n :host([align='justify']) {\n text-align: justify;\n }\n\n /* ========================================\n Truncate (single-line)\n ======================================== */\n\n :host([truncate]) {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ========================================\n Italic\n ======================================== */\n\n :host([italic]) {\n font-style: italic;\n }\n\n /* ========================================\n Block Display for Certain Tags\n ======================================== */\n\n :host([as='p']),\n :host([as='div']),\n :host(:is([as='h1'], [as='h2'], [as='h3'], [as='h4'], [as='h5'], [as='h6'])) {\n display: block;\n }\n\n /* Note: display: inline for span/label/code is handled outside this @layer\n (above) so it can override the unlayered :host { display: block }. */\n}\n"],"mappings":""}
1
+ {"version":3,"file":"text.css.js","names":[],"sources":["../../../src/content/text/text.css?inline"],"sourcesContent":["/* ========================================\n Base Styles & Defaults\n ======================================== */\n\n:host {\n /* Internal variables with CSS custom property fallbacks */\n --_size: var(--text-size, var(--text-base));\n --_weight: var(--text-weight, var(--font-normal));\n --_color: var(--text-color, var(--text-color-body));\n --_line-height: var(--text-line-height, var(--leading-normal));\n --_letter-spacing: var(--text-letter-spacing, normal);\n\n display: block;\n font-size: var(--_size);\n font-weight: var(--_weight);\n line-height: var(--_line-height);\n color: var(--_color);\n letter-spacing: var(--_letter-spacing);\n}\n\n:host([hidden]) {\n display: none;\n}\n\n/* ========================================\n Unlayered display overrides\n These must live outside any @layer so they can beat the unlayered\n display: block on :host. Same cascade rule that was fixed for [lines].\n ======================================== */\n\n/* Multi-line truncation */\n:host([lines]) {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n -webkit-line-clamp: var(--_lines);\n}\n\n/* Inline semantic tags, but not when align/truncate force block layout */\n:host(:is([as='span'], [as='label'], [as='code']):not([align], [truncate])) {\n display: inline;\n}\n\n/* ========================================\n Variant Styles\n ======================================== */\n\n/* Body (default): uses base defaults above */\n\n/* Heading\n Default size: --heading-md (24px)\n Letter-spacing: --tracking-header (-0.05em) per theme guidelines\n Line-height: --leading-tight (1.15) for display headings */\n:host([variant='heading']) {\n --_size: var(--text-size, var(--heading-md));\n --_weight: var(--text-weight, var(--font-semibold));\n --_color: var(--text-color, var(--text-color-heading));\n --_line-height: var(--text-line-height, var(--leading-tight));\n --_letter-spacing: var(--text-letter-spacing, var(--tracking-header));\n}\n\n/* Label: slightly tighter line-height for UI labels */\n:host([variant='label']) {\n --_size: var(--text-size, var(--text-sm));\n --_weight: var(--text-weight, var(--font-medium));\n --_color: var(--text-color, var(--text-color-heading));\n --_line-height: var(--text-line-height, var(--leading-snug));\n}\n\n/* Caption */\n:host([variant='caption']) {\n --_size: var(--text-size, var(--text-sm));\n --_color: var(--text-color, var(--text-color-secondary));\n}\n\n/* Overline */\n:host([variant='overline']) {\n --_size: var(--text-size, var(--text-xs));\n --_weight: var(--text-weight, var(--font-semibold));\n --_line-height: var(--text-line-height, var(--leading-none));\n --_letter-spacing: var(--text-letter-spacing, 0.08em);\n\n text-transform: uppercase;\n}\n\n/* Code */\n:host([variant='code']) {\n --_size: var(--text-size, var(--text-sm));\n\n font-family: var(--font-mono, monospace);\n}\n\n/* ========================================\n Size Variants: body scale (--text-*)\n Used by all variants except heading.\n ======================================== */\n\n:host([size='2xs']) {\n --_size: var(--text-size, var(--text-2xs));\n}\n\n:host([size='xs']) {\n --_size: var(--text-size, var(--text-xs));\n}\n\n:host([size='sm']) {\n --_size: var(--text-size, var(--text-sm));\n}\n\n:host([size='md']) {\n --_size: var(--text-size, var(--text-base));\n}\n\n:host([size='lg']) {\n --_size: var(--text-size, var(--text-lg));\n}\n\n:host([size='xl']) {\n --_size: var(--text-size, var(--text-xl));\n}\n\n:host([size='2xl']) {\n --_size: var(--text-size, var(--text-2xl));\n}\n\n/* ========================================\n Size Variants: heading scale (--heading-*)\n Compound selectors override the body-scale rules above when\n variant=\"heading\" and a size is also provided.\n ======================================== */\n\n:host([variant='heading'][size='xs']) {\n --_size: var(--text-size, var(--heading-xs));\n}\n\n:host([variant='heading'][size='sm']) {\n --_size: var(--text-size, var(--heading-sm));\n}\n\n:host([variant='heading'][size='md']) {\n --_size: var(--text-size, var(--heading-md));\n}\n\n:host([variant='heading'][size='lg']) {\n --_size: var(--text-size, var(--heading-lg));\n}\n\n:host([variant='heading'][size='xl']) {\n --_size: var(--text-size, var(--heading-xl));\n}\n\n:host([variant='heading'][size='2xl']) {\n --_size: var(--text-size, var(--heading-2xl));\n}\n\n/* ========================================\n Weight Variants\n ======================================== */\n\n:host([weight='normal']) {\n --_weight: var(--text-weight, var(--font-normal));\n}\n\n:host([weight='medium']) {\n --_weight: var(--text-weight, var(--font-medium));\n}\n\n:host([weight='semibold']) {\n --_weight: var(--text-weight, var(--font-semibold));\n}\n\n:host([weight='bold']) {\n --_weight: var(--text-weight, var(--font-bold));\n}\n\n/* ========================================\n Color Variants\n Semantic colors map to --text-color-* tokens.\n Theme colors map to --color-* base tokens.\n ======================================== */\n\n:host([color='primary']) {\n --_color: var(--text-color, var(--color-primary));\n}\n\n:host([color='secondary']) {\n --_color: var(--text-color, var(--color-secondary));\n}\n\n:host([color='info']) {\n --_color: var(--text-color, var(--color-info));\n}\n\n:host([color='success']) {\n --_color: var(--text-color, var(--color-success));\n}\n\n:host([color='warning']) {\n --_color: var(--text-color, var(--color-warning));\n}\n\n:host([color='error']) {\n --_color: var(--text-color, var(--color-error));\n}\n\n/* Semantic text colors */\n:host([color='heading']) {\n --_color: var(--text-color, var(--text-color-heading)); /* --color-contrast-900 */\n}\n\n:host([color='body']) {\n --_color: var(--text-color, var(--text-color-body)); /* --color-contrast-800 */\n}\n\n:host([color='muted']) {\n --_color: var(--text-color, var(--text-color-secondary)); /* --color-contrast-600: AA compliant */\n}\n\n:host([color='tertiary']) {\n --_color: var(--text-color, var(--text-color-tertiary)); /* --color-contrast-500: AA for large text */\n}\n\n:host([color='disabled']) {\n --_color: var(--text-color, var(--text-color-disabled)); /* --color-contrast-400 */\n}\n\n:host([color='contrast']) {\n --_color: var(--text-color, var(--text-color-contrast)); /* --color-contrast-100 */\n}\n\n@layer refine.utilities {\n /* ========================================\n Alignment\n ======================================== */\n\n :host([align]) {\n display: block;\n }\n\n :host([align='left']) {\n text-align: start;\n }\n\n :host([align='center']) {\n text-align: center;\n }\n\n :host([align='right']) {\n text-align: end;\n }\n\n :host([align='justify']) {\n text-align: justify;\n }\n\n /* ========================================\n Truncate (single-line)\n ======================================== */\n\n :host([truncate]) {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ========================================\n Italic\n ======================================== */\n\n :host([italic]) {\n font-style: italic;\n }\n\n /* ========================================\n Block Display for Certain Tags\n ======================================== */\n\n :host([as='p']),\n :host([as='div']),\n :host(:is([as='h1'], [as='h2'], [as='h3'], [as='h4'], [as='h5'], [as='h6'])) {\n display: block;\n }\n\n /* Note: display: inline for span/label/code is handled outside this @layer\n (above) so it can override the unlayered :host { display: block }. */\n}\n"],"mappings":""}
@@ -2,7 +2,7 @@
2
2
  export type OreTextProps = {
3
3
  /** Text alignment */
4
4
  align?: 'left' | 'center' | 'right' | 'justify';
5
- /** Semantic HTML element to render as — sets the correct ARIA role/level on the host */
5
+ /** Semantic HTML element to render as: sets the correct ARIA role/level on the host */
6
6
  as?: 'span' | 'p' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'label' | 'code';
7
7
  /** Text color (semantic + theme colors) */
8
8
  color?: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error' | 'heading' | 'body' | 'muted' | 'tertiary' | 'disabled' | 'contrast';
@@ -10,7 +10,7 @@ export type OreTextProps = {
10
10
  italic?: boolean;
11
11
  /** Clamp text to N lines with an ellipsis (multi-line truncation) */
12
12
  lines?: number;
13
- /** Text size — maps to --text-* tokens for body variants and --heading-* tokens for the heading variant */
13
+ /** Text size: maps to --text-* tokens for body variants and --heading-* tokens for the heading variant */
14
14
  size?: '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
15
15
  /** Enable single-line text truncation with ellipsis */
16
16
  truncate?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/content/text/text.ts"],"names":[],"mappings":"AAIA,gCAAgC;AAChC,MAAM,MAAM,YAAY,GAAG;IACzB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAChD,wFAAwF;IACxF,EAAE,CAAC,EAAE,MAAM,GAAG,GAAG,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,CAAC;IACvF,2CAA2C;IAC3C,KAAK,CAAC,EACF,SAAS,GACT,WAAW,GACX,MAAM,GACN,SAAS,GACT,SAAS,GACT,OAAO,GACP,SAAS,GACT,MAAM,GACN,OAAO,GACP,UAAU,GACV,UAAU,GACV,UAAU,CAAC;IACf,wBAAwB;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2GAA2G;IAC3G,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,CAAC;IACxD,uDAAuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;IACzE,kBAAkB;IAClB,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;CACpD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,eAAO,MAAM,QAAQ,EAAG,UAAmB,CAAC"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/content/text/text.ts"],"names":[],"mappings":"AAIA,gCAAgC;AAChC,MAAM,MAAM,YAAY,GAAG;IACzB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAChD,uFAAuF;IACvF,EAAE,CAAC,EAAE,MAAM,GAAG,GAAG,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,CAAC;IACvF,2CAA2C;IAC3C,KAAK,CAAC,EACF,SAAS,GACT,WAAW,GACX,MAAM,GACN,SAAS,GACT,SAAS,GACT,OAAO,GACP,SAAS,GACT,MAAM,GACN,OAAO,GACP,UAAU,GACV,UAAU,GACV,UAAU,CAAC;IACf,wBAAwB;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0GAA0G;IAC1G,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,CAAC;IACxD,uDAAuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;IACzE,kBAAkB;IAClB,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;CACpD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,eAAO,MAAM,QAAQ,EAAG,UAAmB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"cookie-banner.cjs","names":[],"sources":["../src/feedback/cookie-banner/cookie-banner.ts"],"sourcesContent":["import { createId, define, each, getHost, html, prop, useEmit, when } from '@vielzeug/ore';\nimport { signal, watch } from '@vielzeug/ripple';\n\nimport { reducedMotionMixin } from '../../styles';\n\n// The child components this banner renders. The bare-import form is not enough:\n// the per-entry library build drops side-effect-only imports between entry\n// modules, so these re-export the tags as bindings — the bundler keeps the\n// module link, and importing `@vielzeug/refine/cookie-banner` registers\n// ore-button and ore-checkbox on its own.\n\nimport { eventFieldChecked } from '../../inputs/shared/native-field-event';\nimport componentStyles from './cookie-banner.css?inline';\n\nexport { BUTTON_TAG } from '../../inputs/button/button';\nexport { CHECKBOX_TAG } from '../../inputs/checkbox/checkbox';\n/** A consent category offered as an opt-in checkbox. `id` is the key the `decide` detail is built from. */\nexport type OreCookieBannerCategory = { description?: string; id: string; label: string };\n\n/** All rendered labels, each with an English default — pass translated strings for other locales. */\nexport type OreCookieBannerLabels = Partial<{\n acceptAll: string;\n essential: string;\n essentialNote: string;\n heading: string;\n reject: string;\n save: string;\n}>;\n\n/** The consent record emitted by `decide`: every category id plus `essential`, always true. */\nexport type CookieConsentRecord = Record<string, boolean>;\n\n/** Element interface exposing the imperative API for `ore-cookie-banner`. */\nexport interface CookieBannerElement extends HTMLElement, OreCookieBannerProps {\n /** Hides the banner without emitting anything — persistence stays with the consumer. */\n hide(): void;\n /** Shows the banner again, for example from a \"Cookie settings\" link after a decision. */\n show(): void;\n}\n\nexport type OreCookieBannerEvents = {\n /** A decision was made. `detail.consent` holds every category id; persist it and gate embeds on it. */\n decide: { consent: CookieConsentRecord };\n};\n\nexport type OreCookieBannerProps = {\n /** Optional categories rendered as checkboxes; \"Essential\" is always shown as always-on. */\n categories?: OreCookieBannerCategory[];\n /** Current consent record, used to seed the checkboxes when the banner is shown again. */\n consent?: CookieConsentRecord;\n /** Hides the banner automatically after `decide` fires (default true). Set false to keep it open. */\n hideOnDecide?: boolean;\n /** All rendered labels, each with an English default. */\n labels?: OreCookieBannerLabels;\n /** Viewport edge the banner docks to. */\n position?: 'bottom' | 'top';\n};\n\nconst DEFAULT_LABELS = {\n acceptAll: 'Accept all',\n essential: 'Essential',\n essentialNote: 'Required for the site to work',\n heading: 'Your privacy',\n reject: 'Reject non-essential',\n save: 'Save choices',\n} as const;\n\n/**\n * A fixed-position consent banner for cookie/tracker opt-in: a short policy text\n * (default slot, links included), optional per-category opt-in checkboxes with\n * descriptions, and accept-all / reject / save actions.\n *\n * The layout is a single column — heading, policy text, a scrollable category\n * panel, and actions — that scales linearly from one to many categories. Each\n * category renders as a checkbox row; its optional `description` passes through\n * the checkbox's `helper` mechanism, so it is both visible below the label and\n * linked to the control via `aria-describedby`. The category panel scrolls\n * internally when it exceeds `--cookie-banner-categories-max-height`, so the\n * banner itself never grows past the viewport.\n *\n * The banner owns presentation only. It never stores anything: listen for `decide`,\n * persist the emitted record yourself (localStorage, a CMP, your own backend), and\n * gate third-party embeds on it. The banner hides itself after `decide` unless\n * `hide-on-decide` is set to false. Call `show()` again to let the user revise\n * their choice.\n *\n * The banner is a non-modal `role=\"dialog\"`: screen readers announce it when it\n * appears, focus moves to the dialog card itself on `show()` — a neutral target,\n * so no action is preselected for keyboard users — and returns to the previously\n * focused element on `hide()`. A banner that is simply present at page load (never\n * shown via `show()`) does not steal focus and is not announced; consumers that\n * want a load-time announcement should call `show()` from a user gesture.\n *\n * The `decide` record contains exactly the currently configured category ids\n * (plus `essential`, always true). Replace — do not merge — the stored record\n * with it, so categories removed between sessions drop their stale keys.\n *\n * @element ore-cookie-banner\n *\n * @attr {string} position - Viewport edge: 'bottom' (default) | 'top'\n * @attr {boolean} hide-on-decide - Hides the banner after `decide` (default true)\n *\n * @fires decide - A decision was made. detail: { consent } — every category id plus `essential`, always true.\n *\n * @slot - Policy text, including links to the privacy policy and imprint\n *\n * @cssprop --cookie-banner-width - Maximum width of the banner card (default min(560px, viewport-fit))\n * @cssprop --cookie-banner-inset - Distance from the viewport edges (default --size-4)\n * @cssprop --cookie-banner-z-index - Stacking order (default --z-modal)\n * @cssprop --cookie-banner-categories-max-height - Category panel scroll threshold (default 16rem)\n *\n * @part banner - The banner card\n * @part text - The slot wrapper for the policy text\n * @part categories - The scrollable category panel\n * @part actions - The action buttons\n *\n * @example\n * ```html\n * <ore-cookie-banner>\n * We use cookies to keep you logged in and, with your consent, to embed music\n * from YouTube. <a href=\"/privacy\">Privacy policy</a>.\n * </ore-cookie-banner>\n * <script type=\"module\">\n * import '@vielzeug/refine/cookie-banner';\n *\n * const banner = document.querySelector('ore-cookie-banner');\n * banner.categories = [\n * { id: 'music', label: 'Music embeds', description: 'Embed music from YouTube' },\n * { id: 'stats', label: 'Statistics', description: 'Anonymous usage analytics' },\n * ];\n * banner.addEventListener('decide', (event) => {\n * localStorage.setItem('consent', JSON.stringify(event.detail.consent));\n * });\n * </script>\n * ```\n */\nexport const COOKIE_BANNER_TAG = 'ore-cookie-banner' as const;\ndefine<OreCookieBannerProps>(COOKIE_BANNER_TAG, {\n props: {\n categories: prop.data<OreCookieBannerCategory[]>([]),\n consent: prop.data<CookieConsentRecord>({}),\n hideOnDecide: prop.bool(true),\n labels: prop.data<Partial<OreCookieBannerLabels>>({}),\n position: prop.string<'bottom' | 'top'>(),\n },\n setup(props) {\n const host = getHost() as CookieBannerElement;\n const emit = useEmit<OreCookieBannerEvents>();\n const headingId = createId('cookie-banner-heading');\n const textId = createId('cookie-banner-text');\n\n // Working copy of the checkbox state while the banner is open; seeded from\n // `consent` whenever the banner is (re)shown.\n const draft = signal<CookieConsentRecord>({});\n\n const seedDraft = (): void => {\n const base = props.consent.value ?? {};\n draft.value = Object.fromEntries(\n (props.categories.value ?? []).map((category) => [category.id, base[category.id] === true]),\n );\n };\n watch(() => props.consent.value, seedDraft);\n watch(() => props.categories.value, seedDraft);\n seedDraft();\n\n const label = (key: keyof typeof DEFAULT_LABELS): string => props.labels.value?.[key] ?? DEFAULT_LABELS[key];\n\n // The element that had focus before the banner opened; `hide()` returns to it.\n let restoreFocusTo: HTMLElement | null = null;\n\n // Focus lands on the dialog card itself — a neutral target, so no action is\n // preselected for keyboard users. Screen readers announce the dialog's name\n // when focus enters it.\n const focusCard = (): void => {\n const card = host.shadowRoot?.querySelector<HTMLElement>('.banner');\n (card ?? host).focus();\n };\n\n const decide = (consent: CookieConsentRecord): void => {\n emit('decide', { consent: { ...consent, essential: true } });\n if (props.hideOnDecide.value !== false) host.hide();\n };\n\n const acceptAll = (): void => {\n decide(Object.fromEntries((props.categories.value ?? []).map((category) => [category.id, true])));\n };\n\n const reject = (): void => {\n decide(Object.fromEntries((props.categories.value ?? []).map((category) => [category.id, false])));\n };\n\n const save = (): void => {\n decide({ ...draft.value });\n };\n\n const setCategory = (id: string, checked: boolean): void => {\n draft.value = { ...draft.value, [id]: checked };\n };\n\n host.show = (): void => {\n if (!host.hidden) return;\n restoreFocusTo = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n seedDraft();\n host.hidden = false;\n focusCard();\n };\n host.hide = (): void => {\n host.hidden = true;\n restoreFocusTo?.focus();\n restoreFocusTo = null;\n };\n\n const categories = (): OreCookieBannerCategory[] => props.categories.value ?? [];\n\n return html`\n <div\n class=\"banner\"\n part=\"banner\"\n role=\"dialog\"\n aria-modal=\"false\"\n aria-labelledby=\"${headingId}\"\n aria-describedby=\"${textId}\"\n tabindex=\"-1\">\n <h2 class=\"heading\" part=\"heading\" id=\"${headingId}\">${() => label('heading')}</h2>\n <div class=\"text\" part=\"text\" id=\"${textId}\">\n <slot></slot>\n </div>\n ${when(\n () => categories().length > 0,\n () => html`\n <div class=\"categories\" part=\"categories\">\n <div class=\"category-row\">\n <ore-checkbox checked disabled helper=\"${() => label('essentialNote')}\">\n ${() => label('essential')}\n </ore-checkbox>\n </div>\n ${each(\n categories,\n (category) => category.id,\n (category) => html`\n <div class=\"category-row\">\n <ore-checkbox\n checked=\"${() => draft.value[category.value.id] === true}\"\n helper=\"${() => category.value.description ?? ''}\"\n @change=\"${(event: Event) => setCategory(category.value.id, eventFieldChecked(event))}\">\n ${() => category.value.label}\n </ore-checkbox>\n </div>\n `,\n )}\n </div>\n `,\n )}\n <div class=\"actions\" part=\"actions\">\n <ore-button size=\"sm\" variant=\"ghost\" @click=\"${reject}\">${() => label('reject')}</ore-button>\n ${when(\n () => categories().length > 0,\n () => html`<ore-button size=\"sm\" variant=\"bordered\" @click=\"${save}\">${() => label('save')}</ore-button>`,\n )}\n <ore-button color=\"primary\" size=\"sm\" @click=\"${acceptAll}\">${() => label('acceptAll')}</ore-button>\n </div>\n </div>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"cookie-banner.cjs","names":[],"sources":["../src/feedback/cookie-banner/cookie-banner.ts"],"sourcesContent":["import { createId, define, each, getHost, html, prop, useEmit, when } from '@vielzeug/ore';\nimport { signal, watch } from '@vielzeug/ripple';\n\nimport { reducedMotionMixin } from '../../styles';\n\n// The child components this banner renders. The bare-import form is not enough:\n// the per-entry library build drops side-effect-only imports between entry\n// modules, so these re-export the tags as bindings: the bundler keeps the\n// module link, and importing `@vielzeug/refine/cookie-banner` registers\n// ore-button and ore-checkbox on its own.\n\nimport { eventFieldChecked } from '../../inputs/shared/native-field-event';\nimport componentStyles from './cookie-banner.css?inline';\n\nexport { BUTTON_TAG } from '../../inputs/button/button';\nexport { CHECKBOX_TAG } from '../../inputs/checkbox/checkbox';\n/** A consent category offered as an opt-in checkbox. `id` is the key the `decide` detail is built from. */\nexport type OreCookieBannerCategory = { description?: string; id: string; label: string };\n\n/** All rendered labels, each with an English default: pass translated strings for other locales. */\nexport type OreCookieBannerLabels = Partial<{\n acceptAll: string;\n essential: string;\n essentialNote: string;\n heading: string;\n reject: string;\n save: string;\n}>;\n\n/** The consent record emitted by `decide`: every category id plus `essential`, always true. */\nexport type CookieConsentRecord = Record<string, boolean>;\n\n/** Element interface exposing the imperative API for `ore-cookie-banner`. */\nexport interface CookieBannerElement extends HTMLElement, OreCookieBannerProps {\n /** Hides the banner without emitting anything: persistence stays with the consumer. */\n hide(): void;\n /** Shows the banner again, for example from a \"Cookie settings\" link after a decision. */\n show(): void;\n}\n\nexport type OreCookieBannerEvents = {\n /** A decision was made. `detail.consent` holds every category id; persist it and gate embeds on it. */\n decide: { consent: CookieConsentRecord };\n};\n\nexport type OreCookieBannerProps = {\n /** Optional categories rendered as checkboxes; \"Essential\" is always shown as always-on. */\n categories?: OreCookieBannerCategory[];\n /** Current consent record, used to seed the checkboxes when the banner is shown again. */\n consent?: CookieConsentRecord;\n /** Hides the banner automatically after `decide` fires (default true). Set false to keep it open. */\n hideOnDecide?: boolean;\n /** All rendered labels, each with an English default. */\n labels?: OreCookieBannerLabels;\n /** Viewport edge the banner docks to. */\n position?: 'bottom' | 'top';\n};\n\nconst DEFAULT_LABELS = {\n acceptAll: 'Accept all',\n essential: 'Essential',\n essentialNote: 'Required for the site to work',\n heading: 'Your privacy',\n reject: 'Reject non-essential',\n save: 'Save choices',\n} as const;\n\n/**\n * A fixed-position consent banner for cookie/tracker opt-in: a short policy text\n * (default slot, links included), optional per-category opt-in checkboxes with\n * descriptions, and accept-all / reject / save actions.\n *\n * The layout is a single column: heading, policy text, a scrollable category\n * panel, and actions, that scales linearly from one to many categories. Each\n * category renders as a checkbox row; its optional `description` passes through\n * the checkbox's `helper` mechanism, so it is both visible below the label and\n * linked to the control via `aria-describedby`. The category panel scrolls\n * internally when it exceeds `--cookie-banner-categories-max-height`, so the\n * banner itself never grows past the viewport.\n *\n * The banner owns presentation only. It never stores anything: listen for `decide`,\n * persist the emitted record yourself (localStorage, a CMP, your own backend), and\n * gate third-party embeds on it. The banner hides itself after `decide` unless\n * `hide-on-decide` is set to false. Call `show()` again to let the user revise\n * their choice.\n *\n * The banner is a non-modal `role=\"dialog\"`: screen readers announce it when it\n * appears, focus moves to the dialog card itself on `show()`: a neutral target,\n * so no action is preselected for keyboard users, and returns to the previously\n * focused element on `hide()`. A banner that is simply present at page load (never\n * shown via `show()`) does not steal focus and is not announced; consumers that\n * want a load-time announcement should call `show()` from a user gesture.\n *\n * The `decide` record contains exactly the currently configured category ids\n * (plus `essential`, always true). Replace: do not merge: the stored record\n * with it, so categories removed between sessions drop their stale keys.\n *\n * @element ore-cookie-banner\n *\n * @attr {string} position - Viewport edge: 'bottom' (default) | 'top'\n * @attr {boolean} hide-on-decide - Hides the banner after `decide` (default true)\n *\n * @fires decide - A decision was made. detail: { consent }: every category id plus `essential`, always true.\n *\n * @slot - Policy text, including links to the privacy policy and imprint\n *\n * @cssprop --cookie-banner-width - Maximum width of the banner card (default min(560px, viewport-fit))\n * @cssprop --cookie-banner-inset - Distance from the viewport edges (default --size-4)\n * @cssprop --cookie-banner-z-index - Stacking order (default --z-modal)\n * @cssprop --cookie-banner-categories-max-height - Category panel scroll threshold (default 16rem)\n *\n * @part banner - The banner card\n * @part text - The slot wrapper for the policy text\n * @part categories - The scrollable category panel\n * @part actions - The action buttons\n *\n * @example\n * ```html\n * <ore-cookie-banner>\n * We use cookies to keep you logged in and, with your consent, to embed music\n * from YouTube. <a href=\"/privacy\">Privacy policy</a>.\n * </ore-cookie-banner>\n * <script type=\"module\">\n * import '@vielzeug/refine/cookie-banner';\n *\n * const banner = document.querySelector('ore-cookie-banner');\n * banner.categories = [\n * { id: 'music', label: 'Music embeds', description: 'Embed music from YouTube' },\n * { id: 'stats', label: 'Statistics', description: 'Anonymous usage analytics' },\n * ];\n * banner.addEventListener('decide', (event) => {\n * localStorage.setItem('consent', JSON.stringify(event.detail.consent));\n * });\n * </script>\n * ```\n */\nexport const COOKIE_BANNER_TAG = 'ore-cookie-banner' as const;\ndefine<OreCookieBannerProps>(COOKIE_BANNER_TAG, {\n props: {\n categories: prop.data<OreCookieBannerCategory[]>([]),\n consent: prop.data<CookieConsentRecord>({}),\n hideOnDecide: prop.bool(true),\n labels: prop.data<Partial<OreCookieBannerLabels>>({}),\n position: prop.string<'bottom' | 'top'>(),\n },\n setup(props) {\n const host = getHost() as CookieBannerElement;\n const emit = useEmit<OreCookieBannerEvents>();\n const headingId = createId('cookie-banner-heading');\n const textId = createId('cookie-banner-text');\n\n // Working copy of the checkbox state while the banner is open; seeded from\n // `consent` whenever the banner is (re)shown.\n const draft = signal<CookieConsentRecord>({});\n\n const seedDraft = (): void => {\n const base = props.consent.value ?? {};\n draft.value = Object.fromEntries(\n (props.categories.value ?? []).map((category) => [category.id, base[category.id] === true]),\n );\n };\n watch(() => props.consent.value, seedDraft);\n watch(() => props.categories.value, seedDraft);\n seedDraft();\n\n const label = (key: keyof typeof DEFAULT_LABELS): string => props.labels.value?.[key] ?? DEFAULT_LABELS[key];\n\n // The element that had focus before the banner opened; `hide()` returns to it.\n let restoreFocusTo: HTMLElement | null = null;\n\n // Focus lands on the dialog card itself: a neutral target, so no action is\n // preselected for keyboard users. Screen readers announce the dialog's name\n // when focus enters it.\n const focusCard = (): void => {\n const card = host.shadowRoot?.querySelector<HTMLElement>('.banner');\n (card ?? host).focus();\n };\n\n const decide = (consent: CookieConsentRecord): void => {\n emit('decide', { consent: { ...consent, essential: true } });\n if (props.hideOnDecide.value !== false) host.hide();\n };\n\n const acceptAll = (): void => {\n decide(Object.fromEntries((props.categories.value ?? []).map((category) => [category.id, true])));\n };\n\n const reject = (): void => {\n decide(Object.fromEntries((props.categories.value ?? []).map((category) => [category.id, false])));\n };\n\n const save = (): void => {\n decide({ ...draft.value });\n };\n\n const setCategory = (id: string, checked: boolean): void => {\n draft.value = { ...draft.value, [id]: checked };\n };\n\n host.show = (): void => {\n if (!host.hidden) return;\n restoreFocusTo = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n seedDraft();\n host.hidden = false;\n focusCard();\n };\n host.hide = (): void => {\n host.hidden = true;\n restoreFocusTo?.focus();\n restoreFocusTo = null;\n };\n\n const categories = (): OreCookieBannerCategory[] => props.categories.value ?? [];\n\n return html`\n <div\n class=\"banner\"\n part=\"banner\"\n role=\"dialog\"\n aria-modal=\"false\"\n aria-labelledby=\"${headingId}\"\n aria-describedby=\"${textId}\"\n tabindex=\"-1\">\n <h2 class=\"heading\" part=\"heading\" id=\"${headingId}\">${() => label('heading')}</h2>\n <div class=\"text\" part=\"text\" id=\"${textId}\">\n <slot></slot>\n </div>\n ${when(\n () => categories().length > 0,\n () => html`\n <div class=\"categories\" part=\"categories\">\n <div class=\"category-row\">\n <ore-checkbox checked disabled helper=\"${() => label('essentialNote')}\">\n ${() => label('essential')}\n </ore-checkbox>\n </div>\n ${each(\n categories,\n (category) => category.id,\n (category) => html`\n <div class=\"category-row\">\n <ore-checkbox\n checked=\"${() => draft.value[category.value.id] === true}\"\n helper=\"${() => category.value.description ?? ''}\"\n @change=\"${(event: Event) => setCategory(category.value.id, eventFieldChecked(event))}\">\n ${() => category.value.label}\n </ore-checkbox>\n </div>\n `,\n )}\n </div>\n `,\n )}\n <div class=\"actions\" part=\"actions\">\n <ore-button size=\"sm\" variant=\"ghost\" @click=\"${reject}\">${() => label('reject')}</ore-button>\n ${when(\n () => categories().length > 0,\n () => html`<ore-button size=\"sm\" variant=\"bordered\" @click=\"${save}\">${() => label('save')}</ore-button>`,\n )}\n <ore-button color=\"primary\" size=\"sm\" @click=\"${acceptAll}\">${() => label('acceptAll')}</ore-button>\n </div>\n </div>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cookie-banner.js","names":[],"sources":["../src/feedback/cookie-banner/cookie-banner.ts"],"sourcesContent":["import { createId, define, each, getHost, html, prop, useEmit, when } from '@vielzeug/ore';\nimport { signal, watch } from '@vielzeug/ripple';\n\nimport { reducedMotionMixin } from '../../styles';\n\n// The child components this banner renders. The bare-import form is not enough:\n// the per-entry library build drops side-effect-only imports between entry\n// modules, so these re-export the tags as bindings — the bundler keeps the\n// module link, and importing `@vielzeug/refine/cookie-banner` registers\n// ore-button and ore-checkbox on its own.\n\nimport { eventFieldChecked } from '../../inputs/shared/native-field-event';\nimport componentStyles from './cookie-banner.css?inline';\n\nexport { BUTTON_TAG } from '../../inputs/button/button';\nexport { CHECKBOX_TAG } from '../../inputs/checkbox/checkbox';\n/** A consent category offered as an opt-in checkbox. `id` is the key the `decide` detail is built from. */\nexport type OreCookieBannerCategory = { description?: string; id: string; label: string };\n\n/** All rendered labels, each with an English default — pass translated strings for other locales. */\nexport type OreCookieBannerLabels = Partial<{\n acceptAll: string;\n essential: string;\n essentialNote: string;\n heading: string;\n reject: string;\n save: string;\n}>;\n\n/** The consent record emitted by `decide`: every category id plus `essential`, always true. */\nexport type CookieConsentRecord = Record<string, boolean>;\n\n/** Element interface exposing the imperative API for `ore-cookie-banner`. */\nexport interface CookieBannerElement extends HTMLElement, OreCookieBannerProps {\n /** Hides the banner without emitting anything — persistence stays with the consumer. */\n hide(): void;\n /** Shows the banner again, for example from a \"Cookie settings\" link after a decision. */\n show(): void;\n}\n\nexport type OreCookieBannerEvents = {\n /** A decision was made. `detail.consent` holds every category id; persist it and gate embeds on it. */\n decide: { consent: CookieConsentRecord };\n};\n\nexport type OreCookieBannerProps = {\n /** Optional categories rendered as checkboxes; \"Essential\" is always shown as always-on. */\n categories?: OreCookieBannerCategory[];\n /** Current consent record, used to seed the checkboxes when the banner is shown again. */\n consent?: CookieConsentRecord;\n /** Hides the banner automatically after `decide` fires (default true). Set false to keep it open. */\n hideOnDecide?: boolean;\n /** All rendered labels, each with an English default. */\n labels?: OreCookieBannerLabels;\n /** Viewport edge the banner docks to. */\n position?: 'bottom' | 'top';\n};\n\nconst DEFAULT_LABELS = {\n acceptAll: 'Accept all',\n essential: 'Essential',\n essentialNote: 'Required for the site to work',\n heading: 'Your privacy',\n reject: 'Reject non-essential',\n save: 'Save choices',\n} as const;\n\n/**\n * A fixed-position consent banner for cookie/tracker opt-in: a short policy text\n * (default slot, links included), optional per-category opt-in checkboxes with\n * descriptions, and accept-all / reject / save actions.\n *\n * The layout is a single column — heading, policy text, a scrollable category\n * panel, and actions — that scales linearly from one to many categories. Each\n * category renders as a checkbox row; its optional `description` passes through\n * the checkbox's `helper` mechanism, so it is both visible below the label and\n * linked to the control via `aria-describedby`. The category panel scrolls\n * internally when it exceeds `--cookie-banner-categories-max-height`, so the\n * banner itself never grows past the viewport.\n *\n * The banner owns presentation only. It never stores anything: listen for `decide`,\n * persist the emitted record yourself (localStorage, a CMP, your own backend), and\n * gate third-party embeds on it. The banner hides itself after `decide` unless\n * `hide-on-decide` is set to false. Call `show()` again to let the user revise\n * their choice.\n *\n * The banner is a non-modal `role=\"dialog\"`: screen readers announce it when it\n * appears, focus moves to the dialog card itself on `show()` — a neutral target,\n * so no action is preselected for keyboard users — and returns to the previously\n * focused element on `hide()`. A banner that is simply present at page load (never\n * shown via `show()`) does not steal focus and is not announced; consumers that\n * want a load-time announcement should call `show()` from a user gesture.\n *\n * The `decide` record contains exactly the currently configured category ids\n * (plus `essential`, always true). Replace — do not merge — the stored record\n * with it, so categories removed between sessions drop their stale keys.\n *\n * @element ore-cookie-banner\n *\n * @attr {string} position - Viewport edge: 'bottom' (default) | 'top'\n * @attr {boolean} hide-on-decide - Hides the banner after `decide` (default true)\n *\n * @fires decide - A decision was made. detail: { consent } — every category id plus `essential`, always true.\n *\n * @slot - Policy text, including links to the privacy policy and imprint\n *\n * @cssprop --cookie-banner-width - Maximum width of the banner card (default min(560px, viewport-fit))\n * @cssprop --cookie-banner-inset - Distance from the viewport edges (default --size-4)\n * @cssprop --cookie-banner-z-index - Stacking order (default --z-modal)\n * @cssprop --cookie-banner-categories-max-height - Category panel scroll threshold (default 16rem)\n *\n * @part banner - The banner card\n * @part text - The slot wrapper for the policy text\n * @part categories - The scrollable category panel\n * @part actions - The action buttons\n *\n * @example\n * ```html\n * <ore-cookie-banner>\n * We use cookies to keep you logged in and, with your consent, to embed music\n * from YouTube. <a href=\"/privacy\">Privacy policy</a>.\n * </ore-cookie-banner>\n * <script type=\"module\">\n * import '@vielzeug/refine/cookie-banner';\n *\n * const banner = document.querySelector('ore-cookie-banner');\n * banner.categories = [\n * { id: 'music', label: 'Music embeds', description: 'Embed music from YouTube' },\n * { id: 'stats', label: 'Statistics', description: 'Anonymous usage analytics' },\n * ];\n * banner.addEventListener('decide', (event) => {\n * localStorage.setItem('consent', JSON.stringify(event.detail.consent));\n * });\n * </script>\n * ```\n */\nexport const COOKIE_BANNER_TAG = 'ore-cookie-banner' as const;\ndefine<OreCookieBannerProps>(COOKIE_BANNER_TAG, {\n props: {\n categories: prop.data<OreCookieBannerCategory[]>([]),\n consent: prop.data<CookieConsentRecord>({}),\n hideOnDecide: prop.bool(true),\n labels: prop.data<Partial<OreCookieBannerLabels>>({}),\n position: prop.string<'bottom' | 'top'>(),\n },\n setup(props) {\n const host = getHost() as CookieBannerElement;\n const emit = useEmit<OreCookieBannerEvents>();\n const headingId = createId('cookie-banner-heading');\n const textId = createId('cookie-banner-text');\n\n // Working copy of the checkbox state while the banner is open; seeded from\n // `consent` whenever the banner is (re)shown.\n const draft = signal<CookieConsentRecord>({});\n\n const seedDraft = (): void => {\n const base = props.consent.value ?? {};\n draft.value = Object.fromEntries(\n (props.categories.value ?? []).map((category) => [category.id, base[category.id] === true]),\n );\n };\n watch(() => props.consent.value, seedDraft);\n watch(() => props.categories.value, seedDraft);\n seedDraft();\n\n const label = (key: keyof typeof DEFAULT_LABELS): string => props.labels.value?.[key] ?? DEFAULT_LABELS[key];\n\n // The element that had focus before the banner opened; `hide()` returns to it.\n let restoreFocusTo: HTMLElement | null = null;\n\n // Focus lands on the dialog card itself — a neutral target, so no action is\n // preselected for keyboard users. Screen readers announce the dialog's name\n // when focus enters it.\n const focusCard = (): void => {\n const card = host.shadowRoot?.querySelector<HTMLElement>('.banner');\n (card ?? host).focus();\n };\n\n const decide = (consent: CookieConsentRecord): void => {\n emit('decide', { consent: { ...consent, essential: true } });\n if (props.hideOnDecide.value !== false) host.hide();\n };\n\n const acceptAll = (): void => {\n decide(Object.fromEntries((props.categories.value ?? []).map((category) => [category.id, true])));\n };\n\n const reject = (): void => {\n decide(Object.fromEntries((props.categories.value ?? []).map((category) => [category.id, false])));\n };\n\n const save = (): void => {\n decide({ ...draft.value });\n };\n\n const setCategory = (id: string, checked: boolean): void => {\n draft.value = { ...draft.value, [id]: checked };\n };\n\n host.show = (): void => {\n if (!host.hidden) return;\n restoreFocusTo = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n seedDraft();\n host.hidden = false;\n focusCard();\n };\n host.hide = (): void => {\n host.hidden = true;\n restoreFocusTo?.focus();\n restoreFocusTo = null;\n };\n\n const categories = (): OreCookieBannerCategory[] => props.categories.value ?? [];\n\n return html`\n <div\n class=\"banner\"\n part=\"banner\"\n role=\"dialog\"\n aria-modal=\"false\"\n aria-labelledby=\"${headingId}\"\n aria-describedby=\"${textId}\"\n tabindex=\"-1\">\n <h2 class=\"heading\" part=\"heading\" id=\"${headingId}\">${() => label('heading')}</h2>\n <div class=\"text\" part=\"text\" id=\"${textId}\">\n <slot></slot>\n </div>\n ${when(\n () => categories().length > 0,\n () => html`\n <div class=\"categories\" part=\"categories\">\n <div class=\"category-row\">\n <ore-checkbox checked disabled helper=\"${() => label('essentialNote')}\">\n ${() => label('essential')}\n </ore-checkbox>\n </div>\n ${each(\n categories,\n (category) => category.id,\n (category) => html`\n <div class=\"category-row\">\n <ore-checkbox\n checked=\"${() => draft.value[category.value.id] === true}\"\n helper=\"${() => category.value.description ?? ''}\"\n @change=\"${(event: Event) => setCategory(category.value.id, eventFieldChecked(event))}\">\n ${() => category.value.label}\n </ore-checkbox>\n </div>\n `,\n )}\n </div>\n `,\n )}\n <div class=\"actions\" part=\"actions\">\n <ore-button size=\"sm\" variant=\"ghost\" @click=\"${reject}\">${() => label('reject')}</ore-button>\n ${when(\n () => categories().length > 0,\n () => html`<ore-button size=\"sm\" variant=\"bordered\" @click=\"${save}\">${() => label('save')}</ore-button>`,\n )}\n <ore-button color=\"primary\" size=\"sm\" @click=\"${acceptAll}\">${() => label('acceptAll')}</ore-button>\n </div>\n </div>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"cookie-banner.js","names":[],"sources":["../src/feedback/cookie-banner/cookie-banner.ts"],"sourcesContent":["import { createId, define, each, getHost, html, prop, useEmit, when } from '@vielzeug/ore';\nimport { signal, watch } from '@vielzeug/ripple';\n\nimport { reducedMotionMixin } from '../../styles';\n\n// The child components this banner renders. The bare-import form is not enough:\n// the per-entry library build drops side-effect-only imports between entry\n// modules, so these re-export the tags as bindings: the bundler keeps the\n// module link, and importing `@vielzeug/refine/cookie-banner` registers\n// ore-button and ore-checkbox on its own.\n\nimport { eventFieldChecked } from '../../inputs/shared/native-field-event';\nimport componentStyles from './cookie-banner.css?inline';\n\nexport { BUTTON_TAG } from '../../inputs/button/button';\nexport { CHECKBOX_TAG } from '../../inputs/checkbox/checkbox';\n/** A consent category offered as an opt-in checkbox. `id` is the key the `decide` detail is built from. */\nexport type OreCookieBannerCategory = { description?: string; id: string; label: string };\n\n/** All rendered labels, each with an English default: pass translated strings for other locales. */\nexport type OreCookieBannerLabels = Partial<{\n acceptAll: string;\n essential: string;\n essentialNote: string;\n heading: string;\n reject: string;\n save: string;\n}>;\n\n/** The consent record emitted by `decide`: every category id plus `essential`, always true. */\nexport type CookieConsentRecord = Record<string, boolean>;\n\n/** Element interface exposing the imperative API for `ore-cookie-banner`. */\nexport interface CookieBannerElement extends HTMLElement, OreCookieBannerProps {\n /** Hides the banner without emitting anything: persistence stays with the consumer. */\n hide(): void;\n /** Shows the banner again, for example from a \"Cookie settings\" link after a decision. */\n show(): void;\n}\n\nexport type OreCookieBannerEvents = {\n /** A decision was made. `detail.consent` holds every category id; persist it and gate embeds on it. */\n decide: { consent: CookieConsentRecord };\n};\n\nexport type OreCookieBannerProps = {\n /** Optional categories rendered as checkboxes; \"Essential\" is always shown as always-on. */\n categories?: OreCookieBannerCategory[];\n /** Current consent record, used to seed the checkboxes when the banner is shown again. */\n consent?: CookieConsentRecord;\n /** Hides the banner automatically after `decide` fires (default true). Set false to keep it open. */\n hideOnDecide?: boolean;\n /** All rendered labels, each with an English default. */\n labels?: OreCookieBannerLabels;\n /** Viewport edge the banner docks to. */\n position?: 'bottom' | 'top';\n};\n\nconst DEFAULT_LABELS = {\n acceptAll: 'Accept all',\n essential: 'Essential',\n essentialNote: 'Required for the site to work',\n heading: 'Your privacy',\n reject: 'Reject non-essential',\n save: 'Save choices',\n} as const;\n\n/**\n * A fixed-position consent banner for cookie/tracker opt-in: a short policy text\n * (default slot, links included), optional per-category opt-in checkboxes with\n * descriptions, and accept-all / reject / save actions.\n *\n * The layout is a single column: heading, policy text, a scrollable category\n * panel, and actions, that scales linearly from one to many categories. Each\n * category renders as a checkbox row; its optional `description` passes through\n * the checkbox's `helper` mechanism, so it is both visible below the label and\n * linked to the control via `aria-describedby`. The category panel scrolls\n * internally when it exceeds `--cookie-banner-categories-max-height`, so the\n * banner itself never grows past the viewport.\n *\n * The banner owns presentation only. It never stores anything: listen for `decide`,\n * persist the emitted record yourself (localStorage, a CMP, your own backend), and\n * gate third-party embeds on it. The banner hides itself after `decide` unless\n * `hide-on-decide` is set to false. Call `show()` again to let the user revise\n * their choice.\n *\n * The banner is a non-modal `role=\"dialog\"`: screen readers announce it when it\n * appears, focus moves to the dialog card itself on `show()`: a neutral target,\n * so no action is preselected for keyboard users, and returns to the previously\n * focused element on `hide()`. A banner that is simply present at page load (never\n * shown via `show()`) does not steal focus and is not announced; consumers that\n * want a load-time announcement should call `show()` from a user gesture.\n *\n * The `decide` record contains exactly the currently configured category ids\n * (plus `essential`, always true). Replace: do not merge: the stored record\n * with it, so categories removed between sessions drop their stale keys.\n *\n * @element ore-cookie-banner\n *\n * @attr {string} position - Viewport edge: 'bottom' (default) | 'top'\n * @attr {boolean} hide-on-decide - Hides the banner after `decide` (default true)\n *\n * @fires decide - A decision was made. detail: { consent }: every category id plus `essential`, always true.\n *\n * @slot - Policy text, including links to the privacy policy and imprint\n *\n * @cssprop --cookie-banner-width - Maximum width of the banner card (default min(560px, viewport-fit))\n * @cssprop --cookie-banner-inset - Distance from the viewport edges (default --size-4)\n * @cssprop --cookie-banner-z-index - Stacking order (default --z-modal)\n * @cssprop --cookie-banner-categories-max-height - Category panel scroll threshold (default 16rem)\n *\n * @part banner - The banner card\n * @part text - The slot wrapper for the policy text\n * @part categories - The scrollable category panel\n * @part actions - The action buttons\n *\n * @example\n * ```html\n * <ore-cookie-banner>\n * We use cookies to keep you logged in and, with your consent, to embed music\n * from YouTube. <a href=\"/privacy\">Privacy policy</a>.\n * </ore-cookie-banner>\n * <script type=\"module\">\n * import '@vielzeug/refine/cookie-banner';\n *\n * const banner = document.querySelector('ore-cookie-banner');\n * banner.categories = [\n * { id: 'music', label: 'Music embeds', description: 'Embed music from YouTube' },\n * { id: 'stats', label: 'Statistics', description: 'Anonymous usage analytics' },\n * ];\n * banner.addEventListener('decide', (event) => {\n * localStorage.setItem('consent', JSON.stringify(event.detail.consent));\n * });\n * </script>\n * ```\n */\nexport const COOKIE_BANNER_TAG = 'ore-cookie-banner' as const;\ndefine<OreCookieBannerProps>(COOKIE_BANNER_TAG, {\n props: {\n categories: prop.data<OreCookieBannerCategory[]>([]),\n consent: prop.data<CookieConsentRecord>({}),\n hideOnDecide: prop.bool(true),\n labels: prop.data<Partial<OreCookieBannerLabels>>({}),\n position: prop.string<'bottom' | 'top'>(),\n },\n setup(props) {\n const host = getHost() as CookieBannerElement;\n const emit = useEmit<OreCookieBannerEvents>();\n const headingId = createId('cookie-banner-heading');\n const textId = createId('cookie-banner-text');\n\n // Working copy of the checkbox state while the banner is open; seeded from\n // `consent` whenever the banner is (re)shown.\n const draft = signal<CookieConsentRecord>({});\n\n const seedDraft = (): void => {\n const base = props.consent.value ?? {};\n draft.value = Object.fromEntries(\n (props.categories.value ?? []).map((category) => [category.id, base[category.id] === true]),\n );\n };\n watch(() => props.consent.value, seedDraft);\n watch(() => props.categories.value, seedDraft);\n seedDraft();\n\n const label = (key: keyof typeof DEFAULT_LABELS): string => props.labels.value?.[key] ?? DEFAULT_LABELS[key];\n\n // The element that had focus before the banner opened; `hide()` returns to it.\n let restoreFocusTo: HTMLElement | null = null;\n\n // Focus lands on the dialog card itself: a neutral target, so no action is\n // preselected for keyboard users. Screen readers announce the dialog's name\n // when focus enters it.\n const focusCard = (): void => {\n const card = host.shadowRoot?.querySelector<HTMLElement>('.banner');\n (card ?? host).focus();\n };\n\n const decide = (consent: CookieConsentRecord): void => {\n emit('decide', { consent: { ...consent, essential: true } });\n if (props.hideOnDecide.value !== false) host.hide();\n };\n\n const acceptAll = (): void => {\n decide(Object.fromEntries((props.categories.value ?? []).map((category) => [category.id, true])));\n };\n\n const reject = (): void => {\n decide(Object.fromEntries((props.categories.value ?? []).map((category) => [category.id, false])));\n };\n\n const save = (): void => {\n decide({ ...draft.value });\n };\n\n const setCategory = (id: string, checked: boolean): void => {\n draft.value = { ...draft.value, [id]: checked };\n };\n\n host.show = (): void => {\n if (!host.hidden) return;\n restoreFocusTo = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n seedDraft();\n host.hidden = false;\n focusCard();\n };\n host.hide = (): void => {\n host.hidden = true;\n restoreFocusTo?.focus();\n restoreFocusTo = null;\n };\n\n const categories = (): OreCookieBannerCategory[] => props.categories.value ?? [];\n\n return html`\n <div\n class=\"banner\"\n part=\"banner\"\n role=\"dialog\"\n aria-modal=\"false\"\n aria-labelledby=\"${headingId}\"\n aria-describedby=\"${textId}\"\n tabindex=\"-1\">\n <h2 class=\"heading\" part=\"heading\" id=\"${headingId}\">${() => label('heading')}</h2>\n <div class=\"text\" part=\"text\" id=\"${textId}\">\n <slot></slot>\n </div>\n ${when(\n () => categories().length > 0,\n () => html`\n <div class=\"categories\" part=\"categories\">\n <div class=\"category-row\">\n <ore-checkbox checked disabled helper=\"${() => label('essentialNote')}\">\n ${() => label('essential')}\n </ore-checkbox>\n </div>\n ${each(\n categories,\n (category) => category.id,\n (category) => html`\n <div class=\"category-row\">\n <ore-checkbox\n checked=\"${() => draft.value[category.value.id] === true}\"\n helper=\"${() => category.value.description ?? ''}\"\n @change=\"${(event: Event) => setCategory(category.value.id, eventFieldChecked(event))}\">\n ${() => category.value.label}\n </ore-checkbox>\n </div>\n `,\n )}\n </div>\n `,\n )}\n <div class=\"actions\" part=\"actions\">\n <ore-button size=\"sm\" variant=\"ghost\" @click=\"${reject}\">${() => label('reject')}</ore-button>\n ${when(\n () => categories().length > 0,\n () => html`<ore-button size=\"sm\" variant=\"bordered\" @click=\"${save}\">${() => label('save')}</ore-button>`,\n )}\n <ore-button color=\"primary\" size=\"sm\" @click=\"${acceptAll}\">${() => label('acceptAll')}</ore-button>\n </div>\n </div>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./icon.cjs"),n=require("./content/copy-command/copy-command.css.cjs");let r=require("@vielzeug/ripple"),i=require("@vielzeug/ore");var a=`ore-copy-command`;(0,i.define)(a,{props:{rounded:i.prop.string(`md`),size:i.prop.string(`md`),value:i.prop.string(``),variant:i.prop.string(`flat`)},setup(e){let t=(0,i.useEmit)(),n=(0,i.useSlots)(),a=(0,r.signal)(!1),o=(0,r.signal)(!1),s=null;(0,i.onCleanup)(()=>{s!==null&&clearTimeout(s)});let handleCopy=async()=>{let n=e.value.value??``;if(n){s!==null&&clearTimeout(s);try{await navigator.clipboard.writeText(n),a.value=!0,o.value=!1,t(`copy`,{value:n})}catch{a.value=!1,o.value=!0}s=setTimeout(()=>{a.value=!1,o.value=!1,s=null},2e3)}},c=(0,r.computed)(()=>n.has(`suffix`).value),l=(0,r.computed)(()=>a.value?`Copied!`:o.value?`Copy failed — press to try again`:`Copy: ${e.value.value??``}`);return i.html`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./icon.cjs"),n=require("./content/copy-command/copy-command.css.cjs");let r=require("@vielzeug/ripple"),i=require("@vielzeug/ore");var a=`ore-copy-command`;(0,i.define)(a,{props:{rounded:i.prop.string(`md`),size:i.prop.string(`md`),value:i.prop.string(``),variant:i.prop.string(`flat`)},setup(e){let t=(0,i.useEmit)(),n=(0,i.useSlots)(),a=(0,r.signal)(!1),o=(0,r.signal)(!1),s=null;(0,i.onCleanup)(()=>{s!==null&&clearTimeout(s)});let handleCopy=async()=>{let n=e.value.value??``;if(n){s!==null&&clearTimeout(s);try{await navigator.clipboard.writeText(n),a.value=!0,o.value=!1,t(`copy`,{value:n})}catch{a.value=!1,o.value=!0}s=setTimeout(()=>{a.value=!1,o.value=!1,s=null},2e3)}},c=(0,r.computed)(()=>n.has(`suffix`).value),l=(0,r.computed)(()=>a.value?`Copied!`:o.value?`Copy failed: press to try again`:`Copy: ${e.value.value??``}`);return i.html`
2
2
  <div class="wrapper" part="wrapper">
3
3
  <button class="command" part="command" type="button" aria-label="${l}" @click=${handleCopy}>
4
4
  <code class="command-text" part="command-text">${e.value}</code>
@@ -1 +1 @@
1
- {"version":3,"file":"copy-command.cjs","names":[],"sources":["../src/content/copy-command/copy-command.ts"],"sourcesContent":["import { define, html, onCleanup, prop, useEmit, useSlots } from '@vielzeug/ore';\nimport { computed, signal } from '@vielzeug/ripple';\nimport { srOnlyMixin } from '../../styles';\nimport type { ComponentSize, RoundedSize } from '../../types';\n\nimport componentStyles from './copy-command.css?inline';\n\nexport { ICON_TAG } from '../icon/icon';\n/** Copy command component properties */\nexport type OreCopyCommandProps = {\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** The command string to display and copy to clipboard */\n value?: string;\n /** Visual style variant */\n variant?: 'flat' | 'bordered' | 'ghost';\n};\n\n/** Copy command component events */\nexport type OreCopyCommandEvents = {\n /** Emitted when the command is successfully copied to the clipboard */\n copy: { value: string };\n};\n\n/**\n * A styled, copy-to-clipboard command display component.\n * Shows a command string in a monospace code block with a copy button.\n * Clicking the component copies the value to the clipboard, shows a transient\n * check-mark confirmation, and emits a `copy` event.\n *\n * @element ore-copy-command\n *\n * @attr {string} value - The command string to display and copy\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - Visual style: 'flat' | 'bordered' | 'ghost'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires copy - Emitted on successful clipboard write. detail: { value: string }\n *\n * @slot suffix - Optional controls appended after the copy button (e.g. a cycle button)\n *\n * @cssprop --copy-command-bg - Background color of the command block\n * @cssprop --copy-command-color - Text color of the command string\n * @cssprop --copy-command-border-color - Border color\n * @cssprop --copy-command-radius - Border radius (overrides the `rounded` attribute)\n * @cssprop --copy-command-padding - Inner padding of the command button\n * @cssprop --copy-command-font-size - Font size of the command string\n * @cssprop --copy-command-icon-color - Color of the copy/check icon\n * @cssprop --copy-command-hover-bg - Background on hover\n *\n * @part wrapper - Outer flex container\n * @part command - The copy button element\n * @part command-text - The `<code>` element displaying the command string\n * @part copy-icon - The icon wrapper span\n * @part suffix - Suffix slot container\n *\n * @example\n * ```html\n * <ore-copy-command value=\"npm install @vielzeug/ripple\"></ore-copy-command>\n *\n * <ore-copy-command value=\"npx -y @vielzeug/codex\" size=\"sm\" variant=\"bordered\"></ore-copy-command>\n *\n * <!-- With a suffix slot (e.g. a cycle button) -->\n * <ore-copy-command value=\"npm install @vielzeug/ripple\">\n * <button slot=\"suffix\" type=\"button\" aria-label=\"Next package\">›</button>\n * </ore-copy-command>\n * ```\n */\nexport const COPY_COMMAND_TAG = 'ore-copy-command' as const;\n\ndefine<OreCopyCommandProps>(COPY_COMMAND_TAG, {\n props: {\n rounded: prop.string<RoundedSize>('md'),\n size: prop.string<ComponentSize>('md'),\n value: prop.string(''),\n variant: prop.string<'flat' | 'bordered' | 'ghost'>('flat'),\n },\n\n setup(props) {\n const emit = useEmit<OreCopyCommandEvents>();\n const slots = useSlots();\n\n const copied = signal(false);\n const copyFailed = signal(false);\n let resetTimer: ReturnType<typeof setTimeout> | null = null;\n\n onCleanup(() => {\n if (resetTimer !== null) clearTimeout(resetTimer);\n });\n\n const handleCopy = async () => {\n const text = props.value.value ?? '';\n\n if (!text) return;\n\n if (resetTimer !== null) clearTimeout(resetTimer);\n\n try {\n await navigator.clipboard.writeText(text);\n copied.value = true;\n copyFailed.value = false;\n emit('copy', { value: text });\n } catch {\n // Clipboard access denied or unavailable (e.g. insecure context) — surface it instead of failing silently\n copied.value = false;\n copyFailed.value = true;\n }\n\n resetTimer = setTimeout(() => {\n copied.value = false;\n copyFailed.value = false;\n resetTimer = null;\n }, 2000);\n };\n\n const hasSuffix = computed(() => slots.has('suffix').value);\n const btnLabel = computed(() => {\n if (copied.value) return 'Copied!';\n\n if (copyFailed.value) return 'Copy failed — press to try again';\n\n return `Copy: ${props.value.value ?? ''}`;\n });\n\n return html`\n <div class=\"wrapper\" part=\"wrapper\">\n <button class=\"command\" part=\"command\" type=\"button\" aria-label=\"${btnLabel}\" @click=${handleCopy}>\n <code class=\"command-text\" part=\"command-text\">${props.value}</code>\n <span class=\"copy-icon\" part=\"copy-icon\" aria-hidden=\"true\">\n ${() => {\n if (copied.value)\n return html`\n <ore-icon name=\"check\" size=\"14\" class=\"icon-copied\" aria-hidden=\"true\"></ore-icon>\n `;\n\n if (copyFailed.value)\n return html`\n <ore-icon name=\"circle-alert\" size=\"14\" class=\"icon-failed\" aria-hidden=\"true\"></ore-icon>\n `;\n\n return html`\n <ore-icon name=\"copy\" size=\"14\" aria-hidden=\"true\"></ore-icon>\n `;\n }}\n </span>\n </button>\n <div class=\"suffix\" part=\"suffix\" ?hidden=\"${() => !hasSuffix.value}\">\n <slot name=\"suffix\"></slot>\n </div>\n </div>\n <div role=\"status\" class=\"sr-only\">\n ${() => {\n if (copied.value) return 'Copied to clipboard.';\n\n if (copyFailed.value) return 'Copy failed. Select the command text and copy it manually.';\n\n return '';\n }}\n </div>\n `;\n },\n\n styles: [srOnlyMixin, componentStyles],\n});\n"],"mappings":"0QAsEA,IAAa,EAAmB,oBAEhC,EAAA,EAAA,OAAA,CAA4B,EAAkB,CAC5C,MAAO,CACL,QAAS,EAAA,KAAK,OAAoB,IAAI,EACtC,KAAM,EAAA,KAAK,OAAsB,IAAI,EACrC,MAAO,EAAA,KAAK,OAAO,EAAE,EACrB,QAAS,EAAA,KAAK,OAAsC,MAAM,CAC5D,EAEA,MAAM,EAAO,CACX,IAAM,GAAA,EAAO,EAAA,QAAA,CAA8B,EACrC,GAAA,EAAQ,EAAA,SAAA,CAAS,EAEjB,GAAA,EAAS,EAAA,OAAA,CAAO,EAAK,EACrB,GAAA,EAAa,EAAA,OAAA,CAAO,EAAK,EAC3B,EAAmD,MAEvD,EAAA,EAAA,UAAA,KAAgB,CACV,IAAe,MAAM,aAAa,CAAU,CAClD,CAAC,EAED,IAAM,WAAa,SAAY,CAC7B,IAAM,EAAO,EAAM,MAAM,OAAS,GAE7B,KAEL,CAAI,IAAe,MAAM,aAAa,CAAU,EAEhD,GAAI,CACF,MAAM,UAAU,UAAU,UAAU,CAAI,EACxC,EAAO,MAAQ,GACf,EAAW,MAAQ,GACnB,EAAK,OAAQ,CAAE,MAAO,CAAK,CAAC,CAC9B,MAAQ,CAEN,EAAO,MAAQ,GACf,EAAW,MAAQ,EACrB,CAEA,EAAa,eAAiB,CAC5B,EAAO,MAAQ,GACf,EAAW,MAAQ,GACnB,EAAa,IACf,EAAG,GAAI,CAjByC,CAkBlD,EAEM,GAAA,EAAY,EAAA,SAAA,KAAe,EAAM,IAAI,QAAQ,CAAC,CAAC,KAAK,EACpD,GAAA,EAAW,EAAA,SAAA,KACX,EAAO,MAAc,UAErB,EAAW,MAAc,mCAEtB,SAAS,EAAM,MAAM,OAAS,IACtC,EAED,MAAO,GAAA,IAAI;;2EAE4D,EAAS,WAAW,WAAW;2DAC/C,EAAM,MAAM;;kBAGrD,EAAO,MACF,EAAA,IAAI;;kBAIT,EAAW,MACN,EAAA,IAAI;;kBAIN,EAAA,IAAI;;gBAGX;;;yDAG6C,CAAC,EAAU,MAAM;;;;;cAM9D,EAAO,MAAc,uBAErB,EAAW,MAAc,6DAEtB,GACP;;KAGR,EAEA,OAAQ,CAAC,EAAA,YAAa,EAAA,OAAe,CACvC,CAAC"}
1
+ {"version":3,"file":"copy-command.cjs","names":[],"sources":["../src/content/copy-command/copy-command.ts"],"sourcesContent":["import { define, html, onCleanup, prop, useEmit, useSlots } from '@vielzeug/ore';\nimport { computed, signal } from '@vielzeug/ripple';\nimport { srOnlyMixin } from '../../styles';\nimport type { ComponentSize, RoundedSize } from '../../types';\n\nimport componentStyles from './copy-command.css?inline';\n\nexport { ICON_TAG } from '../icon/icon';\n/** Copy command component properties */\nexport type OreCopyCommandProps = {\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** The command string to display and copy to clipboard */\n value?: string;\n /** Visual style variant */\n variant?: 'flat' | 'bordered' | 'ghost';\n};\n\n/** Copy command component events */\nexport type OreCopyCommandEvents = {\n /** Emitted when the command is successfully copied to the clipboard */\n copy: { value: string };\n};\n\n/**\n * A styled, copy-to-clipboard command display component.\n * Shows a command string in a monospace code block with a copy button.\n * Clicking the component copies the value to the clipboard, shows a transient\n * check-mark confirmation, and emits a `copy` event.\n *\n * @element ore-copy-command\n *\n * @attr {string} value - The command string to display and copy\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - Visual style: 'flat' | 'bordered' | 'ghost'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires copy - Emitted on successful clipboard write. detail: { value: string }\n *\n * @slot suffix - Optional controls appended after the copy button (e.g. a cycle button)\n *\n * @cssprop --copy-command-bg - Background color of the command block\n * @cssprop --copy-command-color - Text color of the command string\n * @cssprop --copy-command-border-color - Border color\n * @cssprop --copy-command-radius - Border radius (overrides the `rounded` attribute)\n * @cssprop --copy-command-padding - Inner padding of the command button\n * @cssprop --copy-command-font-size - Font size of the command string\n * @cssprop --copy-command-icon-color - Color of the copy/check icon\n * @cssprop --copy-command-hover-bg - Background on hover\n *\n * @part wrapper - Outer flex container\n * @part command - The copy button element\n * @part command-text - The `<code>` element displaying the command string\n * @part copy-icon - The icon wrapper span\n * @part suffix - Suffix slot container\n *\n * @example\n * ```html\n * <ore-copy-command value=\"npm install @vielzeug/ripple\"></ore-copy-command>\n *\n * <ore-copy-command value=\"npx -y @vielzeug/codex\" size=\"sm\" variant=\"bordered\"></ore-copy-command>\n *\n * <!-- With a suffix slot (e.g. a cycle button) -->\n * <ore-copy-command value=\"npm install @vielzeug/ripple\">\n * <button slot=\"suffix\" type=\"button\" aria-label=\"Next package\">›</button>\n * </ore-copy-command>\n * ```\n */\nexport const COPY_COMMAND_TAG = 'ore-copy-command' as const;\n\ndefine<OreCopyCommandProps>(COPY_COMMAND_TAG, {\n props: {\n rounded: prop.string<RoundedSize>('md'),\n size: prop.string<ComponentSize>('md'),\n value: prop.string(''),\n variant: prop.string<'flat' | 'bordered' | 'ghost'>('flat'),\n },\n\n setup(props) {\n const emit = useEmit<OreCopyCommandEvents>();\n const slots = useSlots();\n\n const copied = signal(false);\n const copyFailed = signal(false);\n let resetTimer: ReturnType<typeof setTimeout> | null = null;\n\n onCleanup(() => {\n if (resetTimer !== null) clearTimeout(resetTimer);\n });\n\n const handleCopy = async () => {\n const text = props.value.value ?? '';\n\n if (!text) return;\n\n if (resetTimer !== null) clearTimeout(resetTimer);\n\n try {\n await navigator.clipboard.writeText(text);\n copied.value = true;\n copyFailed.value = false;\n emit('copy', { value: text });\n } catch {\n // Clipboard access denied or unavailable (e.g. insecure context): surface it instead of failing silently\n copied.value = false;\n copyFailed.value = true;\n }\n\n resetTimer = setTimeout(() => {\n copied.value = false;\n copyFailed.value = false;\n resetTimer = null;\n }, 2000);\n };\n\n const hasSuffix = computed(() => slots.has('suffix').value);\n const btnLabel = computed(() => {\n if (copied.value) return 'Copied!';\n\n if (copyFailed.value) return 'Copy failed: press to try again';\n\n return `Copy: ${props.value.value ?? ''}`;\n });\n\n return html`\n <div class=\"wrapper\" part=\"wrapper\">\n <button class=\"command\" part=\"command\" type=\"button\" aria-label=\"${btnLabel}\" @click=${handleCopy}>\n <code class=\"command-text\" part=\"command-text\">${props.value}</code>\n <span class=\"copy-icon\" part=\"copy-icon\" aria-hidden=\"true\">\n ${() => {\n if (copied.value)\n return html`\n <ore-icon name=\"check\" size=\"14\" class=\"icon-copied\" aria-hidden=\"true\"></ore-icon>\n `;\n\n if (copyFailed.value)\n return html`\n <ore-icon name=\"circle-alert\" size=\"14\" class=\"icon-failed\" aria-hidden=\"true\"></ore-icon>\n `;\n\n return html`\n <ore-icon name=\"copy\" size=\"14\" aria-hidden=\"true\"></ore-icon>\n `;\n }}\n </span>\n </button>\n <div class=\"suffix\" part=\"suffix\" ?hidden=\"${() => !hasSuffix.value}\">\n <slot name=\"suffix\"></slot>\n </div>\n </div>\n <div role=\"status\" class=\"sr-only\">\n ${() => {\n if (copied.value) return 'Copied to clipboard.';\n\n if (copyFailed.value) return 'Copy failed. Select the command text and copy it manually.';\n\n return '';\n }}\n </div>\n `;\n },\n\n styles: [srOnlyMixin, componentStyles],\n});\n"],"mappings":"0QAsEA,IAAa,EAAmB,oBAEhC,EAAA,EAAA,OAAA,CAA4B,EAAkB,CAC5C,MAAO,CACL,QAAS,EAAA,KAAK,OAAoB,IAAI,EACtC,KAAM,EAAA,KAAK,OAAsB,IAAI,EACrC,MAAO,EAAA,KAAK,OAAO,EAAE,EACrB,QAAS,EAAA,KAAK,OAAsC,MAAM,CAC5D,EAEA,MAAM,EAAO,CACX,IAAM,GAAA,EAAO,EAAA,QAAA,CAA8B,EACrC,GAAA,EAAQ,EAAA,SAAA,CAAS,EAEjB,GAAA,EAAS,EAAA,OAAA,CAAO,EAAK,EACrB,GAAA,EAAa,EAAA,OAAA,CAAO,EAAK,EAC3B,EAAmD,MAEvD,EAAA,EAAA,UAAA,KAAgB,CACV,IAAe,MAAM,aAAa,CAAU,CAClD,CAAC,EAED,IAAM,WAAa,SAAY,CAC7B,IAAM,EAAO,EAAM,MAAM,OAAS,GAE7B,KAEL,CAAI,IAAe,MAAM,aAAa,CAAU,EAEhD,GAAI,CACF,MAAM,UAAU,UAAU,UAAU,CAAI,EACxC,EAAO,MAAQ,GACf,EAAW,MAAQ,GACnB,EAAK,OAAQ,CAAE,MAAO,CAAK,CAAC,CAC9B,MAAQ,CAEN,EAAO,MAAQ,GACf,EAAW,MAAQ,EACrB,CAEA,EAAa,eAAiB,CAC5B,EAAO,MAAQ,GACf,EAAW,MAAQ,GACnB,EAAa,IACf,EAAG,GAAI,CAjByC,CAkBlD,EAEM,GAAA,EAAY,EAAA,SAAA,KAAe,EAAM,IAAI,QAAQ,CAAC,CAAC,KAAK,EACpD,GAAA,EAAW,EAAA,SAAA,KACX,EAAO,MAAc,UAErB,EAAW,MAAc,kCAEtB,SAAS,EAAM,MAAM,OAAS,IACtC,EAED,MAAO,GAAA,IAAI;;2EAE4D,EAAS,WAAW,WAAW;2DAC/C,EAAM,MAAM;;kBAGrD,EAAO,MACF,EAAA,IAAI;;kBAIT,EAAW,MACN,EAAA,IAAI;;kBAIN,EAAA,IAAI;;gBAGX;;;yDAG6C,CAAC,EAAU,MAAM;;;;;cAM9D,EAAO,MAAc,uBAErB,EAAW,MAAc,6DAEtB,GACP;;KAGR,EAEA,OAAQ,CAAC,EAAA,YAAa,EAAA,OAAe,CACvC,CAAC"}
@@ -1,4 +1,4 @@
1
- import{srOnlyMixin as e}from"./styles/mixins/accessibility.css.js";import{ICON_TAG as t}from"./icon.js";import n from"./content/copy-command/copy-command.css.js";import{computed as r,signal as i}from"@vielzeug/ripple";import{define as a,html as o,onCleanup as s,prop as c,useEmit as l,useSlots as u}from"@vielzeug/ore";var d=`ore-copy-command`;a(d,{props:{rounded:c.string(`md`),size:c.string(`md`),value:c.string(``),variant:c.string(`flat`)},setup(e){let t=l(),n=u(),a=i(!1),c=i(!1),d=null;s(()=>{d!==null&&clearTimeout(d)});let handleCopy=async()=>{let n=e.value.value??``;if(n){d!==null&&clearTimeout(d);try{await navigator.clipboard.writeText(n),a.value=!0,c.value=!1,t(`copy`,{value:n})}catch{a.value=!1,c.value=!0}d=setTimeout(()=>{a.value=!1,c.value=!1,d=null},2e3)}},f=r(()=>n.has(`suffix`).value),p=r(()=>a.value?`Copied!`:c.value?`Copy failed — press to try again`:`Copy: ${e.value.value??``}`);return o`
1
+ import{srOnlyMixin as e}from"./styles/mixins/accessibility.css.js";import{ICON_TAG as t}from"./icon.js";import n from"./content/copy-command/copy-command.css.js";import{computed as r,signal as i}from"@vielzeug/ripple";import{define as a,html as o,onCleanup as s,prop as c,useEmit as l,useSlots as u}from"@vielzeug/ore";var d=`ore-copy-command`;a(d,{props:{rounded:c.string(`md`),size:c.string(`md`),value:c.string(``),variant:c.string(`flat`)},setup(e){let t=l(),n=u(),a=i(!1),c=i(!1),d=null;s(()=>{d!==null&&clearTimeout(d)});let handleCopy=async()=>{let n=e.value.value??``;if(n){d!==null&&clearTimeout(d);try{await navigator.clipboard.writeText(n),a.value=!0,c.value=!1,t(`copy`,{value:n})}catch{a.value=!1,c.value=!0}d=setTimeout(()=>{a.value=!1,c.value=!1,d=null},2e3)}},f=r(()=>n.has(`suffix`).value),p=r(()=>a.value?`Copied!`:c.value?`Copy failed: press to try again`:`Copy: ${e.value.value??``}`);return o`
2
2
  <div class="wrapper" part="wrapper">
3
3
  <button class="command" part="command" type="button" aria-label="${p}" @click=${handleCopy}>
4
4
  <code class="command-text" part="command-text">${e.value}</code>
@@ -1 +1 @@
1
- {"version":3,"file":"copy-command.js","names":[],"sources":["../src/content/copy-command/copy-command.ts"],"sourcesContent":["import { define, html, onCleanup, prop, useEmit, useSlots } from '@vielzeug/ore';\nimport { computed, signal } from '@vielzeug/ripple';\nimport { srOnlyMixin } from '../../styles';\nimport type { ComponentSize, RoundedSize } from '../../types';\n\nimport componentStyles from './copy-command.css?inline';\n\nexport { ICON_TAG } from '../icon/icon';\n/** Copy command component properties */\nexport type OreCopyCommandProps = {\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** The command string to display and copy to clipboard */\n value?: string;\n /** Visual style variant */\n variant?: 'flat' | 'bordered' | 'ghost';\n};\n\n/** Copy command component events */\nexport type OreCopyCommandEvents = {\n /** Emitted when the command is successfully copied to the clipboard */\n copy: { value: string };\n};\n\n/**\n * A styled, copy-to-clipboard command display component.\n * Shows a command string in a monospace code block with a copy button.\n * Clicking the component copies the value to the clipboard, shows a transient\n * check-mark confirmation, and emits a `copy` event.\n *\n * @element ore-copy-command\n *\n * @attr {string} value - The command string to display and copy\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - Visual style: 'flat' | 'bordered' | 'ghost'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires copy - Emitted on successful clipboard write. detail: { value: string }\n *\n * @slot suffix - Optional controls appended after the copy button (e.g. a cycle button)\n *\n * @cssprop --copy-command-bg - Background color of the command block\n * @cssprop --copy-command-color - Text color of the command string\n * @cssprop --copy-command-border-color - Border color\n * @cssprop --copy-command-radius - Border radius (overrides the `rounded` attribute)\n * @cssprop --copy-command-padding - Inner padding of the command button\n * @cssprop --copy-command-font-size - Font size of the command string\n * @cssprop --copy-command-icon-color - Color of the copy/check icon\n * @cssprop --copy-command-hover-bg - Background on hover\n *\n * @part wrapper - Outer flex container\n * @part command - The copy button element\n * @part command-text - The `<code>` element displaying the command string\n * @part copy-icon - The icon wrapper span\n * @part suffix - Suffix slot container\n *\n * @example\n * ```html\n * <ore-copy-command value=\"npm install @vielzeug/ripple\"></ore-copy-command>\n *\n * <ore-copy-command value=\"npx -y @vielzeug/codex\" size=\"sm\" variant=\"bordered\"></ore-copy-command>\n *\n * <!-- With a suffix slot (e.g. a cycle button) -->\n * <ore-copy-command value=\"npm install @vielzeug/ripple\">\n * <button slot=\"suffix\" type=\"button\" aria-label=\"Next package\">›</button>\n * </ore-copy-command>\n * ```\n */\nexport const COPY_COMMAND_TAG = 'ore-copy-command' as const;\n\ndefine<OreCopyCommandProps>(COPY_COMMAND_TAG, {\n props: {\n rounded: prop.string<RoundedSize>('md'),\n size: prop.string<ComponentSize>('md'),\n value: prop.string(''),\n variant: prop.string<'flat' | 'bordered' | 'ghost'>('flat'),\n },\n\n setup(props) {\n const emit = useEmit<OreCopyCommandEvents>();\n const slots = useSlots();\n\n const copied = signal(false);\n const copyFailed = signal(false);\n let resetTimer: ReturnType<typeof setTimeout> | null = null;\n\n onCleanup(() => {\n if (resetTimer !== null) clearTimeout(resetTimer);\n });\n\n const handleCopy = async () => {\n const text = props.value.value ?? '';\n\n if (!text) return;\n\n if (resetTimer !== null) clearTimeout(resetTimer);\n\n try {\n await navigator.clipboard.writeText(text);\n copied.value = true;\n copyFailed.value = false;\n emit('copy', { value: text });\n } catch {\n // Clipboard access denied or unavailable (e.g. insecure context) — surface it instead of failing silently\n copied.value = false;\n copyFailed.value = true;\n }\n\n resetTimer = setTimeout(() => {\n copied.value = false;\n copyFailed.value = false;\n resetTimer = null;\n }, 2000);\n };\n\n const hasSuffix = computed(() => slots.has('suffix').value);\n const btnLabel = computed(() => {\n if (copied.value) return 'Copied!';\n\n if (copyFailed.value) return 'Copy failed — press to try again';\n\n return `Copy: ${props.value.value ?? ''}`;\n });\n\n return html`\n <div class=\"wrapper\" part=\"wrapper\">\n <button class=\"command\" part=\"command\" type=\"button\" aria-label=\"${btnLabel}\" @click=${handleCopy}>\n <code class=\"command-text\" part=\"command-text\">${props.value}</code>\n <span class=\"copy-icon\" part=\"copy-icon\" aria-hidden=\"true\">\n ${() => {\n if (copied.value)\n return html`\n <ore-icon name=\"check\" size=\"14\" class=\"icon-copied\" aria-hidden=\"true\"></ore-icon>\n `;\n\n if (copyFailed.value)\n return html`\n <ore-icon name=\"circle-alert\" size=\"14\" class=\"icon-failed\" aria-hidden=\"true\"></ore-icon>\n `;\n\n return html`\n <ore-icon name=\"copy\" size=\"14\" aria-hidden=\"true\"></ore-icon>\n `;\n }}\n </span>\n </button>\n <div class=\"suffix\" part=\"suffix\" ?hidden=\"${() => !hasSuffix.value}\">\n <slot name=\"suffix\"></slot>\n </div>\n </div>\n <div role=\"status\" class=\"sr-only\">\n ${() => {\n if (copied.value) return 'Copied to clipboard.';\n\n if (copyFailed.value) return 'Copy failed. Select the command text and copy it manually.';\n\n return '';\n }}\n </div>\n `;\n },\n\n styles: [srOnlyMixin, componentStyles],\n});\n"],"mappings":"+TAsEA,IAAa,EAAmB,mBAEhC,EAA4B,EAAkB,CAC5C,MAAO,CACL,QAAS,EAAK,OAAoB,IAAI,EACtC,KAAM,EAAK,OAAsB,IAAI,EACrC,MAAO,EAAK,OAAO,EAAE,EACrB,QAAS,EAAK,OAAsC,MAAM,CAC5D,EAEA,MAAM,EAAO,CACX,IAAM,EAAO,EAA8B,EACrC,EAAQ,EAAS,EAEjB,EAAS,EAAO,EAAK,EACrB,EAAa,EAAO,EAAK,EAC3B,EAAmD,KAEvD,MAAgB,CACV,IAAe,MAAM,aAAa,CAAU,CAClD,CAAC,EAED,IAAM,WAAa,SAAY,CAC7B,IAAM,EAAO,EAAM,MAAM,OAAS,GAE7B,KAEL,CAAI,IAAe,MAAM,aAAa,CAAU,EAEhD,GAAI,CACF,MAAM,UAAU,UAAU,UAAU,CAAI,EACxC,EAAO,MAAQ,GACf,EAAW,MAAQ,GACnB,EAAK,OAAQ,CAAE,MAAO,CAAK,CAAC,CAC9B,MAAQ,CAEN,EAAO,MAAQ,GACf,EAAW,MAAQ,EACrB,CAEA,EAAa,eAAiB,CAC5B,EAAO,MAAQ,GACf,EAAW,MAAQ,GACnB,EAAa,IACf,EAAG,GAAI,CAjByC,CAkBlD,EAEM,EAAY,MAAe,EAAM,IAAI,QAAQ,CAAC,CAAC,KAAK,EACpD,EAAW,MACX,EAAO,MAAc,UAErB,EAAW,MAAc,mCAEtB,SAAS,EAAM,MAAM,OAAS,IACtC,EAED,MAAO,EAAI;;2EAE4D,EAAS,WAAW,WAAW;2DAC/C,EAAM,MAAM;;kBAGrD,EAAO,MACF,CAAI;;kBAIT,EAAW,MACN,CAAI;;kBAIN,CAAI;;gBAGX;;;yDAG6C,CAAC,EAAU,MAAM;;;;;cAM9D,EAAO,MAAc,uBAErB,EAAW,MAAc,6DAEtB,GACP;;KAGR,EAEA,OAAQ,CAAC,EAAa,CAAe,CACvC,CAAC"}
1
+ {"version":3,"file":"copy-command.js","names":[],"sources":["../src/content/copy-command/copy-command.ts"],"sourcesContent":["import { define, html, onCleanup, prop, useEmit, useSlots } from '@vielzeug/ore';\nimport { computed, signal } from '@vielzeug/ripple';\nimport { srOnlyMixin } from '../../styles';\nimport type { ComponentSize, RoundedSize } from '../../types';\n\nimport componentStyles from './copy-command.css?inline';\n\nexport { ICON_TAG } from '../icon/icon';\n/** Copy command component properties */\nexport type OreCopyCommandProps = {\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** The command string to display and copy to clipboard */\n value?: string;\n /** Visual style variant */\n variant?: 'flat' | 'bordered' | 'ghost';\n};\n\n/** Copy command component events */\nexport type OreCopyCommandEvents = {\n /** Emitted when the command is successfully copied to the clipboard */\n copy: { value: string };\n};\n\n/**\n * A styled, copy-to-clipboard command display component.\n * Shows a command string in a monospace code block with a copy button.\n * Clicking the component copies the value to the clipboard, shows a transient\n * check-mark confirmation, and emits a `copy` event.\n *\n * @element ore-copy-command\n *\n * @attr {string} value - The command string to display and copy\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} variant - Visual style: 'flat' | 'bordered' | 'ghost'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires copy - Emitted on successful clipboard write. detail: { value: string }\n *\n * @slot suffix - Optional controls appended after the copy button (e.g. a cycle button)\n *\n * @cssprop --copy-command-bg - Background color of the command block\n * @cssprop --copy-command-color - Text color of the command string\n * @cssprop --copy-command-border-color - Border color\n * @cssprop --copy-command-radius - Border radius (overrides the `rounded` attribute)\n * @cssprop --copy-command-padding - Inner padding of the command button\n * @cssprop --copy-command-font-size - Font size of the command string\n * @cssprop --copy-command-icon-color - Color of the copy/check icon\n * @cssprop --copy-command-hover-bg - Background on hover\n *\n * @part wrapper - Outer flex container\n * @part command - The copy button element\n * @part command-text - The `<code>` element displaying the command string\n * @part copy-icon - The icon wrapper span\n * @part suffix - Suffix slot container\n *\n * @example\n * ```html\n * <ore-copy-command value=\"npm install @vielzeug/ripple\"></ore-copy-command>\n *\n * <ore-copy-command value=\"npx -y @vielzeug/codex\" size=\"sm\" variant=\"bordered\"></ore-copy-command>\n *\n * <!-- With a suffix slot (e.g. a cycle button) -->\n * <ore-copy-command value=\"npm install @vielzeug/ripple\">\n * <button slot=\"suffix\" type=\"button\" aria-label=\"Next package\">›</button>\n * </ore-copy-command>\n * ```\n */\nexport const COPY_COMMAND_TAG = 'ore-copy-command' as const;\n\ndefine<OreCopyCommandProps>(COPY_COMMAND_TAG, {\n props: {\n rounded: prop.string<RoundedSize>('md'),\n size: prop.string<ComponentSize>('md'),\n value: prop.string(''),\n variant: prop.string<'flat' | 'bordered' | 'ghost'>('flat'),\n },\n\n setup(props) {\n const emit = useEmit<OreCopyCommandEvents>();\n const slots = useSlots();\n\n const copied = signal(false);\n const copyFailed = signal(false);\n let resetTimer: ReturnType<typeof setTimeout> | null = null;\n\n onCleanup(() => {\n if (resetTimer !== null) clearTimeout(resetTimer);\n });\n\n const handleCopy = async () => {\n const text = props.value.value ?? '';\n\n if (!text) return;\n\n if (resetTimer !== null) clearTimeout(resetTimer);\n\n try {\n await navigator.clipboard.writeText(text);\n copied.value = true;\n copyFailed.value = false;\n emit('copy', { value: text });\n } catch {\n // Clipboard access denied or unavailable (e.g. insecure context): surface it instead of failing silently\n copied.value = false;\n copyFailed.value = true;\n }\n\n resetTimer = setTimeout(() => {\n copied.value = false;\n copyFailed.value = false;\n resetTimer = null;\n }, 2000);\n };\n\n const hasSuffix = computed(() => slots.has('suffix').value);\n const btnLabel = computed(() => {\n if (copied.value) return 'Copied!';\n\n if (copyFailed.value) return 'Copy failed: press to try again';\n\n return `Copy: ${props.value.value ?? ''}`;\n });\n\n return html`\n <div class=\"wrapper\" part=\"wrapper\">\n <button class=\"command\" part=\"command\" type=\"button\" aria-label=\"${btnLabel}\" @click=${handleCopy}>\n <code class=\"command-text\" part=\"command-text\">${props.value}</code>\n <span class=\"copy-icon\" part=\"copy-icon\" aria-hidden=\"true\">\n ${() => {\n if (copied.value)\n return html`\n <ore-icon name=\"check\" size=\"14\" class=\"icon-copied\" aria-hidden=\"true\"></ore-icon>\n `;\n\n if (copyFailed.value)\n return html`\n <ore-icon name=\"circle-alert\" size=\"14\" class=\"icon-failed\" aria-hidden=\"true\"></ore-icon>\n `;\n\n return html`\n <ore-icon name=\"copy\" size=\"14\" aria-hidden=\"true\"></ore-icon>\n `;\n }}\n </span>\n </button>\n <div class=\"suffix\" part=\"suffix\" ?hidden=\"${() => !hasSuffix.value}\">\n <slot name=\"suffix\"></slot>\n </div>\n </div>\n <div role=\"status\" class=\"sr-only\">\n ${() => {\n if (copied.value) return 'Copied to clipboard.';\n\n if (copyFailed.value) return 'Copy failed. Select the command text and copy it manually.';\n\n return '';\n }}\n </div>\n `;\n },\n\n styles: [srOnlyMixin, componentStyles],\n});\n"],"mappings":"+TAsEA,IAAa,EAAmB,mBAEhC,EAA4B,EAAkB,CAC5C,MAAO,CACL,QAAS,EAAK,OAAoB,IAAI,EACtC,KAAM,EAAK,OAAsB,IAAI,EACrC,MAAO,EAAK,OAAO,EAAE,EACrB,QAAS,EAAK,OAAsC,MAAM,CAC5D,EAEA,MAAM,EAAO,CACX,IAAM,EAAO,EAA8B,EACrC,EAAQ,EAAS,EAEjB,EAAS,EAAO,EAAK,EACrB,EAAa,EAAO,EAAK,EAC3B,EAAmD,KAEvD,MAAgB,CACV,IAAe,MAAM,aAAa,CAAU,CAClD,CAAC,EAED,IAAM,WAAa,SAAY,CAC7B,IAAM,EAAO,EAAM,MAAM,OAAS,GAE7B,KAEL,CAAI,IAAe,MAAM,aAAa,CAAU,EAEhD,GAAI,CACF,MAAM,UAAU,UAAU,UAAU,CAAI,EACxC,EAAO,MAAQ,GACf,EAAW,MAAQ,GACnB,EAAK,OAAQ,CAAE,MAAO,CAAK,CAAC,CAC9B,MAAQ,CAEN,EAAO,MAAQ,GACf,EAAW,MAAQ,EACrB,CAEA,EAAa,eAAiB,CAC5B,EAAO,MAAQ,GACf,EAAW,MAAQ,GACnB,EAAa,IACf,EAAG,GAAI,CAjByC,CAkBlD,EAEM,EAAY,MAAe,EAAM,IAAI,QAAQ,CAAC,CAAC,KAAK,EACpD,EAAW,MACX,EAAO,MAAc,UAErB,EAAW,MAAc,kCAEtB,SAAS,EAAM,MAAM,OAAS,IACtC,EAED,MAAO,EAAI;;2EAE4D,EAAS,WAAW,WAAW;2DAC/C,EAAM,MAAM;;kBAGrD,EAAO,MACF,CAAI;;kBAIT,EAAW,MACN,CAAI;;kBAIN,CAAI;;gBAGX;;;yDAG6C,CAAC,EAAU,MAAM;;;;;cAM9D,EAAO,MAAc,uBAErB,EAAW,MAAc,6DAEtB,GACP;;KAGR,EAEA,OAAQ,CAAC,EAAa,CAAe,CACvC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"announcer.cjs","names":[],"sources":["../../src/core/announcer.ts"],"sourcesContent":["// ── Live-Region Announcer ─────────────────────────────────────────────────────\n// Programmatic announcements to screen readers without requiring a visible\n// DOM element. Used for status messages, selection feedback, and count updates\n// that live in components where the visible element is hidden or absent.\n//\n// Two visually-hidden live regions are lazily created in the target document's\n// `<body>` — one polite, one assertive. The clear-then-set pattern forces AT to\n// re-read even identical consecutive messages.\n//\n// Behavior is latest-value replacement (debounce), not queueing: rapid\n// consecutive calls to the same politeness cancel pending writes so only the\n// last message survives. JavaScript cannot know when assistive technology\n// finishes speaking, so a queue would be dishonest.\n//\n// Usage:\n// ```ts\n// import { announce } from './announcer';\n// announce(`${selectedCount} items selected`);\n// announce('Error: field is required', { politeness: 'assertive' });\n// announce('Saved', { document: getHost().ownerDocument });\n// ```\n\nimport { SR_ONLY_INLINE_STYLE } from '../styles';\n\n// ── Per-document live regions (WeakMap for automatic GC + test isolation) ────\n\ntype DocumentRegions = { assertive?: HTMLElement; polite?: HTMLElement };\n\nconst _regions = new WeakMap<Document, DocumentRegions>();\nconst _timers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>();\n\n/** Creates and mounts a visually-hidden live region with the given politeness. */\nconst createRegion = (politeness: 'assertive' | 'polite', doc: Document): HTMLElement | null => {\n if (!doc.body) return null;\n\n const el = doc.createElement('div');\n\n el.setAttribute('aria-live', politeness);\n el.setAttribute('aria-atomic', 'true');\n el.setAttribute('data-block-announcer', politeness);\n el.style.cssText = SR_ONLY_INLINE_STYLE;\n\n doc.body.appendChild(el);\n\n return el;\n};\n\n/**\n * Returns the live region for the requested politeness in the given document,\n * creating it on first access. Returns `null` outside a browser context or\n * when the document has no `<body>`.\n */\nconst getRegion = (politeness: 'assertive' | 'polite', doc: Document): HTMLElement | null => {\n let regions = _regions.get(doc);\n\n // Re-create if this specific region was removed from the DOM (e.g. by test teardown).\n if (regions?.[politeness] && !doc.body?.contains(regions[politeness])) {\n regions[politeness] = undefined;\n }\n\n if (!regions?.[politeness]) {\n const el = createRegion(politeness, doc);\n\n if (!el) return null;\n\n if (!regions) {\n regions = {};\n _regions.set(doc, regions);\n }\n\n regions[politeness] = el;\n }\n\n return regions[politeness] ?? null;\n};\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport type AnnouncePoliteness = 'assertive' | 'polite';\n\nexport type AnnounceOptions = {\n /**\n * The document whose `<body>` will host the visually-hidden live region.\n * Defaults to the global `document`. Pass `getHost().ownerDocument` from\n * inside a component that may live in an iframe to announce through the\n * correct accessibility tree.\n */\n document?: Document;\n /**\n * `'polite'` — latest-value replacement; does not interrupt ongoing speech.\n * `'assertive'` — interrupts immediately. Use sparingly; reserve for errors.\n *\n * Default: `'polite'`.\n */\n politeness?: AnnouncePoliteness;\n};\n\n/**\n * Announces a message to screen readers via a singleton visually-hidden\n * live region. Safe to call outside a component lifecycle (no cleanup needed).\n *\n * Uses latest-value replacement: rapid consecutive calls to the same politeness\n * cancel pending writes so only the last message is spoken. The clear-then-set\n * pattern (short delay) forces AT to re-read even when the message text hasn't\n * changed.\n *\n * @example\n * ```ts\n * announce('3 results found');\n * announce('Session expired', { politeness: 'assertive' });\n * announce('Saved', { document: getHost().ownerDocument });\n * ```\n */\nexport const announce = (message: string, options: AnnounceOptions = {}): void => {\n const doc = options.document ?? (typeof document !== 'undefined' ? document : null);\n\n if (!doc) return;\n\n const politeness = options.politeness ?? 'polite';\n const region = getRegion(politeness, doc);\n\n if (!region) return;\n\n // Clear immediately so AT registers a content change, then set after a short\n // delay. This is a debounce window, not a guaranteed speech timing — AT speech\n // timing varies across browsers and screen readers.\n region.textContent = '';\n\n // Use a per-element timer so concurrent polite + assertive announcements don't\n // clobber each other's pending writes.\n const existing = _timers.get(region);\n\n if (existing !== undefined) clearTimeout(existing);\n\n const timer = setTimeout(() => {\n region.textContent = message;\n _timers.delete(region);\n }, 50);\n\n _timers.set(region, timer);\n};\n"],"mappings":"0DA4BA,IAAM,EAAW,IAAI,QACf,EAAU,IAAI,QAGd,cAAgB,EAAoC,IAAsC,CAC9F,GAAI,CAAC,EAAI,KAAM,OAAO,KAEtB,IAAM,EAAK,EAAI,cAAc,KAAK,EASlC,OAPA,EAAG,aAAa,YAAa,CAAU,EACvC,EAAG,aAAa,cAAe,MAAM,EACrC,EAAG,aAAa,uBAAwB,CAAU,EAClD,EAAG,MAAM,QAAU,EAAA,qBAEnB,EAAI,KAAK,YAAY,CAAE,EAEhB,CACT,EAOM,WAAa,EAAoC,IAAsC,CAC3F,IAAI,EAAU,EAAS,IAAI,CAAG,EAO9B,GAJI,IAAU,IAAe,CAAC,EAAI,MAAM,SAAS,EAAQ,EAAW,IAClE,EAAQ,GAAc,IAAA,IAGpB,CAAC,IAAU,GAAa,CAC1B,IAAM,EAAK,aAAa,EAAY,CAAG,EAEvC,GAAI,CAAC,EAAI,OAAO,KAEX,IACH,EAAU,CAAC,EACX,EAAS,IAAI,EAAK,CAAO,GAG3B,EAAQ,GAAc,CACxB,CAEA,OAAO,EAAQ,IAAe,IAChC,EAuCa,UAAY,EAAiB,EAA2B,CAAC,IAAY,CAChF,IAAM,EAAM,EAAQ,WAAa,OAAO,SAAa,IAAc,SAAW,MAE9E,GAAI,CAAC,EAAK,OAGV,IAAM,EAAS,UADI,EAAQ,YAAc,SACJ,CAAG,EAExC,GAAI,CAAC,EAAQ,OAKb,EAAO,YAAc,GAIrB,IAAM,EAAW,EAAQ,IAAI,CAAM,EAE/B,IAAa,IAAA,IAAW,aAAa,CAAQ,EAEjD,IAAM,EAAQ,eAAiB,CAC7B,EAAO,YAAc,EACrB,EAAQ,OAAO,CAAM,CACvB,EAAG,EAAE,EAEL,EAAQ,IAAI,EAAQ,CAAK,CAC3B"}
1
+ {"version":3,"file":"announcer.cjs","names":[],"sources":["../../src/core/announcer.ts"],"sourcesContent":["// ── Live-Region Announcer ─────────────────────────────────────────────────────\n// Programmatic announcements to screen readers without requiring a visible\n// DOM element. Used for status messages, selection feedback, and count updates\n// that live in components where the visible element is hidden or absent.\n//\n// Two visually-hidden live regions are lazily created in the target document's\n// `<body>`: one polite, one assertive. The clear-then-set pattern forces AT to\n// re-read even identical consecutive messages.\n//\n// Behavior is latest-value replacement (debounce), not queueing: rapid\n// consecutive calls to the same politeness cancel pending writes so only the\n// last message survives. JavaScript cannot know when assistive technology\n// finishes speaking, so a queue would be dishonest.\n//\n// Usage:\n// ```ts\n// import { announce } from './announcer';\n// announce(`${selectedCount} items selected`);\n// announce('Error: field is required', { politeness: 'assertive' });\n// announce('Saved', { document: getHost().ownerDocument });\n// ```\n\nimport { SR_ONLY_INLINE_STYLE } from '../styles';\n\n// ── Per-document live regions (WeakMap for automatic GC + test isolation) ────\n\ntype DocumentRegions = { assertive?: HTMLElement; polite?: HTMLElement };\n\nconst _regions = new WeakMap<Document, DocumentRegions>();\nconst _timers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>();\n\n/** Creates and mounts a visually-hidden live region with the given politeness. */\nconst createRegion = (politeness: 'assertive' | 'polite', doc: Document): HTMLElement | null => {\n if (!doc.body) return null;\n\n const el = doc.createElement('div');\n\n el.setAttribute('aria-live', politeness);\n el.setAttribute('aria-atomic', 'true');\n el.setAttribute('data-block-announcer', politeness);\n el.style.cssText = SR_ONLY_INLINE_STYLE;\n\n doc.body.appendChild(el);\n\n return el;\n};\n\n/**\n * Returns the live region for the requested politeness in the given document,\n * creating it on first access. Returns `null` outside a browser context or\n * when the document has no `<body>`.\n */\nconst getRegion = (politeness: 'assertive' | 'polite', doc: Document): HTMLElement | null => {\n let regions = _regions.get(doc);\n\n // Re-create if this specific region was removed from the DOM (e.g. by test teardown).\n if (regions?.[politeness] && !doc.body?.contains(regions[politeness])) {\n regions[politeness] = undefined;\n }\n\n if (!regions?.[politeness]) {\n const el = createRegion(politeness, doc);\n\n if (!el) return null;\n\n if (!regions) {\n regions = {};\n _regions.set(doc, regions);\n }\n\n regions[politeness] = el;\n }\n\n return regions[politeness] ?? null;\n};\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport type AnnouncePoliteness = 'assertive' | 'polite';\n\nexport type AnnounceOptions = {\n /**\n * The document whose `<body>` will host the visually-hidden live region.\n * Defaults to the global `document`. Pass `getHost().ownerDocument` from\n * inside a component that may live in an iframe to announce through the\n * correct accessibility tree.\n */\n document?: Document;\n /**\n * `'polite'`: latest-value replacement; does not interrupt ongoing speech.\n * `'assertive'`: interrupts immediately. Use sparingly; reserve for errors.\n *\n * Default: `'polite'`.\n */\n politeness?: AnnouncePoliteness;\n};\n\n/**\n * Announces a message to screen readers via a singleton visually-hidden\n * live region. Safe to call outside a component lifecycle (no cleanup needed).\n *\n * Uses latest-value replacement: rapid consecutive calls to the same politeness\n * cancel pending writes so only the last message is spoken. The clear-then-set\n * pattern (short delay) forces AT to re-read even when the message text hasn't\n * changed.\n *\n * @example\n * ```ts\n * announce('3 results found');\n * announce('Session expired', { politeness: 'assertive' });\n * announce('Saved', { document: getHost().ownerDocument });\n * ```\n */\nexport const announce = (message: string, options: AnnounceOptions = {}): void => {\n const doc = options.document ?? (typeof document !== 'undefined' ? document : null);\n\n if (!doc) return;\n\n const politeness = options.politeness ?? 'polite';\n const region = getRegion(politeness, doc);\n\n if (!region) return;\n\n // Clear immediately so AT registers a content change, then set after a short\n // delay. This is a debounce window, not a guaranteed speech timing: AT speech\n // timing varies across browsers and screen readers.\n region.textContent = '';\n\n // Use a per-element timer so concurrent polite + assertive announcements don't\n // clobber each other's pending writes.\n const existing = _timers.get(region);\n\n if (existing !== undefined) clearTimeout(existing);\n\n const timer = setTimeout(() => {\n region.textContent = message;\n _timers.delete(region);\n }, 50);\n\n _timers.set(region, timer);\n};\n"],"mappings":"0DA4BA,IAAM,EAAW,IAAI,QACf,EAAU,IAAI,QAGd,cAAgB,EAAoC,IAAsC,CAC9F,GAAI,CAAC,EAAI,KAAM,OAAO,KAEtB,IAAM,EAAK,EAAI,cAAc,KAAK,EASlC,OAPA,EAAG,aAAa,YAAa,CAAU,EACvC,EAAG,aAAa,cAAe,MAAM,EACrC,EAAG,aAAa,uBAAwB,CAAU,EAClD,EAAG,MAAM,QAAU,EAAA,qBAEnB,EAAI,KAAK,YAAY,CAAE,EAEhB,CACT,EAOM,WAAa,EAAoC,IAAsC,CAC3F,IAAI,EAAU,EAAS,IAAI,CAAG,EAO9B,GAJI,IAAU,IAAe,CAAC,EAAI,MAAM,SAAS,EAAQ,EAAW,IAClE,EAAQ,GAAc,IAAA,IAGpB,CAAC,IAAU,GAAa,CAC1B,IAAM,EAAK,aAAa,EAAY,CAAG,EAEvC,GAAI,CAAC,EAAI,OAAO,KAEX,IACH,EAAU,CAAC,EACX,EAAS,IAAI,EAAK,CAAO,GAG3B,EAAQ,GAAc,CACxB,CAEA,OAAO,EAAQ,IAAe,IAChC,EAuCa,UAAY,EAAiB,EAA2B,CAAC,IAAY,CAChF,IAAM,EAAM,EAAQ,WAAa,OAAO,SAAa,IAAc,SAAW,MAE9E,GAAI,CAAC,EAAK,OAGV,IAAM,EAAS,UADI,EAAQ,YAAc,SACJ,CAAG,EAExC,GAAI,CAAC,EAAQ,OAKb,EAAO,YAAc,GAIrB,IAAM,EAAW,EAAQ,IAAI,CAAM,EAE/B,IAAa,IAAA,IAAW,aAAa,CAAQ,EAEjD,IAAM,EAAQ,eAAiB,CAC7B,EAAO,YAAc,EACrB,EAAQ,OAAO,CAAM,CACvB,EAAG,EAAE,EAEL,EAAQ,IAAI,EAAQ,CAAK,CAC3B"}
@@ -8,8 +8,8 @@ export type AnnounceOptions = {
8
8
  */
9
9
  document?: Document;
10
10
  /**
11
- * `'polite'` — latest-value replacement; does not interrupt ongoing speech.
12
- * `'assertive'` — interrupts immediately. Use sparingly; reserve for errors.
11
+ * `'polite'`: latest-value replacement; does not interrupt ongoing speech.
12
+ * `'assertive'`: interrupts immediately. Use sparingly; reserve for errors.
13
13
  *
14
14
  * Default: `'polite'`.
15
15
  */
@@ -1 +1 @@
1
- {"version":3,"file":"announcer.js","names":[],"sources":["../../src/core/announcer.ts"],"sourcesContent":["// ── Live-Region Announcer ─────────────────────────────────────────────────────\n// Programmatic announcements to screen readers without requiring a visible\n// DOM element. Used for status messages, selection feedback, and count updates\n// that live in components where the visible element is hidden or absent.\n//\n// Two visually-hidden live regions are lazily created in the target document's\n// `<body>` — one polite, one assertive. The clear-then-set pattern forces AT to\n// re-read even identical consecutive messages.\n//\n// Behavior is latest-value replacement (debounce), not queueing: rapid\n// consecutive calls to the same politeness cancel pending writes so only the\n// last message survives. JavaScript cannot know when assistive technology\n// finishes speaking, so a queue would be dishonest.\n//\n// Usage:\n// ```ts\n// import { announce } from './announcer';\n// announce(`${selectedCount} items selected`);\n// announce('Error: field is required', { politeness: 'assertive' });\n// announce('Saved', { document: getHost().ownerDocument });\n// ```\n\nimport { SR_ONLY_INLINE_STYLE } from '../styles';\n\n// ── Per-document live regions (WeakMap for automatic GC + test isolation) ────\n\ntype DocumentRegions = { assertive?: HTMLElement; polite?: HTMLElement };\n\nconst _regions = new WeakMap<Document, DocumentRegions>();\nconst _timers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>();\n\n/** Creates and mounts a visually-hidden live region with the given politeness. */\nconst createRegion = (politeness: 'assertive' | 'polite', doc: Document): HTMLElement | null => {\n if (!doc.body) return null;\n\n const el = doc.createElement('div');\n\n el.setAttribute('aria-live', politeness);\n el.setAttribute('aria-atomic', 'true');\n el.setAttribute('data-block-announcer', politeness);\n el.style.cssText = SR_ONLY_INLINE_STYLE;\n\n doc.body.appendChild(el);\n\n return el;\n};\n\n/**\n * Returns the live region for the requested politeness in the given document,\n * creating it on first access. Returns `null` outside a browser context or\n * when the document has no `<body>`.\n */\nconst getRegion = (politeness: 'assertive' | 'polite', doc: Document): HTMLElement | null => {\n let regions = _regions.get(doc);\n\n // Re-create if this specific region was removed from the DOM (e.g. by test teardown).\n if (regions?.[politeness] && !doc.body?.contains(regions[politeness])) {\n regions[politeness] = undefined;\n }\n\n if (!regions?.[politeness]) {\n const el = createRegion(politeness, doc);\n\n if (!el) return null;\n\n if (!regions) {\n regions = {};\n _regions.set(doc, regions);\n }\n\n regions[politeness] = el;\n }\n\n return regions[politeness] ?? null;\n};\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport type AnnouncePoliteness = 'assertive' | 'polite';\n\nexport type AnnounceOptions = {\n /**\n * The document whose `<body>` will host the visually-hidden live region.\n * Defaults to the global `document`. Pass `getHost().ownerDocument` from\n * inside a component that may live in an iframe to announce through the\n * correct accessibility tree.\n */\n document?: Document;\n /**\n * `'polite'` — latest-value replacement; does not interrupt ongoing speech.\n * `'assertive'` — interrupts immediately. Use sparingly; reserve for errors.\n *\n * Default: `'polite'`.\n */\n politeness?: AnnouncePoliteness;\n};\n\n/**\n * Announces a message to screen readers via a singleton visually-hidden\n * live region. Safe to call outside a component lifecycle (no cleanup needed).\n *\n * Uses latest-value replacement: rapid consecutive calls to the same politeness\n * cancel pending writes so only the last message is spoken. The clear-then-set\n * pattern (short delay) forces AT to re-read even when the message text hasn't\n * changed.\n *\n * @example\n * ```ts\n * announce('3 results found');\n * announce('Session expired', { politeness: 'assertive' });\n * announce('Saved', { document: getHost().ownerDocument });\n * ```\n */\nexport const announce = (message: string, options: AnnounceOptions = {}): void => {\n const doc = options.document ?? (typeof document !== 'undefined' ? document : null);\n\n if (!doc) return;\n\n const politeness = options.politeness ?? 'polite';\n const region = getRegion(politeness, doc);\n\n if (!region) return;\n\n // Clear immediately so AT registers a content change, then set after a short\n // delay. This is a debounce window, not a guaranteed speech timing — AT speech\n // timing varies across browsers and screen readers.\n region.textContent = '';\n\n // Use a per-element timer so concurrent polite + assertive announcements don't\n // clobber each other's pending writes.\n const existing = _timers.get(region);\n\n if (existing !== undefined) clearTimeout(existing);\n\n const timer = setTimeout(() => {\n region.textContent = message;\n _timers.delete(region);\n }, 50);\n\n _timers.set(region, timer);\n};\n"],"mappings":"6EA4BA,IAAM,EAAW,IAAI,QACf,EAAU,IAAI,QAGd,cAAgB,EAAoC,IAAsC,CAC9F,GAAI,CAAC,EAAI,KAAM,OAAO,KAEtB,IAAM,EAAK,EAAI,cAAc,KAAK,EASlC,OAPA,EAAG,aAAa,YAAa,CAAU,EACvC,EAAG,aAAa,cAAe,MAAM,EACrC,EAAG,aAAa,uBAAwB,CAAU,EAClD,EAAG,MAAM,QAAU,EAEnB,EAAI,KAAK,YAAY,CAAE,EAEhB,CACT,EAOM,WAAa,EAAoC,IAAsC,CAC3F,IAAI,EAAU,EAAS,IAAI,CAAG,EAO9B,GAJI,IAAU,IAAe,CAAC,EAAI,MAAM,SAAS,EAAQ,EAAW,IAClE,EAAQ,GAAc,IAAA,IAGpB,CAAC,IAAU,GAAa,CAC1B,IAAM,EAAK,aAAa,EAAY,CAAG,EAEvC,GAAI,CAAC,EAAI,OAAO,KAEX,IACH,EAAU,CAAC,EACX,EAAS,IAAI,EAAK,CAAO,GAG3B,EAAQ,GAAc,CACxB,CAEA,OAAO,EAAQ,IAAe,IAChC,EAuCa,UAAY,EAAiB,EAA2B,CAAC,IAAY,CAChF,IAAM,EAAM,EAAQ,WAAa,OAAO,SAAa,IAAc,SAAW,MAE9E,GAAI,CAAC,EAAK,OAGV,IAAM,EAAS,UADI,EAAQ,YAAc,SACJ,CAAG,EAExC,GAAI,CAAC,EAAQ,OAKb,EAAO,YAAc,GAIrB,IAAM,EAAW,EAAQ,IAAI,CAAM,EAE/B,IAAa,IAAA,IAAW,aAAa,CAAQ,EAEjD,IAAM,EAAQ,eAAiB,CAC7B,EAAO,YAAc,EACrB,EAAQ,OAAO,CAAM,CACvB,EAAG,EAAE,EAEL,EAAQ,IAAI,EAAQ,CAAK,CAC3B"}
1
+ {"version":3,"file":"announcer.js","names":[],"sources":["../../src/core/announcer.ts"],"sourcesContent":["// ── Live-Region Announcer ─────────────────────────────────────────────────────\n// Programmatic announcements to screen readers without requiring a visible\n// DOM element. Used for status messages, selection feedback, and count updates\n// that live in components where the visible element is hidden or absent.\n//\n// Two visually-hidden live regions are lazily created in the target document's\n// `<body>`: one polite, one assertive. The clear-then-set pattern forces AT to\n// re-read even identical consecutive messages.\n//\n// Behavior is latest-value replacement (debounce), not queueing: rapid\n// consecutive calls to the same politeness cancel pending writes so only the\n// last message survives. JavaScript cannot know when assistive technology\n// finishes speaking, so a queue would be dishonest.\n//\n// Usage:\n// ```ts\n// import { announce } from './announcer';\n// announce(`${selectedCount} items selected`);\n// announce('Error: field is required', { politeness: 'assertive' });\n// announce('Saved', { document: getHost().ownerDocument });\n// ```\n\nimport { SR_ONLY_INLINE_STYLE } from '../styles';\n\n// ── Per-document live regions (WeakMap for automatic GC + test isolation) ────\n\ntype DocumentRegions = { assertive?: HTMLElement; polite?: HTMLElement };\n\nconst _regions = new WeakMap<Document, DocumentRegions>();\nconst _timers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>();\n\n/** Creates and mounts a visually-hidden live region with the given politeness. */\nconst createRegion = (politeness: 'assertive' | 'polite', doc: Document): HTMLElement | null => {\n if (!doc.body) return null;\n\n const el = doc.createElement('div');\n\n el.setAttribute('aria-live', politeness);\n el.setAttribute('aria-atomic', 'true');\n el.setAttribute('data-block-announcer', politeness);\n el.style.cssText = SR_ONLY_INLINE_STYLE;\n\n doc.body.appendChild(el);\n\n return el;\n};\n\n/**\n * Returns the live region for the requested politeness in the given document,\n * creating it on first access. Returns `null` outside a browser context or\n * when the document has no `<body>`.\n */\nconst getRegion = (politeness: 'assertive' | 'polite', doc: Document): HTMLElement | null => {\n let regions = _regions.get(doc);\n\n // Re-create if this specific region was removed from the DOM (e.g. by test teardown).\n if (regions?.[politeness] && !doc.body?.contains(regions[politeness])) {\n regions[politeness] = undefined;\n }\n\n if (!regions?.[politeness]) {\n const el = createRegion(politeness, doc);\n\n if (!el) return null;\n\n if (!regions) {\n regions = {};\n _regions.set(doc, regions);\n }\n\n regions[politeness] = el;\n }\n\n return regions[politeness] ?? null;\n};\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport type AnnouncePoliteness = 'assertive' | 'polite';\n\nexport type AnnounceOptions = {\n /**\n * The document whose `<body>` will host the visually-hidden live region.\n * Defaults to the global `document`. Pass `getHost().ownerDocument` from\n * inside a component that may live in an iframe to announce through the\n * correct accessibility tree.\n */\n document?: Document;\n /**\n * `'polite'`: latest-value replacement; does not interrupt ongoing speech.\n * `'assertive'`: interrupts immediately. Use sparingly; reserve for errors.\n *\n * Default: `'polite'`.\n */\n politeness?: AnnouncePoliteness;\n};\n\n/**\n * Announces a message to screen readers via a singleton visually-hidden\n * live region. Safe to call outside a component lifecycle (no cleanup needed).\n *\n * Uses latest-value replacement: rapid consecutive calls to the same politeness\n * cancel pending writes so only the last message is spoken. The clear-then-set\n * pattern (short delay) forces AT to re-read even when the message text hasn't\n * changed.\n *\n * @example\n * ```ts\n * announce('3 results found');\n * announce('Session expired', { politeness: 'assertive' });\n * announce('Saved', { document: getHost().ownerDocument });\n * ```\n */\nexport const announce = (message: string, options: AnnounceOptions = {}): void => {\n const doc = options.document ?? (typeof document !== 'undefined' ? document : null);\n\n if (!doc) return;\n\n const politeness = options.politeness ?? 'polite';\n const region = getRegion(politeness, doc);\n\n if (!region) return;\n\n // Clear immediately so AT registers a content change, then set after a short\n // delay. This is a debounce window, not a guaranteed speech timing: AT speech\n // timing varies across browsers and screen readers.\n region.textContent = '';\n\n // Use a per-element timer so concurrent polite + assertive announcements don't\n // clobber each other's pending writes.\n const existing = _timers.get(region);\n\n if (existing !== undefined) clearTimeout(existing);\n\n const timer = setTimeout(() => {\n region.textContent = message;\n _timers.delete(region);\n }, 50);\n\n _timers.set(region, timer);\n};\n"],"mappings":"6EA4BA,IAAM,EAAW,IAAI,QACf,EAAU,IAAI,QAGd,cAAgB,EAAoC,IAAsC,CAC9F,GAAI,CAAC,EAAI,KAAM,OAAO,KAEtB,IAAM,EAAK,EAAI,cAAc,KAAK,EASlC,OAPA,EAAG,aAAa,YAAa,CAAU,EACvC,EAAG,aAAa,cAAe,MAAM,EACrC,EAAG,aAAa,uBAAwB,CAAU,EAClD,EAAG,MAAM,QAAU,EAEnB,EAAI,KAAK,YAAY,CAAE,EAEhB,CACT,EAOM,WAAa,EAAoC,IAAsC,CAC3F,IAAI,EAAU,EAAS,IAAI,CAAG,EAO9B,GAJI,IAAU,IAAe,CAAC,EAAI,MAAM,SAAS,EAAQ,EAAW,IAClE,EAAQ,GAAc,IAAA,IAGpB,CAAC,IAAU,GAAa,CAC1B,IAAM,EAAK,aAAa,EAAY,CAAG,EAEvC,GAAI,CAAC,EAAI,OAAO,KAEX,IACH,EAAU,CAAC,EACX,EAAS,IAAI,EAAK,CAAO,GAG3B,EAAQ,GAAc,CACxB,CAEA,OAAO,EAAQ,IAAe,IAChC,EAuCa,UAAY,EAAiB,EAA2B,CAAC,IAAY,CAChF,IAAM,EAAM,EAAQ,WAAa,OAAO,SAAa,IAAc,SAAW,MAE9E,GAAI,CAAC,EAAK,OAGV,IAAM,EAAS,UADI,EAAQ,YAAc,SACJ,CAAG,EAExC,GAAI,CAAC,EAAQ,OAKb,EAAO,YAAc,GAIrB,IAAM,EAAW,EAAQ,IAAI,CAAM,EAE/B,IAAa,IAAA,IAAW,aAAa,CAAQ,EAEjD,IAAM,EAAQ,eAAiB,CAC7B,EAAO,YAAc,EACrB,EAAQ,OAAO,CAAM,CACvB,EAAG,EAAE,EAEL,EAAQ,IAAI,EAAQ,CAAK,CAC3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"aria-reflection.cjs","names":[],"sources":["../../src/core/aria-reflection.ts"],"sourcesContent":["/**\n * Cross-shadow-root ARIA relationship helper.\n *\n * Plain IDREF attributes (`aria-controls`, `aria-describedby`, `aria-labelledby`, …) cannot\n * resolve across shadow-tree boundaries — the browser only looks up an `id` within the same\n * root as the referencing element, so `<div id=\"x\">` inside one component's shadow root is\n * invisible to `aria-controls=\"x\"` set inside another component's shadow root, even when both\n * custom elements are siblings in the light DOM. This is a platform limitation, not a bug —\n * see the WICG \"reference target\" explainer for the full rationale.\n *\n * Modern browsers instead expose element-reflection IDL properties\n * (`ariaControlsElements`, `ariaDescribedByElements`, `ariaLabelledByElements`, …) that accept\n * live `Element` references directly, sidestepping ID lookup entirely — these work across\n * shadow roots by design. Where unsupported, this falls back to the plain IDREF attribute,\n * which is still correct for same-root pairings and degrades gracefully (no relationship)\n * for cross-root ones instead of throwing.\n */\n\nexport type AriaReflectionProperty = 'ariaControlsElements' | 'ariaDescribedByElements' | 'ariaLabelledByElements';\n\nconst ATTR_BY_PROPERTY: Record<AriaReflectionProperty, string> = {\n ariaControlsElements: 'aria-controls',\n ariaDescribedByElements: 'aria-describedby',\n ariaLabelledByElements: 'aria-labelledby',\n};\n\n/**\n * Associates `el` with `targets` via the given ARIA relationship. Prefers the\n * cross-shadow-root-safe element-reflection property; falls back to a plain IDREF attribute\n * built from `targets`' own `id`s when the browser doesn't support element reflection yet.\n *\n * Pass an empty array to clear the relationship.\n */\nexport function setAriaReflection(el: Element, property: AriaReflectionProperty, targets: readonly Element[]): void {\n if (property in el) {\n (el as unknown as Record<AriaReflectionProperty, readonly Element[] | null>)[property] =\n targets.length > 0 ? targets : null;\n\n return;\n }\n\n const attr = ATTR_BY_PROPERTY[property];\n const ids = targets.map((target) => target.id).filter(Boolean);\n\n if (ids.length === 0) el.removeAttribute(attr);\n else el.setAttribute(attr, ids.join(' '));\n}\n"],"mappings":"AAoBA,IAAM,EAA2D,CAC/D,qBAAsB,gBACtB,wBAAyB,mBACzB,uBAAwB,iBAC1B,EASA,SAAgB,kBAAkB,EAAa,EAAkC,EAAmC,CAClH,GAAI,KAAY,EAAI,CAClB,EAA6E,GAC3E,EAAQ,OAAS,EAAI,EAAU,KAEjC,MACF,CAEA,IAAM,EAAO,EAAiB,GACxB,EAAM,EAAQ,IAAK,GAAW,EAAO,EAAE,CAAC,CAAC,OAAO,OAAO,EAEzD,EAAI,SAAW,EAAG,EAAG,gBAAgB,CAAI,EACxC,EAAG,aAAa,EAAM,EAAI,KAAK,GAAG,CAAC,CAC1C"}
1
+ {"version":3,"file":"aria-reflection.cjs","names":[],"sources":["../../src/core/aria-reflection.ts"],"sourcesContent":["/**\n * Cross-shadow-root ARIA relationship helper.\n *\n * Plain IDREF attributes (`aria-controls`, `aria-describedby`, `aria-labelledby`, …) cannot\n * resolve across shadow-tree boundaries: the browser only looks up an `id` within the same\n * root as the referencing element, so `<div id=\"x\">` inside one component's shadow root is\n * invisible to `aria-controls=\"x\"` set inside another component's shadow root, even when both\n * custom elements are siblings in the light DOM. This is a platform limitation, not a bug :\n * see the WICG \"reference target\" explainer for the full rationale.\n *\n * Modern browsers instead expose element-reflection IDL properties\n * (`ariaControlsElements`, `ariaDescribedByElements`, `ariaLabelledByElements`, …) that accept\n * live `Element` references directly, sidestepping ID lookup entirely: these work across\n * shadow roots by design. Where unsupported, this falls back to the plain IDREF attribute,\n * which is still correct for same-root pairings and degrades gracefully (no relationship)\n * for cross-root ones instead of throwing.\n */\n\nexport type AriaReflectionProperty = 'ariaControlsElements' | 'ariaDescribedByElements' | 'ariaLabelledByElements';\n\nconst ATTR_BY_PROPERTY: Record<AriaReflectionProperty, string> = {\n ariaControlsElements: 'aria-controls',\n ariaDescribedByElements: 'aria-describedby',\n ariaLabelledByElements: 'aria-labelledby',\n};\n\n/**\n * Associates `el` with `targets` via the given ARIA relationship. Prefers the\n * cross-shadow-root-safe element-reflection property; falls back to a plain IDREF attribute\n * built from `targets`' own `id`s when the browser doesn't support element reflection yet.\n *\n * Pass an empty array to clear the relationship.\n */\nexport function setAriaReflection(el: Element, property: AriaReflectionProperty, targets: readonly Element[]): void {\n if (property in el) {\n (el as unknown as Record<AriaReflectionProperty, readonly Element[] | null>)[property] =\n targets.length > 0 ? targets : null;\n\n return;\n }\n\n const attr = ATTR_BY_PROPERTY[property];\n const ids = targets.map((target) => target.id).filter(Boolean);\n\n if (ids.length === 0) el.removeAttribute(attr);\n else el.setAttribute(attr, ids.join(' '));\n}\n"],"mappings":"AAoBA,IAAM,EAA2D,CAC/D,qBAAsB,gBACtB,wBAAyB,mBACzB,uBAAwB,iBAC1B,EASA,SAAgB,kBAAkB,EAAa,EAAkC,EAAmC,CAClH,GAAI,KAAY,EAAI,CAClB,EAA6E,GAC3E,EAAQ,OAAS,EAAI,EAAU,KAEjC,MACF,CAEA,IAAM,EAAO,EAAiB,GACxB,EAAM,EAAQ,IAAK,GAAW,EAAO,EAAE,CAAC,CAAC,OAAO,OAAO,EAEzD,EAAI,SAAW,EAAG,EAAG,gBAAgB,CAAI,EACxC,EAAG,aAAa,EAAM,EAAI,KAAK,GAAG,CAAC,CAC1C"}
@@ -2,15 +2,15 @@
2
2
  * Cross-shadow-root ARIA relationship helper.
3
3
  *
4
4
  * Plain IDREF attributes (`aria-controls`, `aria-describedby`, `aria-labelledby`, …) cannot
5
- * resolve across shadow-tree boundaries — the browser only looks up an `id` within the same
5
+ * resolve across shadow-tree boundaries: the browser only looks up an `id` within the same
6
6
  * root as the referencing element, so `<div id="x">` inside one component's shadow root is
7
7
  * invisible to `aria-controls="x"` set inside another component's shadow root, even when both
8
- * custom elements are siblings in the light DOM. This is a platform limitation, not a bug —
8
+ * custom elements are siblings in the light DOM. This is a platform limitation, not a bug :
9
9
  * see the WICG "reference target" explainer for the full rationale.
10
10
  *
11
11
  * Modern browsers instead expose element-reflection IDL properties
12
12
  * (`ariaControlsElements`, `ariaDescribedByElements`, `ariaLabelledByElements`, …) that accept
13
- * live `Element` references directly, sidestepping ID lookup entirely — these work across
13
+ * live `Element` references directly, sidestepping ID lookup entirely: these work across
14
14
  * shadow roots by design. Where unsupported, this falls back to the plain IDREF attribute,
15
15
  * which is still correct for same-root pairings and degrades gracefully (no relationship)
16
16
  * for cross-root ones instead of throwing.