@vielzeug/refine 1.5.0 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/dist/async.cjs +9 -9
  2. package/dist/async.cjs.map +1 -1
  3. package/dist/async.js +30 -31
  4. package/dist/async.js.map +1 -1
  5. package/dist/button.cjs +6 -6
  6. package/dist/button.cjs.map +1 -1
  7. package/dist/button.js +29 -30
  8. package/dist/button.js.map +1 -1
  9. package/dist/calendar.cjs +44 -44
  10. package/dist/calendar.cjs.map +1 -1
  11. package/dist/calendar.js +115 -117
  12. package/dist/calendar.js.map +1 -1
  13. package/dist/checkbox-group.cjs +7 -7
  14. package/dist/checkbox-group.cjs.map +1 -1
  15. package/dist/checkbox-group.js +71 -73
  16. package/dist/checkbox-group.js.map +1 -1
  17. package/dist/checkbox.cjs +3 -3
  18. package/dist/checkbox.cjs.map +1 -1
  19. package/dist/checkbox.js +28 -29
  20. package/dist/checkbox.js.map +1 -1
  21. package/dist/code-window.cjs +7 -7
  22. package/dist/code-window.cjs.map +1 -1
  23. package/dist/code-window.js +17 -18
  24. package/dist/code-window.js.map +1 -1
  25. package/dist/combobox.cjs +1 -1
  26. package/dist/combobox.cjs.map +1 -1
  27. package/dist/combobox.js +11 -12
  28. package/dist/combobox.js.map +1 -1
  29. package/dist/custom-elements.json +3581 -3581
  30. package/dist/datagrid.cjs +66 -66
  31. package/dist/datagrid.cjs.map +1 -1
  32. package/dist/datagrid.js +267 -268
  33. package/dist/datagrid.js.map +1 -1
  34. package/dist/date-picker.cjs +11 -11
  35. package/dist/date-picker.cjs.map +1 -1
  36. package/dist/date-picker.js +8 -9
  37. package/dist/date-picker.js.map +1 -1
  38. package/dist/file-input.cjs +6 -6
  39. package/dist/file-input.cjs.map +1 -1
  40. package/dist/file-input.js +50 -51
  41. package/dist/file-input.js.map +1 -1
  42. package/dist/grid.cjs +1 -1
  43. package/dist/grid.cjs.map +1 -1
  44. package/dist/grid.js +47 -48
  45. package/dist/grid.js.map +1 -1
  46. package/dist/icon.cjs +1 -1
  47. package/dist/icon.cjs.map +1 -1
  48. package/dist/icon.js +26 -27
  49. package/dist/icon.js.map +1 -1
  50. package/dist/input.cjs +20 -20
  51. package/dist/input.cjs.map +1 -1
  52. package/dist/input.js +98 -100
  53. package/dist/input.js.map +1 -1
  54. package/dist/layout/navbar/navbar.css.cjs +1 -1
  55. package/dist/layout/navbar/navbar.css.cjs.map +1 -1
  56. package/dist/layout/navbar/navbar.css.js +1 -1
  57. package/dist/layout/navbar/navbar.css.js.map +1 -1
  58. package/dist/layout/navbar/navbar.d.ts +1 -0
  59. package/dist/layout/navbar/navbar.d.ts.map +1 -1
  60. package/dist/message-composer.cjs +18 -18
  61. package/dist/message-composer.cjs.map +1 -1
  62. package/dist/message-composer.js +113 -115
  63. package/dist/message-composer.js.map +1 -1
  64. package/dist/navbar.cjs +16 -13
  65. package/dist/navbar.cjs.map +1 -1
  66. package/dist/navbar.js +84 -79
  67. package/dist/navbar.js.map +1 -1
  68. package/dist/otp-input.cjs +4 -4
  69. package/dist/otp-input.cjs.map +1 -1
  70. package/dist/otp-input.js +31 -32
  71. package/dist/otp-input.js.map +1 -1
  72. package/dist/progress.cjs +13 -13
  73. package/dist/progress.cjs.map +1 -1
  74. package/dist/progress.js +45 -46
  75. package/dist/progress.js.map +1 -1
  76. package/dist/radio-group.cjs +5 -5
  77. package/dist/radio-group.cjs.map +1 -1
  78. package/dist/radio-group.js +83 -85
  79. package/dist/radio-group.js.map +1 -1
  80. package/dist/radio.cjs +3 -3
  81. package/dist/radio.cjs.map +1 -1
  82. package/dist/radio.js +27 -28
  83. package/dist/radio.js.map +1 -1
  84. package/dist/rating.cjs +4 -4
  85. package/dist/rating.cjs.map +1 -1
  86. package/dist/rating.js +30 -31
  87. package/dist/rating.js.map +1 -1
  88. package/dist/refine.cjs +8 -5
  89. package/dist/refine.cjs.map +1 -1
  90. package/dist/refine.iife.js +8 -5
  91. package/dist/refine.iife.js.map +1 -1
  92. package/dist/refine.js +8 -5
  93. package/dist/refine.js.map +1 -1
  94. package/dist/select.cjs +4 -4
  95. package/dist/select.cjs.map +1 -1
  96. package/dist/select.js +88 -89
  97. package/dist/select.js.map +1 -1
  98. package/dist/sidebar.cjs +11 -11
  99. package/dist/sidebar.cjs.map +1 -1
  100. package/dist/sidebar.js +97 -98
  101. package/dist/sidebar.js.map +1 -1
  102. package/dist/skeleton.cjs +3 -3
  103. package/dist/skeleton.cjs.map +1 -1
  104. package/dist/skeleton.js +23 -24
  105. package/dist/skeleton.js.map +1 -1
  106. package/dist/slider.cjs +3 -3
  107. package/dist/slider.cjs.map +1 -1
  108. package/dist/slider.js +65 -66
  109. package/dist/slider.js.map +1 -1
  110. package/dist/switch.cjs +3 -3
  111. package/dist/switch.cjs.map +1 -1
  112. package/dist/switch.js +23 -24
  113. package/dist/switch.js.map +1 -1
  114. package/dist/tab-panel.cjs +7 -7
  115. package/dist/tab-panel.cjs.map +1 -1
  116. package/dist/tab-panel.js +21 -23
  117. package/dist/tab-panel.js.map +1 -1
  118. package/dist/testing.cjs +1 -1
  119. package/dist/testing.cjs.map +1 -1
  120. package/dist/testing.js +4 -5
  121. package/dist/testing.js.map +1 -1
  122. package/dist/textarea.cjs +13 -13
  123. package/dist/textarea.cjs.map +1 -1
  124. package/dist/textarea.js +92 -94
  125. package/dist/textarea.js.map +1 -1
  126. package/dist/time-picker.cjs +9 -9
  127. package/dist/time-picker.cjs.map +1 -1
  128. package/dist/time-picker.js +47 -48
  129. package/dist/time-picker.js.map +1 -1
  130. package/package.json +1 -1
  131. package/dist/ore/dist/_dev.cjs +0 -2
  132. package/dist/ore/dist/_dev.cjs.map +0 -1
  133. package/dist/ore/dist/_dev.js +0 -12
  134. package/dist/ore/dist/_dev.js.map +0 -1
  135. package/dist/ore/dist/base-element.cjs +0 -2
  136. package/dist/ore/dist/base-element.cjs.map +0 -1
  137. package/dist/ore/dist/base-element.js +0 -158
  138. package/dist/ore/dist/base-element.js.map +0 -1
  139. package/dist/ore/dist/context.cjs +0 -2
  140. package/dist/ore/dist/context.cjs.map +0 -1
  141. package/dist/ore/dist/context.js +0 -11
  142. package/dist/ore/dist/context.js.map +0 -1
  143. package/dist/ore/dist/define.cjs +0 -2
  144. package/dist/ore/dist/define.cjs.map +0 -1
  145. package/dist/ore/dist/define.js +0 -27
  146. package/dist/ore/dist/define.js.map +0 -1
  147. package/dist/ore/dist/directives/classMap.cjs +0 -1
  148. package/dist/ore/dist/directives/classMap.js +0 -1
  149. package/dist/ore/dist/directives/each.cjs +0 -1
  150. package/dist/ore/dist/directives/each.js +0 -5
  151. package/dist/ore/dist/directives/live.cjs +0 -2
  152. package/dist/ore/dist/directives/live.cjs.map +0 -1
  153. package/dist/ore/dist/directives/live.js +0 -6
  154. package/dist/ore/dist/directives/live.js.map +0 -1
  155. package/dist/ore/dist/directives/model.cjs +0 -1
  156. package/dist/ore/dist/directives/model.js +0 -3
  157. package/dist/ore/dist/directives/raw.cjs +0 -2
  158. package/dist/ore/dist/directives/raw.cjs.map +0 -1
  159. package/dist/ore/dist/directives/raw.js +0 -38
  160. package/dist/ore/dist/directives/raw.js.map +0 -1
  161. package/dist/ore/dist/directives/styleMap.cjs +0 -2
  162. package/dist/ore/dist/directives/styleMap.cjs.map +0 -1
  163. package/dist/ore/dist/directives/styleMap.js +0 -20
  164. package/dist/ore/dist/directives/styleMap.js.map +0 -1
  165. package/dist/ore/dist/directives/when.cjs +0 -2
  166. package/dist/ore/dist/directives/when.cjs.map +0 -1
  167. package/dist/ore/dist/directives/when.js +0 -38
  168. package/dist/ore/dist/directives/when.js.map +0 -1
  169. package/dist/ore/dist/directives.cjs +0 -1
  170. package/dist/ore/dist/directives.js +0 -8
  171. package/dist/ore/dist/errors.cjs +0 -3
  172. package/dist/ore/dist/errors.cjs.map +0 -1
  173. package/dist/ore/dist/errors.js +0 -50
  174. package/dist/ore/dist/errors.js.map +0 -1
  175. package/dist/ore/dist/forms/context.cjs +0 -2
  176. package/dist/ore/dist/forms/context.cjs.map +0 -1
  177. package/dist/ore/dist/forms/context.js +0 -6
  178. package/dist/ore/dist/forms/context.js.map +0 -1
  179. package/dist/ore/dist/forms/field.cjs +0 -2
  180. package/dist/ore/dist/forms/field.cjs.map +0 -1
  181. package/dist/ore/dist/forms/field.js +0 -42
  182. package/dist/ore/dist/forms/field.js.map +0 -1
  183. package/dist/ore/dist/forms.cjs +0 -1
  184. package/dist/ore/dist/forms.js +0 -3
  185. package/dist/ore/dist/observers/intersection-observe.cjs +0 -2
  186. package/dist/ore/dist/observers/intersection-observe.cjs.map +0 -1
  187. package/dist/ore/dist/observers/intersection-observe.js +0 -12
  188. package/dist/ore/dist/observers/intersection-observe.js.map +0 -1
  189. package/dist/ore/dist/observers/media-observe.cjs +0 -1
  190. package/dist/ore/dist/observers/media-observe.js +0 -1
  191. package/dist/ore/dist/observers/mutation-observe.cjs +0 -1
  192. package/dist/ore/dist/observers/mutation-observe.js +0 -1
  193. package/dist/ore/dist/observers/resize-observe.cjs +0 -2
  194. package/dist/ore/dist/observers/resize-observe.cjs.map +0 -1
  195. package/dist/ore/dist/observers/resize-observe.js +0 -20
  196. package/dist/ore/dist/observers/resize-observe.js.map +0 -1
  197. package/dist/ore/dist/observers.cjs +0 -1
  198. package/dist/ore/dist/observers.js +0 -5
  199. package/dist/ore/dist/props.cjs +0 -2
  200. package/dist/ore/dist/props.cjs.map +0 -1
  201. package/dist/ore/dist/props.js +0 -56
  202. package/dist/ore/dist/props.js.map +0 -1
  203. package/dist/ore/dist/runtime.cjs +0 -2
  204. package/dist/ore/dist/runtime.cjs.map +0 -1
  205. package/dist/ore/dist/runtime.js +0 -25
  206. package/dist/ore/dist/runtime.js.map +0 -1
  207. package/dist/ore/dist/testing/flush.cjs +0 -2
  208. package/dist/ore/dist/testing/flush.cjs.map +0 -1
  209. package/dist/ore/dist/testing/flush.js +0 -11
  210. package/dist/ore/dist/testing/flush.js.map +0 -1
  211. package/dist/ore/dist/testing/mount.cjs +0 -2
  212. package/dist/ore/dist/testing/mount.cjs.map +0 -1
  213. package/dist/ore/dist/testing/mount.js +0 -94
  214. package/dist/ore/dist/testing/mount.js.map +0 -1
  215. package/dist/ore/dist/testing/query.cjs +0 -2
  216. package/dist/ore/dist/testing/query.cjs.map +0 -1
  217. package/dist/ore/dist/testing/query.js +0 -12
  218. package/dist/ore/dist/testing/query.js.map +0 -1
  219. package/dist/ore/dist/testing/render-hook.cjs +0 -1
  220. package/dist/ore/dist/testing/render-hook.js +0 -3
  221. package/dist/ore/dist/testing/testing.cjs +0 -1
  222. package/dist/ore/dist/testing/testing.js +0 -4
  223. package/dist/ore/dist/testing/wait.cjs +0 -1
  224. package/dist/ore/dist/testing/wait.js +0 -1
  225. package/dist/ore/dist/testing.cjs +0 -1
  226. package/dist/ore/dist/testing.js +0 -6
  227. package/dist/ore/dist/types/bindings.cjs +0 -2
  228. package/dist/ore/dist/types/bindings.cjs.map +0 -1
  229. package/dist/ore/dist/types/bindings.js +0 -15
  230. package/dist/ore/dist/types/bindings.js.map +0 -1
  231. package/dist/ore/dist/types.cjs +0 -2
  232. package/dist/ore/dist/types.cjs.map +0 -1
  233. package/dist/ore/dist/types.js +0 -15
  234. package/dist/ore/dist/types.js.map +0 -1
  235. package/dist/ore/dist/utils/css.cjs +0 -2
  236. package/dist/ore/dist/utils/css.cjs.map +0 -1
  237. package/dist/ore/dist/utils/css.js +0 -18
  238. package/dist/ore/dist/utils/css.js.map +0 -1
  239. package/dist/ore/dist/utils/dom.cjs +0 -2
  240. package/dist/ore/dist/utils/dom.cjs.map +0 -1
  241. package/dist/ore/dist/utils/dom.js +0 -43
  242. package/dist/ore/dist/utils/dom.js.map +0 -1
  243. package/dist/ore/dist/utils/id.cjs +0 -2
  244. package/dist/ore/dist/utils/id.cjs.map +0 -1
  245. package/dist/ore/dist/utils/id.js +0 -4
  246. package/dist/ore/dist/utils/id.js.map +0 -1
