@vielzeug/refine 2.2.5 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/dist/accordion-item.cjs +1 -1
  2. package/dist/accordion-item.cjs.map +1 -1
  3. package/dist/accordion-item.js +1 -1
  4. package/dist/accordion-item.js.map +1 -1
  5. package/dist/card.cjs +1 -1
  6. package/dist/card.cjs.map +1 -1
  7. package/dist/card.js +1 -1
  8. package/dist/card.js.map +1 -1
  9. package/dist/carousel.cjs +1 -1
  10. package/dist/carousel.cjs.map +1 -1
  11. package/dist/carousel.js +1 -1
  12. package/dist/carousel.js.map +1 -1
  13. package/dist/chat-message.cjs +9 -9
  14. package/dist/chat-message.cjs.map +1 -1
  15. package/dist/chat-message.js +41 -38
  16. package/dist/chat-message.js.map +1 -1
  17. package/dist/combobox.cjs +9 -7
  18. package/dist/combobox.cjs.map +1 -1
  19. package/dist/combobox.js +55 -48
  20. package/dist/combobox.js.map +1 -1
  21. package/dist/command-palette.cjs +1 -1
  22. package/dist/command-palette.cjs.map +1 -1
  23. package/dist/command-palette.js +1 -1
  24. package/dist/command-palette.js.map +1 -1
  25. package/dist/content/index.d.ts +2 -0
  26. package/dist/content/index.d.ts.map +1 -1
  27. package/dist/content/list/list-item.css.cjs +1 -1
  28. package/dist/content/list/list-item.css.cjs.map +1 -1
  29. package/dist/content/list/list-item.css.js +1 -1
  30. package/dist/content/list/list-item.css.js.map +1 -1
  31. package/dist/content/list/list-item.d.ts +9 -0
  32. package/dist/content/list/list-item.d.ts.map +1 -1
  33. package/dist/content/stats/stats.css.cjs +2 -0
  34. package/dist/content/stats/stats.css.cjs.map +1 -0
  35. package/dist/content/stats/stats.css.js +6 -0
  36. package/dist/content/stats/stats.css.js.map +1 -0
  37. package/dist/content/stats/stats.d.ts +72 -0
  38. package/dist/content/stats/stats.d.ts.map +1 -0
  39. package/dist/core/announcer.cjs +1 -1
  40. package/dist/core/announcer.cjs.map +1 -1
  41. package/dist/core/announcer.d.ts +13 -3
  42. package/dist/core/announcer.d.ts.map +1 -1
  43. package/dist/core/announcer.js +16 -9
  44. package/dist/core/announcer.js.map +1 -1
  45. package/dist/core/auto-resize.cjs +1 -1
  46. package/dist/core/auto-resize.cjs.map +1 -1
  47. package/dist/core/auto-resize.js +1 -1
  48. package/dist/core/auto-resize.js.map +1 -1
  49. package/dist/custom-elements.json +4457 -4161
  50. package/dist/datagrid.cjs +199 -179
  51. package/dist/datagrid.cjs.map +1 -1
  52. package/dist/datagrid.js +435 -321
  53. package/dist/datagrid.js.map +1 -1
  54. package/dist/disclosure/tab-item/tab-item.css.cjs +1 -1
  55. package/dist/disclosure/tab-item/tab-item.css.cjs.map +1 -1
  56. package/dist/disclosure/tab-item/tab-item.css.js +1 -1
  57. package/dist/disclosure/tab-item/tab-item.css.js.map +1 -1
  58. package/dist/disclosure/tab-item/tab-item.d.ts +3 -0
  59. package/dist/disclosure/tab-item/tab-item.d.ts.map +1 -1
  60. package/dist/disclosure/tabs/tabs.css.cjs +1 -1
  61. package/dist/disclosure/tabs/tabs.css.cjs.map +1 -1
  62. package/dist/disclosure/tabs/tabs.css.js +1 -1
  63. package/dist/disclosure/tabs/tabs.css.js.map +1 -1
  64. package/dist/disclosure/tabs/tabs.d.ts +4 -0
  65. package/dist/disclosure/tabs/tabs.d.ts.map +1 -1
  66. package/dist/feedback/typing-indicator/typing-indicator.d.ts +2 -2
  67. package/dist/file-input.cjs +49 -49
  68. package/dist/file-input.cjs.map +1 -1
  69. package/dist/file-input.js +128 -127
  70. package/dist/file-input.js.map +1 -1
  71. package/dist/frameworks/elements.d.ts +2 -1
  72. package/dist/icon.cjs +1 -1
  73. package/dist/icon.cjs.map +1 -1
  74. package/dist/icon.js +1 -1
  75. package/dist/icon.js.map +1 -1
  76. package/dist/input.cjs +5 -4
  77. package/dist/input.cjs.map +1 -1
  78. package/dist/input.js +14 -8
  79. package/dist/input.js.map +1 -1
  80. package/dist/inputs/combobox/combobox-options.cjs +1 -1
  81. package/dist/inputs/combobox/combobox-options.cjs.map +1 -1
  82. package/dist/inputs/combobox/combobox-options.d.ts.map +1 -1
  83. package/dist/inputs/combobox/combobox-options.js +1 -0
  84. package/dist/inputs/combobox/combobox-options.js.map +1 -1
  85. package/dist/inputs/combobox/combobox.css.cjs +1 -1
  86. package/dist/inputs/combobox/combobox.css.cjs.map +1 -1
  87. package/dist/inputs/combobox/combobox.css.js +1 -1
  88. package/dist/inputs/combobox/combobox.css.js.map +1 -1
  89. package/dist/inputs/combobox/combobox.d.ts +1 -0
  90. package/dist/inputs/combobox/combobox.d.ts.map +1 -1
  91. package/dist/inputs/combobox/combobox.types.d.ts +7 -0
  92. package/dist/inputs/combobox/combobox.types.d.ts.map +1 -1
  93. package/dist/inputs/datagrid/datagrid-model.cjs +1 -1
  94. package/dist/inputs/datagrid/datagrid-model.cjs.map +1 -1
  95. package/dist/inputs/datagrid/datagrid-model.d.ts +17 -3
  96. package/dist/inputs/datagrid/datagrid-model.d.ts.map +1 -1
  97. package/dist/inputs/datagrid/datagrid-model.js +93 -70
  98. package/dist/inputs/datagrid/datagrid-model.js.map +1 -1
  99. package/dist/inputs/datagrid/datagrid.css.cjs +1 -1
  100. package/dist/inputs/datagrid/datagrid.css.cjs.map +1 -1
  101. package/dist/inputs/datagrid/datagrid.css.js +1 -1
  102. package/dist/inputs/datagrid/datagrid.css.js.map +1 -1
  103. package/dist/inputs/datagrid/datagrid.d.ts +54 -4
  104. package/dist/inputs/datagrid/datagrid.d.ts.map +1 -1
  105. package/dist/inputs/file-input/file-input-upload.cjs +1 -1
  106. package/dist/inputs/file-input/file-input-upload.cjs.map +1 -1
  107. package/dist/inputs/file-input/file-input-upload.d.ts +2 -0
  108. package/dist/inputs/file-input/file-input-upload.d.ts.map +1 -1
  109. package/dist/inputs/file-input/file-input-upload.js +5 -2
  110. package/dist/inputs/file-input/file-input-upload.js.map +1 -1
  111. package/dist/inputs/file-input/file-input.d.ts.map +1 -1
  112. package/dist/inputs/index.d.ts +1 -1
  113. package/dist/inputs/index.d.ts.map +1 -1
  114. package/dist/inputs/input/input.css.cjs +1 -1
  115. package/dist/inputs/input/input.css.cjs.map +1 -1
  116. package/dist/inputs/input/input.css.js +1 -1
  117. package/dist/inputs/input/input.css.js.map +1 -1
  118. package/dist/inputs/input/input.d.ts +3 -0
  119. package/dist/inputs/input/input.d.ts.map +1 -1
  120. package/dist/inputs/select/select.css.cjs +1 -1
  121. package/dist/inputs/select/select.css.cjs.map +1 -1
  122. package/dist/inputs/select/select.css.js +1 -1
  123. package/dist/inputs/select/select.css.js.map +1 -1
  124. package/dist/inputs/select/select.d.ts +5 -0
  125. package/dist/inputs/select/select.d.ts.map +1 -1
  126. package/dist/layout/navbar/navbar.d.ts +2 -0
  127. package/dist/layout/navbar/navbar.d.ts.map +1 -1
  128. package/dist/layout/sidebar/sidebar.css.cjs +1 -1
  129. package/dist/layout/sidebar/sidebar.css.cjs.map +1 -1
  130. package/dist/layout/sidebar/sidebar.css.js +1 -1
  131. package/dist/layout/sidebar/sidebar.css.js.map +1 -1
  132. package/dist/layout/sidebar/sidebar.d.ts +8 -0
  133. package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
  134. package/dist/list-item.cjs +6 -5
  135. package/dist/list-item.cjs.map +1 -1
  136. package/dist/list-item.js +23 -17
  137. package/dist/list-item.js.map +1 -1
  138. package/dist/list.cjs +1 -1
  139. package/dist/list.cjs.map +1 -1
  140. package/dist/list.js +1 -1
  141. package/dist/list.js.map +1 -1
  142. package/dist/menu.cjs +1 -1
  143. package/dist/menu.cjs.map +1 -1
  144. package/dist/menu.js +1 -1
  145. package/dist/menu.js.map +1 -1
  146. package/dist/navbar.cjs +2 -2
  147. package/dist/navbar.cjs.map +1 -1
  148. package/dist/navbar.js +7 -5
  149. package/dist/navbar.js.map +1 -1
  150. package/dist/navigation-menu.cjs +1 -1
  151. package/dist/navigation-menu.cjs.map +1 -1
  152. package/dist/navigation-menu.js +1 -1
  153. package/dist/navigation-menu.js.map +1 -1
  154. package/dist/overlay/popover/popover.css.cjs +1 -1
  155. package/dist/overlay/popover/popover.css.cjs.map +1 -1
  156. package/dist/overlay/popover/popover.css.js +1 -1
  157. package/dist/overlay/popover/popover.css.js.map +1 -1
  158. package/dist/overlay/popover/popover.d.ts +1 -0
  159. package/dist/overlay/popover/popover.d.ts.map +1 -1
  160. package/dist/overlay/shared/use-dialog.cjs +1 -1
  161. package/dist/overlay/shared/use-dialog.cjs.map +1 -1
  162. package/dist/overlay/shared/use-dialog.js +1 -1
  163. package/dist/overlay/shared/use-dialog.js.map +1 -1
  164. package/dist/overlay/shared/use-floating-trigger.cjs +1 -1
  165. package/dist/overlay/shared/use-floating-trigger.cjs.map +1 -1
  166. package/dist/overlay/shared/use-floating-trigger.js +1 -1
  167. package/dist/overlay/shared/use-floating-trigger.js.map +1 -1
  168. package/dist/popover.cjs.map +1 -1
  169. package/dist/popover.js.map +1 -1
  170. package/dist/radio.cjs +1 -1
  171. package/dist/radio.cjs.map +1 -1
  172. package/dist/radio.js +10 -8
  173. package/dist/radio.js.map +1 -1
  174. package/dist/rating.cjs +1 -1
  175. package/dist/rating.cjs.map +1 -1
  176. package/dist/rating.js +1 -1
  177. package/dist/rating.js.map +1 -1
  178. package/dist/refine.cjs +428 -379
  179. package/dist/refine.cjs.map +1 -1
  180. package/dist/refine.iife.js +428 -379
  181. package/dist/refine.iife.js.map +1 -1
  182. package/dist/refine.js +458 -409
  183. package/dist/refine.js.map +1 -1
  184. package/dist/select.cjs +16 -14
  185. package/dist/select.cjs.map +1 -1
  186. package/dist/select.js +118 -112
  187. package/dist/select.js.map +1 -1
  188. package/dist/sidebar.cjs +28 -23
  189. package/dist/sidebar.cjs.map +1 -1
  190. package/dist/sidebar.js +112 -77
  191. package/dist/sidebar.js.map +1 -1
  192. package/dist/slider.cjs +1 -1
  193. package/dist/slider.cjs.map +1 -1
  194. package/dist/slider.js +1 -1
  195. package/dist/slider.js.map +1 -1
  196. package/dist/stats.cjs +18 -0
  197. package/dist/stats.cjs.map +1 -0
  198. package/dist/stats.js +88 -0
  199. package/dist/stats.js.map +1 -0
  200. package/dist/stepper.cjs +1 -1
  201. package/dist/stepper.cjs.map +1 -1
  202. package/dist/stepper.js +1 -1
  203. package/dist/stepper.js.map +1 -1
  204. package/dist/styles/mixins/animation.css.cjs +2 -0
  205. package/dist/styles/mixins/animation.css.cjs.map +1 -1
  206. package/dist/styles/mixins/animation.css.d.ts.map +1 -1
  207. package/dist/styles/mixins/animation.css.js +2 -0
  208. package/dist/styles/mixins/animation.css.js.map +1 -1
  209. package/dist/tab-item.cjs +1 -1
  210. package/dist/tab-item.cjs.map +1 -1
  211. package/dist/tab-item.js +3 -2
  212. package/dist/tab-item.js.map +1 -1
  213. package/dist/tabs.cjs +1 -1
  214. package/dist/tabs.cjs.map +1 -1
  215. package/dist/tabs.js +3 -1
  216. package/dist/tabs.js.map +1 -1
  217. package/dist/toast.cjs.map +1 -1
  218. package/dist/toast.js.map +1 -1
  219. package/dist/typing-indicator.cjs +1 -1
  220. package/dist/typing-indicator.cjs.map +1 -1
  221. package/dist/typing-indicator.js +8 -7
  222. package/dist/typing-indicator.js.map +1 -1
  223. package/package.json +66 -58