package/dist/async.cjs CHANGED
@@ -1,11 +1,11 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./ore/dist/directives/when.cjs");require("./ore/dist/directives.cjs");const t=require("./styles/mixins/accessibility.css.cjs"),n=require("./feedback/async/async.css.cjs");let r=require("@vielzeug/ore"),i=require("@vielzeug/ripple");var a=`ore-async`;(0,r.define)(a,{props:{"empty-description":r.prop.string(),"empty-label":r.prop.string(`No content yet`),"error-description":r.prop.string(),"error-label":r.prop.string(`Something went wrong`),retryable:r.prop.bool(!1),status:r.prop.oneOf([`idle`,`loading`,`empty`,`error`,`success`],`success`)},setup(t){let n=(0,r.useEmit)(),a=(0,i.signal)(!1),o=(0,i.signal)(!1),s=(0,i.signal)(!1);(0,r.bind)({attr:{ariaBusy:()=>t.status.value===`loading`?`true`:`false`,ariaLabel:()=>t.status.value===`loading`?`Loading…`:null,ariaLive:()=>t.status.value===`error`?`assertive`:`polite`,status:t.status}});let c=(e,t)=>()=>t?.value?r.html`<p class="${e}">${t}</p>`:``;return r.html`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./feedback/async/async.css.cjs");let n=require("@vielzeug/ore"),r=require("@vielzeug/ore/directives"),i=require("@vielzeug/ripple");var a=`ore-async`;(0,n.define)(a,{props:{"empty-description":n.prop.string(),"empty-label":n.prop.string(`No content yet`),"error-description":n.prop.string(),"error-label":n.prop.string(`Something went wrong`),retryable:n.prop.bool(!1),status:n.prop.oneOf([`idle`,`loading`,`empty`,`error`,`success`],`success`)},setup(e){let t=(0,n.useEmit)(),a=(0,i.signal)(!1),o=(0,i.signal)(!1),s=(0,i.signal)(!1);(0,n.bind)({attr:{ariaBusy:()=>e.status.value===`loading`?`true`:`false`,ariaLabel:()=>e.status.value===`loading`?`Loading…`:null,ariaLive:()=>e.status.value===`error`?`assertive`:`polite`,status:e.status}});let c=(e,t)=>()=>t?.value?n.html`<p class="${e}">${t}</p>`:``;return n.html`
2
2
  <div class="region region-idle" role="presentation"></div>
3
3
 
4
4
  <div class="region region-loading" role="status">
5
5
  <slot
6
6
  name="loading"
7
7
  @slotchange=${e=>{a.value=e.target.assignedNodes().length>0}}></slot>
8
- ${e.when(a,()=>r.html``,()=>r.html`
8
+ ${(0,r.when)(a,()=>n.html``,()=>n.html`
9
9
  <div class="loading-default" aria-hidden="true">
10
10
  <ore-skeleton variant="text" lines="1" width="40%"></ore-skeleton>
11
11
  <ore-skeleton variant="text" lines="3" width="100%"></ore-skeleton>
@@ -18,12 +18,12 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
18
18
  <slot
19
19
  name="empty"
20
20
  @slotchange=${e=>{o.value=e.target.assignedNodes().length>0}}></slot>
21
- ${e.when(o,()=>r.html``,()=>r.html`
21
+ ${(0,r.when)(o,()=>n.html``,()=>n.html`
22
22
  <div class="empty-state" role="status">
23
23
  <div class="icon">
24
24
  <ore-icon name="inbox" size="100%" stroke-width="1.75" aria-hidden="true"></ore-icon>
25
25
  </div>
26
- ${c(`title`,t[`empty-label`])} ${c(`description`,t[`empty-description`])}
26
+ ${c(`title`,e[`empty-label`])} ${c(`description`,e[`empty-description`])}
27
27
  </div>
28
28
  `)}
29
29
  </div>
@@ -32,14 +32,14 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
32
32
  <slot
33
33
  name="error"
34
34
  @slotchange=${e=>{s.value=e.target.assignedNodes().length>0}}></slot>
35
- ${e.when(s,()=>r.html``,()=>r.html`
35
+ ${(0,r.when)(s,()=>n.html``,()=>n.html`
36
36
  <div class="error-state" role="alert">
37
37
  <div class="icon">
38
38
  <ore-icon name="triangle-alert" size="100%" stroke-width="1.75" aria-hidden="true"></ore-icon>
39
39
  </div>
40
- ${c(`title`,t[`error-label`])} ${c(`description`,t[`error-description`])}
41
- ${e.when(()=>!!t.retryable.value,()=>r.html`
42
- <button class="retry-btn" type="button" @click=${()=>n(`retry`)}>
40
+ ${c(`title`,e[`error-label`])} ${c(`description`,e[`error-description`])}
41
+ ${(0,r.when)(()=>!!e.retryable.value,()=>n.html`
42
+ <button class="retry-btn" type="button" @click=${()=>t(`retry`)}>
43
43
  <ore-icon name="refresh-cw" size="1em" stroke-width="2" aria-hidden="true"></ore-icon>
44
44
  Try again
45
45
  </button>
@@ -51,5 +51,5 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
51
51
  <div class="region region-success" role="presentation">
52
52
  <slot></slot>
53
53
  </div>
54
- `},styles:[t.reducedMotionMixin,n.default]}),exports.ASYNC_TAG=a;
54
+ `},styles:[e.reducedMotionMixin,t.default]}),exports.ASYNC_TAG=a;
55
55
  //# sourceMappingURL=async.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"async.cjs","names":[],"sources":["../src/feedback/async/async.ts"],"sourcesContent":["import { define, html, prop, bind, useEmit } from '@vielzeug/ore';\nimport { when } from '@vielzeug/ore/directives';\nimport { type Readable, signal } from '@vielzeug/ripple';\n\nimport { reducedMotionMixin } from '../../styles';\nimport componentStyles from './async.css?inline';\n\nexport type AsyncStatus = 'idle' | 'loading' | 'empty' | 'error' | 'success';\n\nexport type OreAsyncEvents = {\n retry: void;\n};\n\nexport type OreAsyncProps = {\n 'empty-description'?: string;\n 'empty-label'?: string;\n 'error-description'?: string;\n 'error-label'?: string;\n retryable?: boolean;\n status?: AsyncStatus;\n};\n\n/**\n * A container for handling asynchronous states (loading, empty, error, success).\n * Simplifies data fetching UI by providing consistent fallbacks.\n *\n * @element ore-async\n *\n * @attr {string} status - current state: 'idle' | 'loading' | 'empty' | 'error' | 'success' (default: 'success')\n * @attr {boolean} retryable - show retry button in error state (default: false)\n * @attr {string} empty-label - title for empty state (default: 'No content yet')\n * @attr {string} empty-description - optional text for empty state\n * @attr {string} error-label - title for error state (default: 'Something went wrong')\n * @attr {string} error-description - optional text for error state\n *\n * @fires retry - Emitted when the retry button is clicked (no detail payload)\n *\n * @slot - default content shown in 'success' state\n * @slot loading - custom loading UI (overrides default skeletons)\n * @slot empty - custom empty UI (overrides default icon/label)\n * @slot error - custom error UI (overrides default icon/label)\n *\n * @cssprop --async-color - Text/icon color used by default async state content\n * @cssprop --async-gap - Vertical spacing between icon, title, description, and actions\n * @cssprop --async-icon-size - Icon size for built-in loading/empty/error visuals\n * @example\n * ```html\n * <!-- Success state: default slot is shown -->\n * <ore-async status=\"success\">\n * <ul><li>Item one</li><li>Item two</li></ul>\n * </ore-async>\n *\n * <!-- Loading state: shows skeleton placeholders -->\n * <ore-async status=\"loading\"></ore-async>\n *\n * <!-- Empty state with custom message -->\n * <ore-async status=\"empty\" empty-label=\"No results\" empty-description=\"Try adjusting your filters.\"></ore-async>\n *\n * <!-- Error state with retry button -->\n * <ore-async status=\"error\" retryable error-label=\"Failed to load\" error-description=\"Check your connection.\"></ore-async>\n * ```\n */\nexport const ASYNC_TAG = 'ore-async' as const;\ndefine<OreAsyncProps>(ASYNC_TAG, {\n props: {\n 'empty-description': prop.string(),\n 'empty-label': prop.string('No content yet'),\n 'error-description': prop.string(),\n 'error-label': prop.string('Something went wrong'),\n retryable: prop.bool(false),\n status: prop.oneOf(['idle', 'loading', 'empty', 'error', 'success'] as const, 'success'),\n },\n setup(props) {\n const emit = useEmit<OreAsyncEvents>();\n\n const hasLoadingSlot = signal(false);\n const hasEmptySlot = signal(false);\n const hasErrorSlot = signal(false);\n\n // Reflect status onto the host so CSS can show/hide each region.\n // ARIA attributes are driven reactively by bind().\n bind({\n attr: {\n ariaBusy: () => (props.status.value === 'loading' ? 'true' : 'false'),\n ariaLabel: () => (props.status.value === 'loading' ? 'Loading…' : null),\n ariaLive: () => (props.status.value === 'error' ? 'assertive' : 'polite'),\n status: props.status,\n },\n });\n\n const renderText = (className: 'title' | 'description', text: Readable<string | undefined> | undefined) => () =>\n text?.value ? html`<p class=\"${className}\">${text}</p>` : '';\n\n // All four regions are always in the shadow DOM — CSS on :host([status=\"…\"])\n // toggles their visibility. This means:\n // - No DOM churn on status transitions (no teardown/rebuild of slot elements).\n // - Live regions are always present, so screen readers announce correctly.\n // - focus is never lost across status changes.\n return html`\n <div class=\"region region-idle\" role=\"presentation\"></div>\n\n <div class=\"region region-loading\" role=\"status\">\n <slot\n name=\"loading\"\n @slotchange=${(e: Event) => {\n hasLoadingSlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasLoadingSlot,\n () => html``,\n () => html`\n <div class=\"loading-default\" aria-hidden=\"true\">\n <ore-skeleton variant=\"text\" lines=\"1\" width=\"40%\"></ore-skeleton>\n <ore-skeleton variant=\"text\" lines=\"3\" width=\"100%\"></ore-skeleton>\n <ore-skeleton variant=\"text\" lines=\"1\" width=\"60%\"></ore-skeleton>\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-empty\">\n <slot\n name=\"empty\"\n @slotchange=${(e: Event) => {\n hasEmptySlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasEmptySlot,\n () => html``,\n () => html`\n <div class=\"empty-state\" role=\"status\">\n <div class=\"icon\">\n <ore-icon name=\"inbox\" size=\"100%\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </div>\n ${renderText('title', props['empty-label'])} ${renderText('description', props['empty-description'])}\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-error\">\n <slot\n name=\"error\"\n @slotchange=${(e: Event) => {\n hasErrorSlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasErrorSlot,\n () => html``,\n () => html`\n <div class=\"error-state\" role=\"alert\">\n <div class=\"icon\">\n <ore-icon name=\"triangle-alert\" size=\"100%\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </div>\n ${renderText('title', props['error-label'])} ${renderText('description', props['error-description'])}\n ${when(\n () => Boolean(props.retryable.value),\n () => html`\n <button class=\"retry-btn\" type=\"button\" @click=${() => emit('retry')}>\n <ore-icon name=\"refresh-cw\" size=\"1em\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n Try again\n </button>\n `,\n )}\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-success\" role=\"presentation\">\n <slot></slot>\n </div>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":"4TA8DA,IAAa,EAAY,yBACH,EAAW,CAC/B,MAAO,CACL,oBAAqB,EAAA,KAAK,OAAO,EACjC,cAAe,EAAA,KAAK,OAAO,gBAAgB,EAC3C,oBAAqB,EAAA,KAAK,OAAO,EACjC,cAAe,EAAA,KAAK,OAAO,sBAAsB,EACjD,UAAW,EAAA,KAAK,KAAK,EAAK,EAC1B,OAAQ,EAAA,KAAK,MAAM,CAAC,OAAQ,UAAW,QAAS,QAAS,SAAS,EAAY,SAAS,CACzF,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAA,EAAA,QAAA,CAA+B,EAE/B,GAAA,EAAA,EAAA,OAAA,CAAwB,EAAK,EAC7B,GAAA,EAAA,EAAA,OAAA,CAAsB,EAAK,EAC3B,GAAA,EAAA,EAAA,OAAA,CAAsB,EAAK,GAIjC,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,aAAiB,EAAM,OAAO,QAAU,UAAY,OAAS,QAC7D,cAAkB,EAAM,OAAO,QAAU,UAAY,WAAa,KAClE,aAAiB,EAAM,OAAO,QAAU,QAAU,YAAc,SAChE,OAAQ,EAAM,MAChB,CACF,CAAC,EAED,IAAM,GAAc,EAAoC,QACtD,GAAM,MAAQ,EAAA,IAAI,aAAa,EAAU,IAAI,EAAK,MAAQ,GAO5D,MAAO,GAAA,IAAI;;;;;;wBAMU,GAAa,CAC1B,EAAe,MAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,OAAS,CAChF,EAAE;UACF,EAAA,KACA,MACM,EAAA,IAAI,OACJ,EAAA,IAAI;;;;;;WAOZ,EAAE;;;;;;wBAMe,GAAa,CAC1B,EAAa,MAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,OAAS,CAC9E,EAAE;UACF,EAAA,KACA,MACM,EAAA,IAAI,OACJ,EAAA,IAAI;;;;;gBAKJ,EAAW,QAAS,EAAM,cAAc,EAAE,GAAG,EAAW,cAAe,EAAM,oBAAoB,EAAE;;WAG3G,EAAE;;;;;;wBAMe,GAAa,CAC1B,EAAa,MAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,OAAS,CAC9E,EAAE;UACF,EAAA,KACA,MACM,EAAA,IAAI,OACJ,EAAA,IAAI;;;;;gBAKJ,EAAW,QAAS,EAAM,cAAc,EAAE,GAAG,EAAW,cAAe,EAAM,oBAAoB,EAAE;gBACnG,EAAA,SACM,EAAQ,EAAM,UAAU,UACxB,EAAA,IAAI;uEAC+C,EAAK,OAAO,EAAE;;;;iBAKzE,EAAE;;WAGR,EAAE;;;;;;KAOR,EACA,OAAQ,CAAC,EAAA,mBAAoB,EAAA,OAAe,CAC9C,CAAC"}
1
+ {"version":3,"file":"async.cjs","names":[],"sources":["../src/feedback/async/async.ts"],"sourcesContent":["import { define, html, prop, bind, useEmit } from '@vielzeug/ore';\nimport { when } from '@vielzeug/ore/directives';\nimport { type Readable, signal } from '@vielzeug/ripple';\n\nimport { reducedMotionMixin } from '../../styles';\nimport componentStyles from './async.css?inline';\n\nexport type AsyncStatus = 'idle' | 'loading' | 'empty' | 'error' | 'success';\n\nexport type OreAsyncEvents = {\n retry: void;\n};\n\nexport type OreAsyncProps = {\n 'empty-description'?: string;\n 'empty-label'?: string;\n 'error-description'?: string;\n 'error-label'?: string;\n retryable?: boolean;\n status?: AsyncStatus;\n};\n\n/**\n * A container for handling asynchronous states (loading, empty, error, success).\n * Simplifies data fetching UI by providing consistent fallbacks.\n *\n * @element ore-async\n *\n * @attr {string} status - current state: 'idle' | 'loading' | 'empty' | 'error' | 'success' (default: 'success')\n * @attr {boolean} retryable - show retry button in error state (default: false)\n * @attr {string} empty-label - title for empty state (default: 'No content yet')\n * @attr {string} empty-description - optional text for empty state\n * @attr {string} error-label - title for error state (default: 'Something went wrong')\n * @attr {string} error-description - optional text for error state\n *\n * @fires retry - Emitted when the retry button is clicked (no detail payload)\n *\n * @slot - default content shown in 'success' state\n * @slot loading - custom loading UI (overrides default skeletons)\n * @slot empty - custom empty UI (overrides default icon/label)\n * @slot error - custom error UI (overrides default icon/label)\n *\n * @cssprop --async-color - Text/icon color used by default async state content\n * @cssprop --async-gap - Vertical spacing between icon, title, description, and actions\n * @cssprop --async-icon-size - Icon size for built-in loading/empty/error visuals\n * @example\n * ```html\n * <!-- Success state: default slot is shown -->\n * <ore-async status=\"success\">\n * <ul><li>Item one</li><li>Item two</li></ul>\n * </ore-async>\n *\n * <!-- Loading state: shows skeleton placeholders -->\n * <ore-async status=\"loading\"></ore-async>\n *\n * <!-- Empty state with custom message -->\n * <ore-async status=\"empty\" empty-label=\"No results\" empty-description=\"Try adjusting your filters.\"></ore-async>\n *\n * <!-- Error state with retry button -->\n * <ore-async status=\"error\" retryable error-label=\"Failed to load\" error-description=\"Check your connection.\"></ore-async>\n * ```\n */\nexport const ASYNC_TAG = 'ore-async' as const;\ndefine<OreAsyncProps>(ASYNC_TAG, {\n props: {\n 'empty-description': prop.string(),\n 'empty-label': prop.string('No content yet'),\n 'error-description': prop.string(),\n 'error-label': prop.string('Something went wrong'),\n retryable: prop.bool(false),\n status: prop.oneOf(['idle', 'loading', 'empty', 'error', 'success'] as const, 'success'),\n },\n setup(props) {\n const emit = useEmit<OreAsyncEvents>();\n\n const hasLoadingSlot = signal(false);\n const hasEmptySlot = signal(false);\n const hasErrorSlot = signal(false);\n\n // Reflect status onto the host so CSS can show/hide each region.\n // ARIA attributes are driven reactively by bind().\n bind({\n attr: {\n ariaBusy: () => (props.status.value === 'loading' ? 'true' : 'false'),\n ariaLabel: () => (props.status.value === 'loading' ? 'Loading…' : null),\n ariaLive: () => (props.status.value === 'error' ? 'assertive' : 'polite'),\n status: props.status,\n },\n });\n\n const renderText = (className: 'title' | 'description', text: Readable<string | undefined> | undefined) => () =>\n text?.value ? html`<p class=\"${className}\">${text}</p>` : '';\n\n // All four regions are always in the shadow DOM — CSS on :host([status=\"…\"])\n // toggles their visibility. This means:\n // - No DOM churn on status transitions (no teardown/rebuild of slot elements).\n // - Live regions are always present, so screen readers announce correctly.\n // - focus is never lost across status changes.\n return html`\n <div class=\"region region-idle\" role=\"presentation\"></div>\n\n <div class=\"region region-loading\" role=\"status\">\n <slot\n name=\"loading\"\n @slotchange=${(e: Event) => {\n hasLoadingSlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasLoadingSlot,\n () => html``,\n () => html`\n <div class=\"loading-default\" aria-hidden=\"true\">\n <ore-skeleton variant=\"text\" lines=\"1\" width=\"40%\"></ore-skeleton>\n <ore-skeleton variant=\"text\" lines=\"3\" width=\"100%\"></ore-skeleton>\n <ore-skeleton variant=\"text\" lines=\"1\" width=\"60%\"></ore-skeleton>\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-empty\">\n <slot\n name=\"empty\"\n @slotchange=${(e: Event) => {\n hasEmptySlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasEmptySlot,\n () => html``,\n () => html`\n <div class=\"empty-state\" role=\"status\">\n <div class=\"icon\">\n <ore-icon name=\"inbox\" size=\"100%\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </div>\n ${renderText('title', props['empty-label'])} ${renderText('description', props['empty-description'])}\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-error\">\n <slot\n name=\"error\"\n @slotchange=${(e: Event) => {\n hasErrorSlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasErrorSlot,\n () => html``,\n () => html`\n <div class=\"error-state\" role=\"alert\">\n <div class=\"icon\">\n <ore-icon name=\"triangle-alert\" size=\"100%\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </div>\n ${renderText('title', props['error-label'])} ${renderText('description', props['error-description'])}\n ${when(\n () => Boolean(props.retryable.value),\n () => html`\n <button class=\"retry-btn\" type=\"button\" @click=${() => emit('retry')}>\n <ore-icon name=\"refresh-cw\" size=\"1em\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n Try again\n </button>\n `,\n )}\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-success\" role=\"presentation\">\n <slot></slot>\n </div>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":"2QA8DA,IAAa,EAAY,yBACH,EAAW,CAC/B,MAAO,CACL,oBAAqB,EAAA,KAAK,OAAO,EACjC,cAAe,EAAA,KAAK,OAAO,gBAAgB,EAC3C,oBAAqB,EAAA,KAAK,OAAO,EACjC,cAAe,EAAA,KAAK,OAAO,sBAAsB,EACjD,UAAW,EAAA,KAAK,KAAK,EAAK,EAC1B,OAAQ,EAAA,KAAK,MAAM,CAAC,OAAQ,UAAW,QAAS,QAAS,SAAS,EAAY,SAAS,CACzF,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAA,EAAA,QAAA,CAA+B,EAE/B,GAAA,EAAA,EAAA,OAAA,CAAwB,EAAK,EAC7B,GAAA,EAAA,EAAA,OAAA,CAAsB,EAAK,EAC3B,GAAA,EAAA,EAAA,OAAA,CAAsB,EAAK,GAIjC,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,aAAiB,EAAM,OAAO,QAAU,UAAY,OAAS,QAC7D,cAAkB,EAAM,OAAO,QAAU,UAAY,WAAa,KAClE,aAAiB,EAAM,OAAO,QAAU,QAAU,YAAc,SAChE,OAAQ,EAAM,MAChB,CACF,CAAC,EAED,IAAM,GAAc,EAAoC,QACtD,GAAM,MAAQ,EAAA,IAAI,aAAa,EAAU,IAAI,EAAK,MAAQ,GAO5D,MAAO,GAAA,IAAI;;;;;;wBAMU,GAAa,CAC1B,EAAe,MAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,OAAS,CAChF,EAAE;qBAEF,MACM,EAAA,IAAI,OACJ,EAAA,IAAI;;;;;;WAOZ,EAAE;;;;;;wBAMe,GAAa,CAC1B,EAAa,MAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,OAAS,CAC9E,EAAE;qBAEF,MACM,EAAA,IAAI,OACJ,EAAA,IAAI;;;;;gBAKJ,EAAW,QAAS,EAAM,cAAc,EAAE,GAAG,EAAW,cAAe,EAAM,oBAAoB,EAAE;;WAG3G,EAAE;;;;;;wBAMe,GAAa,CAC1B,EAAa,MAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,OAAS,CAC9E,EAAE;qBAEF,MACM,EAAA,IAAI,OACJ,EAAA,IAAI;;;;;gBAKJ,EAAW,QAAS,EAAM,cAAc,EAAE,GAAG,EAAW,cAAe,EAAM,oBAAoB,EAAE;+BAE7F,EAAQ,EAAM,UAAU,UACxB,EAAA,IAAI;uEAC+C,EAAK,OAAO,EAAE;;;;iBAKzE,EAAE;;WAGR,EAAE;;;;;;KAOR,EACA,OAAQ,CAAC,EAAA,mBAAoB,EAAA,OAAe,CAC9C,CAAC"}
package/dist/async.js CHANGED
@@ -1,19 +1,18 @@
1
- import { when as e } from "./ore/dist/directives/when.js";
2
- import "./ore/dist/directives.js";
3
- import { reducedMotionMixin as t } from "./styles/mixins/accessibility.css.js";
4
- import n from "./feedback/async/async.css.js";
5
- import { bind as r, define as i, html as a, prop as o, useEmit as s } from "@vielzeug/ore";
1
+ import { reducedMotionMixin as e } from "./styles/mixins/accessibility.css.js";
2
+ import t from "./feedback/async/async.css.js";
3
+ import { bind as n, define as r, html as i, prop as a, useEmit as o } from "@vielzeug/ore";
4
+ import { when as s } from "@vielzeug/ore/directives";
6
5
  import { signal as c } from "@vielzeug/ripple";
7
6
  //#region src/feedback/async/async.ts
8
7
  var l = "ore-async";
9
- i(l, {
8
+ r(l, {
10
9
  props: {
11
- "empty-description": o.string(),
12
- "empty-label": o.string("No content yet"),
13
- "error-description": o.string(),
14
- "error-label": o.string("Something went wrong"),
15
- retryable: o.bool(!1),
16
- status: o.oneOf([
10
+ "empty-description": a.string(),
11
+ "empty-label": a.string("No content yet"),
12
+ "error-description": a.string(),
13
+ "error-label": a.string("Something went wrong"),
14
+ retryable: a.bool(!1),
15
+ status: a.oneOf([
17
16
  "idle",
18
17
  "loading",
19
18
  "empty",
@@ -21,25 +20,25 @@ i(l, {
21
20
  "success"
22
21
  ], "success")
23
22
  },
24
- setup(t) {
25
- let n = s(), i = c(!1), o = c(!1), l = c(!1);
26
- r({ attr: {
27
- ariaBusy: () => t.status.value === "loading" ? "true" : "false",
28
- ariaLabel: () => t.status.value === "loading" ? "Loading…" : null,
29
- ariaLive: () => t.status.value === "error" ? "assertive" : "polite",
30
- status: t.status
23
+ setup(e) {
24
+ let t = o(), r = c(!1), a = c(!1), l = c(!1);
25
+ n({ attr: {
26
+ ariaBusy: () => e.status.value === "loading" ? "true" : "false",
27
+ ariaLabel: () => e.status.value === "loading" ? "Loading…" : null,
28
+ ariaLive: () => e.status.value === "error" ? "assertive" : "polite",
29
+ status: e.status
31
30
  } });
32
- let u = (e, t) => () => t?.value ? a`<p class="${e}">${t}</p>` : "";
33
- return a`
31
+ let u = (e, t) => () => t?.value ? i`<p class="${e}">${t}</p>` : "";
32
+ return i`
34
33
  <div class="region region-idle" role="presentation"></div>
35
34
 
36
35
  <div class="region region-loading" role="status">
37
36
  <slot
38
37
  name="loading"
39
38
  @slotchange=${(e) => {
40
- i.value = e.target.assignedNodes().length > 0;
39
+ r.value = e.target.assignedNodes().length > 0;
41
40
  }}></slot>
42
- ${e(i, () => a``, () => a`
41
+ ${s(r, () => i``, () => i`
43
42
  <div class="loading-default" aria-hidden="true">
44
43
  <ore-skeleton variant="text" lines="1" width="40%"></ore-skeleton>
45
44
  <ore-skeleton variant="text" lines="3" width="100%"></ore-skeleton>
@@ -52,14 +51,14 @@ i(l, {
52
51
  <slot
53
52
  name="empty"
54
53
  @slotchange=${(e) => {
55
- o.value = e.target.assignedNodes().length > 0;
54
+ a.value = e.target.assignedNodes().length > 0;
56
55
  }}></slot>
57
- ${e(o, () => a``, () => a`
56
+ ${s(a, () => i``, () => i`
58
57
  <div class="empty-state" role="status">
59
58
  <div class="icon">
60
59
  <ore-icon name="inbox" size="100%" stroke-width="1.75" aria-hidden="true"></ore-icon>
61
60
  </div>
62
- ${u("title", t["empty-label"])} ${u("description", t["empty-description"])}
61
+ ${u("title", e["empty-label"])} ${u("description", e["empty-description"])}
63
62
  </div>
64
63
  `)}
65
64
  </div>
@@ -70,14 +69,14 @@ i(l, {
70
69
  @slotchange=${(e) => {
71
70
  l.value = e.target.assignedNodes().length > 0;
72
71
  }}></slot>
73
- ${e(l, () => a``, () => a`
72
+ ${s(l, () => i``, () => i`
74
73
  <div class="error-state" role="alert">
75
74
  <div class="icon">
76
75
  <ore-icon name="triangle-alert" size="100%" stroke-width="1.75" aria-hidden="true"></ore-icon>
77
76
  </div>
78
- ${u("title", t["error-label"])} ${u("description", t["error-description"])}
79
- ${e(() => !!t.retryable.value, () => a`
80
- <button class="retry-btn" type="button" @click=${() => n("retry")}>
77
+ ${u("title", e["error-label"])} ${u("description", e["error-description"])}
78
+ ${s(() => !!e.retryable.value, () => i`
79
+ <button class="retry-btn" type="button" @click=${() => t("retry")}>
81
80
  <ore-icon name="refresh-cw" size="1em" stroke-width="2" aria-hidden="true"></ore-icon>
82
81
  Try again
83
82
  </button>
@@ -91,7 +90,7 @@ i(l, {
91
90
  </div>
92
91
  `;
93
92
  },
94
- styles: [t, n]
93
+ styles: [e, t]
95
94
  });
96
95
  //#endregion
97
96
  export { l as ASYNC_TAG };
package/dist/async.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"async.js","names":[],"sources":["../src/feedback/async/async.ts"],"sourcesContent":["import { define, html, prop, bind, useEmit } from '@vielzeug/ore';\nimport { when } from '@vielzeug/ore/directives';\nimport { type Readable, signal } from '@vielzeug/ripple';\n\nimport { reducedMotionMixin } from '../../styles';\nimport componentStyles from './async.css?inline';\n\nexport type AsyncStatus = 'idle' | 'loading' | 'empty' | 'error' | 'success';\n\nexport type OreAsyncEvents = {\n retry: void;\n};\n\nexport type OreAsyncProps = {\n 'empty-description'?: string;\n 'empty-label'?: string;\n 'error-description'?: string;\n 'error-label'?: string;\n retryable?: boolean;\n status?: AsyncStatus;\n};\n\n/**\n * A container for handling asynchronous states (loading, empty, error, success).\n * Simplifies data fetching UI by providing consistent fallbacks.\n *\n * @element ore-async\n *\n * @attr {string} status - current state: 'idle' | 'loading' | 'empty' | 'error' | 'success' (default: 'success')\n * @attr {boolean} retryable - show retry button in error state (default: false)\n * @attr {string} empty-label - title for empty state (default: 'No content yet')\n * @attr {string} empty-description - optional text for empty state\n * @attr {string} error-label - title for error state (default: 'Something went wrong')\n * @attr {string} error-description - optional text for error state\n *\n * @fires retry - Emitted when the retry button is clicked (no detail payload)\n *\n * @slot - default content shown in 'success' state\n * @slot loading - custom loading UI (overrides default skeletons)\n * @slot empty - custom empty UI (overrides default icon/label)\n * @slot error - custom error UI (overrides default icon/label)\n *\n * @cssprop --async-color - Text/icon color used by default async state content\n * @cssprop --async-gap - Vertical spacing between icon, title, description, and actions\n * @cssprop --async-icon-size - Icon size for built-in loading/empty/error visuals\n * @example\n * ```html\n * <!-- Success state: default slot is shown -->\n * <ore-async status=\"success\">\n * <ul><li>Item one</li><li>Item two</li></ul>\n * </ore-async>\n *\n * <!-- Loading state: shows skeleton placeholders -->\n * <ore-async status=\"loading\"></ore-async>\n *\n * <!-- Empty state with custom message -->\n * <ore-async status=\"empty\" empty-label=\"No results\" empty-description=\"Try adjusting your filters.\"></ore-async>\n *\n * <!-- Error state with retry button -->\n * <ore-async status=\"error\" retryable error-label=\"Failed to load\" error-description=\"Check your connection.\"></ore-async>\n * ```\n */\nexport const ASYNC_TAG = 'ore-async' as const;\ndefine<OreAsyncProps>(ASYNC_TAG, {\n props: {\n 'empty-description': prop.string(),\n 'empty-label': prop.string('No content yet'),\n 'error-description': prop.string(),\n 'error-label': prop.string('Something went wrong'),\n retryable: prop.bool(false),\n status: prop.oneOf(['idle', 'loading', 'empty', 'error', 'success'] as const, 'success'),\n },\n setup(props) {\n const emit = useEmit<OreAsyncEvents>();\n\n const hasLoadingSlot = signal(false);\n const hasEmptySlot = signal(false);\n const hasErrorSlot = signal(false);\n\n // Reflect status onto the host so CSS can show/hide each region.\n // ARIA attributes are driven reactively by bind().\n bind({\n attr: {\n ariaBusy: () => (props.status.value === 'loading' ? 'true' : 'false'),\n ariaLabel: () => (props.status.value === 'loading' ? 'Loading…' : null),\n ariaLive: () => (props.status.value === 'error' ? 'assertive' : 'polite'),\n status: props.status,\n },\n });\n\n const renderText = (className: 'title' | 'description', text: Readable<string | undefined> | undefined) => () =>\n text?.value ? html`<p class=\"${className}\">${text}</p>` : '';\n\n // All four regions are always in the shadow DOM — CSS on :host([status=\"…\"])\n // toggles their visibility. This means:\n // - No DOM churn on status transitions (no teardown/rebuild of slot elements).\n // - Live regions are always present, so screen readers announce correctly.\n // - focus is never lost across status changes.\n return html`\n <div class=\"region region-idle\" role=\"presentation\"></div>\n\n <div class=\"region region-loading\" role=\"status\">\n <slot\n name=\"loading\"\n @slotchange=${(e: Event) => {\n hasLoadingSlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasLoadingSlot,\n () => html``,\n () => html`\n <div class=\"loading-default\" aria-hidden=\"true\">\n <ore-skeleton variant=\"text\" lines=\"1\" width=\"40%\"></ore-skeleton>\n <ore-skeleton variant=\"text\" lines=\"3\" width=\"100%\"></ore-skeleton>\n <ore-skeleton variant=\"text\" lines=\"1\" width=\"60%\"></ore-skeleton>\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-empty\">\n <slot\n name=\"empty\"\n @slotchange=${(e: Event) => {\n hasEmptySlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasEmptySlot,\n () => html``,\n () => html`\n <div class=\"empty-state\" role=\"status\">\n <div class=\"icon\">\n <ore-icon name=\"inbox\" size=\"100%\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </div>\n ${renderText('title', props['empty-label'])} ${renderText('description', props['empty-description'])}\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-error\">\n <slot\n name=\"error\"\n @slotchange=${(e: Event) => {\n hasErrorSlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasErrorSlot,\n () => html``,\n () => html`\n <div class=\"error-state\" role=\"alert\">\n <div class=\"icon\">\n <ore-icon name=\"triangle-alert\" size=\"100%\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </div>\n ${renderText('title', props['error-label'])} ${renderText('description', props['error-description'])}\n ${when(\n () => Boolean(props.retryable.value),\n () => html`\n <button class=\"retry-btn\" type=\"button\" @click=${() => emit('retry')}>\n <ore-icon name=\"refresh-cw\" size=\"1em\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n Try again\n </button>\n `,\n )}\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-success\" role=\"presentation\">\n <slot></slot>\n </div>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":";;;;;;;AA8DA,IAAa,IAAY;AACzB,EAAsB,GAAW;CAC/B,OAAO;EACL,qBAAqB,EAAK,OAAO;EACjC,eAAe,EAAK,OAAO,gBAAgB;EAC3C,qBAAqB,EAAK,OAAO;EACjC,eAAe,EAAK,OAAO,sBAAsB;EACjD,WAAW,EAAK,KAAK,EAAK;EAC1B,QAAQ,EAAK,MAAM;GAAC;GAAQ;GAAW;GAAS;GAAS;EAAS,GAAY,SAAS;CACzF;CACA,MAAM,GAAO;EACX,IAAM,IAAO,EAAwB,GAE/B,IAAiB,EAAO,EAAK,GAC7B,IAAe,EAAO,EAAK,GAC3B,IAAe,EAAO,EAAK;EAIjC,EAAK,EACH,MAAM;GACJ,gBAAiB,EAAM,OAAO,UAAU,YAAY,SAAS;GAC7D,iBAAkB,EAAM,OAAO,UAAU,YAAY,aAAa;GAClE,gBAAiB,EAAM,OAAO,UAAU,UAAU,cAAc;GAChE,QAAQ,EAAM;EAChB,EACF,CAAC;EAED,IAAM,KAAc,GAAoC,YACtD,GAAM,QAAQ,CAAI,aAAa,EAAU,IAAI,EAAK,QAAQ;EAO5D,OAAO,CAAI;;;;;;yBAMU,MAAa;GAC1B,EAAe,QAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,SAAS;EAChF,EAAE;UACF,EACA,SACM,CAAI,UACJ,CAAI;;;;;;WAOZ,EAAE;;;;;;yBAMe,MAAa;GAC1B,EAAa,QAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,SAAS;EAC9E,EAAE;UACF,EACA,SACM,CAAI,UACJ,CAAI;;;;;gBAKJ,EAAW,SAAS,EAAM,cAAc,EAAE,GAAG,EAAW,eAAe,EAAM,oBAAoB,EAAE;;WAG3G,EAAE;;;;;;yBAMe,MAAa;GAC1B,EAAa,QAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,SAAS;EAC9E,EAAE;UACF,EACA,SACM,CAAI,UACJ,CAAI;;;;;gBAKJ,EAAW,SAAS,EAAM,cAAc,EAAE,GAAG,EAAW,eAAe,EAAM,oBAAoB,EAAE;gBACnG,QACM,EAAQ,EAAM,UAAU,aACxB,CAAI;yEAC+C,EAAK,OAAO,EAAE;;;;iBAKzE,EAAE;;WAGR,EAAE;;;;;;;CAOR;CACA,QAAQ,CAAC,GAAoB,CAAe;AAC9C,CAAC"}
1
+ {"version":3,"file":"async.js","names":[],"sources":["../src/feedback/async/async.ts"],"sourcesContent":["import { define, html, prop, bind, useEmit } from '@vielzeug/ore';\nimport { when } from '@vielzeug/ore/directives';\nimport { type Readable, signal } from '@vielzeug/ripple';\n\nimport { reducedMotionMixin } from '../../styles';\nimport componentStyles from './async.css?inline';\n\nexport type AsyncStatus = 'idle' | 'loading' | 'empty' | 'error' | 'success';\n\nexport type OreAsyncEvents = {\n retry: void;\n};\n\nexport type OreAsyncProps = {\n 'empty-description'?: string;\n 'empty-label'?: string;\n 'error-description'?: string;\n 'error-label'?: string;\n retryable?: boolean;\n status?: AsyncStatus;\n};\n\n/**\n * A container for handling asynchronous states (loading, empty, error, success).\n * Simplifies data fetching UI by providing consistent fallbacks.\n *\n * @element ore-async\n *\n * @attr {string} status - current state: 'idle' | 'loading' | 'empty' | 'error' | 'success' (default: 'success')\n * @attr {boolean} retryable - show retry button in error state (default: false)\n * @attr {string} empty-label - title for empty state (default: 'No content yet')\n * @attr {string} empty-description - optional text for empty state\n * @attr {string} error-label - title for error state (default: 'Something went wrong')\n * @attr {string} error-description - optional text for error state\n *\n * @fires retry - Emitted when the retry button is clicked (no detail payload)\n *\n * @slot - default content shown in 'success' state\n * @slot loading - custom loading UI (overrides default skeletons)\n * @slot empty - custom empty UI (overrides default icon/label)\n * @slot error - custom error UI (overrides default icon/label)\n *\n * @cssprop --async-color - Text/icon color used by default async state content\n * @cssprop --async-gap - Vertical spacing between icon, title, description, and actions\n * @cssprop --async-icon-size - Icon size for built-in loading/empty/error visuals\n * @example\n * ```html\n * <!-- Success state: default slot is shown -->\n * <ore-async status=\"success\">\n * <ul><li>Item one</li><li>Item two</li></ul>\n * </ore-async>\n *\n * <!-- Loading state: shows skeleton placeholders -->\n * <ore-async status=\"loading\"></ore-async>\n *\n * <!-- Empty state with custom message -->\n * <ore-async status=\"empty\" empty-label=\"No results\" empty-description=\"Try adjusting your filters.\"></ore-async>\n *\n * <!-- Error state with retry button -->\n * <ore-async status=\"error\" retryable error-label=\"Failed to load\" error-description=\"Check your connection.\"></ore-async>\n * ```\n */\nexport const ASYNC_TAG = 'ore-async' as const;\ndefine<OreAsyncProps>(ASYNC_TAG, {\n props: {\n 'empty-description': prop.string(),\n 'empty-label': prop.string('No content yet'),\n 'error-description': prop.string(),\n 'error-label': prop.string('Something went wrong'),\n retryable: prop.bool(false),\n status: prop.oneOf(['idle', 'loading', 'empty', 'error', 'success'] as const, 'success'),\n },\n setup(props) {\n const emit = useEmit<OreAsyncEvents>();\n\n const hasLoadingSlot = signal(false);\n const hasEmptySlot = signal(false);\n const hasErrorSlot = signal(false);\n\n // Reflect status onto the host so CSS can show/hide each region.\n // ARIA attributes are driven reactively by bind().\n bind({\n attr: {\n ariaBusy: () => (props.status.value === 'loading' ? 'true' : 'false'),\n ariaLabel: () => (props.status.value === 'loading' ? 'Loading…' : null),\n ariaLive: () => (props.status.value === 'error' ? 'assertive' : 'polite'),\n status: props.status,\n },\n });\n\n const renderText = (className: 'title' | 'description', text: Readable<string | undefined> | undefined) => () =>\n text?.value ? html`<p class=\"${className}\">${text}</p>` : '';\n\n // All four regions are always in the shadow DOM — CSS on :host([status=\"…\"])\n // toggles their visibility. This means:\n // - No DOM churn on status transitions (no teardown/rebuild of slot elements).\n // - Live regions are always present, so screen readers announce correctly.\n // - focus is never lost across status changes.\n return html`\n <div class=\"region region-idle\" role=\"presentation\"></div>\n\n <div class=\"region region-loading\" role=\"status\">\n <slot\n name=\"loading\"\n @slotchange=${(e: Event) => {\n hasLoadingSlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasLoadingSlot,\n () => html``,\n () => html`\n <div class=\"loading-default\" aria-hidden=\"true\">\n <ore-skeleton variant=\"text\" lines=\"1\" width=\"40%\"></ore-skeleton>\n <ore-skeleton variant=\"text\" lines=\"3\" width=\"100%\"></ore-skeleton>\n <ore-skeleton variant=\"text\" lines=\"1\" width=\"60%\"></ore-skeleton>\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-empty\">\n <slot\n name=\"empty\"\n @slotchange=${(e: Event) => {\n hasEmptySlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasEmptySlot,\n () => html``,\n () => html`\n <div class=\"empty-state\" role=\"status\">\n <div class=\"icon\">\n <ore-icon name=\"inbox\" size=\"100%\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </div>\n ${renderText('title', props['empty-label'])} ${renderText('description', props['empty-description'])}\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-error\">\n <slot\n name=\"error\"\n @slotchange=${(e: Event) => {\n hasErrorSlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n }}></slot>\n ${when(\n hasErrorSlot,\n () => html``,\n () => html`\n <div class=\"error-state\" role=\"alert\">\n <div class=\"icon\">\n <ore-icon name=\"triangle-alert\" size=\"100%\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n </div>\n ${renderText('title', props['error-label'])} ${renderText('description', props['error-description'])}\n ${when(\n () => Boolean(props.retryable.value),\n () => html`\n <button class=\"retry-btn\" type=\"button\" @click=${() => emit('retry')}>\n <ore-icon name=\"refresh-cw\" size=\"1em\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n Try again\n </button>\n `,\n )}\n </div>\n `,\n )}\n </div>\n\n <div class=\"region region-success\" role=\"presentation\">\n <slot></slot>\n </div>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":";;;;;;AA8DA,IAAa,IAAY;AACzB,EAAsB,GAAW;CAC/B,OAAO;EACL,qBAAqB,EAAK,OAAO;EACjC,eAAe,EAAK,OAAO,gBAAgB;EAC3C,qBAAqB,EAAK,OAAO;EACjC,eAAe,EAAK,OAAO,sBAAsB;EACjD,WAAW,EAAK,KAAK,EAAK;EAC1B,QAAQ,EAAK,MAAM;GAAC;GAAQ;GAAW;GAAS;GAAS;EAAS,GAAY,SAAS;CACzF;CACA,MAAM,GAAO;EACX,IAAM,IAAO,EAAwB,GAE/B,IAAiB,EAAO,EAAK,GAC7B,IAAe,EAAO,EAAK,GAC3B,IAAe,EAAO,EAAK;EAIjC,EAAK,EACH,MAAM;GACJ,gBAAiB,EAAM,OAAO,UAAU,YAAY,SAAS;GAC7D,iBAAkB,EAAM,OAAO,UAAU,YAAY,aAAa;GAClE,gBAAiB,EAAM,OAAO,UAAU,UAAU,cAAc;GAChE,QAAQ,EAAM;EAChB,EACF,CAAC;EAED,IAAM,KAAc,GAAoC,YACtD,GAAM,QAAQ,CAAI,aAAa,EAAU,IAAI,EAAK,QAAQ;EAO5D,OAAO,CAAI;;;;;;yBAMU,MAAa;GAC1B,EAAe,QAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,SAAS;EAChF,EAAE;UACF,EACA,SACM,CAAI,UACJ,CAAI;;;;;;WAOZ,EAAE;;;;;;yBAMe,MAAa;GAC1B,EAAa,QAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,SAAS;EAC9E,EAAE;UACF,EACA,SACM,CAAI,UACJ,CAAI;;;;;gBAKJ,EAAW,SAAS,EAAM,cAAc,EAAE,GAAG,EAAW,eAAe,EAAM,oBAAoB,EAAE;;WAG3G,EAAE;;;;;;yBAMe,MAAa;GAC1B,EAAa,QAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,SAAS;EAC9E,EAAE;UACF,EACA,SACM,CAAI,UACJ,CAAI;;;;;gBAKJ,EAAW,SAAS,EAAM,cAAc,EAAE,GAAG,EAAW,eAAe,EAAM,oBAAoB,EAAE;gBACnG,QACM,EAAQ,EAAM,UAAU,aACxB,CAAI;yEAC+C,EAAK,OAAO,EAAE;;;;iBAKzE,EAAE;;WAGR,EAAE;;;;;;;CAOR;CACA,QAAQ,CAAC,GAAoB,CAAe;AAC9C,CAAC"}
package/dist/button.cjs CHANGED
@@ -1,18 +1,18 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/shape.css.cjs"),n=require("./styles/mixins/states.css.cjs"),r=require("./styles/mixins/theme.css.cjs"),i=require("./styles/mixins/animation.css.cjs"),a=require("./styles/mixins/variants.css.cjs"),o=require("./shared/prop-bundles.cjs"),s=require("./ore/dist/forms/field.cjs");require("./ore/dist/forms.cjs");const c=require("./utils/link.cjs"),l=require("./button-group.cjs"),u=require("./inputs/shared/use-form-action.cjs"),d=require("./inputs/button/button.css.cjs");let f=require("@vielzeug/ore"),p=require("@vielzeug/ripple");var m=[`solid`,`flat`,`bordered`,`outline`,`ghost`,`text`,`frost`],h=`ore-button`;(0,f.define)(h,{formAssociated:!0,props:{...o.commonProps,effect:f.prop.string(),fullwidth:f.prop.bool(!1),href:f.prop.string(),iconOnly:f.prop.bool(!1),label:f.prop.string(),rel:f.prop.string(),target:f.prop.string(),type:f.prop.oneOf([`button`,`submit`,`reset`],`button`),variant:f.prop.oneOf(m,`solid`)},setup(e){let t=(0,f.getHost)(),n=(0,f.inject)(l.BUTTON_GROUP_CTX),r=(0,p.computed)(()=>n?.color.value??e.color.value),i=(0,p.computed)(()=>n?.size.value??e.size.value),a=(0,p.computed)(()=>n?.variant.value??e.variant.value),o=(0,p.computed)(()=>!!(e.disabled.value||e.loading.value)),{effectiveRel:d,isLink:m}=c.useLinkProps(e.href,e.rel,e.target),h=s.useField({disabled:o,toFormValue:()=>null,value:(0,p.computed)(()=>``)}),g=n=>{if(o.value){n.preventDefault();return}u.useFormAction(()=>m.value?null:h.internals.form,e.type,o,t)(n)};(0,f.bind)({attr:{"aria-busy":e.loading,"aria-disabled":o,"aria-label":e.label,color:r,effect:e.effect,role:(0,p.computed)(()=>t.getAttribute(`role`)??(m.value?`link`:`button`)),size:i,tabindex:(0,p.computed)(()=>o.value?`-1`:`0`),variant:a},on:{keydown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),(t.shadowRoot?.querySelector(`[part="button"]`))?.click())}}});let _=f.html`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/shape.css.cjs"),n=require("./styles/mixins/states.css.cjs"),r=require("./styles/mixins/theme.css.cjs"),i=require("./styles/mixins/animation.css.cjs"),a=require("./styles/mixins/variants.css.cjs"),o=require("./shared/prop-bundles.cjs"),s=require("./utils/link.cjs"),c=require("./button-group.cjs"),l=require("./inputs/shared/use-form-action.cjs"),u=require("./inputs/button/button.css.cjs");let d=require("@vielzeug/ore"),f=require("@vielzeug/ripple"),p=require("@vielzeug/ore/forms");var m=[`solid`,`flat`,`bordered`,`outline`,`ghost`,`text`,`frost`],h=`ore-button`;(0,d.define)(h,{formAssociated:!0,props:{...o.commonProps,effect:d.prop.string(),fullwidth:d.prop.bool(!1),href:d.prop.string(),iconOnly:d.prop.bool(!1),label:d.prop.string(),rel:d.prop.string(),target:d.prop.string(),type:d.prop.oneOf([`button`,`submit`,`reset`],`button`),variant:d.prop.oneOf(m,`solid`)},setup(e){let t=(0,d.getHost)(),n=(0,d.inject)(c.BUTTON_GROUP_CTX),r=(0,f.computed)(()=>n?.color.value??e.color.value),i=(0,f.computed)(()=>n?.size.value??e.size.value),a=(0,f.computed)(()=>n?.variant.value??e.variant.value),o=(0,f.computed)(()=>!!(e.disabled.value||e.loading.value)),{effectiveRel:u,isLink:m}=s.useLinkProps(e.href,e.rel,e.target),h=(0,p.useField)({disabled:o,toFormValue:()=>null,value:(0,f.computed)(()=>``)}),g=n=>{if(o.value){n.preventDefault();return}l.useFormAction(()=>m.value?null:h.internals.form,e.type,o,t)(n)};(0,d.bind)({attr:{"aria-busy":e.loading,"aria-disabled":o,"aria-label":e.label,color:r,effect:e.effect,role:(0,f.computed)(()=>t.getAttribute(`role`)??(m.value?`link`:`button`)),size:i,tabindex:(0,f.computed)(()=>o.value?`-1`:`0`),variant:a},on:{keydown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),(t.shadowRoot?.querySelector(`[part="button"]`))?.click())}}});let _=d.html`
2
2
  <span class="loader" part="loader" aria-label="Loading" ?hidden=${()=>!e.loading.value}></span>
3
3
  <slot name="prefix"></slot>
4
4
  <span class="content" part="content"><slot></slot></span>
5
5
  <slot name="suffix"></slot>
6
- `;return f.html`
7
- ${()=>m.value?f.html`<a
6
+ `;return d.html`
7
+ ${()=>m.value?d.html`<a
8
8
  part="button"
9
9
  role="presentation"
10
10
  tabindex="-1"
11
11
  href="${e.href}"
12
- :rel="${d}"
12
+ :rel="${u}"
13
13
  :target="${e.target}"
14
14
  @click="${g}">
15
15
  ${_}
16
- </a>`:f.html`<span part="button" role="presentation" @click="${g}">${_}</span>`}
17
- `},shadow:{delegatesFocus:!1},styles:[r.colorThemeMixin,e.coarsePointerMixin,e.reducedMotionMixin,t.roundedVariantMixin,r.forcedColorsMixin,t.sizeVariantMixin({lg:{fontSize:`var(--text-base)`,gap:`var(--size-2-5)`,height:`var(--size-12)`,iconSize:`var(--size-6)`,lineHeight:`var(--leading-relaxed)`,padding:`var(--size-2-5) var(--size-5)`},sm:{fontSize:`var(--text-sm)`,gap:`var(--size-1-5)`,height:`var(--size-8)`,iconSize:`var(--size-4)`,lineHeight:`var(--leading-tight)`,padding:`var(--size-1-5) var(--size-3)`}}),a.frostVariantMixin(`[part="button"]`),i.rainbowEffectMixin(`[part="button"]`),i.shineEffectMixin(`[part="button"]`),n.disabledLoadingMixin,d.default]}),exports.BUTTON_TAG=h,exports.BUTTON_VARIANTS=m;
16
+ </a>`:d.html`<span part="button" role="presentation" @click="${g}">${_}</span>`}
17
+ `},shadow:{delegatesFocus:!1},styles:[r.colorThemeMixin,e.coarsePointerMixin,e.reducedMotionMixin,t.roundedVariantMixin,r.forcedColorsMixin,t.sizeVariantMixin({lg:{fontSize:`var(--text-base)`,gap:`var(--size-2-5)`,height:`var(--size-12)`,iconSize:`var(--size-6)`,lineHeight:`var(--leading-relaxed)`,padding:`var(--size-2-5) var(--size-5)`},sm:{fontSize:`var(--text-sm)`,gap:`var(--size-1-5)`,height:`var(--size-8)`,iconSize:`var(--size-4)`,lineHeight:`var(--leading-tight)`,padding:`var(--size-1-5) var(--size-3)`}}),a.frostVariantMixin(`[part="button"]`),i.rainbowEffectMixin(`[part="button"]`),i.shineEffectMixin(`[part="button"]`),n.disabledLoadingMixin,u.default]}),exports.BUTTON_TAG=h,exports.BUTTON_VARIANTS=m;
18
18
  //# sourceMappingURL=button.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"button.cjs","names":[],"sources":["../src/inputs/button/button.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { ButtonType, ComponentSize, LinkTarget, RoundedSize, ThemeColor } from '../../types';\n\nimport { commonProps } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledLoadingMixin,\n forcedColorsMixin,\n frostVariantMixin,\n rainbowEffectMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n shineEffectMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { useLinkProps } from '../../utils';\nimport { BUTTON_GROUP_CTX } from '../button-group/button-group';\nimport { useFormAction } from '../shared';\nimport componentStyles from './button.css?inline';\n\nexport const BUTTON_VARIANTS = ['solid', 'flat', 'bordered', 'outline', 'ghost', 'text', 'frost'] as const;\n\n/** Visual variant for ore-button — derived from BUTTON_VARIANTS for a single source of truth. */\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\n\n/** Animated border effect for ore-button. */\nexport type ButtonEffect = 'shine' | 'rainbow';\n\n/** Button component properties */\nexport type OreButtonProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Animated border effect: 'shine' (color-aware neon sweep) or 'rainbow' */\n effect?: ButtonEffect;\n /** Full width button (100% of container) */\n fullwidth?: boolean;\n /** When set, renders an `<a>` instead of `<button>` */\n href?: string;\n /** Icon-only mode (square aspect ratio, no padding) */\n iconOnly?: boolean;\n /** Accessible label for the inner button — required for icon-only buttons */\n label?: string;\n /** Show loading state with spinner */\n loading?: boolean;\n /** Link rel attribute (requires href) */\n rel?: string;\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** Link target (requires href) */\n target?: LinkTarget;\n /** HTML button type attribute */\n type?: ButtonType;\n /** Visual style variant */\n variant?: ButtonVariant;\n};\n\n/**\n * A customizable button component with multiple variants, sizes, and states.\n * Supports icons, loading states, and animated border effects.\n *\n * @element ore-button\n *\n * @attr {string} type - HTML button type: 'button' | 'submit' | 'reset'\n * @attr {boolean} disabled - Disable button interaction\n * @attr {boolean} loading - Show loading state with spinner\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'text' | 'frost'\n * @attr {string} size - Button size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} effect - Animated border effect: 'shine' | 'rainbow'\n * @attr {boolean} icon-only - Icon-only mode (square aspect ratio, no padding)\n * @attr {boolean} fullwidth - Full width button (100% of container)\n *\n * @fires click - Emitted when button is clicked (unless disabled/loading)\n *\n * @slot - Button content (text, icons, etc.)\n * @slot prefix - Content before the button text (e.g., icons)\n * @slot suffix - Content after the button text (e.g., icons, badges)\n *\n * @part button - The inner button or anchor element\n * @part loader - The loading spinner element\n * @part content - The button content wrapper\n *\n * @cssprop --button-bg - Background color override\n * @cssprop --button-color - Text color override\n * @cssprop --button-border - Border width override\n * @cssprop --button-border-color - Border color override\n * @cssprop --button-radius - Border radius override\n * @cssprop --button-padding - Inner padding override\n * @cssprop --button-gap - Gap between icon and text override\n * @cssprop --button-font-size - Font size override\n * @cssprop --button-frost-active-bg - Background when pressed (frost variant)\n * @cssprop --button-frost-active-border-color - Border color when pressed (frost variant)\n *\n * @example\n * ```html\n * <ore-button variant=\"solid\" color=\"primary\">Click me</ore-button>\n * <ore-button loading color=\"success\">Processing...</ore-button>\n * <ore-button effect=\"shine\" color=\"primary\">Shine</ore-button>\n * <ore-button effect=\"rainbow\" variant=\"frost\">Rainbow</ore-button>\n * ```\n */\nexport const BUTTON_TAG = 'ore-button' as const;\ndefine<OreButtonProps>(BUTTON_TAG, {\n formAssociated: true,\n props: {\n ...commonProps,\n effect: prop.string<ButtonEffect>(),\n fullwidth: prop.bool(false),\n href: prop.string(),\n iconOnly: prop.bool(false),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string<LinkTarget>(),\n type: prop.oneOf(['button', 'submit', 'reset'] as const, 'button'),\n variant: prop.oneOf(BUTTON_VARIANTS, 'solid'),\n },\n setup(props) {\n const el = getHost();\n\n // Prefer group context over own props for color/size/variant.\n const groupCtx = inject(BUTTON_GROUP_CTX);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const effectiveSize = computed(() => groupCtx?.size.value ?? props.size.value);\n const effectiveVariant = computed(() => groupCtx?.variant.value ?? props.variant.value);\n\n const isDisabled = computed(() => !!(props.disabled.value || props.loading.value));\n\n // isLink and effectiveRel are computed from signals — correct even if href changes at runtime.\n const { effectiveRel, isLink } = useLinkProps(props.href, props.rel, props.target);\n\n // Form association: relay submit/reset clicks to the associated form.\n // The inner <button> always has type=\"button\" so shadow DOM never drives native form actions.\n // getForm() returns null for link mode at runtime, so no form actions fire.\n const formField = useField({\n disabled: isDisabled,\n toFormValue: () => null,\n value: computed(() => ''),\n });\n\n const handleClick = (e: MouseEvent) => {\n if (isDisabled.value) {\n e.preventDefault();\n\n return;\n }\n\n // Link mode renders a real <a href> below — the browser handles left/middle/ctrl-click\n // navigation, target, and rel natively. No manual window.open/location.href needed.\n useFormAction(() => (isLink.value ? null : formField.internals.form), props.type, isDisabled, el)(e);\n };\n\n const handleKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (el.shadowRoot?.querySelector('[part=\"button\"]') as HTMLElement)?.click();\n }\n };\n\n // ARIA attributes live on the host.\n bind({\n attr: {\n 'aria-busy': props.loading,\n 'aria-disabled': isDisabled,\n 'aria-label': props.label,\n color: effectiveColor,\n effect: props.effect,\n role: computed(() => el.getAttribute('role') ?? (isLink.value ? 'link' : 'button')),\n size: effectiveSize,\n tabindex: computed(() => (isDisabled.value ? '-1' : '0')),\n variant: effectiveVariant,\n },\n on: {\n keydown: handleKeydown,\n },\n });\n\n const buttonContent = html`\n <span class=\"loader\" part=\"loader\" aria-label=\"Loading\" ?hidden=${() => !props.loading.value}></span>\n <slot name=\"prefix\"></slot>\n <span class=\"content\" part=\"content\"><slot></slot></span>\n <slot name=\"suffix\"></slot>\n `;\n\n // The inner element (<a> or <span>) is deliberately non-focusable/decorative — this\n // component's real interactive semantics (role, tabindex, aria-label) live on the\n // custom-element host itself (see the `bind()` block above), since ore-button is\n // formAssociated and needs the host, not an inner element, to carry ElementInternals.\n // This differs from ore-navbar-item / ore-sidebar-item, which aren't form-associated\n // and so render their <a> as the one real focusable/semantic element.\n return html`\n ${() =>\n isLink.value\n ? html`<a\n part=\"button\"\n role=\"presentation\"\n tabindex=\"-1\"\n href=\"${props.href}\"\n :rel=\"${effectiveRel}\"\n :target=\"${props.target}\"\n @click=\"${handleClick}\">\n ${buttonContent}\n </a>`\n : html`<span part=\"button\" role=\"presentation\" @click=\"${handleClick}\">${buttonContent}</span>`}\n `;\n },\n shadow: { delegatesFocus: false },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n height: 'var(--size-12)',\n iconSize: 'var(--size-6)',\n lineHeight: 'var(--leading-relaxed)',\n padding: 'var(--size-2-5) var(--size-5)',\n },\n sm: {\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-1-5)',\n height: 'var(--size-8)',\n iconSize: 'var(--size-4)',\n lineHeight: 'var(--leading-tight)',\n padding: 'var(--size-1-5) var(--size-3)',\n },\n }),\n frostVariantMixin('[part=\"button\"]'),\n rainbowEffectMixin('[part=\"button\"]'),\n shineEffectMixin('[part=\"button\"]'),\n disabledLoadingMixin,\n componentStyles,\n ],\n});\n"],"mappings":"wqBAwBA,IAAa,EAAkB,CAAC,QAAS,OAAQ,WAAY,UAAW,QAAS,OAAQ,OAAO,EAsFnF,EAAa,0BACH,EAAY,CACjC,eAAgB,GAChB,MAAO,CACL,GAAG,EAAA,YACH,OAAQ,EAAA,KAAK,OAAqB,EAClC,UAAW,EAAA,KAAK,KAAK,EAAK,EAC1B,KAAM,EAAA,KAAK,OAAO,EAClB,SAAU,EAAA,KAAK,KAAK,EAAK,EACzB,MAAO,EAAA,KAAK,OAAO,EACnB,IAAK,EAAA,KAAK,OAAO,EACjB,OAAQ,EAAA,KAAK,OAAmB,EAChC,KAAM,EAAA,KAAK,MAAM,CAAC,SAAU,SAAU,OAAO,EAAY,QAAQ,EACjE,QAAS,EAAA,KAAK,MAAM,EAAiB,OAAO,CAC9C,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAA,EAAA,QAAA,CAAa,EAGb,GAAA,EAAA,EAAA,OAAA,CAAkB,EAAA,gBAAgB,EAClC,GAAA,EAAA,EAAA,SAAA,KAAgC,GAAU,MAAM,OAAS,EAAM,MAAM,KAAK,EAC1E,GAAA,EAAA,EAAA,SAAA,KAA+B,GAAU,KAAK,OAAS,EAAM,KAAK,KAAK,EACvE,GAAA,EAAA,EAAA,SAAA,KAAkC,GAAU,QAAQ,OAAS,EAAM,QAAQ,KAAK,EAEhF,GAAA,EAAA,EAAA,SAAA,KAA4B,CAAC,EAAE,EAAM,SAAS,OAAS,EAAM,QAAQ,MAAM,EAG3E,CAAE,eAAc,UAAW,EAAA,aAAa,EAAM,KAAM,EAAM,IAAK,EAAM,MAAM,EAK3E,EAAY,EAAA,SAAS,CACzB,SAAU,EACV,gBAAmB,KACnB,OAAA,EAAA,EAAA,SAAA,KAAsB,EAAE,CAC1B,CAAC,EAEK,EAAe,GAAkB,CACrC,GAAI,EAAW,MAAO,CACpB,EAAE,eAAe,EAEjB,MACF,CAIA,EAAA,kBAAqB,EAAO,MAAQ,KAAO,EAAU,UAAU,KAAO,EAAM,KAAM,EAAY,CAAE,CAAC,CAAC,CAAC,CACrG,GAUA,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,YAAa,EAAM,QACnB,gBAAiB,EACjB,aAAc,EAAM,MACpB,MAAO,EACP,OAAQ,EAAM,OACd,MAAA,EAAA,EAAA,SAAA,KAAqB,EAAG,aAAa,MAAM,IAAM,EAAO,MAAQ,OAAS,SAAS,EAClF,KAAM,EACN,UAAA,EAAA,EAAA,SAAA,KAA0B,EAAW,MAAQ,KAAO,GAAI,EACxD,QAAS,CACX,EACA,GAAI,CACF,QArBmB,GAAqB,EACtC,EAAE,MAAQ,SAAW,EAAE,MAAQ,OACjC,EAAE,eAAe,GAChB,EAAG,YAAY,cAAc,iBAAiB,EAAA,EAAmB,MAAM,EAE5E,CAiBE,CACF,CAAC,EAED,IAAM,EAAgB,EAAA,IAAI;4EACgD,CAAC,EAAM,QAAQ,MAAM;;;;MAY/F,MAAO,GAAA,IAAI;YAEP,EAAO,MACH,EAAA,IAAI;;;;sBAIM,EAAM,KAAK;sBACX,EAAa;yBACV,EAAM,OAAO;wBACd,EAAY;gBACpB,EAAc;kBAElB,EAAA,IAAI,mDAAmD,EAAY,IAAI,EAAc,SAAS;KAExG,EACA,OAAQ,CAAE,eAAgB,EAAM,EAChC,OAAQ,CACN,EAAA,gBACA,EAAA,mBACA,EAAA,mBACA,EAAA,oBACA,EAAA,kBACA,EAAA,iBAAiB,CACf,GAAI,CACF,SAAU,mBACV,IAAK,kBACL,OAAQ,iBACR,SAAU,gBACV,WAAY,yBACZ,QAAS,+BACX,EACA,GAAI,CACF,SAAU,iBACV,IAAK,kBACL,OAAQ,gBACR,SAAU,gBACV,WAAY,uBACZ,QAAS,+BACX,CACF,CAAC,EACD,EAAA,kBAAkB,iBAAiB,EACnC,EAAA,mBAAmB,iBAAiB,EACpC,EAAA,iBAAiB,iBAAiB,EAClC,EAAA,qBACA,EAAA,OACF,CACF,CAAC"}
1
+ {"version":3,"file":"button.cjs","names":[],"sources":["../src/inputs/button/button.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { ButtonType, ComponentSize, LinkTarget, RoundedSize, ThemeColor } from '../../types';\n\nimport { commonProps } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledLoadingMixin,\n forcedColorsMixin,\n frostVariantMixin,\n rainbowEffectMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n shineEffectMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { useLinkProps } from '../../utils';\nimport { BUTTON_GROUP_CTX } from '../button-group/button-group';\nimport { useFormAction } from '../shared';\nimport componentStyles from './button.css?inline';\n\nexport const BUTTON_VARIANTS = ['solid', 'flat', 'bordered', 'outline', 'ghost', 'text', 'frost'] as const;\n\n/** Visual variant for ore-button — derived from BUTTON_VARIANTS for a single source of truth. */\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\n\n/** Animated border effect for ore-button. */\nexport type ButtonEffect = 'shine' | 'rainbow';\n\n/** Button component properties */\nexport type OreButtonProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Animated border effect: 'shine' (color-aware neon sweep) or 'rainbow' */\n effect?: ButtonEffect;\n /** Full width button (100% of container) */\n fullwidth?: boolean;\n /** When set, renders an `<a>` instead of `<button>` */\n href?: string;\n /** Icon-only mode (square aspect ratio, no padding) */\n iconOnly?: boolean;\n /** Accessible label for the inner button — required for icon-only buttons */\n label?: string;\n /** Show loading state with spinner */\n loading?: boolean;\n /** Link rel attribute (requires href) */\n rel?: string;\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** Link target (requires href) */\n target?: LinkTarget;\n /** HTML button type attribute */\n type?: ButtonType;\n /** Visual style variant */\n variant?: ButtonVariant;\n};\n\n/**\n * A customizable button component with multiple variants, sizes, and states.\n * Supports icons, loading states, and animated border effects.\n *\n * @element ore-button\n *\n * @attr {string} type - HTML button type: 'button' | 'submit' | 'reset'\n * @attr {boolean} disabled - Disable button interaction\n * @attr {boolean} loading - Show loading state with spinner\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'text' | 'frost'\n * @attr {string} size - Button size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} effect - Animated border effect: 'shine' | 'rainbow'\n * @attr {boolean} icon-only - Icon-only mode (square aspect ratio, no padding)\n * @attr {boolean} fullwidth - Full width button (100% of container)\n *\n * @fires click - Emitted when button is clicked (unless disabled/loading)\n *\n * @slot - Button content (text, icons, etc.)\n * @slot prefix - Content before the button text (e.g., icons)\n * @slot suffix - Content after the button text (e.g., icons, badges)\n *\n * @part button - The inner button or anchor element\n * @part loader - The loading spinner element\n * @part content - The button content wrapper\n *\n * @cssprop --button-bg - Background color override\n * @cssprop --button-color - Text color override\n * @cssprop --button-border - Border width override\n * @cssprop --button-border-color - Border color override\n * @cssprop --button-radius - Border radius override\n * @cssprop --button-padding - Inner padding override\n * @cssprop --button-gap - Gap between icon and text override\n * @cssprop --button-font-size - Font size override\n * @cssprop --button-frost-active-bg - Background when pressed (frost variant)\n * @cssprop --button-frost-active-border-color - Border color when pressed (frost variant)\n *\n * @example\n * ```html\n * <ore-button variant=\"solid\" color=\"primary\">Click me</ore-button>\n * <ore-button loading color=\"success\">Processing...</ore-button>\n * <ore-button effect=\"shine\" color=\"primary\">Shine</ore-button>\n * <ore-button effect=\"rainbow\" variant=\"frost\">Rainbow</ore-button>\n * ```\n */\nexport const BUTTON_TAG = 'ore-button' as const;\ndefine<OreButtonProps>(BUTTON_TAG, {\n formAssociated: true,\n props: {\n ...commonProps,\n effect: prop.string<ButtonEffect>(),\n fullwidth: prop.bool(false),\n href: prop.string(),\n iconOnly: prop.bool(false),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string<LinkTarget>(),\n type: prop.oneOf(['button', 'submit', 'reset'] as const, 'button'),\n variant: prop.oneOf(BUTTON_VARIANTS, 'solid'),\n },\n setup(props) {\n const el = getHost();\n\n // Prefer group context over own props for color/size/variant.\n const groupCtx = inject(BUTTON_GROUP_CTX);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const effectiveSize = computed(() => groupCtx?.size.value ?? props.size.value);\n const effectiveVariant = computed(() => groupCtx?.variant.value ?? props.variant.value);\n\n const isDisabled = computed(() => !!(props.disabled.value || props.loading.value));\n\n // isLink and effectiveRel are computed from signals — correct even if href changes at runtime.\n const { effectiveRel, isLink } = useLinkProps(props.href, props.rel, props.target);\n\n // Form association: relay submit/reset clicks to the associated form.\n // The inner <button> always has type=\"button\" so shadow DOM never drives native form actions.\n // getForm() returns null for link mode at runtime, so no form actions fire.\n const formField = useField({\n disabled: isDisabled,\n toFormValue: () => null,\n value: computed(() => ''),\n });\n\n const handleClick = (e: MouseEvent) => {\n if (isDisabled.value) {\n e.preventDefault();\n\n return;\n }\n\n // Link mode renders a real <a href> below — the browser handles left/middle/ctrl-click\n // navigation, target, and rel natively. No manual window.open/location.href needed.\n useFormAction(() => (isLink.value ? null : formField.internals.form), props.type, isDisabled, el)(e);\n };\n\n const handleKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (el.shadowRoot?.querySelector('[part=\"button\"]') as HTMLElement)?.click();\n }\n };\n\n // ARIA attributes live on the host.\n bind({\n attr: {\n 'aria-busy': props.loading,\n 'aria-disabled': isDisabled,\n 'aria-label': props.label,\n color: effectiveColor,\n effect: props.effect,\n role: computed(() => el.getAttribute('role') ?? (isLink.value ? 'link' : 'button')),\n size: effectiveSize,\n tabindex: computed(() => (isDisabled.value ? '-1' : '0')),\n variant: effectiveVariant,\n },\n on: {\n keydown: handleKeydown,\n },\n });\n\n const buttonContent = html`\n <span class=\"loader\" part=\"loader\" aria-label=\"Loading\" ?hidden=${() => !props.loading.value}></span>\n <slot name=\"prefix\"></slot>\n <span class=\"content\" part=\"content\"><slot></slot></span>\n <slot name=\"suffix\"></slot>\n `;\n\n // The inner element (<a> or <span>) is deliberately non-focusable/decorative — this\n // component's real interactive semantics (role, tabindex, aria-label) live on the\n // custom-element host itself (see the `bind()` block above), since ore-button is\n // formAssociated and needs the host, not an inner element, to carry ElementInternals.\n // This differs from ore-navbar-item / ore-sidebar-item, which aren't form-associated\n // and so render their <a> as the one real focusable/semantic element.\n return html`\n ${() =>\n isLink.value\n ? html`<a\n part=\"button\"\n role=\"presentation\"\n tabindex=\"-1\"\n href=\"${props.href}\"\n :rel=\"${effectiveRel}\"\n :target=\"${props.target}\"\n @click=\"${handleClick}\">\n ${buttonContent}\n </a>`\n : html`<span part=\"button\" role=\"presentation\" @click=\"${handleClick}\">${buttonContent}</span>`}\n `;\n },\n shadow: { delegatesFocus: false },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n height: 'var(--size-12)',\n iconSize: 'var(--size-6)',\n lineHeight: 'var(--leading-relaxed)',\n padding: 'var(--size-2-5) var(--size-5)',\n },\n sm: {\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-1-5)',\n height: 'var(--size-8)',\n iconSize: 'var(--size-4)',\n lineHeight: 'var(--leading-tight)',\n padding: 'var(--size-1-5) var(--size-3)',\n },\n }),\n frostVariantMixin('[part=\"button\"]'),\n rainbowEffectMixin('[part=\"button\"]'),\n shineEffectMixin('[part=\"button\"]'),\n disabledLoadingMixin,\n componentStyles,\n ],\n});\n"],"mappings":"2nBAwBA,IAAa,EAAkB,CAAC,QAAS,OAAQ,WAAY,UAAW,QAAS,OAAQ,OAAO,EAsFnF,EAAa,0BACH,EAAY,CACjC,eAAgB,GAChB,MAAO,CACL,GAAG,EAAA,YACH,OAAQ,EAAA,KAAK,OAAqB,EAClC,UAAW,EAAA,KAAK,KAAK,EAAK,EAC1B,KAAM,EAAA,KAAK,OAAO,EAClB,SAAU,EAAA,KAAK,KAAK,EAAK,EACzB,MAAO,EAAA,KAAK,OAAO,EACnB,IAAK,EAAA,KAAK,OAAO,EACjB,OAAQ,EAAA,KAAK,OAAmB,EAChC,KAAM,EAAA,KAAK,MAAM,CAAC,SAAU,SAAU,OAAO,EAAY,QAAQ,EACjE,QAAS,EAAA,KAAK,MAAM,EAAiB,OAAO,CAC9C,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAA,EAAA,QAAA,CAAa,EAGb,GAAA,EAAA,EAAA,OAAA,CAAkB,EAAA,gBAAgB,EAClC,GAAA,EAAA,EAAA,SAAA,KAAgC,GAAU,MAAM,OAAS,EAAM,MAAM,KAAK,EAC1E,GAAA,EAAA,EAAA,SAAA,KAA+B,GAAU,KAAK,OAAS,EAAM,KAAK,KAAK,EACvE,GAAA,EAAA,EAAA,SAAA,KAAkC,GAAU,QAAQ,OAAS,EAAM,QAAQ,KAAK,EAEhF,GAAA,EAAA,EAAA,SAAA,KAA4B,CAAC,EAAE,EAAM,SAAS,OAAS,EAAM,QAAQ,MAAM,EAG3E,CAAE,eAAc,UAAW,EAAA,aAAa,EAAM,KAAM,EAAM,IAAK,EAAM,MAAM,EAK3E,GAAA,EAAA,EAAA,SAAA,CAAqB,CACzB,SAAU,EACV,gBAAmB,KACnB,OAAA,EAAA,EAAA,SAAA,KAAsB,EAAE,CAC1B,CAAC,EAEK,EAAe,GAAkB,CACrC,GAAI,EAAW,MAAO,CACpB,EAAE,eAAe,EAEjB,MACF,CAIA,EAAA,kBAAqB,EAAO,MAAQ,KAAO,EAAU,UAAU,KAAO,EAAM,KAAM,EAAY,CAAE,CAAC,CAAC,CAAC,CACrG,GAUA,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,YAAa,EAAM,QACnB,gBAAiB,EACjB,aAAc,EAAM,MACpB,MAAO,EACP,OAAQ,EAAM,OACd,MAAA,EAAA,EAAA,SAAA,KAAqB,EAAG,aAAa,MAAM,IAAM,EAAO,MAAQ,OAAS,SAAS,EAClF,KAAM,EACN,UAAA,EAAA,EAAA,SAAA,KAA0B,EAAW,MAAQ,KAAO,GAAI,EACxD,QAAS,CACX,EACA,GAAI,CACF,QArBmB,GAAqB,EACtC,EAAE,MAAQ,SAAW,EAAE,MAAQ,OACjC,EAAE,eAAe,GAChB,EAAG,YAAY,cAAc,iBAAiB,EAAA,EAAmB,MAAM,EAE5E,CAiBE,CACF,CAAC,EAED,IAAM,EAAgB,EAAA,IAAI;4EACgD,CAAC,EAAM,QAAQ,MAAM;;;;MAY/F,MAAO,GAAA,IAAI;YAEP,EAAO,MACH,EAAA,IAAI;;;;sBAIM,EAAM,KAAK;sBACX,EAAa;yBACV,EAAM,OAAO;wBACd,EAAY;gBACpB,EAAc;kBAElB,EAAA,IAAI,mDAAmD,EAAY,IAAI,EAAc,SAAS;KAExG,EACA,OAAQ,CAAE,eAAgB,EAAM,EAChC,OAAQ,CACN,EAAA,gBACA,EAAA,mBACA,EAAA,mBACA,EAAA,oBACA,EAAA,kBACA,EAAA,iBAAiB,CACf,GAAI,CACF,SAAU,mBACV,IAAK,kBACL,OAAQ,iBACR,SAAU,gBACV,WAAY,yBACZ,QAAS,+BACX,EACA,GAAI,CACF,SAAU,iBACV,IAAK,kBACL,OAAQ,gBACR,SAAU,gBACV,WAAY,uBACZ,QAAS,+BACX,CACF,CAAC,EACD,EAAA,kBAAkB,iBAAiB,EACnC,EAAA,mBAAmB,iBAAiB,EACpC,EAAA,iBAAiB,iBAAiB,EAClC,EAAA,qBACA,EAAA,OACF,CACF,CAAC"}
package/dist/button.js CHANGED
@@ -5,14 +5,13 @@ import { colorThemeMixin as a, forcedColorsMixin as o } from "./styles/mixins/th
5
5
  import { rainbowEffectMixin as s, shineEffectMixin as c } from "./styles/mixins/animation.css.js";
6
6
  import { frostVariantMixin as l } from "./styles/mixins/variants.css.js";
7
7
  import { commonProps as u } from "./shared/prop-bundles.js";
8
- import { useField as d } from "./ore/dist/forms/field.js";
9
- import "./ore/dist/forms.js";
10
- import { useLinkProps as f } from "./utils/link.js";
11
- import { BUTTON_GROUP_CTX as p } from "./button-group.js";
12
- import { useFormAction as m } from "./inputs/shared/use-form-action.js";
13
- import h from "./inputs/button/button.css.js";
14
- import { bind as g, define as _, getHost as v, html as y, inject as b, prop as x } from "@vielzeug/ore";
15
- import { computed as S } from "@vielzeug/ripple";
8
+ import { useLinkProps as d } from "./utils/link.js";
9
+ import { BUTTON_GROUP_CTX as f } from "./button-group.js";
10
+ import { useFormAction as p } from "./inputs/shared/use-form-action.js";
11
+ import m from "./inputs/button/button.css.js";
12
+ import { bind as h, define as g, getHost as _, html as v, inject as y, prop as b } from "@vielzeug/ore";
13
+ import { computed as x } from "@vielzeug/ripple";
14
+ import { useField as S } from "@vielzeug/ore/forms";
16
15
  //#region src/inputs/button/button.ts
17
16
  var C = [
18
17
  "solid",
@@ -23,60 +22,60 @@ var C = [
23
22
  "text",
24
23
  "frost"
25
24
  ], w = "ore-button";
26
- _(w, {
25
+ g(w, {
27
26
  formAssociated: !0,
28
27
  props: {
29
28
  ...u,
30
- effect: x.string(),
31
- fullwidth: x.bool(!1),
32
- href: x.string(),
33
- iconOnly: x.bool(!1),
34
- label: x.string(),
35
- rel: x.string(),
36
- target: x.string(),
37
- type: x.oneOf([
29
+ effect: b.string(),
30
+ fullwidth: b.bool(!1),
31
+ href: b.string(),
32
+ iconOnly: b.bool(!1),
33
+ label: b.string(),
34
+ rel: b.string(),
35
+ target: b.string(),
36
+ type: b.oneOf([
38
37
  "button",
39
38
  "submit",
40
39
  "reset"
41
40
  ], "button"),
42
- variant: x.oneOf(C, "solid")
41
+ variant: b.oneOf(C, "solid")
43
42
  },
44
43
  setup(e) {
45
- let t = v(), n = b(p), r = S(() => n?.color.value ?? e.color.value), i = S(() => n?.size.value ?? e.size.value), a = S(() => n?.variant.value ?? e.variant.value), o = S(() => !!(e.disabled.value || e.loading.value)), { effectiveRel: s, isLink: c } = f(e.href, e.rel, e.target), l = d({
44
+ let t = _(), n = y(f), r = x(() => n?.color.value ?? e.color.value), i = x(() => n?.size.value ?? e.size.value), a = x(() => n?.variant.value ?? e.variant.value), o = x(() => !!(e.disabled.value || e.loading.value)), { effectiveRel: s, isLink: c } = d(e.href, e.rel, e.target), l = S({
46
45
  disabled: o,
47
46
  toFormValue: () => null,
48
- value: S(() => "")
47
+ value: x(() => "")
49
48
  }), u = (n) => {
50
49
  if (o.value) {
51
50
  n.preventDefault();
52
51
  return;
53
52
  }
54
- m(() => c.value ? null : l.internals.form, e.type, o, t)(n);
53
+ p(() => c.value ? null : l.internals.form, e.type, o, t)(n);
55
54
  };
56
- g({
55
+ h({
57
56
  attr: {
58
57
  "aria-busy": e.loading,
59
58
  "aria-disabled": o,
60
59
  "aria-label": e.label,
61
60
  color: r,
62
61
  effect: e.effect,
63
- role: S(() => t.getAttribute("role") ?? (c.value ? "link" : "button")),
62
+ role: x(() => t.getAttribute("role") ?? (c.value ? "link" : "button")),
64
63
  size: i,
65
- tabindex: S(() => o.value ? "-1" : "0"),
64
+ tabindex: x(() => o.value ? "-1" : "0"),
66
65
  variant: a
67
66
  },
68
67
  on: { keydown: (e) => {
69
68
  (e.key === "Enter" || e.key === " ") && (e.preventDefault(), (t.shadowRoot?.querySelector("[part=\"button\"]"))?.click());
70
69
  } }
71
70
  });
72
- let h = y`
71
+ let m = v`
73
72
  <span class="loader" part="loader" aria-label="Loading" ?hidden=${() => !e.loading.value}></span>
74
73
  <slot name="prefix"></slot>
75
74
  <span class="content" part="content"><slot></slot></span>
76
75
  <slot name="suffix"></slot>
77
76
  `;
78
- return y`
79
- ${() => c.value ? y`<a
77
+ return v`
78
+ ${() => c.value ? v`<a
80
79
  part="button"
81
80
  role="presentation"
82
81
  tabindex="-1"
@@ -84,8 +83,8 @@ _(w, {
84
83
  :rel="${s}"
85
84
  :target="${e.target}"
86
85
  @click="${u}">
87
- ${h}
88
- </a>` : y`<span part="button" role="presentation" @click="${u}">${h}</span>`}
86
+ ${m}
87
+ </a>` : v`<span part="button" role="presentation" @click="${u}">${m}</span>`}
89
88
  `;
90
89
  },
91
90
  shadow: { delegatesFocus: !1 },
@@ -117,7 +116,7 @@ _(w, {
117
116
  s("[part=\"button\"]"),
118
117
  c("[part=\"button\"]"),
119
118
  i,
120
- h
119
+ m
121
120
  ]
122
121
  });
123
122
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"button.js","names":[],"sources":["../src/inputs/button/button.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { ButtonType, ComponentSize, LinkTarget, RoundedSize, ThemeColor } from '../../types';\n\nimport { commonProps } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledLoadingMixin,\n forcedColorsMixin,\n frostVariantMixin,\n rainbowEffectMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n shineEffectMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { useLinkProps } from '../../utils';\nimport { BUTTON_GROUP_CTX } from '../button-group/button-group';\nimport { useFormAction } from '../shared';\nimport componentStyles from './button.css?inline';\n\nexport const BUTTON_VARIANTS = ['solid', 'flat', 'bordered', 'outline', 'ghost', 'text', 'frost'] as const;\n\n/** Visual variant for ore-button — derived from BUTTON_VARIANTS for a single source of truth. */\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\n\n/** Animated border effect for ore-button. */\nexport type ButtonEffect = 'shine' | 'rainbow';\n\n/** Button component properties */\nexport type OreButtonProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Animated border effect: 'shine' (color-aware neon sweep) or 'rainbow' */\n effect?: ButtonEffect;\n /** Full width button (100% of container) */\n fullwidth?: boolean;\n /** When set, renders an `<a>` instead of `<button>` */\n href?: string;\n /** Icon-only mode (square aspect ratio, no padding) */\n iconOnly?: boolean;\n /** Accessible label for the inner button — required for icon-only buttons */\n label?: string;\n /** Show loading state with spinner */\n loading?: boolean;\n /** Link rel attribute (requires href) */\n rel?: string;\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** Link target (requires href) */\n target?: LinkTarget;\n /** HTML button type attribute */\n type?: ButtonType;\n /** Visual style variant */\n variant?: ButtonVariant;\n};\n\n/**\n * A customizable button component with multiple variants, sizes, and states.\n * Supports icons, loading states, and animated border effects.\n *\n * @element ore-button\n *\n * @attr {string} type - HTML button type: 'button' | 'submit' | 'reset'\n * @attr {boolean} disabled - Disable button interaction\n * @attr {boolean} loading - Show loading state with spinner\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'text' | 'frost'\n * @attr {string} size - Button size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} effect - Animated border effect: 'shine' | 'rainbow'\n * @attr {boolean} icon-only - Icon-only mode (square aspect ratio, no padding)\n * @attr {boolean} fullwidth - Full width button (100% of container)\n *\n * @fires click - Emitted when button is clicked (unless disabled/loading)\n *\n * @slot - Button content (text, icons, etc.)\n * @slot prefix - Content before the button text (e.g., icons)\n * @slot suffix - Content after the button text (e.g., icons, badges)\n *\n * @part button - The inner button or anchor element\n * @part loader - The loading spinner element\n * @part content - The button content wrapper\n *\n * @cssprop --button-bg - Background color override\n * @cssprop --button-color - Text color override\n * @cssprop --button-border - Border width override\n * @cssprop --button-border-color - Border color override\n * @cssprop --button-radius - Border radius override\n * @cssprop --button-padding - Inner padding override\n * @cssprop --button-gap - Gap between icon and text override\n * @cssprop --button-font-size - Font size override\n * @cssprop --button-frost-active-bg - Background when pressed (frost variant)\n * @cssprop --button-frost-active-border-color - Border color when pressed (frost variant)\n *\n * @example\n * ```html\n * <ore-button variant=\"solid\" color=\"primary\">Click me</ore-button>\n * <ore-button loading color=\"success\">Processing...</ore-button>\n * <ore-button effect=\"shine\" color=\"primary\">Shine</ore-button>\n * <ore-button effect=\"rainbow\" variant=\"frost\">Rainbow</ore-button>\n * ```\n */\nexport const BUTTON_TAG = 'ore-button' as const;\ndefine<OreButtonProps>(BUTTON_TAG, {\n formAssociated: true,\n props: {\n ...commonProps,\n effect: prop.string<ButtonEffect>(),\n fullwidth: prop.bool(false),\n href: prop.string(),\n iconOnly: prop.bool(false),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string<LinkTarget>(),\n type: prop.oneOf(['button', 'submit', 'reset'] as const, 'button'),\n variant: prop.oneOf(BUTTON_VARIANTS, 'solid'),\n },\n setup(props) {\n const el = getHost();\n\n // Prefer group context over own props for color/size/variant.\n const groupCtx = inject(BUTTON_GROUP_CTX);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const effectiveSize = computed(() => groupCtx?.size.value ?? props.size.value);\n const effectiveVariant = computed(() => groupCtx?.variant.value ?? props.variant.value);\n\n const isDisabled = computed(() => !!(props.disabled.value || props.loading.value));\n\n // isLink and effectiveRel are computed from signals — correct even if href changes at runtime.\n const { effectiveRel, isLink } = useLinkProps(props.href, props.rel, props.target);\n\n // Form association: relay submit/reset clicks to the associated form.\n // The inner <button> always has type=\"button\" so shadow DOM never drives native form actions.\n // getForm() returns null for link mode at runtime, so no form actions fire.\n const formField = useField({\n disabled: isDisabled,\n toFormValue: () => null,\n value: computed(() => ''),\n });\n\n const handleClick = (e: MouseEvent) => {\n if (isDisabled.value) {\n e.preventDefault();\n\n return;\n }\n\n // Link mode renders a real <a href> below — the browser handles left/middle/ctrl-click\n // navigation, target, and rel natively. No manual window.open/location.href needed.\n useFormAction(() => (isLink.value ? null : formField.internals.form), props.type, isDisabled, el)(e);\n };\n\n const handleKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (el.shadowRoot?.querySelector('[part=\"button\"]') as HTMLElement)?.click();\n }\n };\n\n // ARIA attributes live on the host.\n bind({\n attr: {\n 'aria-busy': props.loading,\n 'aria-disabled': isDisabled,\n 'aria-label': props.label,\n color: effectiveColor,\n effect: props.effect,\n role: computed(() => el.getAttribute('role') ?? (isLink.value ? 'link' : 'button')),\n size: effectiveSize,\n tabindex: computed(() => (isDisabled.value ? '-1' : '0')),\n variant: effectiveVariant,\n },\n on: {\n keydown: handleKeydown,\n },\n });\n\n const buttonContent = html`\n <span class=\"loader\" part=\"loader\" aria-label=\"Loading\" ?hidden=${() => !props.loading.value}></span>\n <slot name=\"prefix\"></slot>\n <span class=\"content\" part=\"content\"><slot></slot></span>\n <slot name=\"suffix\"></slot>\n `;\n\n // The inner element (<a> or <span>) is deliberately non-focusable/decorative — this\n // component's real interactive semantics (role, tabindex, aria-label) live on the\n // custom-element host itself (see the `bind()` block above), since ore-button is\n // formAssociated and needs the host, not an inner element, to carry ElementInternals.\n // This differs from ore-navbar-item / ore-sidebar-item, which aren't form-associated\n // and so render their <a> as the one real focusable/semantic element.\n return html`\n ${() =>\n isLink.value\n ? html`<a\n part=\"button\"\n role=\"presentation\"\n tabindex=\"-1\"\n href=\"${props.href}\"\n :rel=\"${effectiveRel}\"\n :target=\"${props.target}\"\n @click=\"${handleClick}\">\n ${buttonContent}\n </a>`\n : html`<span part=\"button\" role=\"presentation\" @click=\"${handleClick}\">${buttonContent}</span>`}\n `;\n },\n shadow: { delegatesFocus: false },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n height: 'var(--size-12)',\n iconSize: 'var(--size-6)',\n lineHeight: 'var(--leading-relaxed)',\n padding: 'var(--size-2-5) var(--size-5)',\n },\n sm: {\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-1-5)',\n height: 'var(--size-8)',\n iconSize: 'var(--size-4)',\n lineHeight: 'var(--leading-tight)',\n padding: 'var(--size-1-5) var(--size-3)',\n },\n }),\n frostVariantMixin('[part=\"button\"]'),\n rainbowEffectMixin('[part=\"button\"]'),\n shineEffectMixin('[part=\"button\"]'),\n disabledLoadingMixin,\n componentStyles,\n ],\n});\n"],"mappings":";;;;;;;;;;;;;;;;AAwBA,IAAa,IAAkB;CAAC;CAAS;CAAQ;CAAY;CAAW;CAAS;CAAQ;AAAO,GAsFnF,IAAa;AAC1B,EAAuB,GAAY;CACjC,gBAAgB;CAChB,OAAO;EACL,GAAG;EACH,QAAQ,EAAK,OAAqB;EAClC,WAAW,EAAK,KAAK,EAAK;EAC1B,MAAM,EAAK,OAAO;EAClB,UAAU,EAAK,KAAK,EAAK;EACzB,OAAO,EAAK,OAAO;EACnB,KAAK,EAAK,OAAO;EACjB,QAAQ,EAAK,OAAmB;EAChC,MAAM,EAAK,MAAM;GAAC;GAAU;GAAU;EAAO,GAAY,QAAQ;EACjE,SAAS,EAAK,MAAM,GAAiB,OAAO;CAC9C;CACA,MAAM,GAAO;EACX,IAAM,IAAK,EAAQ,GAGb,IAAW,EAAO,CAAgB,GAClC,IAAiB,QAAe,GAAU,MAAM,SAAS,EAAM,MAAM,KAAK,GAC1E,IAAgB,QAAe,GAAU,KAAK,SAAS,EAAM,KAAK,KAAK,GACvE,IAAmB,QAAe,GAAU,QAAQ,SAAS,EAAM,QAAQ,KAAK,GAEhF,IAAa,QAAe,CAAC,EAAE,EAAM,SAAS,SAAS,EAAM,QAAQ,MAAM,GAG3E,EAAE,iBAAc,cAAW,EAAa,EAAM,MAAM,EAAM,KAAK,EAAM,MAAM,GAK3E,IAAY,EAAS;GACzB,UAAU;GACV,mBAAmB;GACnB,OAAO,QAAe,EAAE;EAC1B,CAAC,GAEK,KAAe,MAAkB;GACrC,IAAI,EAAW,OAAO;IACpB,EAAE,eAAe;IAEjB;GACF;GAIA,QAAqB,EAAO,QAAQ,OAAO,EAAU,UAAU,MAAO,EAAM,MAAM,GAAY,CAAE,CAAC,CAAC,CAAC;EACrG;EAUA,EAAK;GACH,MAAM;IACJ,aAAa,EAAM;IACnB,iBAAiB;IACjB,cAAc,EAAM;IACpB,OAAO;IACP,QAAQ,EAAM;IACd,MAAM,QAAe,EAAG,aAAa,MAAM,MAAM,EAAO,QAAQ,SAAS,SAAS;IAClF,MAAM;IACN,UAAU,QAAgB,EAAW,QAAQ,OAAO,GAAI;IACxD,SAAS;GACX;GACA,IAAI,EACF,UArBmB,MAAqB;IAC1C,CAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,eAAe,IAChB,EAAG,YAAY,cAAc,mBAAiB,EAAA,EAAmB,MAAM;GAE5E,EAiBE;EACF,CAAC;EAED,IAAM,IAAgB,CAAI;8EACgD,CAAC,EAAM,QAAQ,MAAM;;;;;EAY/F,OAAO,CAAI;cAEP,EAAO,QACH,CAAI;;;;sBAIM,EAAM,KAAK;sBACX,EAAa;yBACV,EAAM,OAAO;wBACd,EAAY;gBACpB,EAAc;oBAElB,CAAI,mDAAmD,EAAY,IAAI,EAAc,SAAS;;CAExG;CACA,QAAQ,EAAE,gBAAgB,GAAM;CAChC,QAAQ;EACN;EACA;EACA;EACA;EACA;EACA,EAAiB;GACf,IAAI;IACF,UAAU;IACV,KAAK;IACL,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,SAAS;GACX;GACA,IAAI;IACF,UAAU;IACV,KAAK;IACL,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,SAAS;GACX;EACF,CAAC;EACD,EAAkB,mBAAiB;EACnC,EAAmB,mBAAiB;EACpC,EAAiB,mBAAiB;EAClC;EACA;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"button.js","names":[],"sources":["../src/inputs/button/button.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { ButtonType, ComponentSize, LinkTarget, RoundedSize, ThemeColor } from '../../types';\n\nimport { commonProps } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledLoadingMixin,\n forcedColorsMixin,\n frostVariantMixin,\n rainbowEffectMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n shineEffectMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { useLinkProps } from '../../utils';\nimport { BUTTON_GROUP_CTX } from '../button-group/button-group';\nimport { useFormAction } from '../shared';\nimport componentStyles from './button.css?inline';\n\nexport const BUTTON_VARIANTS = ['solid', 'flat', 'bordered', 'outline', 'ghost', 'text', 'frost'] as const;\n\n/** Visual variant for ore-button — derived from BUTTON_VARIANTS for a single source of truth. */\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\n\n/** Animated border effect for ore-button. */\nexport type ButtonEffect = 'shine' | 'rainbow';\n\n/** Button component properties */\nexport type OreButtonProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Animated border effect: 'shine' (color-aware neon sweep) or 'rainbow' */\n effect?: ButtonEffect;\n /** Full width button (100% of container) */\n fullwidth?: boolean;\n /** When set, renders an `<a>` instead of `<button>` */\n href?: string;\n /** Icon-only mode (square aspect ratio, no padding) */\n iconOnly?: boolean;\n /** Accessible label for the inner button — required for icon-only buttons */\n label?: string;\n /** Show loading state with spinner */\n loading?: boolean;\n /** Link rel attribute (requires href) */\n rel?: string;\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** Link target (requires href) */\n target?: LinkTarget;\n /** HTML button type attribute */\n type?: ButtonType;\n /** Visual style variant */\n variant?: ButtonVariant;\n};\n\n/**\n * A customizable button component with multiple variants, sizes, and states.\n * Supports icons, loading states, and animated border effects.\n *\n * @element ore-button\n *\n * @attr {string} type - HTML button type: 'button' | 'submit' | 'reset'\n * @attr {boolean} disabled - Disable button interaction\n * @attr {boolean} loading - Show loading state with spinner\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'text' | 'frost'\n * @attr {string} size - Button size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} effect - Animated border effect: 'shine' | 'rainbow'\n * @attr {boolean} icon-only - Icon-only mode (square aspect ratio, no padding)\n * @attr {boolean} fullwidth - Full width button (100% of container)\n *\n * @fires click - Emitted when button is clicked (unless disabled/loading)\n *\n * @slot - Button content (text, icons, etc.)\n * @slot prefix - Content before the button text (e.g., icons)\n * @slot suffix - Content after the button text (e.g., icons, badges)\n *\n * @part button - The inner button or anchor element\n * @part loader - The loading spinner element\n * @part content - The button content wrapper\n *\n * @cssprop --button-bg - Background color override\n * @cssprop --button-color - Text color override\n * @cssprop --button-border - Border width override\n * @cssprop --button-border-color - Border color override\n * @cssprop --button-radius - Border radius override\n * @cssprop --button-padding - Inner padding override\n * @cssprop --button-gap - Gap between icon and text override\n * @cssprop --button-font-size - Font size override\n * @cssprop --button-frost-active-bg - Background when pressed (frost variant)\n * @cssprop --button-frost-active-border-color - Border color when pressed (frost variant)\n *\n * @example\n * ```html\n * <ore-button variant=\"solid\" color=\"primary\">Click me</ore-button>\n * <ore-button loading color=\"success\">Processing...</ore-button>\n * <ore-button effect=\"shine\" color=\"primary\">Shine</ore-button>\n * <ore-button effect=\"rainbow\" variant=\"frost\">Rainbow</ore-button>\n * ```\n */\nexport const BUTTON_TAG = 'ore-button' as const;\ndefine<OreButtonProps>(BUTTON_TAG, {\n formAssociated: true,\n props: {\n ...commonProps,\n effect: prop.string<ButtonEffect>(),\n fullwidth: prop.bool(false),\n href: prop.string(),\n iconOnly: prop.bool(false),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string<LinkTarget>(),\n type: prop.oneOf(['button', 'submit', 'reset'] as const, 'button'),\n variant: prop.oneOf(BUTTON_VARIANTS, 'solid'),\n },\n setup(props) {\n const el = getHost();\n\n // Prefer group context over own props for color/size/variant.\n const groupCtx = inject(BUTTON_GROUP_CTX);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const effectiveSize = computed(() => groupCtx?.size.value ?? props.size.value);\n const effectiveVariant = computed(() => groupCtx?.variant.value ?? props.variant.value);\n\n const isDisabled = computed(() => !!(props.disabled.value || props.loading.value));\n\n // isLink and effectiveRel are computed from signals — correct even if href changes at runtime.\n const { effectiveRel, isLink } = useLinkProps(props.href, props.rel, props.target);\n\n // Form association: relay submit/reset clicks to the associated form.\n // The inner <button> always has type=\"button\" so shadow DOM never drives native form actions.\n // getForm() returns null for link mode at runtime, so no form actions fire.\n const formField = useField({\n disabled: isDisabled,\n toFormValue: () => null,\n value: computed(() => ''),\n });\n\n const handleClick = (e: MouseEvent) => {\n if (isDisabled.value) {\n e.preventDefault();\n\n return;\n }\n\n // Link mode renders a real <a href> below — the browser handles left/middle/ctrl-click\n // navigation, target, and rel natively. No manual window.open/location.href needed.\n useFormAction(() => (isLink.value ? null : formField.internals.form), props.type, isDisabled, el)(e);\n };\n\n const handleKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (el.shadowRoot?.querySelector('[part=\"button\"]') as HTMLElement)?.click();\n }\n };\n\n // ARIA attributes live on the host.\n bind({\n attr: {\n 'aria-busy': props.loading,\n 'aria-disabled': isDisabled,\n 'aria-label': props.label,\n color: effectiveColor,\n effect: props.effect,\n role: computed(() => el.getAttribute('role') ?? (isLink.value ? 'link' : 'button')),\n size: effectiveSize,\n tabindex: computed(() => (isDisabled.value ? '-1' : '0')),\n variant: effectiveVariant,\n },\n on: {\n keydown: handleKeydown,\n },\n });\n\n const buttonContent = html`\n <span class=\"loader\" part=\"loader\" aria-label=\"Loading\" ?hidden=${() => !props.loading.value}></span>\n <slot name=\"prefix\"></slot>\n <span class=\"content\" part=\"content\"><slot></slot></span>\n <slot name=\"suffix\"></slot>\n `;\n\n // The inner element (<a> or <span>) is deliberately non-focusable/decorative — this\n // component's real interactive semantics (role, tabindex, aria-label) live on the\n // custom-element host itself (see the `bind()` block above), since ore-button is\n // formAssociated and needs the host, not an inner element, to carry ElementInternals.\n // This differs from ore-navbar-item / ore-sidebar-item, which aren't form-associated\n // and so render their <a> as the one real focusable/semantic element.\n return html`\n ${() =>\n isLink.value\n ? html`<a\n part=\"button\"\n role=\"presentation\"\n tabindex=\"-1\"\n href=\"${props.href}\"\n :rel=\"${effectiveRel}\"\n :target=\"${props.target}\"\n @click=\"${handleClick}\">\n ${buttonContent}\n </a>`\n : html`<span part=\"button\" role=\"presentation\" @click=\"${handleClick}\">${buttonContent}</span>`}\n `;\n },\n shadow: { delegatesFocus: false },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n height: 'var(--size-12)',\n iconSize: 'var(--size-6)',\n lineHeight: 'var(--leading-relaxed)',\n padding: 'var(--size-2-5) var(--size-5)',\n },\n sm: {\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-1-5)',\n height: 'var(--size-8)',\n iconSize: 'var(--size-4)',\n lineHeight: 'var(--leading-tight)',\n padding: 'var(--size-1-5) var(--size-3)',\n },\n }),\n frostVariantMixin('[part=\"button\"]'),\n rainbowEffectMixin('[part=\"button\"]'),\n shineEffectMixin('[part=\"button\"]'),\n disabledLoadingMixin,\n componentStyles,\n ],\n});\n"],"mappings":";;;;;;;;;;;;;;;AAwBA,IAAa,IAAkB;CAAC;CAAS;CAAQ;CAAY;CAAW;CAAS;CAAQ;AAAO,GAsFnF,IAAa;AAC1B,EAAuB,GAAY;CACjC,gBAAgB;CAChB,OAAO;EACL,GAAG;EACH,QAAQ,EAAK,OAAqB;EAClC,WAAW,EAAK,KAAK,EAAK;EAC1B,MAAM,EAAK,OAAO;EAClB,UAAU,EAAK,KAAK,EAAK;EACzB,OAAO,EAAK,OAAO;EACnB,KAAK,EAAK,OAAO;EACjB,QAAQ,EAAK,OAAmB;EAChC,MAAM,EAAK,MAAM;GAAC;GAAU;GAAU;EAAO,GAAY,QAAQ;EACjE,SAAS,EAAK,MAAM,GAAiB,OAAO;CAC9C;CACA,MAAM,GAAO;EACX,IAAM,IAAK,EAAQ,GAGb,IAAW,EAAO,CAAgB,GAClC,IAAiB,QAAe,GAAU,MAAM,SAAS,EAAM,MAAM,KAAK,GAC1E,IAAgB,QAAe,GAAU,KAAK,SAAS,EAAM,KAAK,KAAK,GACvE,IAAmB,QAAe,GAAU,QAAQ,SAAS,EAAM,QAAQ,KAAK,GAEhF,IAAa,QAAe,CAAC,EAAE,EAAM,SAAS,SAAS,EAAM,QAAQ,MAAM,GAG3E,EAAE,iBAAc,cAAW,EAAa,EAAM,MAAM,EAAM,KAAK,EAAM,MAAM,GAK3E,IAAY,EAAS;GACzB,UAAU;GACV,mBAAmB;GACnB,OAAO,QAAe,EAAE;EAC1B,CAAC,GAEK,KAAe,MAAkB;GACrC,IAAI,EAAW,OAAO;IACpB,EAAE,eAAe;IAEjB;GACF;GAIA,QAAqB,EAAO,QAAQ,OAAO,EAAU,UAAU,MAAO,EAAM,MAAM,GAAY,CAAE,CAAC,CAAC,CAAC;EACrG;EAUA,EAAK;GACH,MAAM;IACJ,aAAa,EAAM;IACnB,iBAAiB;IACjB,cAAc,EAAM;IACpB,OAAO;IACP,QAAQ,EAAM;IACd,MAAM,QAAe,EAAG,aAAa,MAAM,MAAM,EAAO,QAAQ,SAAS,SAAS;IAClF,MAAM;IACN,UAAU,QAAgB,EAAW,QAAQ,OAAO,GAAI;IACxD,SAAS;GACX;GACA,IAAI,EACF,UArBmB,MAAqB;IAC1C,CAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,eAAe,IAChB,EAAG,YAAY,cAAc,mBAAiB,EAAA,EAAmB,MAAM;GAE5E,EAiBE;EACF,CAAC;EAED,IAAM,IAAgB,CAAI;8EACgD,CAAC,EAAM,QAAQ,MAAM;;;;;EAY/F,OAAO,CAAI;cAEP,EAAO,QACH,CAAI;;;;sBAIM,EAAM,KAAK;sBACX,EAAa;yBACV,EAAM,OAAO;wBACd,EAAY;gBACpB,EAAc;oBAElB,CAAI,mDAAmD,EAAY,IAAI,EAAc,SAAS;;CAExG;CACA,QAAQ,EAAE,gBAAgB,GAAM;CAChC,QAAQ;EACN;EACA;EACA;EACA;EACA;EACA,EAAiB;GACf,IAAI;IACF,UAAU;IACV,KAAK;IACL,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,SAAS;GACX;GACA,IAAI;IACF,UAAU;IACV,KAAK;IACL,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,SAAS;GACX;EACF,CAAC;EACD,EAAkB,mBAAiB;EACnC,EAAmB,mBAAiB;EACpC,EAAiB,mBAAiB;EAClC;EACA;CACF;AACF,CAAC"}