@@ -1 +1 @@
1
- {"version":3,"file":"list-item.css.js","names":[],"sources":["../../../src/content/list/list-item.css?inline"],"sourcesContent":["@layer refine.base {\n /* `--_gap` / `--_padding` / `--_font-size` are intentionally not emitted by any mixin in this\n component's own `styles` array — they're inherited from the parent `ore-list`'s\n `sizeVariantMixin(LIST_SIZE_PRESET)` call the same way `ore-menu-item` inherits\n `--_gap`/`--_font-size` from `ore-menu` (see `LIST_SIZE_PRESET`'s doc comment). The\n `var(..., fallback)` defaults below cover standalone use outside an `ore-list`. */\n :host {\n --_actions-width: var(--list-item-actions-width, var(--size-24));\n position: relative;\n display: block;\n overflow: hidden;\n color: var(--color-contrast-700);\n }\n\n /* ========================================\n Row\n ======================================== */\n\n .row {\n position: relative;\n z-index: 1;\n box-sizing: border-box;\n display: flex;\n gap: var(--_gap, var(--size-4));\n align-items: center;\n width: 100%;\n min-height: var(--_min-height, var(--size-4));\n padding: var(--_padding, var(--size-2-5) var(--size-4));\n padding-inline-end: var(--_padding-inline-end, var(--size-6));\n font-size: var(--_font-size, var(--text-sm));\n text-align: start;\n touch-action: pan-y;\n cursor: default;\n outline: none;\n background: var(--list-item-bg, var(--color-canvas));\n border: none;\n transform: translateX(calc(var(--_swipe-x, 0) * var(--_actions-width)));\n transition: var(--_motion-transition, transform var(--transition-fast), background var(--transition-fast));\n }\n\n .leading,\n .trailing {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n }\n\n .content {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--size-0-5);\n min-width: 0;\n }\n\n .title {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .description {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--text-xs);\n color: var(--color-contrast-400);\n white-space: nowrap;\n }\n\n .description:empty {\n display: none;\n }\n\n .row:hover {\n background: var(--list-item-hover-bg, var(--color-contrast-100));\n }\n\n .row:focus-visible {\n outline: var(--focus-ring);\n outline-offset: -2px;\n }\n\n /* ========================================\n Selected / Disabled States\n ======================================== */\n\n :host([selected]) .row {\n color: var(--color-primary);\n background: var(--list-item-selected-bg, color-mix(in oklch, var(--color-primary) 10%, transparent));\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: 0.6;\n }\n\n /* ========================================\n Swipe Action Panels\n ======================================== */\n\n .actions {\n position: absolute;\n inset-block: 0;\n display: flex;\n align-items: stretch;\n width: var(--_actions-width);\n transition: var(--_motion-transition, transform var(--transition-fast));\n }\n\n /* A single slotted action button fills the whole revealed panel — the common case (one big\n tappable target, e.g. a full-height \"Delete\" button) rather than a small icon-sized hit area.\n This stretches the slotted element's own light-DOM box, which is enough for plain elements\n (a native <button>, an <a>, ...). It is NOT enough on its own for `ore-button`: its visible\n surface is an inner `[part=\"button\"]` with a fixed height from its size preset, independent\n of its own host box size — pass `fullheight` on the `ore-button` itself too (see its own\n `fullheight` prop) to actually fill this panel edge-to-edge. */\n .actions ::slotted(*) {\n flex: 1;\n height: 100%;\n }\n\n .actions-left {\n left: 0;\n transform: translateX(calc((var(--_swipe-x, 0) - 1) * 100%));\n }\n\n .actions-right {\n right: 0;\n transform: translateX(calc((var(--_swipe-x, 0) + 1) * 100%));\n }\n\n /* `:host(:has(...))` (`:has()` as `:host()`'s functional argument) is invalid — `:host()`\n only accepts a compound selector and doesn't accept `:has()` inside it. The plain `:host`\n pseudo-class chained directly with `:has()` (no parentheses) is the valid form. Kept as\n separate rules (not grouped via a comma with the `[revealed]` rules) so a selector list\n doesn't silently drop the whole rule if either half were ever invalid again. */\n :host([revealed='left']) {\n --_swipe-x: 1;\n }\n\n :host([revealed='right']) {\n --_swipe-x: -1;\n }\n\n :host:has(.actions-left:focus-within) {\n --_swipe-x: 1;\n }\n\n :host:has(.actions-right:focus-within) {\n --_swipe-x: -1;\n }\n\n /* Suppress the transition while a drag is actively updating `--_swipe-x` inline so the row\n and action panels track the pointer 1:1 instead of lagging behind on every move event. */\n :host([data-dragging]) .row,\n :host([data-dragging]) .actions-left,\n :host([data-dragging]) .actions-right {\n transition: none;\n }\n\n /* Feedback that the drag has passed into \"swipe all the way through\" territory (close to\n CONFIRM_THRESHOLD) — set by list-item.ts's onMove once past ~85% of that distance, cleared\n on release/cancel/commit. Visual-only; the confirm itself fires from JS regardless of CSS. */\n :host([data-confirming]) .actions-left,\n :host([data-confirming]) .actions-right {\n filter: saturate(1.4) brightness(0.94);\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"list-item.css.js","names":[],"sources":["../../../src/content/list/list-item.css?inline"],"sourcesContent":["@layer refine.base {\n /* `--_gap` / `--_padding` / `--_font-size` are intentionally not emitted by any mixin in this\n component's own `styles` array — they're inherited from the parent `ore-list`'s\n `sizeVariantMixin(LIST_SIZE_PRESET)` call the same way `ore-menu-item` inherits\n `--_gap`/`--_font-size` from `ore-menu` (see `LIST_SIZE_PRESET`'s doc comment). The\n `var(..., fallback)` defaults below cover standalone use outside an `ore-list`. */\n :host {\n --_actions-width: var(--list-item-actions-width, var(--size-24));\n position: relative;\n display: block;\n overflow: hidden;\n color: var(--color-contrast-700);\n }\n\n /* ========================================\n Row\n ======================================== */\n\n .row {\n position: relative;\n z-index: 1;\n box-sizing: border-box;\n display: flex;\n gap: var(--_gap, var(--size-4));\n align-items: center;\n width: 100%;\n min-height: var(--_min-height, var(--size-4));\n padding: var(--_padding, var(--size-2-5) var(--size-4));\n padding-inline-end: var(--_padding-inline-end, var(--size-6));\n font-size: var(--_font-size, var(--text-sm));\n text-align: start;\n touch-action: pan-y;\n cursor: default;\n outline: none;\n background: var(--list-item-bg, var(--color-canvas));\n border: none;\n transform: translateX(calc(var(--_swipe-x, 0) * var(--_actions-width)));\n transition: var(--_motion-transition, transform var(--transition-fast), background var(--transition-fast));\n }\n\n :host([data-two-line]) .row {\n min-height: var(--_min-height-two-line, var(--size-18));\n }\n\n :host([actionable]) .row {\n cursor: pointer;\n }\n\n .leading,\n .trailing {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n }\n\n .leading[hidden],\n .trailing[hidden] {\n display: none;\n }\n\n .content {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--size-0-5);\n min-width: 0;\n }\n\n .title {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .description {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--text-xs);\n color: var(--color-contrast-600);\n white-space: nowrap;\n }\n\n .description:empty {\n display: none;\n }\n\n .row:hover {\n background: var(--list-item-hover-bg, var(--color-contrast-100));\n }\n\n .row:focus-visible {\n outline: var(--focus-ring);\n outline-offset: -2px;\n }\n\n /* ========================================\n Selected / Disabled States\n ======================================== */\n\n :host([selected]) .row {\n color: var(--color-primary);\n background: var(--list-item-selected-bg, color-mix(in oklch, var(--color-primary) 10%, transparent));\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: 0.6;\n }\n\n /* ========================================\n Swipe Action Panels\n ======================================== */\n\n .actions {\n position: absolute;\n inset-block: 0;\n display: flex;\n align-items: stretch;\n width: var(--_actions-width);\n transition: var(--_motion-transition, transform var(--transition-fast));\n }\n\n /* A single slotted action button fills the whole revealed panel — the common case (one big\n tappable target, e.g. a full-height \"Delete\" button) rather than a small icon-sized hit area.\n This stretches the slotted element's own light-DOM box, which is enough for plain elements\n (a native <button>, an <a>, ...). It is NOT enough on its own for `ore-button`: its visible\n surface is an inner `[part=\"button\"]` with a fixed height from its size preset, independent\n of its own host box size — pass `fullheight` on the `ore-button` itself too (see its own\n `fullheight` prop) to actually fill this panel edge-to-edge. */\n .actions ::slotted(*) {\n flex: 1;\n height: 100%;\n }\n\n .actions-left {\n left: 0;\n transform: translateX(calc((var(--_swipe-x, 0) - 1) * 100%));\n }\n\n .actions-right {\n right: 0;\n transform: translateX(calc((var(--_swipe-x, 0) + 1) * 100%));\n }\n\n /* `:host(:has(...))` (`:has()` as `:host()`'s functional argument) is invalid — `:host()`\n only accepts a compound selector and doesn't accept `:has()` inside it. The plain `:host`\n pseudo-class chained directly with `:has()` (no parentheses) is the valid form. Kept as\n separate rules (not grouped via a comma with the `[revealed]` rules) so a selector list\n doesn't silently drop the whole rule if either half were ever invalid again. */\n :host([revealed='left']) {\n --_swipe-x: 1;\n }\n\n :host([revealed='right']) {\n --_swipe-x: -1;\n }\n\n :host:has(.actions-left:focus-within) {\n --_swipe-x: 1;\n }\n\n :host:has(.actions-right:focus-within) {\n --_swipe-x: -1;\n }\n\n /* Suppress the transition while a drag is actively updating `--_swipe-x` inline so the row\n and action panels track the pointer 1:1 instead of lagging behind on every move event. */\n :host([data-dragging]) .row,\n :host([data-dragging]) .actions-left,\n :host([data-dragging]) .actions-right {\n transition: none;\n }\n\n /* Feedback that the drag has passed into \"swipe all the way through\" territory (close to\n CONFIRM_THRESHOLD) — set by list-item.ts's onMove once past ~85% of that distance, cleared\n on release/cancel/commit. Visual-only; the confirm itself fires from JS regardless of CSS. */\n :host([data-confirming]) .actions-left,\n :host([data-confirming]) .actions-right {\n filter: saturate(1.4) brightness(0.94);\n }\n}\n"],"mappings":""}
@@ -2,6 +2,11 @@
2
2
  export type ListItemRevealSide = 'left' | 'right';
3
3
  /** Events emitted by the list-item component */
4
4
  export type OreListItemEvents = {
5
+ /** Emitted when an actionable item is activated by pointer or keyboard. */
6
+ activate: {
7
+ item: HTMLElement;
8
+ value: null | string;
9
+ };
5
10
  /** Emitted when the revealed action panel closes (gesture, tap, or programmatic). */
6
11
  conceal: {
7
12
  item: HTMLElement;
@@ -32,6 +37,8 @@ export type OreListItemEvents = {
32
37
  };
33
38
  /** List-item component properties */
34
39
  export type OreListItemProps = {
40
+ /** Enable pointer and Enter/Space activation with button semantics. */
41
+ actionable?: boolean;
35
42
  /** Disable this item — blocks pointer interaction, swipe actions, and selection */
36
43
  disabled?: boolean;
37
44
  /** Which action panel is revealed: 'left' | 'right' (absent = closed). Settable programmatically. */
@@ -63,11 +70,13 @@ export type OreListItemProps = {
63
70
  *
64
71
  * @element ore-list-item
65
72
  *
73
+ * @attr {boolean} actionable - Enable pointer and Enter/Space activation with button semantics
66
74
  * @attr {boolean} disabled - Disable this item
67
75
  * @attr {boolean} selected - Read-only reflected attribute: `true` when this item's `value` matches the parent `ore-list`'s `value`. Not independently settable.
68
76
  * @attr {string} value - Opaque value compared against the parent list's `value` to derive `selected`; also reported in select/change events
69
77
  * @attr {string} revealed - Which action panel is revealed: 'left' | 'right'
70
78
  *
79
+ * @fires activate - Actionable item is activated. detail: { item: HTMLElement, value: string | null }
71
80
  * @fires select - Item becomes selected. detail: { item: HTMLElement, value: string | null }
72
81
  * @fires deselect - Item becomes deselected. detail: { item: HTMLElement, value: string | null }
73
82
  * @fires reveal - An action panel opens. detail: { item: HTMLElement, side: 'left' | 'right' }
@@ -1 +1 @@
1
- {"version":3,"file":"list-item.d.ts","sourceRoot":"","sources":["../../../src/content/list/list-item.ts"],"names":[],"mappings":"AAqBA,sEAAsE;AACtE,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,OAAO,CAAC;AAElD,gDAAgD;AAChD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,qFAAqF;IACrF,OAAO,EAAE;QAAE,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC;IAC/B;;;OAGG;IACH,OAAO,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,kBAAkB,CAAA;KAAE,CAAC;IACzD,gDAAgD;IAChD,QAAQ,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,IAAI,GAAG,MAAM,CAAA;KAAE,CAAC;IACtD,0DAA0D;IAC1D,MAAM,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,kBAAkB,CAAA;KAAE,CAAC;IACxD,iGAAiG;IACjG,MAAM,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,IAAI,GAAG,MAAM,CAAA;KAAE,CAAC;CACrD,CAAC;AAEF,qCAAqC;AACrC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAMF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,eAAO,MAAM,aAAa,EAAG,eAAwB,CAAC"}
1
+ {"version":3,"file":"list-item.d.ts","sourceRoot":"","sources":["../../../src/content/list/list-item.ts"],"names":[],"mappings":"AAqBA,sEAAsE;AACtE,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,OAAO,CAAC;AAElD,gDAAgD;AAChD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2EAA2E;IAC3E,QAAQ,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,IAAI,GAAG,MAAM,CAAA;KAAE,CAAC;IACtD,qFAAqF;IACrF,OAAO,EAAE;QAAE,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC;IAC/B;;;OAGG;IACH,OAAO,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,kBAAkB,CAAA;KAAE,CAAC;IACzD,gDAAgD;IAChD,QAAQ,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,IAAI,GAAG,MAAM,CAAA;KAAE,CAAC;IACtD,0DAA0D;IAC1D,MAAM,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,kBAAkB,CAAA;KAAE,CAAC;IACxD,iGAAiG;IACjG,MAAM,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,IAAI,GAAG,MAAM,CAAA;KAAE,CAAC;CACrD,CAAC;AAEF,qCAAqC;AACrC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,uEAAuE;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAMF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,eAAO,MAAM,aAAa,EAAG,eAAwB,CAAC"}
@@ -0,0 +1,2 @@
1
+ var e=`@layer refine.base{:host{--_bg:var(--stats-bg,var(--color-canvas));--_color:var(--stats-color,var(--color-contrast-900));--_border-color:var(--stats-border-color,var(--color-divider));--_radius:var(--stats-radius,var(--rounded-lg));--_padding:var(--stats-padding,var(--size-4));--_min-height:var(--stats-min-height,var(--size-32));--_value-size:var(--stats-value-size,var(--text-2xl));--_icon-size:var(--size-9);--_gap:var(--size-3);min-width:0;display:block}.stats{box-sizing:border-box;gap:var(--_gap);width:100%;min-width:0;min-height:var(--_min-height);padding:var(--_padding);color:var(--_color);background:var(--_bg);border:var(--border) solid var(--_border-color);border-radius:var(--_radius);flex-direction:column;display:flex;position:relative;overflow:hidden}.header,.body,.footer{min-width:0;display:flex}.header{gap:var(--size-2);align-items:center}.icon{flex:0 0 var(--_icon-size);width:var(--_icon-size);height:var(--_icon-size);color:var(--_theme-base,var(--color-primary));background:var(--stats-icon-bg,var(--_theme-backdrop,var(--color-contrast-100)));border-radius:var(--rounded-md);place-items:center;display:grid}.label,.description,.trend{text-overflow:ellipsis;font-size:var(--text-xs);white-space:nowrap;overflow:hidden}.label{font-weight:var(--font-semibold);color:var(--color-contrast-700)}.body{gap:var(--size-3);flex:1;justify-content:space-between;align-items:end}.value{text-overflow:ellipsis;min-width:0;font-size:var(--_value-size);font-variant-numeric:tabular-nums;line-height:var(--leading-tight);color:inherit;letter-spacing:-.03em;white-space:nowrap;overflow:hidden}.visual{flex:0 45%;justify-content:end;align-items:end;min-width:50%;display:flex;position:absolute;bottom:0;right:0}.footer{gap:var(--size-1);align-items:center}.trend{font-weight:var(--font-semibold);color:var(--color-contrast-700);flex-shrink:0}.trend[data-direction=up]{color:var(--stats-trend-up,var(--color-success))}.trend[data-direction=down]{color:var(--stats-trend-down,var(--color-error))}.description{min-width:0;color:var(--color-contrast-700)}.loading{z-index:2;pointer-events:none;background:linear-gradient(100deg, color-mix(in oklch, var(--_bg) 88%, var(--color-contrast-200)) 20%, color-mix(in oklch, var(--_bg) 72%, var(--color-contrast-200)) 45%, color-mix(in oklch, var(--_bg) 88%, var(--color-contrast-200)) 70%);background-size:220% 100%;display:none;position:absolute;inset:0}[hidden]{display:none}}@layer refine.variants{:host([variant=plain]) .stats{background:var(--stats-bg,transparent);border:0;border-radius:0;min-height:0}:host([variant=solid]){--_bg:var(--stats-bg,var(--_theme-base));--_color:var(--stats-color,var(--_theme-contrast));--_border-color:var(--stats-border-color,var(--_theme-base))}:host([variant=solid]) .label,:host([variant=solid]) .description,:host([variant=solid]) .trend{color:color-mix(in oklch, var(--_color) 78%, transparent)}}@layer refine.overrides{:host([loading]) .loading{animation:var(--_motion-animation,stats-loading 1.4s linear infinite);display:block}:host([loading]) .header,:host([loading]) .body,:host([loading]) .footer{visibility:hidden}:host([disabled]){pointer-events:none;opacity:.55;filter:grayscale(.35)}}@keyframes stats-loading{0%{background-position:100% 0}to{background-position:-120% 0}}`;exports.default=e;
2
+ //# sourceMappingURL=stats.css.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.css.cjs","names":[],"sources":["../../../src/content/stats/stats.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_bg: var(--stats-bg, var(--color-canvas));\n --_color: var(--stats-color, var(--color-contrast-900));\n --_border-color: var(--stats-border-color, var(--color-divider));\n --_radius: var(--stats-radius, var(--rounded-lg));\n --_padding: var(--stats-padding, var(--size-4));\n --_min-height: var(--stats-min-height, var(--size-32));\n --_value-size: var(--stats-value-size, var(--text-2xl));\n --_icon-size: var(--size-9);\n --_gap: var(--size-3);\n\n display: block;\n min-width: 0;\n }\n\n .stats {\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: var(--_gap);\n width: 100%;\n min-width: 0;\n min-height: var(--_min-height);\n padding: var(--_padding);\n overflow: hidden;\n color: var(--_color);\n background: var(--_bg);\n border: var(--border) solid var(--_border-color);\n border-radius: var(--_radius);\n }\n\n .header,\n .body,\n .footer {\n display: flex;\n min-width: 0;\n }\n\n .header {\n gap: var(--size-2);\n align-items: center;\n }\n\n .icon {\n display: grid;\n flex: 0 0 var(--_icon-size);\n place-items: center;\n width: var(--_icon-size);\n height: var(--_icon-size);\n color: var(--_theme-base, var(--color-primary));\n background: var(--stats-icon-bg, var(--_theme-backdrop, var(--color-contrast-100)));\n border-radius: var(--rounded-md);\n }\n\n .label,\n .description,\n .trend {\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--text-xs);\n white-space: nowrap;\n }\n\n .label {\n font-weight: var(--font-semibold);\n color: var(--color-contrast-700);\n }\n\n .body {\n flex: 1;\n gap: var(--size-3);\n align-items: end;\n justify-content: space-between;\n }\n\n .value {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--_value-size);\n font-variant-numeric: tabular-nums;\n line-height: var(--leading-tight);\n color: inherit;\n letter-spacing: -0.03em;\n white-space: nowrap;\n }\n\n .visual {\n position: absolute;\n right: 0;\n bottom: 0;\n display: flex;\n flex: 0 1 45%;\n align-items: end;\n justify-content: end;\n min-width: 50%;\n }\n\n .footer {\n gap: var(--size-1);\n align-items: center;\n }\n\n .trend {\n flex-shrink: 0;\n font-weight: var(--font-semibold);\n color: var(--color-contrast-700);\n }\n\n .trend[data-direction='up'] {\n color: var(--stats-trend-up, var(--color-success));\n }\n\n .trend[data-direction='down'] {\n color: var(--stats-trend-down, var(--color-error));\n }\n\n .description {\n min-width: 0;\n color: var(--color-contrast-700);\n }\n\n .loading {\n position: absolute;\n inset: 0;\n z-index: 2;\n display: none;\n pointer-events: none;\n background: linear-gradient(\n 100deg,\n color-mix(in oklch, var(--_bg) 88%, var(--color-contrast-200)) 20%,\n color-mix(in oklch, var(--_bg) 72%, var(--color-contrast-200)) 45%,\n color-mix(in oklch, var(--_bg) 88%, var(--color-contrast-200)) 70%\n );\n background-size: 220% 100%;\n }\n\n [hidden] {\n display: none;\n }\n}\n\n@layer refine.variants {\n :host([variant='plain']) .stats {\n min-height: 0;\n background: var(--stats-bg, transparent);\n border: 0;\n border-radius: 0;\n }\n\n :host([variant='solid']) {\n --_bg: var(--stats-bg, var(--_theme-base));\n --_color: var(--stats-color, var(--_theme-contrast));\n --_border-color: var(--stats-border-color, var(--_theme-base));\n }\n\n :host([variant='solid']) .label,\n :host([variant='solid']) .description,\n :host([variant='solid']) .trend {\n color: color-mix(in oklch, var(--_color) 78%, transparent);\n }\n}\n\n@layer refine.overrides {\n :host([loading]) .loading {\n display: block;\n animation: var(--_motion-animation, stats-loading 1.4s linear infinite);\n }\n\n :host([loading]) .header,\n :host([loading]) .body,\n :host([loading]) .footer {\n visibility: hidden;\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: 0.55;\n filter: grayscale(0.35);\n }\n}\n\n@keyframes stats-loading {\n from {\n background-position: 100% 0;\n }\n\n to {\n background-position: -120% 0;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,6 @@
1
+ //#region src/content/stats/stats.css?inline
2
+ var e = "@layer refine.base{:host{--_bg:var(--stats-bg,var(--color-canvas));--_color:var(--stats-color,var(--color-contrast-900));--_border-color:var(--stats-border-color,var(--color-divider));--_radius:var(--stats-radius,var(--rounded-lg));--_padding:var(--stats-padding,var(--size-4));--_min-height:var(--stats-min-height,var(--size-32));--_value-size:var(--stats-value-size,var(--text-2xl));--_icon-size:var(--size-9);--_gap:var(--size-3);min-width:0;display:block}.stats{box-sizing:border-box;gap:var(--_gap);width:100%;min-width:0;min-height:var(--_min-height);padding:var(--_padding);color:var(--_color);background:var(--_bg);border:var(--border) solid var(--_border-color);border-radius:var(--_radius);flex-direction:column;display:flex;position:relative;overflow:hidden}.header,.body,.footer{min-width:0;display:flex}.header{gap:var(--size-2);align-items:center}.icon{flex:0 0 var(--_icon-size);width:var(--_icon-size);height:var(--_icon-size);color:var(--_theme-base,var(--color-primary));background:var(--stats-icon-bg,var(--_theme-backdrop,var(--color-contrast-100)));border-radius:var(--rounded-md);place-items:center;display:grid}.label,.description,.trend{text-overflow:ellipsis;font-size:var(--text-xs);white-space:nowrap;overflow:hidden}.label{font-weight:var(--font-semibold);color:var(--color-contrast-700)}.body{gap:var(--size-3);flex:1;justify-content:space-between;align-items:end}.value{text-overflow:ellipsis;min-width:0;font-size:var(--_value-size);font-variant-numeric:tabular-nums;line-height:var(--leading-tight);color:inherit;letter-spacing:-.03em;white-space:nowrap;overflow:hidden}.visual{flex:0 45%;justify-content:end;align-items:end;min-width:50%;display:flex;position:absolute;bottom:0;right:0}.footer{gap:var(--size-1);align-items:center}.trend{font-weight:var(--font-semibold);color:var(--color-contrast-700);flex-shrink:0}.trend[data-direction=up]{color:var(--stats-trend-up,var(--color-success))}.trend[data-direction=down]{color:var(--stats-trend-down,var(--color-error))}.description{min-width:0;color:var(--color-contrast-700)}.loading{z-index:2;pointer-events:none;background:linear-gradient(100deg, color-mix(in oklch, var(--_bg) 88%, var(--color-contrast-200)) 20%, color-mix(in oklch, var(--_bg) 72%, var(--color-contrast-200)) 45%, color-mix(in oklch, var(--_bg) 88%, var(--color-contrast-200)) 70%);background-size:220% 100%;display:none;position:absolute;inset:0}[hidden]{display:none}}@layer refine.variants{:host([variant=plain]) .stats{background:var(--stats-bg,transparent);border:0;border-radius:0;min-height:0}:host([variant=solid]){--_bg:var(--stats-bg,var(--_theme-base));--_color:var(--stats-color,var(--_theme-contrast));--_border-color:var(--stats-border-color,var(--_theme-base))}:host([variant=solid]) .label,:host([variant=solid]) .description,:host([variant=solid]) .trend{color:color-mix(in oklch, var(--_color) 78%, transparent)}}@layer refine.overrides{:host([loading]) .loading{animation:var(--_motion-animation,stats-loading 1.4s linear infinite);display:block}:host([loading]) .header,:host([loading]) .body,:host([loading]) .footer{visibility:hidden}:host([disabled]){pointer-events:none;opacity:.55;filter:grayscale(.35)}}@keyframes stats-loading{0%{background-position:100% 0}to{background-position:-120% 0}}";
3
+ //#endregion
4
+ export { e as default };
5
+
6
+ //# sourceMappingURL=stats.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.css.js","names":[],"sources":["../../../src/content/stats/stats.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_bg: var(--stats-bg, var(--color-canvas));\n --_color: var(--stats-color, var(--color-contrast-900));\n --_border-color: var(--stats-border-color, var(--color-divider));\n --_radius: var(--stats-radius, var(--rounded-lg));\n --_padding: var(--stats-padding, var(--size-4));\n --_min-height: var(--stats-min-height, var(--size-32));\n --_value-size: var(--stats-value-size, var(--text-2xl));\n --_icon-size: var(--size-9);\n --_gap: var(--size-3);\n\n display: block;\n min-width: 0;\n }\n\n .stats {\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: var(--_gap);\n width: 100%;\n min-width: 0;\n min-height: var(--_min-height);\n padding: var(--_padding);\n overflow: hidden;\n color: var(--_color);\n background: var(--_bg);\n border: var(--border) solid var(--_border-color);\n border-radius: var(--_radius);\n }\n\n .header,\n .body,\n .footer {\n display: flex;\n min-width: 0;\n }\n\n .header {\n gap: var(--size-2);\n align-items: center;\n }\n\n .icon {\n display: grid;\n flex: 0 0 var(--_icon-size);\n place-items: center;\n width: var(--_icon-size);\n height: var(--_icon-size);\n color: var(--_theme-base, var(--color-primary));\n background: var(--stats-icon-bg, var(--_theme-backdrop, var(--color-contrast-100)));\n border-radius: var(--rounded-md);\n }\n\n .label,\n .description,\n .trend {\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--text-xs);\n white-space: nowrap;\n }\n\n .label {\n font-weight: var(--font-semibold);\n color: var(--color-contrast-700);\n }\n\n .body {\n flex: 1;\n gap: var(--size-3);\n align-items: end;\n justify-content: space-between;\n }\n\n .value {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--_value-size);\n font-variant-numeric: tabular-nums;\n line-height: var(--leading-tight);\n color: inherit;\n letter-spacing: -0.03em;\n white-space: nowrap;\n }\n\n .visual {\n position: absolute;\n right: 0;\n bottom: 0;\n display: flex;\n flex: 0 1 45%;\n align-items: end;\n justify-content: end;\n min-width: 50%;\n }\n\n .footer {\n gap: var(--size-1);\n align-items: center;\n }\n\n .trend {\n flex-shrink: 0;\n font-weight: var(--font-semibold);\n color: var(--color-contrast-700);\n }\n\n .trend[data-direction='up'] {\n color: var(--stats-trend-up, var(--color-success));\n }\n\n .trend[data-direction='down'] {\n color: var(--stats-trend-down, var(--color-error));\n }\n\n .description {\n min-width: 0;\n color: var(--color-contrast-700);\n }\n\n .loading {\n position: absolute;\n inset: 0;\n z-index: 2;\n display: none;\n pointer-events: none;\n background: linear-gradient(\n 100deg,\n color-mix(in oklch, var(--_bg) 88%, var(--color-contrast-200)) 20%,\n color-mix(in oklch, var(--_bg) 72%, var(--color-contrast-200)) 45%,\n color-mix(in oklch, var(--_bg) 88%, var(--color-contrast-200)) 70%\n );\n background-size: 220% 100%;\n }\n\n [hidden] {\n display: none;\n }\n}\n\n@layer refine.variants {\n :host([variant='plain']) .stats {\n min-height: 0;\n background: var(--stats-bg, transparent);\n border: 0;\n border-radius: 0;\n }\n\n :host([variant='solid']) {\n --_bg: var(--stats-bg, var(--_theme-base));\n --_color: var(--stats-color, var(--_theme-contrast));\n --_border-color: var(--stats-border-color, var(--_theme-base));\n }\n\n :host([variant='solid']) .label,\n :host([variant='solid']) .description,\n :host([variant='solid']) .trend {\n color: color-mix(in oklch, var(--_color) 78%, transparent);\n }\n}\n\n@layer refine.overrides {\n :host([loading]) .loading {\n display: block;\n animation: var(--_motion-animation, stats-loading 1.4s linear infinite);\n }\n\n :host([loading]) .header,\n :host([loading]) .body,\n :host([loading]) .footer {\n visibility: hidden;\n }\n\n :host([disabled]) {\n pointer-events: none;\n opacity: 0.55;\n filter: grayscale(0.35);\n }\n}\n\n@keyframes stats-loading {\n from {\n background-position: 100% 0;\n }\n\n to {\n background-position: -120% 0;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,72 @@
1
+ import type { ComponentSize, RoundedSize, ThemeColor } from '../../types';
2
+ export type StatsTrendDirection = 'down' | 'neutral' | 'up';
3
+ export type StatsVariant = 'outlined' | 'plain' | 'solid';
4
+ export type OreStatsProps = {
5
+ color?: ThemeColor;
6
+ description?: string;
7
+ disabled?: boolean;
8
+ label?: string;
9
+ loading?: boolean;
10
+ rounded?: RoundedSize;
11
+ size?: ComponentSize;
12
+ trend?: string;
13
+ 'trend-direction'?: StatsTrendDirection;
14
+ value?: string;
15
+ variant?: StatsVariant;
16
+ };
17
+ /**
18
+ * A focused metric card for a label, primary value, supporting context, trend, icon, and optional visual.
19
+ *
20
+ * @element ore-stats
21
+ *
22
+ * @attr {string} label - Metric label
23
+ * @attr {string} value - Primary metric value
24
+ * @attr {string} description - Supporting context
25
+ * @attr {string} trend - Trend text
26
+ * @attr {string} trend-direction - Trend direction: 'up' | 'down' | 'neutral'
27
+ * @attr {string} variant - Visual variant: 'outlined' | 'plain' | 'solid'
28
+ * @attr {string} color - Semantic theme color
29
+ * @attr {string} size - Size: 'sm' | 'md' | 'lg'
30
+ * @attr {string} rounded - Border radius size
31
+ * @attr {boolean} loading - Show loading state
32
+ * @attr {boolean} disabled - Show unavailable state
33
+ *
34
+ * @slot icon - Leading metric icon
35
+ * @slot label - Replaces label text
36
+ * @slot value - Replaces value text
37
+ * @slot visual - Compact chart, progress, or other metric visual
38
+ * @slot trend - Replaces trend text
39
+ * @slot description - Replaces supporting context
40
+ *
41
+ * @cssprop --stats-bg - Card background
42
+ * @cssprop --stats-color - Card foreground color
43
+ * @cssprop --stats-border-color - Card border color
44
+ * @cssprop --stats-radius - Card border radius
45
+ * @cssprop --stats-padding - Card padding
46
+ * @cssprop --stats-min-height - Minimum card height
47
+ * @cssprop --stats-value-size - Primary value font size
48
+ * @cssprop --stats-icon-bg - Icon surface background
49
+ * @cssprop --stats-trend-up - Positive trend color
50
+ * @cssprop --stats-trend-down - Negative trend color
51
+ *
52
+ * @part card - Card surface
53
+ * @part loading - Loading indicator
54
+ * @part header - Label and icon row
55
+ * @part icon - Icon container
56
+ * @part label - Label container
57
+ * @part body - Value and visual row
58
+ * @part value - Primary value container
59
+ * @part visual - Visual slot container
60
+ * @part footer - Trend and description row
61
+ * @part trend - Trend container
62
+ * @part description - Description container
63
+ *
64
+ * @example
65
+ * ```html
66
+ * <ore-stats label="Revenue" value="$42,800" trend="+12%" trend-direction="up">
67
+ * <ore-icon slot="icon" name="chart-line"></ore-icon>
68
+ * </ore-stats>
69
+ * ```
70
+ */
71
+ export declare const STATS_TAG: "ore-stats";
72
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../src/content/stats/stats.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG1E,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;AAC5D,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,eAAO,MAAM,SAAS,EAAG,WAAoB,CAAC"}
@@ -1,2 +1,2 @@
1
- const e=require("../styles/mixins/accessibility.css.cjs");var t=new WeakMap,n=new WeakMap,r=(t,n)=>{let r=n.createElement(`div`);return r.setAttribute(`aria-live`,t),r.setAttribute(`aria-atomic`,`true`),r.setAttribute(`data-block-announcer`,t),r.style.cssText=e.SR_ONLY_INLINE_STYLE,n.body.appendChild(r),r},i=(e,n)=>{let i=t.get(n);return i?.[e]&&!n.body.contains(i[e])&&(i[e]=void 0),i?.[e]||(i||(i={},t.set(n,i)),i[e]=r(e,n)),i[e]??null},a=(e,t={})=>{if(typeof document>`u`)return;let r=i(t.politeness??`polite`,document);if(!r)return;r.textContent=``;let a=n.get(r);a!==void 0&&clearTimeout(a);let o=setTimeout(()=>{r.textContent=e,n.delete(r)},50);n.set(r,o)};exports.announce=a;
1
+ const e=require("../styles/mixins/accessibility.css.cjs");var t=new WeakMap,n=new WeakMap,r=(t,n)=>{if(!n.body)return null;let r=n.createElement(`div`);return r.setAttribute(`aria-live`,t),r.setAttribute(`aria-atomic`,`true`),r.setAttribute(`data-block-announcer`,t),r.style.cssText=e.SR_ONLY_INLINE_STYLE,n.body.appendChild(r),r},i=(e,n)=>{let i=t.get(n);if(i?.[e]&&!n.body?.contains(i[e])&&(i[e]=void 0),!i?.[e]){let a=r(e,n);if(!a)return null;i||(i={},t.set(n,i)),i[e]=a}return i[e]??null},a=(e,t={})=>{let r=t.document??(typeof document<`u`?document:null);if(!r)return;let a=i(t.politeness??`polite`,r);if(!a)return;a.textContent=``;let o=n.get(a);o!==void 0&&clearTimeout(o);let s=setTimeout(()=>{a.textContent=e,n.delete(a)},50);n.set(a,s)};exports.announce=a;
2
2
  //# sourceMappingURL=announcer.cjs.map
@@ -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 `<body>` — one polite,\n// one assertive. The clear-then-set pattern forces AT to re-read even identical\n// consecutive messages.\n//\n// Usage:\n// ```ts\n// import { announce } from './announcer';\n// announce(`${selectedCount} items selected`);\n// announce('Error: field is required', { politeness: 'assertive' });\n// ```\n\nimport { SR_ONLY_INLINE_STYLE } from '../styles';\n\n// ── Visually-hidden style ─────────────────────────────────────────────────────\n// Shared with the CSS-side `.sr-only` mixin — see `src/styles/mixins/accessibility.css.ts`.\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 => {\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.\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 if (!regions) {\n regions = {};\n _regions.set(doc, regions);\n }\n\n regions[politeness] = createRegion(politeness, doc);\n }\n\n return regions[politeness] ?? null;\n};\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport type AnnouncePoliteness = 'assertive' | 'polite';\n\nexport type AnnounceOptions = {\n /**\n * `'polite'` — queues the announcement; 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 * The clear-then-set pattern (50 ms delay) forces AT to re-read even when the\n * message text hasn't changed.\n *\n * @example\n * ```ts\n * announce('3 results found');\n * announce('Session expired', { politeness: 'assertive' });\n * ```\n */\nexport const announce = (message: string, options: AnnounceOptions = {}): void => {\n if (typeof document === 'undefined') return;\n\n const politeness = options.politeness ?? 'polite';\n const region = getRegion(politeness, document);\n\n if (!region) return;\n\n // Clear immediately so AT registers a content change, then set after a short\n // delay. The 50 ms window is the established minimum for most AT/browser combos.\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":"0DAyBA,IAAM,EAAW,IAAI,QACf,EAAU,IAAI,QAGd,GAAgB,EAAoC,IAA+B,CACvF,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,EAMM,GAAa,EAAoC,IAAsC,CAC3F,IAAI,EAAU,EAAS,IAAI,CAAG,EAgB9B,OAbI,IAAU,IAAe,CAAC,EAAI,KAAK,SAAS,EAAQ,EAAW,IACjE,EAAQ,GAAc,IAAA,IAGnB,IAAU,KACR,IACH,EAAU,CAAC,EACX,EAAS,IAAI,EAAK,CAAO,GAG3B,EAAQ,GAAc,EAAa,EAAY,CAAG,GAG7C,EAAQ,IAAe,IAChC,EA6Ba,GAAY,EAAiB,EAA2B,CAAC,IAAY,CAChF,GAAI,OAAO,SAAa,IAAa,OAGrC,IAAM,EAAS,EADI,EAAQ,YAAc,SACJ,QAAQ,EAE7C,GAAI,CAAC,EAAQ,OAIb,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,GAAgB,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,GAAa,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,EAAa,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,GAAY,EAAiB,EAA2B,CAAC,IAAY,CAChF,IAAM,EAAM,EAAQ,WAAa,OAAO,SAAa,IAAc,SAAW,MAE9E,GAAI,CAAC,EAAK,OAGV,IAAM,EAAS,EADI,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,7 +1,14 @@
1
1
  export type AnnouncePoliteness = 'assertive' | 'polite';
2
2
  export type AnnounceOptions = {
3
3
  /**
4
- * `'polite'` — queues the announcement; does not interrupt ongoing speech.
4
+ * The document whose `<body>` will host the visually-hidden live region.
5
+ * Defaults to the global `document`. Pass `getHost().ownerDocument` from
6
+ * inside a component that may live in an iframe to announce through the
7
+ * correct accessibility tree.
8
+ */
9
+ document?: Document;
10
+ /**
11
+ * `'polite'` — latest-value replacement; does not interrupt ongoing speech.
5
12
  * `'assertive'` — interrupts immediately. Use sparingly; reserve for errors.
6
13
  *
7
14
  * Default: `'polite'`.
@@ -12,13 +19,16 @@ export type AnnounceOptions = {
12
19
  * Announces a message to screen readers via a singleton visually-hidden
13
20
  * live region. Safe to call outside a component lifecycle (no cleanup needed).
14
21
  *
15
- * The clear-then-set pattern (50 ms delay) forces AT to re-read even when the
16
- * message text hasn't changed.
22
+ * Uses latest-value replacement: rapid consecutive calls to the same politeness
23
+ * cancel pending writes so only the last message is spoken. The clear-then-set
24
+ * pattern (short delay) forces AT to re-read even when the message text hasn't
25
+ * changed.
17
26
  *
18
27
  * @example
19
28
  * ```ts
20
29
  * announce('3 results found');
21
30
  * announce('Session expired', { politeness: 'assertive' });
31
+ * announce('Saved', { document: getHost().ownerDocument });
22
32
  * ```
23
33
  */
24
34
  export declare const announce: (message: string, options?: AnnounceOptions) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"announcer.d.ts","sourceRoot":"","sources":["../../src/core/announcer.ts"],"names":[],"mappings":"AAoEA,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,QAAQ,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,EAAE,UAAS,eAAoB,KAAG,IAwBzE,CAAC"}
1
+ {"version":3,"file":"announcer.d.ts","sourceRoot":"","sources":["../../src/core/announcer.ts"],"names":[],"mappings":"AA8EA,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,QAAQ,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,EAAE,UAAS,eAAoB,KAAG,IA2BzE,CAAC"}
@@ -1,22 +1,29 @@
1
1
  import { SR_ONLY_INLINE_STYLE as e } from "../styles/mixins/accessibility.css.js";
2
2
  //#region src/core/announcer.ts
3
3
  var t = /* @__PURE__ */ new WeakMap(), n = /* @__PURE__ */ new WeakMap(), r = (t, n) => {
4
+ if (!n.body) return null;
4
5
  let r = n.createElement("div");
5
6
  return r.setAttribute("aria-live", t), r.setAttribute("aria-atomic", "true"), r.setAttribute("data-block-announcer", t), r.style.cssText = e, n.body.appendChild(r), r;
6
7
  }, i = (e, n) => {
7
8
  let i = t.get(n);
8
- return i?.[e] && !n.body.contains(i[e]) && (i[e] = void 0), i?.[e] || (i || (i = {}, t.set(n, i)), i[e] = r(e, n)), i[e] ?? null;
9
+ if (i?.[e] && !n.body?.contains(i[e]) && (i[e] = void 0), !i?.[e]) {
10
+ let a = r(e, n);
11
+ if (!a) return null;
12
+ i || (i = {}, t.set(n, i)), i[e] = a;
13
+ }
14
+ return i[e] ?? null;
9
15
  }, a = (e, t = {}) => {
10
- if (typeof document > "u") return;
11
- let r = i(t.politeness ?? "polite", document);
16
+ let r = t.document ?? (typeof document < "u" ? document : null);
12
17
  if (!r) return;
13
- r.textContent = "";
14
- let a = n.get(r);
15
- a !== void 0 && clearTimeout(a);
16
- let o = setTimeout(() => {
17
- r.textContent = e, n.delete(r);
18
+ let a = i(t.politeness ?? "polite", r);
19
+ if (!a) return;
20
+ a.textContent = "";
21
+ let o = n.get(a);
22
+ o !== void 0 && clearTimeout(o);
23
+ let s = setTimeout(() => {
24
+ a.textContent = e, n.delete(a);
18
25
  }, 50);
19
- n.set(r, o);
26
+ n.set(a, s);
20
27
  };
21
28
  //#endregion
22
29
  export { a as announce };
@@ -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 `<body>` — one polite,\n// one assertive. The clear-then-set pattern forces AT to re-read even identical\n// consecutive messages.\n//\n// Usage:\n// ```ts\n// import { announce } from './announcer';\n// announce(`${selectedCount} items selected`);\n// announce('Error: field is required', { politeness: 'assertive' });\n// ```\n\nimport { SR_ONLY_INLINE_STYLE } from '../styles';\n\n// ── Visually-hidden style ─────────────────────────────────────────────────────\n// Shared with the CSS-side `.sr-only` mixin — see `src/styles/mixins/accessibility.css.ts`.\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 => {\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.\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 if (!regions) {\n regions = {};\n _regions.set(doc, regions);\n }\n\n regions[politeness] = createRegion(politeness, doc);\n }\n\n return regions[politeness] ?? null;\n};\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport type AnnouncePoliteness = 'assertive' | 'polite';\n\nexport type AnnounceOptions = {\n /**\n * `'polite'` — queues the announcement; 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 * The clear-then-set pattern (50 ms delay) forces AT to re-read even when the\n * message text hasn't changed.\n *\n * @example\n * ```ts\n * announce('3 results found');\n * announce('Session expired', { politeness: 'assertive' });\n * ```\n */\nexport const announce = (message: string, options: AnnounceOptions = {}): void => {\n if (typeof document === 'undefined') return;\n\n const politeness = options.politeness ?? 'polite';\n const region = getRegion(politeness, document);\n\n if (!region) return;\n\n // Clear immediately so AT registers a content change, then set after a short\n // delay. The 50 ms window is the established minimum for most AT/browser combos.\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":";;AAyBA,IAAM,oBAAW,IAAI,QAAmC,GAClD,oBAAU,IAAI,QAAoD,GAGlE,KAAgB,GAAoC,MAA+B;CACvF,IAAM,IAAK,EAAI,cAAc,KAAK;CASlC,OAPA,EAAG,aAAa,aAAa,CAAU,GACvC,EAAG,aAAa,eAAe,MAAM,GACrC,EAAG,aAAa,wBAAwB,CAAU,GAClD,EAAG,MAAM,UAAU,GAEnB,EAAI,KAAK,YAAY,CAAE,GAEhB;AACT,GAMM,KAAa,GAAoC,MAAsC;CAC3F,IAAI,IAAU,EAAS,IAAI,CAAG;CAgB9B,OAbI,IAAU,MAAe,CAAC,EAAI,KAAK,SAAS,EAAQ,EAAW,MACjE,EAAQ,KAAc,KAAA,IAGnB,IAAU,OACR,MACH,IAAU,CAAC,GACX,EAAS,IAAI,GAAK,CAAO,IAG3B,EAAQ,KAAc,EAAa,GAAY,CAAG,IAG7C,EAAQ,MAAe;AAChC,GA6Ba,KAAY,GAAiB,IAA2B,CAAC,MAAY;CAChF,IAAI,OAAO,WAAa,KAAa;CAGrC,IAAM,IAAS,EADI,EAAQ,cAAc,UACJ,QAAQ;CAE7C,IAAI,CAAC,GAAQ;CAIb,EAAO,cAAc;CAIrB,IAAM,IAAW,EAAQ,IAAI,CAAM;CAEnC,AAAI,MAAa,KAAA,KAAW,aAAa,CAAQ;CAEjD,IAAM,IAAQ,iBAAiB;EAE7B,AADA,EAAO,cAAc,GACrB,EAAQ,OAAO,CAAM;CACvB,GAAG,EAAE;CAEL,EAAQ,IAAI,GAAQ,CAAK;AAC3B"}
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":";;AA4BA,IAAM,oBAAW,IAAI,QAAmC,GAClD,oBAAU,IAAI,QAAoD,GAGlE,KAAgB,GAAoC,MAAsC;CAC9F,IAAI,CAAC,EAAI,MAAM,OAAO;CAEtB,IAAM,IAAK,EAAI,cAAc,KAAK;CASlC,OAPA,EAAG,aAAa,aAAa,CAAU,GACvC,EAAG,aAAa,eAAe,MAAM,GACrC,EAAG,aAAa,wBAAwB,CAAU,GAClD,EAAG,MAAM,UAAU,GAEnB,EAAI,KAAK,YAAY,CAAE,GAEhB;AACT,GAOM,KAAa,GAAoC,MAAsC;CAC3F,IAAI,IAAU,EAAS,IAAI,CAAG;CAO9B,IAJI,IAAU,MAAe,CAAC,EAAI,MAAM,SAAS,EAAQ,EAAW,MAClE,EAAQ,KAAc,KAAA,IAGpB,CAAC,IAAU,IAAa;EAC1B,IAAM,IAAK,EAAa,GAAY,CAAG;EAEvC,IAAI,CAAC,GAAI,OAAO;EAOhB,AALK,MACH,IAAU,CAAC,GACX,EAAS,IAAI,GAAK,CAAO,IAG3B,EAAQ,KAAc;CACxB;CAEA,OAAO,EAAQ,MAAe;AAChC,GAuCa,KAAY,GAAiB,IAA2B,CAAC,MAAY;CAChF,IAAM,IAAM,EAAQ,aAAa,OAAO,WAAa,MAAc,WAAW;CAE9E,IAAI,CAAC,GAAK;CAGV,IAAM,IAAS,EADI,EAAQ,cAAc,UACJ,CAAG;CAExC,IAAI,CAAC,GAAQ;CAKb,EAAO,cAAc;CAIrB,IAAM,IAAW,EAAQ,IAAI,CAAM;CAEnC,AAAI,MAAa,KAAA,KAAW,aAAa,CAAQ;CAEjD,IAAM,IAAQ,iBAAiB;EAE7B,AADA,EAAO,cAAc,GACrB,EAAQ,OAAO,CAAM;CACvB,GAAG,EAAE;CAEL,EAAQ,IAAI,GAAQ,CAAK;AAC3B"}
@@ -1,2 +1,2 @@
1
- var e=(e={})=>{let t=null,n=()=>{!t||e.enabled?.value===!1||(t.style.height=`auto`,t.style.height=`${t.scrollHeight}px`)};return{recompute:n,wire:e=>(t=e,e.addEventListener(`input`,n),requestAnimationFrame(n),()=>{e.removeEventListener(`input`,n),t===e&&(t=null)})}};exports.createAutoResize=e;
1
+ var e=(e={})=>{let t=null,n=()=>{t&&e.enabled?.value!==!1&&(t.style.height=`auto`,t.style.height=`${t.scrollHeight}px`)};return{recompute:n,wire:e=>(t=e,e.addEventListener(`input`,n),requestAnimationFrame(n),()=>{e.removeEventListener(`input`,n),t===e&&(t=null)})}};exports.createAutoResize=e;
2
2
  //# sourceMappingURL=auto-resize.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"auto-resize.cjs","names":[],"sources":["../../src/core/auto-resize.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nexport type AutoResizeOptions = {\n /** Gates growth; omit for an always-on composer field, or pass a signal for a toggleable `auto-resize` attribute. */\n enabled?: Readable<boolean | undefined>;\n};\n\nexport type AutoResizeControl = {\n /** Recomputes height from the wired element's current content. No-ops when `enabled` is false or nothing is wired. */\n recompute: () => void;\n /** Wire onto the raw `<textarea>` when it mounts. Returns a detach function to call on unmount. */\n wire: (el: HTMLTextAreaElement) => () => void;\n};\n\n/**\n * Grows a `<textarea>`'s height to fit its content, shared by every component that offers\n * auto-resize (`ore-textarea`, `ore-message-composer`) instead of each reimplementing it.\n *\n * Owns only height — manual-resize cursor/direction is a per-component styling concern, not\n * shared behavior, so it stays local to whichever component renders a resize handle.\n *\n * @example\n * ```ts\n * const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n *\n * onElement(textareaRef, (el) => autoResize.wire(el));\n * // after a programmatic value change that doesn't fire a native `input` event:\n * autoResize.recompute();\n * ```\n */\nexport const createAutoResize = (options: AutoResizeOptions = {}): AutoResizeControl => {\n let wiredEl: HTMLTextAreaElement | null = null;\n\n const recompute = (): void => {\n if (!wiredEl || options.enabled?.value === false) return;\n\n wiredEl.style.height = 'auto';\n wiredEl.style.height = `${wiredEl.scrollHeight}px`;\n };\n\n const wire = (el: HTMLTextAreaElement): (() => void) => {\n wiredEl = el;\n el.addEventListener('input', recompute);\n\n // Deferred a frame: right after mount, the browser hasn't necessarily finished laying out\n // the textarea yet, so a synchronous `scrollHeight` read here can be stale — often reading\n // as 0 and collapsing the field until the next real `input` event happens to correct it.\n // `ore-textarea` has always worked around this locally with its own extra `requestAnimationFrame`\n // call; folding the deferral into `wire()` itself means every consumer gets it for free.\n requestAnimationFrame(recompute);\n\n return () => {\n el.removeEventListener('input', recompute);\n\n if (wiredEl === el) wiredEl = null;\n };\n };\n\n return { recompute, wire };\n};\n"],"mappings":"AA8BA,IAAa,GAAoB,EAA6B,CAAC,IAAyB,CACtF,IAAI,EAAsC,KAEpC,MAAwB,CACxB,CAAC,GAAW,EAAQ,SAAS,QAAU,KAE3C,EAAQ,MAAM,OAAS,OACvB,EAAQ,MAAM,OAAS,GAAG,EAAQ,aAAa,IACjD,EAoBA,MAAO,CAAE,YAAW,KAlBN,IACZ,EAAU,EACV,EAAG,iBAAiB,QAAS,CAAS,EAOtC,sBAAsB,CAAS,MAElB,CACX,EAAG,oBAAoB,QAAS,CAAS,EAErC,IAAY,IAAI,EAAU,KAChC,EAGuB,CAC3B"}
1
+ {"version":3,"file":"auto-resize.cjs","names":[],"sources":["../../src/core/auto-resize.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nexport type AutoResizeOptions = {\n /** Gates growth; omit for an always-on composer field, or pass a signal for a toggleable `auto-resize` attribute. */\n enabled?: Readable<boolean | undefined>;\n};\n\nexport type AutoResizeControl = {\n /** Recomputes height from the wired element's current content. No-ops when `enabled` is false or nothing is wired. */\n recompute: () => void;\n /** Wire onto the raw `<textarea>` when it mounts. Returns a detach function to call on unmount. */\n wire: (el: HTMLTextAreaElement) => () => void;\n};\n\n/**\n * Grows a `<textarea>`'s height to fit its content, shared by every component that offers\n * auto-resize (`ore-textarea`, `ore-message-composer`) instead of each reimplementing it.\n *\n * Owns only height — manual-resize cursor/direction is a per-component styling concern, not\n * shared behavior, so it stays local to whichever component renders a resize handle.\n *\n * @example\n * ```ts\n * const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n *\n * onElement(textareaRef, (el) => autoResize.wire(el));\n * // after a programmatic value change that doesn't fire a native `input` event:\n * autoResize.recompute();\n * ```\n */\nexport const createAutoResize = (options: AutoResizeOptions = {}): AutoResizeControl => {\n let wiredEl: HTMLTextAreaElement | null = null;\n\n const recompute = (): void => {\n if (!wiredEl || options.enabled?.value === false) return;\n\n wiredEl.style.height = 'auto';\n wiredEl.style.height = `${wiredEl.scrollHeight}px`;\n };\n\n const wire = (el: HTMLTextAreaElement): (() => void) => {\n wiredEl = el;\n el.addEventListener('input', recompute);\n\n // Deferred a frame: right after mount, the browser hasn't necessarily finished laying out\n // the textarea yet, so a synchronous `scrollHeight` read here can be stale — often reading\n // as 0 and collapsing the field until the next real `input` event happens to correct it.\n // `ore-textarea` has always worked around this locally with its own extra `requestAnimationFrame`\n // call; folding the deferral into `wire()` itself means every consumer gets it for free.\n requestAnimationFrame(recompute);\n\n return () => {\n el.removeEventListener('input', recompute);\n\n if (wiredEl === el) wiredEl = null;\n };\n };\n\n return { recompute, wire };\n};\n"],"mappings":"AA8BA,IAAa,GAAoB,EAA6B,CAAC,IAAyB,CACtF,IAAI,EAAsC,KAEpC,MAAwB,CACvB,GAAW,EAAQ,SAAS,QAAU,KAE3C,EAAQ,MAAM,OAAS,OACvB,EAAQ,MAAM,OAAS,GAAG,EAAQ,aAAa,IACjD,EAoBA,MAAO,CAAE,YAAW,KAlBN,IACZ,EAAU,EACV,EAAG,iBAAiB,QAAS,CAAS,EAOtC,sBAAsB,CAAS,MAElB,CACX,EAAG,oBAAoB,QAAS,CAAS,EAErC,IAAY,IAAI,EAAU,KAChC,EAGuB,CAC3B"}
@@ -1,7 +1,7 @@
1
1
  //#region src/core/auto-resize.ts
2
2
  var e = (e = {}) => {
3
3
  let t = null, n = () => {
4
- !t || e.enabled?.value === !1 || (t.style.height = "auto", t.style.height = `${t.scrollHeight}px`);
4
+ t && e.enabled?.value !== !1 && (t.style.height = "auto", t.style.height = `${t.scrollHeight}px`);
5
5
  };
6
6
  return {
7
7
  recompute: n,
@@ -1 +1 @@
1
- {"version":3,"file":"auto-resize.js","names":[],"sources":["../../src/core/auto-resize.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nexport type AutoResizeOptions = {\n /** Gates growth; omit for an always-on composer field, or pass a signal for a toggleable `auto-resize` attribute. */\n enabled?: Readable<boolean | undefined>;\n};\n\nexport type AutoResizeControl = {\n /** Recomputes height from the wired element's current content. No-ops when `enabled` is false or nothing is wired. */\n recompute: () => void;\n /** Wire onto the raw `<textarea>` when it mounts. Returns a detach function to call on unmount. */\n wire: (el: HTMLTextAreaElement) => () => void;\n};\n\n/**\n * Grows a `<textarea>`'s height to fit its content, shared by every component that offers\n * auto-resize (`ore-textarea`, `ore-message-composer`) instead of each reimplementing it.\n *\n * Owns only height — manual-resize cursor/direction is a per-component styling concern, not\n * shared behavior, so it stays local to whichever component renders a resize handle.\n *\n * @example\n * ```ts\n * const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n *\n * onElement(textareaRef, (el) => autoResize.wire(el));\n * // after a programmatic value change that doesn't fire a native `input` event:\n * autoResize.recompute();\n * ```\n */\nexport const createAutoResize = (options: AutoResizeOptions = {}): AutoResizeControl => {\n let wiredEl: HTMLTextAreaElement | null = null;\n\n const recompute = (): void => {\n if (!wiredEl || options.enabled?.value === false) return;\n\n wiredEl.style.height = 'auto';\n wiredEl.style.height = `${wiredEl.scrollHeight}px`;\n };\n\n const wire = (el: HTMLTextAreaElement): (() => void) => {\n wiredEl = el;\n el.addEventListener('input', recompute);\n\n // Deferred a frame: right after mount, the browser hasn't necessarily finished laying out\n // the textarea yet, so a synchronous `scrollHeight` read here can be stale — often reading\n // as 0 and collapsing the field until the next real `input` event happens to correct it.\n // `ore-textarea` has always worked around this locally with its own extra `requestAnimationFrame`\n // call; folding the deferral into `wire()` itself means every consumer gets it for free.\n requestAnimationFrame(recompute);\n\n return () => {\n el.removeEventListener('input', recompute);\n\n if (wiredEl === el) wiredEl = null;\n };\n };\n\n return { recompute, wire };\n};\n"],"mappings":";AA8BA,IAAa,KAAoB,IAA6B,CAAC,MAAyB;CACtF,IAAI,IAAsC,MAEpC,UAAwB;EACxB,CAAC,KAAW,EAAQ,SAAS,UAAU,OAE3C,EAAQ,MAAM,SAAS,QACvB,EAAQ,MAAM,SAAS,GAAG,EAAQ,aAAa;CACjD;CAoBA,OAAO;EAAE;EAAW,OAlBN,OACZ,IAAU,GACV,EAAG,iBAAiB,SAAS,CAAS,GAOtC,sBAAsB,CAAS,SAElB;GAGX,AAFA,EAAG,oBAAoB,SAAS,CAAS,GAErC,MAAY,MAAI,IAAU;EAChC;CAGuB;AAC3B"}
1
+ {"version":3,"file":"auto-resize.js","names":[],"sources":["../../src/core/auto-resize.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nexport type AutoResizeOptions = {\n /** Gates growth; omit for an always-on composer field, or pass a signal for a toggleable `auto-resize` attribute. */\n enabled?: Readable<boolean | undefined>;\n};\n\nexport type AutoResizeControl = {\n /** Recomputes height from the wired element's current content. No-ops when `enabled` is false or nothing is wired. */\n recompute: () => void;\n /** Wire onto the raw `<textarea>` when it mounts. Returns a detach function to call on unmount. */\n wire: (el: HTMLTextAreaElement) => () => void;\n};\n\n/**\n * Grows a `<textarea>`'s height to fit its content, shared by every component that offers\n * auto-resize (`ore-textarea`, `ore-message-composer`) instead of each reimplementing it.\n *\n * Owns only height — manual-resize cursor/direction is a per-component styling concern, not\n * shared behavior, so it stays local to whichever component renders a resize handle.\n *\n * @example\n * ```ts\n * const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n *\n * onElement(textareaRef, (el) => autoResize.wire(el));\n * // after a programmatic value change that doesn't fire a native `input` event:\n * autoResize.recompute();\n * ```\n */\nexport const createAutoResize = (options: AutoResizeOptions = {}): AutoResizeControl => {\n let wiredEl: HTMLTextAreaElement | null = null;\n\n const recompute = (): void => {\n if (!wiredEl || options.enabled?.value === false) return;\n\n wiredEl.style.height = 'auto';\n wiredEl.style.height = `${wiredEl.scrollHeight}px`;\n };\n\n const wire = (el: HTMLTextAreaElement): (() => void) => {\n wiredEl = el;\n el.addEventListener('input', recompute);\n\n // Deferred a frame: right after mount, the browser hasn't necessarily finished laying out\n // the textarea yet, so a synchronous `scrollHeight` read here can be stale — often reading\n // as 0 and collapsing the field until the next real `input` event happens to correct it.\n // `ore-textarea` has always worked around this locally with its own extra `requestAnimationFrame`\n // call; folding the deferral into `wire()` itself means every consumer gets it for free.\n requestAnimationFrame(recompute);\n\n return () => {\n el.removeEventListener('input', recompute);\n\n if (wiredEl === el) wiredEl = null;\n };\n };\n\n return { recompute, wire };\n};\n"],"mappings":";AA8BA,IAAa,KAAoB,IAA6B,CAAC,MAAyB;CACtF,IAAI,IAAsC,MAEpC,UAAwB;EACxB,AAAC,KAAW,EAAQ,SAAS,UAAU,OAE3C,EAAQ,MAAM,SAAS,QACvB,EAAQ,MAAM,SAAS,GAAG,EAAQ,aAAa;CACjD;CAoBA,OAAO;EAAE;EAAW,OAlBN,OACZ,IAAU,GACV,EAAG,iBAAiB,SAAS,CAAS,GAOtC,sBAAsB,CAAS,SAElB;GAGX,AAFA,EAAG,oBAAoB,SAAS,CAAS,GAErC,MAAY,MAAI,IAAU;EAChC;CAGuB;AAC3B"}