@vielzeug/refine 1.3.1 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/checkbox-group.cjs +7 -7
  2. package/dist/checkbox-group.cjs.map +1 -1
  3. package/dist/checkbox-group.js +35 -26
  4. package/dist/checkbox-group.js.map +1 -1
  5. package/dist/checkbox.cjs +3 -3
  6. package/dist/checkbox.cjs.map +1 -1
  7. package/dist/checkbox.js +24 -19
  8. package/dist/checkbox.js.map +1 -1
  9. package/dist/combobox.cjs +24 -24
  10. package/dist/combobox.cjs.map +1 -1
  11. package/dist/combobox.js +133 -129
  12. package/dist/combobox.js.map +1 -1
  13. package/dist/custom-elements.json +5473 -4925
  14. package/dist/headless/auto-resize.cjs +2 -0
  15. package/dist/headless/auto-resize.cjs.map +1 -0
  16. package/dist/headless/auto-resize.d.ts +29 -0
  17. package/dist/headless/auto-resize.d.ts.map +1 -0
  18. package/dist/headless/auto-resize.js +16 -0
  19. package/dist/headless/auto-resize.js.map +1 -0
  20. package/dist/headless/checkable.cjs +1 -1
  21. package/dist/headless/checkable.cjs.map +1 -1
  22. package/dist/headless/checkable.d.ts +25 -7
  23. package/dist/headless/checkable.d.ts.map +1 -1
  24. package/dist/headless/checkable.js +19 -14
  25. package/dist/headless/checkable.js.map +1 -1
  26. package/dist/headless/choice-field.cjs +1 -1
  27. package/dist/headless/choice-field.cjs.map +1 -1
  28. package/dist/headless/choice-field.d.ts +21 -0
  29. package/dist/headless/choice-field.d.ts.map +1 -1
  30. package/dist/headless/choice-field.js +27 -19
  31. package/dist/headless/choice-field.js.map +1 -1
  32. package/dist/headless/composer.cjs +2 -0
  33. package/dist/headless/composer.cjs.map +1 -0
  34. package/dist/headless/composer.d.ts +44 -0
  35. package/dist/headless/composer.d.ts.map +1 -0
  36. package/dist/headless/composer.js +21 -0
  37. package/dist/headless/composer.js.map +1 -0
  38. package/dist/headless/field-base.cjs +1 -1
  39. package/dist/headless/field-base.cjs.map +1 -1
  40. package/dist/headless/field-base.d.ts +97 -84
  41. package/dist/headless/field-base.d.ts.map +1 -1
  42. package/dist/headless/field-base.js +43 -32
  43. package/dist/headless/field-base.js.map +1 -1
  44. package/dist/headless/index.d.ts +3 -1
  45. package/dist/headless/index.d.ts.map +1 -1
  46. package/dist/headless/text-field.cjs +1 -1
  47. package/dist/headless/text-field.cjs.map +1 -1
  48. package/dist/headless/text-field.d.ts +40 -0
  49. package/dist/headless/text-field.d.ts.map +1 -1
  50. package/dist/headless/text-field.js +30 -25
  51. package/dist/headless/text-field.js.map +1 -1
  52. package/dist/headless.cjs +1 -1
  53. package/dist/headless.js +27 -25
  54. package/dist/index.cjs +1 -1
  55. package/dist/index.js +61 -60
  56. package/dist/input.cjs +9 -9
  57. package/dist/input.cjs.map +1 -1
  58. package/dist/input.js +49 -44
  59. package/dist/input.js.map +1 -1
  60. package/dist/inputs/checkbox/checkbox.d.ts +3 -0
  61. package/dist/inputs/checkbox/checkbox.d.ts.map +1 -1
  62. package/dist/inputs/combobox/combobox.d.ts +1 -0
  63. package/dist/inputs/combobox/combobox.d.ts.map +1 -1
  64. package/dist/inputs/combobox/combobox.types.d.ts +2 -0
  65. package/dist/inputs/combobox/combobox.types.d.ts.map +1 -1
  66. package/dist/inputs/index.d.ts +2 -0
  67. package/dist/inputs/index.d.ts.map +1 -1
  68. package/dist/inputs/input/input.d.ts.map +1 -1
  69. package/dist/inputs/message-composer/message-composer.css.cjs +2 -0
  70. package/dist/inputs/message-composer/message-composer.css.cjs.map +1 -0
  71. package/dist/inputs/message-composer/message-composer.css.js +6 -0
  72. package/dist/inputs/message-composer/message-composer.css.js.map +1 -0
  73. package/dist/inputs/message-composer/message-composer.d.ts +139 -0
  74. package/dist/inputs/message-composer/message-composer.d.ts.map +1 -0
  75. package/dist/inputs/shared/field-binding.cjs +1 -1
  76. package/dist/inputs/shared/field-binding.cjs.map +1 -1
  77. package/dist/inputs/shared/field-binding.d.ts +15 -0
  78. package/dist/inputs/shared/field-binding.d.ts.map +1 -1
  79. package/dist/inputs/shared/field-binding.js +4 -3
  80. package/dist/inputs/shared/field-binding.js.map +1 -1
  81. package/dist/inputs/shared/form-context.cjs.map +1 -1
  82. package/dist/inputs/shared/form-context.d.ts +6 -0
  83. package/dist/inputs/shared/form-context.d.ts.map +1 -1
  84. package/dist/inputs/shared/form-context.js.map +1 -1
  85. package/dist/inputs/textarea/textarea.d.ts.map +1 -1
  86. package/dist/layout/navbar/navbar.css.cjs +1 -1
  87. package/dist/layout/navbar/navbar.css.cjs.map +1 -1
  88. package/dist/layout/navbar/navbar.css.js +1 -1
  89. package/dist/layout/navbar/navbar.css.js.map +1 -1
  90. package/dist/message-composer.cjs +52 -0
  91. package/dist/message-composer.cjs.map +1 -0
  92. package/dist/message-composer.js +168 -0
  93. package/dist/message-composer.js.map +1 -0
  94. package/dist/ore/dist/base-element.cjs +1 -1
  95. package/dist/ore/dist/base-element.cjs.map +1 -1
  96. package/dist/ore/dist/base-element.js +13 -3
  97. package/dist/ore/dist/base-element.js.map +1 -1
  98. package/dist/ore/dist/forms/field.cjs +1 -1
  99. package/dist/ore/dist/forms/field.cjs.map +1 -1
  100. package/dist/ore/dist/forms/field.js +29 -22
  101. package/dist/ore/dist/forms/field.js.map +1 -1
  102. package/dist/ore/dist/runtime.cjs +1 -1
  103. package/dist/ore/dist/runtime.cjs.map +1 -1
  104. package/dist/ore/dist/runtime.js +3 -1
  105. package/dist/ore/dist/runtime.js.map +1 -1
  106. package/dist/radio-group.cjs +9 -9
  107. package/dist/radio-group.cjs.map +1 -1
  108. package/dist/radio-group.js +44 -38
  109. package/dist/radio-group.js.map +1 -1
  110. package/dist/radio.cjs +3 -3
  111. package/dist/radio.cjs.map +1 -1
  112. package/dist/radio.js +23 -24
  113. package/dist/radio.js.map +1 -1
  114. package/dist/refine.cjs +228 -178
  115. package/dist/refine.cjs.map +1 -1
  116. package/dist/refine.iife.js +228 -178
  117. package/dist/refine.iife.js.map +1 -1
  118. package/dist/refine.js +251 -201
  119. package/dist/refine.js.map +1 -1
  120. package/dist/select.cjs +31 -31
  121. package/dist/select.cjs.map +1 -1
  122. package/dist/select.js +125 -122
  123. package/dist/select.js.map +1 -1
  124. package/dist/shared/size-presets.cjs +1 -1
  125. package/dist/shared/size-presets.cjs.map +1 -1
  126. package/dist/shared/size-presets.d.ts +23 -0
  127. package/dist/shared/size-presets.d.ts.map +1 -1
  128. package/dist/shared/size-presets.js +17 -1
  129. package/dist/shared/size-presets.js.map +1 -1
  130. package/dist/switch.cjs +3 -3
  131. package/dist/switch.cjs.map +1 -1
  132. package/dist/switch.js +17 -18
  133. package/dist/switch.js.map +1 -1
  134. package/dist/textarea.cjs +15 -15
  135. package/dist/textarea.cjs.map +1 -1
  136. package/dist/textarea.js +64 -60
  137. package/dist/textarea.js.map +1 -1
  138. package/package.json +22 -10
@@ -77,6 +77,22 @@ var e = {
77
77
  size: "var(--size-4)"
78
78
  }
79
79
  }, a = {
80
+ lg: {
81
+ "--_padding": "var(--size-4)",
82
+ fontSize: "var(--text-base)",
83
+ gap: "var(--size-2-5)"
84
+ },
85
+ md: {
86
+ "--_padding": "var(--size-3)",
87
+ fontSize: "var(--text-sm)",
88
+ gap: "var(--size-2)"
89
+ },
90
+ sm: {
91
+ "--_padding": "var(--size-2)",
92
+ fontSize: "var(--text-xs)",
93
+ gap: "var(--size-1-5)"
94
+ }
95
+ }, o = {
80
96
  lg: {
81
97
  "--_min-height": "var(--size-40)",
82
98
  fontSize: "var(--text-base)"
@@ -91,6 +107,6 @@ var e = {
91
107
  }
92
108
  };
93
109
  //#endregion
94
- export { n as CONTROL_SIZE_PRESET, e as FIELD_SIZE_PRESET, a as FILE_INPUT_SIZE_PRESET, i as SLIDER_SIZE_PRESET, r as SWITCH_SIZE_PRESET, t as TEXTAREA_SIZE_PRESET };
110
+ export { n as CONTROL_SIZE_PRESET, e as FIELD_SIZE_PRESET, o as FILE_INPUT_SIZE_PRESET, a as MESSAGE_COMPOSER_SIZE_PRESET, i as SLIDER_SIZE_PRESET, r as SWITCH_SIZE_PRESET, t as TEXTAREA_SIZE_PRESET };
95
111
 
96
112
  //# sourceMappingURL=size-presets.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"size-presets.js","names":[],"sources":["../../src/shared/size-presets.ts"],"sourcesContent":["import type { SizeConfig } from '../styles/mixins/shape.css';\nimport type { ComponentSize } from './types';\n\n/** A preset with an entry for every ComponentSize key. */\ntype FullSizePreset = Record<ComponentSize, SizeConfig>;\n\n/** A preset that may omit some ComponentSize keys (sizeVariantMixin fills the gaps with its defaults). */\ntype PartialSizePreset = Partial<Record<ComponentSize, SizeConfig>>;\n\nexport const FIELD_SIZE_PRESET = {\n lg: {\n '--_field-height': 'var(--size-12)',\n '--_padding': 'var(--size-2-5) var(--size-3-5)',\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n },\n md: {\n '--_field-height': 'var(--size-10)',\n '--_padding': 'var(--size-1-5) var(--size-3)',\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-2)',\n },\n sm: {\n '--_field-height': 'var(--size-8)',\n '--_padding': 'var(--size-1) var(--size-2)',\n fontSize: 'var(--text-xs)',\n gap: 'var(--size-1-5)',\n },\n} satisfies FullSizePreset;\n\nexport const TEXTAREA_SIZE_PRESET = {\n lg: {\n '--_padding': 'var(--size-2-5) var(--size-3-5)',\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n },\n md: {\n '--_padding': 'var(--size-1-5) var(--size-3)',\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-2)',\n },\n sm: {\n '--_padding': 'var(--size-1) var(--size-2)',\n fontSize: 'var(--text-xs)',\n gap: 'var(--size-1-5)',\n },\n} satisfies FullSizePreset;\n\n/** Shared size preset for checkbox and radio (icon + label layout). */\nexport const CONTROL_SIZE_PRESET = {\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n size: 'var(--size-6)',\n },\n sm: {\n fontSize: 'var(--text-xs)',\n gap: 'var(--size-1-5)',\n size: 'var(--size-4)',\n },\n} satisfies PartialSizePreset;\n\n/** Shared size preset for the switch toggle control. */\nexport const SWITCH_SIZE_PRESET = {\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-3)',\n height: 'var(--size-7)',\n thumbSize: 'var(--size-6)',\n width: 'var(--size-14)',\n },\n sm: {\n fontSize: 'var(--text-xs)',\n gap: 'var(--size-2)',\n height: 'var(--size-5)',\n thumbSize: 'var(--size-4)',\n width: 'var(--size-9)',\n },\n} satisfies PartialSizePreset;\n\n/** Shared size preset for the slider track/thumb. */\nexport const SLIDER_SIZE_PRESET = {\n lg: {\n fontSize: 'var(--text-base)',\n height: 'calc(var(--size-5) - var(--size-1))',\n size: 'var(--size-5)',\n },\n md: {\n fontSize: 'var(--text-base)',\n height: 'var(--size-3)',\n size: 'var(--size-5)',\n },\n sm: {\n fontSize: 'var(--text-xs)',\n height: 'var(--size-2)',\n size: 'var(--size-4)',\n },\n} satisfies FullSizePreset;\n\n/** Shared size preset for the file-input dropzone. */\nexport const FILE_INPUT_SIZE_PRESET = {\n lg: {\n '--_min-height': 'var(--size-40)',\n fontSize: 'var(--text-base)',\n },\n md: {\n '--_min-height': 'var(--size-32)',\n fontSize: 'var(--text-sm)',\n },\n sm: {\n '--_min-height': 'var(--size-28)',\n fontSize: 'var(--text-xs)',\n },\n} satisfies FullSizePreset;\n"],"mappings":";AASA,IAAa,IAAoB;CAC/B,IAAI;EACF,mBAAmB;EACnB,cAAc;EACd,UAAU;EACV,KAAK;CACP;CACA,IAAI;EACF,mBAAmB;EACnB,cAAc;EACd,UAAU;EACV,KAAK;CACP;CACA,IAAI;EACF,mBAAmB;EACnB,cAAc;EACd,UAAU;EACV,KAAK;CACP;AACF,GAEa,IAAuB;CAClC,IAAI;EACF,cAAc;EACd,UAAU;EACV,KAAK;CACP;CACA,IAAI;EACF,cAAc;EACd,UAAU;EACV,KAAK;CACP;CACA,IAAI;EACF,cAAc;EACd,UAAU;EACV,KAAK;CACP;AACF,GAGa,IAAsB;CACjC,IAAI;EACF,UAAU;EACV,KAAK;EACL,MAAM;CACR;CACA,IAAI;EACF,UAAU;EACV,KAAK;EACL,MAAM;CACR;AACF,GAGa,IAAqB;CAChC,IAAI;EACF,UAAU;EACV,KAAK;EACL,QAAQ;EACR,WAAW;EACX,OAAO;CACT;CACA,IAAI;EACF,UAAU;EACV,KAAK;EACL,QAAQ;EACR,WAAW;EACX,OAAO;CACT;AACF,GAGa,IAAqB;CAChC,IAAI;EACF,UAAU;EACV,QAAQ;EACR,MAAM;CACR;CACA,IAAI;EACF,UAAU;EACV,QAAQ;EACR,MAAM;CACR;CACA,IAAI;EACF,UAAU;EACV,QAAQ;EACR,MAAM;CACR;AACF,GAGa,IAAyB;CACpC,IAAI;EACF,iBAAiB;EACjB,UAAU;CACZ;CACA,IAAI;EACF,iBAAiB;EACjB,UAAU;CACZ;CACA,IAAI;EACF,iBAAiB;EACjB,UAAU;CACZ;AACF"}
1
+ {"version":3,"file":"size-presets.js","names":[],"sources":["../../src/shared/size-presets.ts"],"sourcesContent":["import type { SizeConfig } from '../styles/mixins/shape.css';\nimport type { ComponentSize } from './types';\n\n/** A preset with an entry for every ComponentSize key. */\ntype FullSizePreset = Record<ComponentSize, SizeConfig>;\n\n/** A preset that may omit some ComponentSize keys (sizeVariantMixin fills the gaps with its defaults). */\ntype PartialSizePreset = Partial<Record<ComponentSize, SizeConfig>>;\n\nexport const FIELD_SIZE_PRESET = {\n lg: {\n '--_field-height': 'var(--size-12)',\n '--_padding': 'var(--size-2-5) var(--size-3-5)',\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n },\n md: {\n '--_field-height': 'var(--size-10)',\n '--_padding': 'var(--size-1-5) var(--size-3)',\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-2)',\n },\n sm: {\n '--_field-height': 'var(--size-8)',\n '--_padding': 'var(--size-1) var(--size-2)',\n fontSize: 'var(--text-xs)',\n gap: 'var(--size-1-5)',\n },\n} satisfies FullSizePreset;\n\nexport const TEXTAREA_SIZE_PRESET = {\n lg: {\n '--_padding': 'var(--size-2-5) var(--size-3-5)',\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n },\n md: {\n '--_padding': 'var(--size-1-5) var(--size-3)',\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-2)',\n },\n sm: {\n '--_padding': 'var(--size-1) var(--size-2)',\n fontSize: 'var(--text-xs)',\n gap: 'var(--size-1-5)',\n },\n} satisfies FullSizePreset;\n\n/** Shared size preset for checkbox and radio (icon + label layout). */\nexport const CONTROL_SIZE_PRESET = {\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n size: 'var(--size-6)',\n },\n sm: {\n fontSize: 'var(--text-xs)',\n gap: 'var(--size-1-5)',\n size: 'var(--size-4)',\n },\n} satisfies PartialSizePreset;\n\n/** Shared size preset for the switch toggle control. */\nexport const SWITCH_SIZE_PRESET = {\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-3)',\n height: 'var(--size-7)',\n thumbSize: 'var(--size-6)',\n width: 'var(--size-14)',\n },\n sm: {\n fontSize: 'var(--text-xs)',\n gap: 'var(--size-2)',\n height: 'var(--size-5)',\n thumbSize: 'var(--size-4)',\n width: 'var(--size-9)',\n },\n} satisfies PartialSizePreset;\n\n/** Shared size preset for the slider track/thumb. */\nexport const SLIDER_SIZE_PRESET = {\n lg: {\n fontSize: 'var(--text-base)',\n height: 'calc(var(--size-5) - var(--size-1))',\n size: 'var(--size-5)',\n },\n md: {\n fontSize: 'var(--text-base)',\n height: 'var(--size-3)',\n size: 'var(--size-5)',\n },\n sm: {\n fontSize: 'var(--text-xs)',\n height: 'var(--size-2)',\n size: 'var(--size-4)',\n },\n} satisfies FullSizePreset;\n\n/**\n * Size preset for the message composer card. Padding is symmetric (unlike\n * `TEXTAREA_SIZE_PRESET`'s field-shaped padding) since this scales a card that wraps a field\n * *and* a toolbar row, not a bare field — `md`'s padding matches the value the card used before\n * `size` scaled anything, so the default look is unchanged.\n */\nexport const MESSAGE_COMPOSER_SIZE_PRESET = {\n lg: {\n '--_padding': 'var(--size-4)',\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n },\n md: {\n '--_padding': 'var(--size-3)',\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-2)',\n },\n sm: {\n '--_padding': 'var(--size-2)',\n fontSize: 'var(--text-xs)',\n gap: 'var(--size-1-5)',\n },\n} satisfies FullSizePreset;\n\n/** Shared size preset for the file-input dropzone. */\nexport const FILE_INPUT_SIZE_PRESET = {\n lg: {\n '--_min-height': 'var(--size-40)',\n fontSize: 'var(--text-base)',\n },\n md: {\n '--_min-height': 'var(--size-32)',\n fontSize: 'var(--text-sm)',\n },\n sm: {\n '--_min-height': 'var(--size-28)',\n fontSize: 'var(--text-xs)',\n },\n} satisfies FullSizePreset;\n"],"mappings":";AASA,IAAa,IAAoB;CAC/B,IAAI;EACF,mBAAmB;EACnB,cAAc;EACd,UAAU;EACV,KAAK;CACP;CACA,IAAI;EACF,mBAAmB;EACnB,cAAc;EACd,UAAU;EACV,KAAK;CACP;CACA,IAAI;EACF,mBAAmB;EACnB,cAAc;EACd,UAAU;EACV,KAAK;CACP;AACF,GAEa,IAAuB;CAClC,IAAI;EACF,cAAc;EACd,UAAU;EACV,KAAK;CACP;CACA,IAAI;EACF,cAAc;EACd,UAAU;EACV,KAAK;CACP;CACA,IAAI;EACF,cAAc;EACd,UAAU;EACV,KAAK;CACP;AACF,GAGa,IAAsB;CACjC,IAAI;EACF,UAAU;EACV,KAAK;EACL,MAAM;CACR;CACA,IAAI;EACF,UAAU;EACV,KAAK;EACL,MAAM;CACR;AACF,GAGa,IAAqB;CAChC,IAAI;EACF,UAAU;EACV,KAAK;EACL,QAAQ;EACR,WAAW;EACX,OAAO;CACT;CACA,IAAI;EACF,UAAU;EACV,KAAK;EACL,QAAQ;EACR,WAAW;EACX,OAAO;CACT;AACF,GAGa,IAAqB;CAChC,IAAI;EACF,UAAU;EACV,QAAQ;EACR,MAAM;CACR;CACA,IAAI;EACF,UAAU;EACV,QAAQ;EACR,MAAM;CACR;CACA,IAAI;EACF,UAAU;EACV,QAAQ;EACR,MAAM;CACR;AACF,GAQa,IAA+B;CAC1C,IAAI;EACF,cAAc;EACd,UAAU;EACV,KAAK;CACP;CACA,IAAI;EACF,cAAc;EACd,UAAU;EACV,KAAK;CACP;CACA,IAAI;EACF,cAAc;EACd,UAAU;EACV,KAAK;CACP;AACF,GAGa,IAAyB;CACpC,IAAI;EACF,iBAAiB;EACjB,UAAU;CACZ;CACA,IAAI;EACF,iBAAiB;EACjB,UAAU;CACZ;CACA,IAAI;EACF,iBAAiB;EACjB,UAAU;CACZ;AACF"}
package/dist/switch.cjs CHANGED
@@ -1,10 +1,10 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/shape.css.cjs"),t=require("./styles/mixins/states.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./shared/prop-bundles.cjs"),i=require("./shared/size-presets.cjs"),a=require("./headless/ore.cjs"),o=require("./headless/checkable.cjs"),s=require("./ore/dist/forms/field.cjs");require("./ore/dist/forms.cjs");const c=require("./inputs/shared/field-binding.cjs"),l=require("./inputs/shared/form-context.cjs"),u=require("./inputs/shared/templates.cjs"),d=require("./inputs/switch/switch.css.cjs");let f=require("@vielzeug/ore");var p=`ore-switch`;(0,f.define)(p,{formAssociated:!0,props:{...r.themableBundle,...r.sizableBundle,...r.disablableBundle,checked:f.prop.bool(!1),error:f.prop.string(),helper:f.prop.string(),name:f.prop.string(),value:f.prop.string(`on`)},setup(e){let t=(0,f.useEmit)(),n=(0,f.inject)(l.FORM_CTX),r=l.useFormContext(e,n),i=null,d=o.createCheckable({checked:e.checked,clearIndeterminateFirst:!1,disabled:r.disabled,error:e.error,getFormField:()=>i,helper:e.helper,onToggle:e=>{d.triggerValidation(`change`),t(`change`,e)},prefix:`switch`,signal:a.lifecycleSignal(f.onCleanup),validateOn:n?.validateOn,value:e.value});i=s.useField({disabled:d.disabled,toFormValue:e=>e,value:d.checkableFormValue});let{assistiveId:p,checked:m,disabled:h,errorText:g,handleClick:_,handleKeydown:v,helperText:y,labelId:b}=d;return c.applyCheckableBinding(r.size,{assistiveId:p,checked:m,disabled:h,errorText:g,handleClick:_,handleKeydown:v,helperText:y,labelId:b},`switch`),f.html`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/shape.css.cjs"),t=require("./styles/mixins/states.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./shared/prop-bundles.cjs"),i=require("./shared/size-presets.cjs"),a=require("./headless/ore.cjs"),o=require("./headless/checkable.cjs"),s=require("./ore/dist/forms/field.cjs");require("./ore/dist/forms.cjs");const c=require("./inputs/shared/field-binding.cjs"),l=require("./inputs/shared/form-context.cjs"),u=require("./inputs/shared/templates.cjs"),d=require("./inputs/switch/switch.css.cjs");let f=require("@vielzeug/ore");var p=`ore-switch`;(0,f.define)(p,{formAssociated:!0,props:{...r.themableBundle,...r.sizableBundle,...r.disablableBundle,checked:f.prop.bool(!1),error:f.prop.string(),helper:f.prop.string(),name:f.prop.string(),value:f.prop.string(`on`)},setup(e){let t=(0,f.useEmit)(),n=(0,f.inject)(l.FORM_CTX),r=l.useFormContext(e,n),i=o.createCheckable({checked:e.checked,clearIndeterminateFirst:!1,disabled:r.disabled,error:e.error,helper:e.helper,onToggle:e=>{i.triggerValidation(`change`),t(`change`,e)},prefix:`switch`,signal:a.lifecycleSignal(f.onCleanup),validateOn:n?.validateOn,value:e.value});i.attachFormField(s.useField({disabled:i.disabled,toFormValue:e=>e,value:i.checkableFormValue}));let{assistiveId:d,checked:p,disabled:m,errorText:h,handleClick:g,handleKeydown:_,helperText:v,labelId:y}=i;return c.applyCheckableBinding(r.size,{assistiveId:d,checked:p,disabled:m,errorText:h,handleClick:g,handleKeydown:_,helperText:v,labelId:y},`switch`),f.html`
2
2
  <div class="switch-wrapper" part="switch">
3
3
  <div class="switch-track" part="track">
4
4
  <div class="switch-thumb" part="thumb"></div>
5
5
  </div>
6
6
  </div>
7
- <span class="label" part="label" id="${b}"><slot></slot></span>
8
- ${u.renderHelperRegion(p,g,y)}
7
+ <span class="label" part="label" id="${y}"><slot></slot></span>
8
+ ${u.renderHelperRegion(d,h,v)}
9
9
  `},styles:[n.colorThemeMixin,n.forcedColorsFormControlMixin,t.disabledStateMixin,e.sizeVariantMixin(i.SWITCH_SIZE_PRESET),d.default]}),exports.SWITCH_TAG=p;
10
10
  //# sourceMappingURL=switch.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"switch.cjs","names":[],"sources":["../src/inputs/switch/switch.ts"],"sourcesContent":["import { define, html, inject, prop, onCleanup, useEmit } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\n\nimport type { CheckableProps, ComponentSize, ThemeColor } from '../../types';\n\nimport { type CheckableChangePayload, lifecycleSignal, createCheckable } from '../../headless';\nimport { disablableBundle, sizableBundle, SWITCH_SIZE_PRESET, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, forcedColorsFormControlMixin, sizeVariantMixin } from '../../styles';\nimport { applyCheckableBinding } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './switch.css?inline';\n\nexport type OreSwitchEvents = {\n change: CheckableChangePayload;\n};\n\nexport type OreSwitchProps = CheckableProps & {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message (marks field as invalid) */\n error?: string;\n /** Helper text displayed below the switch */\n helper?: string;\n /** Component size */\n size?: ComponentSize;\n};\n\n/**\n * A toggle switch component for binary on/off states.\n *\n * @element ore-switch\n *\n * @attr {boolean} checked - Checked/on state\n * @attr {boolean} disabled - Disable switch interaction\n * @attr {string} value - Field value\n * @attr {string} name - Form field name\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Switch size: 'sm' | 'md' | 'lg'\n * @attr {string} error - Error message (marks field as invalid)\n * @attr {string} helper - Helper text displayed below the switch\n *\n * @fires change - Emitted when switch is toggled. detail: { checked: boolean, value: string, originalEvent?: Event }\n *\n * @slot - Switch label text\n *\n * @cssprop --switch-width - Track width\n * @cssprop --switch-height - Track height\n * @cssprop --switch-track-bg - Inactive track background color\n * @cssprop --switch-checked-bg - Active/checked track background color\n * @cssprop --switch-thumb-bg - Thumb background color\n * @cssprop --switch-font-size - Label font size\n * @part switch - The switch wrapper element\n * @part track - The switch track element\n * @part thumb - The switch thumb element\n * @part label - The label element\n * @part helper-text - The helper/error text element\n *\n * @example\n * ```html\n * <ore-switch name=\"notifications\" checked color=\"primary\">Enable notifications</ore-switch>\n * <ore-switch name=\"darkMode\" size=\"sm\">Dark mode</ore-switch>\n * <ore-switch disabled helper=\"Contact admin to change\">Admin only</ore-switch>\n * ```\n */\nexport const SWITCH_TAG = 'ore-switch' as const;\ndefine<OreSwitchProps>(SWITCH_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n checked: prop.bool(false),\n error: prop.string(),\n helper: prop.string(),\n name: prop.string(),\n value: prop.string('on'),\n },\n setup(props) {\n const emit = useEmit<OreSwitchEvents>();\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n let _formField: { reportValidity(): void } | null = null;\n const checkable = createCheckable({\n checked: props.checked,\n clearIndeterminateFirst: false,\n disabled: fCtxProps.disabled,\n error: props.error,\n getFormField: () => _formField,\n helper: props.helper,\n onToggle: (payload) => {\n checkable.triggerValidation('change');\n emit('change', payload);\n },\n prefix: 'switch',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n _formField = useField<string | null>({\n disabled: checkable.disabled,\n toFormValue: (v) => v,\n value: checkable.checkableFormValue,\n });\n\n const { assistiveId, checked, disabled, errorText, handleClick, handleKeydown, helperText, labelId } = checkable;\n\n applyCheckableBinding(\n fCtxProps.size,\n { assistiveId, checked, disabled, errorText, handleClick, handleKeydown, helperText, labelId },\n 'switch',\n );\n\n return html`\n <div class=\"switch-wrapper\" part=\"switch\">\n <div class=\"switch-track\" part=\"track\">\n <div class=\"switch-thumb\" part=\"thumb\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" id=\"${labelId}\"><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n colorThemeMixin,\n forcedColorsFormControlMixin,\n disabledStateMixin,\n sizeVariantMixin(SWITCH_SIZE_PRESET),\n componentStyles,\n ],\n});\n"],"mappings":"goBAmEA,IAAa,EAAa,0BACH,EAAY,CACjC,eAAgB,GAChB,MAAO,CACL,GAAG,EAAA,eACH,GAAG,EAAA,cACH,GAAG,EAAA,iBACH,QAAS,EAAA,KAAK,KAAK,EAAK,EACxB,MAAO,EAAA,KAAK,OAAO,EACnB,OAAQ,EAAA,KAAK,OAAO,EACpB,KAAM,EAAA,KAAK,OAAO,EAClB,MAAO,EAAA,KAAK,OAAO,IAAI,CACzB,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAA,EAAA,QAAA,CAAgC,EAEhC,GAAA,EAAA,EAAA,OAAA,CAAiB,EAAA,QAAQ,EACzB,EAAY,EAAA,eAAe,EAAO,CAAO,EAE3C,EAAgD,KAC9C,EAAY,EAAA,gBAAgB,CAChC,QAAS,EAAM,QACf,wBAAyB,GACzB,SAAU,EAAU,SACpB,MAAO,EAAM,MACb,iBAAoB,EACpB,OAAQ,EAAM,OACd,SAAW,GAAY,CACrB,EAAU,kBAAkB,QAAQ,EACpC,EAAK,SAAU,CAAO,CACxB,EACA,OAAQ,SACR,OAAQ,EAAA,gBAAgB,EAAA,SAAS,EACjC,WAAY,GAAS,WACrB,MAAO,EAAM,KACf,CAAC,EAED,EAAa,EAAA,SAAwB,CACnC,SAAU,EAAU,SACpB,YAAc,GAAM,EACpB,MAAO,EAAU,kBACnB,CAAC,EAED,GAAM,CAAE,cAAa,UAAS,WAAU,YAAW,cAAa,gBAAe,aAAY,WAAY,EAQvG,OANA,EAAA,sBACE,EAAU,KACV,CAAE,cAAa,UAAS,WAAU,YAAW,cAAa,gBAAe,aAAY,SAAQ,EAC7F,QACF,EAEO,EAAA,IAAI;;;;;;6CAM8B,EAAQ;QAC7C,EAAA,mBAAmB,EAAa,EAAW,CAAU,EAAE;KAE7D,EACA,OAAQ,CACN,EAAA,gBACA,EAAA,6BACA,EAAA,mBACA,EAAA,iBAAiB,EAAA,kBAAkB,EACnC,EAAA,OACF,CACF,CAAC"}
1
+ {"version":3,"file":"switch.cjs","names":[],"sources":["../src/inputs/switch/switch.ts"],"sourcesContent":["import { define, html, inject, prop, onCleanup, useEmit } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\n\nimport type { CheckableProps, ComponentSize, ThemeColor } from '../../types';\n\nimport { type CheckableChangePayload, lifecycleSignal, createCheckable } from '../../headless';\nimport { disablableBundle, sizableBundle, SWITCH_SIZE_PRESET, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, forcedColorsFormControlMixin, sizeVariantMixin } from '../../styles';\nimport { applyCheckableBinding } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './switch.css?inline';\n\nexport type OreSwitchEvents = {\n change: CheckableChangePayload;\n};\n\nexport type OreSwitchProps = CheckableProps & {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message (marks field as invalid) */\n error?: string;\n /** Helper text displayed below the switch */\n helper?: string;\n /** Component size */\n size?: ComponentSize;\n};\n\n/**\n * A toggle switch component for binary on/off states.\n *\n * @element ore-switch\n *\n * @attr {boolean} checked - Checked/on state\n * @attr {boolean} disabled - Disable switch interaction\n * @attr {string} value - Field value\n * @attr {string} name - Form field name\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Switch size: 'sm' | 'md' | 'lg'\n * @attr {string} error - Error message (marks field as invalid)\n * @attr {string} helper - Helper text displayed below the switch\n *\n * @fires change - Emitted when switch is toggled. detail: { checked: boolean, value: string, originalEvent?: Event }\n *\n * @slot - Switch label text\n *\n * @cssprop --switch-width - Track width\n * @cssprop --switch-height - Track height\n * @cssprop --switch-track-bg - Inactive track background color\n * @cssprop --switch-checked-bg - Active/checked track background color\n * @cssprop --switch-thumb-bg - Thumb background color\n * @cssprop --switch-font-size - Label font size\n * @part switch - The switch wrapper element\n * @part track - The switch track element\n * @part thumb - The switch thumb element\n * @part label - The label element\n * @part helper-text - The helper/error text element\n *\n * @example\n * ```html\n * <ore-switch name=\"notifications\" checked color=\"primary\">Enable notifications</ore-switch>\n * <ore-switch name=\"darkMode\" size=\"sm\">Dark mode</ore-switch>\n * <ore-switch disabled helper=\"Contact admin to change\">Admin only</ore-switch>\n * ```\n */\nexport const SWITCH_TAG = 'ore-switch' as const;\ndefine<OreSwitchProps>(SWITCH_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n checked: prop.bool(false),\n error: prop.string(),\n helper: prop.string(),\n name: prop.string(),\n value: prop.string('on'),\n },\n setup(props) {\n const emit = useEmit<OreSwitchEvents>();\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const checkable = createCheckable({\n checked: props.checked,\n clearIndeterminateFirst: false,\n disabled: fCtxProps.disabled,\n error: props.error,\n helper: props.helper,\n onToggle: (payload) => {\n checkable.triggerValidation('change');\n emit('change', payload);\n },\n prefix: 'switch',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n checkable.attachFormField(\n useField<string | null>({\n disabled: checkable.disabled,\n toFormValue: (v) => v,\n value: checkable.checkableFormValue,\n }),\n );\n\n const { assistiveId, checked, disabled, errorText, handleClick, handleKeydown, helperText, labelId } = checkable;\n\n applyCheckableBinding(\n fCtxProps.size,\n { assistiveId, checked, disabled, errorText, handleClick, handleKeydown, helperText, labelId },\n 'switch',\n );\n\n return html`\n <div class=\"switch-wrapper\" part=\"switch\">\n <div class=\"switch-track\" part=\"track\">\n <div class=\"switch-thumb\" part=\"thumb\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" id=\"${labelId}\"><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n colorThemeMixin,\n forcedColorsFormControlMixin,\n disabledStateMixin,\n sizeVariantMixin(SWITCH_SIZE_PRESET),\n componentStyles,\n ],\n});\n"],"mappings":"goBAmEA,IAAa,EAAa,0BACH,EAAY,CACjC,eAAgB,GAChB,MAAO,CACL,GAAG,EAAA,eACH,GAAG,EAAA,cACH,GAAG,EAAA,iBACH,QAAS,EAAA,KAAK,KAAK,EAAK,EACxB,MAAO,EAAA,KAAK,OAAO,EACnB,OAAQ,EAAA,KAAK,OAAO,EACpB,KAAM,EAAA,KAAK,OAAO,EAClB,MAAO,EAAA,KAAK,OAAO,IAAI,CACzB,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAA,EAAA,QAAA,CAAgC,EAEhC,GAAA,EAAA,EAAA,OAAA,CAAiB,EAAA,QAAQ,EACzB,EAAY,EAAA,eAAe,EAAO,CAAO,EAEzC,EAAY,EAAA,gBAAgB,CAChC,QAAS,EAAM,QACf,wBAAyB,GACzB,SAAU,EAAU,SACpB,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,SAAW,GAAY,CACrB,EAAU,kBAAkB,QAAQ,EACpC,EAAK,SAAU,CAAO,CACxB,EACA,OAAQ,SACR,OAAQ,EAAA,gBAAgB,EAAA,SAAS,EACjC,WAAY,GAAS,WACrB,MAAO,EAAM,KACf,CAAC,EAED,EAAU,gBACR,EAAA,SAAwB,CACtB,SAAU,EAAU,SACpB,YAAc,GAAM,EACpB,MAAO,EAAU,kBACnB,CAAC,CACH,EAEA,GAAM,CAAE,cAAa,UAAS,WAAU,YAAW,cAAa,gBAAe,aAAY,WAAY,EAQvG,OANA,EAAA,sBACE,EAAU,KACV,CAAE,cAAa,UAAS,WAAU,YAAW,cAAa,gBAAe,aAAY,SAAQ,EAC7F,QACF,EAEO,EAAA,IAAI;;;;;;6CAM8B,EAAQ;QAC7C,EAAA,mBAAmB,EAAa,EAAW,CAAU,EAAE;KAE7D,EACA,OAAQ,CACN,EAAA,gBACA,EAAA,6BACA,EAAA,mBACA,EAAA,iBAAiB,EAAA,kBAAkB,EACnC,EAAA,OACF,CACF,CAAC"}
package/dist/switch.js CHANGED
@@ -27,44 +27,43 @@ g(S, {
27
27
  value: b.string("on")
28
28
  },
29
29
  setup(e) {
30
- let t = x(), n = v(f), r = p(e, n), i = null, a = l({
30
+ let t = x(), n = v(f), r = p(e, n), i = l({
31
31
  checked: e.checked,
32
32
  clearIndeterminateFirst: !1,
33
33
  disabled: r.disabled,
34
34
  error: e.error,
35
- getFormField: () => i,
36
35
  helper: e.helper,
37
36
  onToggle: (e) => {
38
- a.triggerValidation("change"), t("change", e);
37
+ i.triggerValidation("change"), t("change", e);
39
38
  },
40
39
  prefix: "switch",
41
40
  signal: c(y),
42
41
  validateOn: n?.validateOn,
43
42
  value: e.value
44
43
  });
45
- i = u({
46
- disabled: a.disabled,
44
+ i.attachFormField(u({
45
+ disabled: i.disabled,
47
46
  toFormValue: (e) => e,
48
- value: a.checkableFormValue
49
- });
50
- let { assistiveId: o, checked: s, disabled: h, errorText: g, handleClick: b, handleKeydown: S, helperText: C, labelId: w } = a;
47
+ value: i.checkableFormValue
48
+ }));
49
+ let { assistiveId: a, checked: o, disabled: s, errorText: h, handleClick: g, handleKeydown: b, helperText: S, labelId: C } = i;
51
50
  return d(r.size, {
52
- assistiveId: o,
53
- checked: s,
54
- disabled: h,
55
- errorText: g,
56
- handleClick: b,
57
- handleKeydown: S,
58
- helperText: C,
59
- labelId: w
51
+ assistiveId: a,
52
+ checked: o,
53
+ disabled: s,
54
+ errorText: h,
55
+ handleClick: g,
56
+ handleKeydown: b,
57
+ helperText: S,
58
+ labelId: C
60
59
  }, "switch"), _`
61
60
  <div class="switch-wrapper" part="switch">
62
61
  <div class="switch-track" part="track">
63
62
  <div class="switch-thumb" part="thumb"></div>
64
63
  </div>
65
64
  </div>
66
- <span class="label" part="label" id="${w}"><slot></slot></span>
67
- ${m(o, g, C)}
65
+ <span class="label" part="label" id="${C}"><slot></slot></span>
66
+ ${m(a, h, S)}
68
67
  `;
69
68
  },
70
69
  styles: [
@@ -1 +1 @@
1
- {"version":3,"file":"switch.js","names":[],"sources":["../src/inputs/switch/switch.ts"],"sourcesContent":["import { define, html, inject, prop, onCleanup, useEmit } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\n\nimport type { CheckableProps, ComponentSize, ThemeColor } from '../../types';\n\nimport { type CheckableChangePayload, lifecycleSignal, createCheckable } from '../../headless';\nimport { disablableBundle, sizableBundle, SWITCH_SIZE_PRESET, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, forcedColorsFormControlMixin, sizeVariantMixin } from '../../styles';\nimport { applyCheckableBinding } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './switch.css?inline';\n\nexport type OreSwitchEvents = {\n change: CheckableChangePayload;\n};\n\nexport type OreSwitchProps = CheckableProps & {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message (marks field as invalid) */\n error?: string;\n /** Helper text displayed below the switch */\n helper?: string;\n /** Component size */\n size?: ComponentSize;\n};\n\n/**\n * A toggle switch component for binary on/off states.\n *\n * @element ore-switch\n *\n * @attr {boolean} checked - Checked/on state\n * @attr {boolean} disabled - Disable switch interaction\n * @attr {string} value - Field value\n * @attr {string} name - Form field name\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Switch size: 'sm' | 'md' | 'lg'\n * @attr {string} error - Error message (marks field as invalid)\n * @attr {string} helper - Helper text displayed below the switch\n *\n * @fires change - Emitted when switch is toggled. detail: { checked: boolean, value: string, originalEvent?: Event }\n *\n * @slot - Switch label text\n *\n * @cssprop --switch-width - Track width\n * @cssprop --switch-height - Track height\n * @cssprop --switch-track-bg - Inactive track background color\n * @cssprop --switch-checked-bg - Active/checked track background color\n * @cssprop --switch-thumb-bg - Thumb background color\n * @cssprop --switch-font-size - Label font size\n * @part switch - The switch wrapper element\n * @part track - The switch track element\n * @part thumb - The switch thumb element\n * @part label - The label element\n * @part helper-text - The helper/error text element\n *\n * @example\n * ```html\n * <ore-switch name=\"notifications\" checked color=\"primary\">Enable notifications</ore-switch>\n * <ore-switch name=\"darkMode\" size=\"sm\">Dark mode</ore-switch>\n * <ore-switch disabled helper=\"Contact admin to change\">Admin only</ore-switch>\n * ```\n */\nexport const SWITCH_TAG = 'ore-switch' as const;\ndefine<OreSwitchProps>(SWITCH_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n checked: prop.bool(false),\n error: prop.string(),\n helper: prop.string(),\n name: prop.string(),\n value: prop.string('on'),\n },\n setup(props) {\n const emit = useEmit<OreSwitchEvents>();\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n let _formField: { reportValidity(): void } | null = null;\n const checkable = createCheckable({\n checked: props.checked,\n clearIndeterminateFirst: false,\n disabled: fCtxProps.disabled,\n error: props.error,\n getFormField: () => _formField,\n helper: props.helper,\n onToggle: (payload) => {\n checkable.triggerValidation('change');\n emit('change', payload);\n },\n prefix: 'switch',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n _formField = useField<string | null>({\n disabled: checkable.disabled,\n toFormValue: (v) => v,\n value: checkable.checkableFormValue,\n });\n\n const { assistiveId, checked, disabled, errorText, handleClick, handleKeydown, helperText, labelId } = checkable;\n\n applyCheckableBinding(\n fCtxProps.size,\n { assistiveId, checked, disabled, errorText, handleClick, handleKeydown, helperText, labelId },\n 'switch',\n );\n\n return html`\n <div class=\"switch-wrapper\" part=\"switch\">\n <div class=\"switch-track\" part=\"track\">\n <div class=\"switch-thumb\" part=\"thumb\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" id=\"${labelId}\"><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n colorThemeMixin,\n forcedColorsFormControlMixin,\n disabledStateMixin,\n sizeVariantMixin(SWITCH_SIZE_PRESET),\n componentStyles,\n ],\n});\n"],"mappings":";;;;;;;;;;;;;;;AAmEA,IAAa,IAAa;AAC1B,EAAuB,GAAY;CACjC,gBAAgB;CAChB,OAAO;EACL,GAAG;EACH,GAAG;EACH,GAAG;EACH,SAAS,EAAK,KAAK,EAAK;EACxB,OAAO,EAAK,OAAO;EACnB,QAAQ,EAAK,OAAO;EACpB,MAAM,EAAK,OAAO;EAClB,OAAO,EAAK,OAAO,IAAI;CACzB;CACA,MAAM,GAAO;EACX,IAAM,IAAO,EAAyB,GAEhC,IAAU,EAAO,CAAQ,GACzB,IAAY,EAAe,GAAO,CAAO,GAE3C,IAAgD,MAC9C,IAAY,EAAgB;GAChC,SAAS,EAAM;GACf,yBAAyB;GACzB,UAAU,EAAU;GACpB,OAAO,EAAM;GACb,oBAAoB;GACpB,QAAQ,EAAM;GACd,WAAW,MAAY;IAErB,AADA,EAAU,kBAAkB,QAAQ,GACpC,EAAK,UAAU,CAAO;GACxB;GACA,QAAQ;GACR,QAAQ,EAAgB,CAAS;GACjC,YAAY,GAAS;GACrB,OAAO,EAAM;EACf,CAAC;EAED,IAAa,EAAwB;GACnC,UAAU,EAAU;GACpB,cAAc,MAAM;GACpB,OAAO,EAAU;EACnB,CAAC;EAED,IAAM,EAAE,gBAAa,YAAS,aAAU,cAAW,gBAAa,kBAAe,eAAY,eAAY;EAQvG,OANA,EACE,EAAU,MACV;GAAE;GAAa;GAAS;GAAU;GAAW;GAAa;GAAe;GAAY;EAAQ,GAC7F,QACF,GAEO,CAAI;;;;;;6CAM8B,EAAQ;QAC7C,EAAmB,GAAa,GAAW,CAAU,EAAE;;CAE7D;CACA,QAAQ;EACN;EACA;EACA;EACA,EAAiB,CAAkB;EACnC;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"switch.js","names":[],"sources":["../src/inputs/switch/switch.ts"],"sourcesContent":["import { define, html, inject, prop, onCleanup, useEmit } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\n\nimport type { CheckableProps, ComponentSize, ThemeColor } from '../../types';\n\nimport { type CheckableChangePayload, lifecycleSignal, createCheckable } from '../../headless';\nimport { disablableBundle, sizableBundle, SWITCH_SIZE_PRESET, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, forcedColorsFormControlMixin, sizeVariantMixin } from '../../styles';\nimport { applyCheckableBinding } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './switch.css?inline';\n\nexport type OreSwitchEvents = {\n change: CheckableChangePayload;\n};\n\nexport type OreSwitchProps = CheckableProps & {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message (marks field as invalid) */\n error?: string;\n /** Helper text displayed below the switch */\n helper?: string;\n /** Component size */\n size?: ComponentSize;\n};\n\n/**\n * A toggle switch component for binary on/off states.\n *\n * @element ore-switch\n *\n * @attr {boolean} checked - Checked/on state\n * @attr {boolean} disabled - Disable switch interaction\n * @attr {string} value - Field value\n * @attr {string} name - Form field name\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Switch size: 'sm' | 'md' | 'lg'\n * @attr {string} error - Error message (marks field as invalid)\n * @attr {string} helper - Helper text displayed below the switch\n *\n * @fires change - Emitted when switch is toggled. detail: { checked: boolean, value: string, originalEvent?: Event }\n *\n * @slot - Switch label text\n *\n * @cssprop --switch-width - Track width\n * @cssprop --switch-height - Track height\n * @cssprop --switch-track-bg - Inactive track background color\n * @cssprop --switch-checked-bg - Active/checked track background color\n * @cssprop --switch-thumb-bg - Thumb background color\n * @cssprop --switch-font-size - Label font size\n * @part switch - The switch wrapper element\n * @part track - The switch track element\n * @part thumb - The switch thumb element\n * @part label - The label element\n * @part helper-text - The helper/error text element\n *\n * @example\n * ```html\n * <ore-switch name=\"notifications\" checked color=\"primary\">Enable notifications</ore-switch>\n * <ore-switch name=\"darkMode\" size=\"sm\">Dark mode</ore-switch>\n * <ore-switch disabled helper=\"Contact admin to change\">Admin only</ore-switch>\n * ```\n */\nexport const SWITCH_TAG = 'ore-switch' as const;\ndefine<OreSwitchProps>(SWITCH_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n checked: prop.bool(false),\n error: prop.string(),\n helper: prop.string(),\n name: prop.string(),\n value: prop.string('on'),\n },\n setup(props) {\n const emit = useEmit<OreSwitchEvents>();\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const checkable = createCheckable({\n checked: props.checked,\n clearIndeterminateFirst: false,\n disabled: fCtxProps.disabled,\n error: props.error,\n helper: props.helper,\n onToggle: (payload) => {\n checkable.triggerValidation('change');\n emit('change', payload);\n },\n prefix: 'switch',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n checkable.attachFormField(\n useField<string | null>({\n disabled: checkable.disabled,\n toFormValue: (v) => v,\n value: checkable.checkableFormValue,\n }),\n );\n\n const { assistiveId, checked, disabled, errorText, handleClick, handleKeydown, helperText, labelId } = checkable;\n\n applyCheckableBinding(\n fCtxProps.size,\n { assistiveId, checked, disabled, errorText, handleClick, handleKeydown, helperText, labelId },\n 'switch',\n );\n\n return html`\n <div class=\"switch-wrapper\" part=\"switch\">\n <div class=\"switch-track\" part=\"track\">\n <div class=\"switch-thumb\" part=\"thumb\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" id=\"${labelId}\"><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n colorThemeMixin,\n forcedColorsFormControlMixin,\n disabledStateMixin,\n sizeVariantMixin(SWITCH_SIZE_PRESET),\n componentStyles,\n ],\n});\n"],"mappings":";;;;;;;;;;;;;;;AAmEA,IAAa,IAAa;AAC1B,EAAuB,GAAY;CACjC,gBAAgB;CAChB,OAAO;EACL,GAAG;EACH,GAAG;EACH,GAAG;EACH,SAAS,EAAK,KAAK,EAAK;EACxB,OAAO,EAAK,OAAO;EACnB,QAAQ,EAAK,OAAO;EACpB,MAAM,EAAK,OAAO;EAClB,OAAO,EAAK,OAAO,IAAI;CACzB;CACA,MAAM,GAAO;EACX,IAAM,IAAO,EAAyB,GAEhC,IAAU,EAAO,CAAQ,GACzB,IAAY,EAAe,GAAO,CAAO,GAEzC,IAAY,EAAgB;GAChC,SAAS,EAAM;GACf,yBAAyB;GACzB,UAAU,EAAU;GACpB,OAAO,EAAM;GACb,QAAQ,EAAM;GACd,WAAW,MAAY;IAErB,AADA,EAAU,kBAAkB,QAAQ,GACpC,EAAK,UAAU,CAAO;GACxB;GACA,QAAQ;GACR,QAAQ,EAAgB,CAAS;GACjC,YAAY,GAAS;GACrB,OAAO,EAAM;EACf,CAAC;EAED,EAAU,gBACR,EAAwB;GACtB,UAAU,EAAU;GACpB,cAAc,MAAM;GACpB,OAAO,EAAU;EACnB,CAAC,CACH;EAEA,IAAM,EAAE,gBAAa,YAAS,aAAU,cAAW,gBAAa,kBAAe,eAAY,eAAY;EAQvG,OANA,EACE,EAAU,MACV;GAAE;GAAa;GAAS;GAAU;GAAW;GAAa;GAAe;GAAY;EAAQ,GAC7F,QACF,GAEO,CAAI;;;;;;6CAM8B,EAAQ;QAC7C,EAAmB,GAAa,GAAW,CAAU,EAAE;;CAE7D;CACA,QAAQ;EACN;EACA;EACA;EACA,EAAiB,CAAkB;EACnC;CACF;AACF,CAAC"}
package/dist/textarea.cjs CHANGED
@@ -1,13 +1,13 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./ore/dist/directives/live.cjs");require("./ore/dist/directives.cjs");const t=require("./styles/mixins/shape.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./styles/index.cjs"),i=require("./shared/prop-bundles.cjs"),a=require("./shared/size-presets.cjs"),o=require("./headless/ore.cjs"),s=require("./headless/text-field.cjs"),c=require("./ore/dist/forms/field.cjs");require("./ore/dist/forms.cjs");const l=require("./inputs/shared/form-context.cjs"),u=require("./inputs/textarea/textarea.css.cjs");let d=require("@vielzeug/ore"),f=require("@vielzeug/ripple");var p=`ore-textarea`;(0,d.define)(p,{formAssociated:!0,props:{...i.themableBundle,...i.sizableBundle,...i.disablableBundle,...i.roundableBundle,"auto-resize":d.prop.bool(!1),error:d.prop.string(),fullwidth:d.prop.bool(!1),helper:d.prop.string(),label:d.prop.string(),"label-placement":d.prop.oneOf([`inset`,`outside`],`inset`),maxlength:d.prop.json(void 0),name:d.prop.string(),"no-resize":d.prop.bool(!1),placeholder:d.prop.string(),readonly:d.prop.bool(!1),ref:d.prop.json(void 0),required:d.prop.bool(!1),resize:d.prop.string(),rows:d.prop.json(void 0),value:d.prop.string(),variant:d.prop.string()},setup(t){let n=(0,d.useEmit)(),r=d.watchEffect,i=(0,d.inject)(l.FORM_CTX),a=l.useFormContext(t,i),u=(0,d.ref)(),p=()=>{if(!t[`auto-resize`].value||!u.value)return;let e=u.value;e.style.height=`auto`,e.style.height=`${e.scrollHeight}px`},m=o.lifecycleSignal(d.onCleanup),h=null,g=s.createTextField({disabled:a.disabled,error:t.error,getFormField:()=>h,helper:t.helper,label:t.label,labelPlacement:t[`label-placement`],maxLength:t.maxlength,onBeforeInput:p,onChange:(e,t)=>{n(`change`,{originalEvent:e,value:t})},onInput:(e,t)=>{n(`input`,{originalEvent:e,value:t})},prefix:`textarea`,signal:m,validateOn:i?.validateOn,value:t.value});h=c.useField({disabled:g.disabled,toFormValue:e=>e,value:g.value});let{ariaDescribedBy:_,ariaErrorMessage:v,ariaInvalid:y,ariaLabelledBy:b,assistiveId:x,counter:S,errorId:C,errorText:w,fieldId:T,helperText:E,labelId:D,labelVisible:O}=g;return(0,d.onElement)(u,e=>{let n=g.wire(e);t.ref.value?.(e);let i=(0,f.watch)(t.ref,t=>{t?.(e)}),a=r(()=>{e.style.resize=t[`auto-resize`].value||t[`no-resize`].value?`none`:t.resize.value||`vertical`,t[`auto-resize`].value&&requestAnimationFrame(p)});return()=>{i.dispose(),t.ref.value?.(null),n(),a()}}),(0,d.bind)({attr:{error:()=>w.value||void 0,size:a.size,variant:a.variant}}),d.html`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./ore/dist/directives/live.cjs");require("./ore/dist/directives.cjs");const t=require("./styles/mixins/shape.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./styles/index.cjs"),i=require("./shared/prop-bundles.cjs"),a=require("./shared/size-presets.cjs"),o=require("./headless/ore.cjs"),s=require("./headless/auto-resize.cjs"),c=require("./headless/field-base.cjs"),l=require("./headless/text-field.cjs"),u=require("./ore/dist/forms/field.cjs");require("./ore/dist/forms.cjs");const d=require("./inputs/shared/field-binding.cjs"),f=require("./inputs/shared/form-context.cjs"),p=require("./inputs/textarea/textarea.css.cjs");let m=require("@vielzeug/ore"),h=require("@vielzeug/ripple");var g=`ore-textarea`;(0,m.define)(g,{formAssociated:!0,props:{...i.themableBundle,...i.sizableBundle,...i.disablableBundle,...i.roundableBundle,"auto-resize":m.prop.bool(!1),error:m.prop.string(),fullwidth:m.prop.bool(!1),helper:m.prop.string(),label:m.prop.string(),"label-placement":m.prop.oneOf([`inset`,`outside`],`inset`),maxlength:m.prop.json(void 0),name:m.prop.string(),"no-resize":m.prop.bool(!1),placeholder:m.prop.string(),readonly:m.prop.bool(!1),ref:m.prop.json(void 0),required:m.prop.bool(!1),resize:m.prop.string(),rows:m.prop.json(void 0),value:m.prop.string(),variant:m.prop.string()},setup(t){let n=(0,m.useEmit)(),r=m.watchEffect,i=(0,m.inject)(f.FORM_CTX),a=f.useFormContext(t,i),p=(0,m.ref)(),g=s.createAutoResize({enabled:t[`auto-resize`]}),_=o.lifecycleSignal(m.onCleanup),v=l.createTextField({disabled:a.disabled,error:t.error,helper:t.helper,label:t.label,labelPlacement:t[`label-placement`],maxLength:t.maxlength,onChange:(e,t)=>{n(`change`,{originalEvent:e,value:t})},onInput:(e,t)=>{n(`input`,{originalEvent:e,value:t})},prefix:`textarea`,readonly:t.readonly,required:t.required,signal:_,validateOn:i?.validateOn,value:t.value});v.attachFormField(u.useField({disabled:v.disabled,onReset:()=>{v.reset(),requestAnimationFrame(()=>g.recompute())},toFormValue:e=>e,validationMessage:v.validationMessage,validity:v.validity,value:v.value}));let{ariaDescribedBy:y,ariaErrorMessage:b,ariaInvalid:x,ariaLabelledBy:S,assistiveId:C,counter:w,errorId:T,errorText:E,fieldId:D,helperText:O,labelId:k,labelVisible:A}=v;return(0,m.onElement)(p,e=>{let n=v.wire(e),i=g.wire(e);t.ref.value?.(e);let a=(0,h.watch)(t.ref,t=>{t?.(e)}),o=r(()=>{e.style.resize=t[`auto-resize`].value||t[`no-resize`].value?`none`:t.resize.value||`vertical`,requestAnimationFrame(()=>g.recompute())});return()=>{a.dispose(),t.ref.value?.(null),n(),i(),o()}}),(0,m.bind)({attr:{error:d.errorAttr(E),size:a.size,variant:a.variant}}),m.html`
2
2
  <div class="textarea-wrapper" part="wrapper">
3
- <label class="label" part="label" for="${T}" id="${D}" ?hidden="${()=>!O.value}"
3
+ <label class="label" part="label" for="${D}" id="${k}" ?hidden="${()=>!A.value}"
4
4
  >${t.label}</label
5
5
  >
6
6
  <div class="field" part="field">
7
7
  <textarea
8
8
  part="textarea"
9
- ref="${u}"
10
- id="${T}"
9
+ ref="${p}"
10
+ id="${D}"
11
11
  :name="${t.name}"
12
12
  :placeholder="${t.placeholder}"
13
13
  :rows="${t.rows}"
@@ -15,19 +15,19 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
15
15
  ?disabled="${t.disabled}"
16
16
  ?readonly="${t.readonly}"
17
17
  ?required="${t.required}"
18
- :value="${e.live(g.value)}"
19
- :aria-describedby="${_}"
20
- :aria-errormessage="${v}"
21
- :aria-invalid="${y}"
22
- :aria-labelledby="${b}"></textarea>
18
+ :value="${e.live(v.value)}"
19
+ :aria-describedby="${y}"
20
+ :aria-errormessage="${b}"
21
+ :aria-invalid="${x}"
22
+ :aria-labelledby="${S}"></textarea>
23
23
  </div>
24
- <span class="${()=>S?.value.counterAtLimit?`counter at-limit`:S?.value.counterNearLimit?`counter near-limit`:`counter`}" aria-live="polite" ?hidden="${()=>!S}">${()=>S?.value.counterText.replace(` / `,`/`)??``}</span>
25
- <div id="${x}" class="helper-text" aria-live="polite" part="helper" ?hidden="${()=>!!w.value||!E.value}">
26
- ${()=>E.value}
24
+ <span class="${()=>c.counterClassName(w?.value)}" aria-live="polite" ?hidden="${()=>!w}">${()=>w?.value.counterText.replace(` / `,`/`)??``}</span>
25
+ <div id="${C}" class="helper-text" aria-live="polite" part="helper" ?hidden="${()=>!!E.value||!O.value}">
26
+ ${()=>O.value}
27
27
  </div>
28
- <div id="${C}" class="helper-text" role="alert" part="error" ?hidden="${()=>!w.value}">
29
- ${()=>w.value}
28
+ <div id="${T}" class="helper-text" role="alert" part="error" ?hidden="${()=>!E.value}">
29
+ ${()=>E.value}
30
30
  </div>
31
31
  </div>
32
- `},shadow:{delegatesFocus:!0},styles:[...r.fieldMixins,t.sizeVariantMixin(a.TEXTAREA_SIZE_PRESET),n.forcedColorsFocusMixin(`textarea`),u.default]}),exports.TEXTAREA_TAG=p;
32
+ `},shadow:{delegatesFocus:!0},styles:[...r.fieldMixins,t.sizeVariantMixin(a.TEXTAREA_SIZE_PRESET),n.forcedColorsFocusMixin(`textarea`),p.default]}),exports.TEXTAREA_TAG=g;
33
33
  //# sourceMappingURL=textarea.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.cjs","names":[],"sources":["../src/inputs/textarea/textarea.ts"],"sourcesContent":["import { define, html, inject, prop, ref, bind, onCleanup, onElement, useEmit, watchEffect } from '@vielzeug/ore';\nimport { live } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { watch as rippleWatch } from '@vielzeug/ripple';\n\nimport type { TextFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\n\nimport { lifecycleSignal, createTextField } from '../../headless';\nimport { disablableBundle, roundableBundle, sizableBundle, TEXTAREA_SIZE_PRESET, themableBundle } from '../../shared';\nimport { fieldMixins, forcedColorsFocusMixin, sizeVariantMixin } from '../../styles';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './textarea.css?inline';\n\n/** Textarea component properties */\n\nexport type OreTextareaEvents = {\n change: { originalEvent: Event; value: string };\n input: { originalEvent: Event; value: string };\n};\n\nexport type OreTextareaProps = TextFieldProps<Exclude<VisualVariant, 'frost' | 'text'>> & {\n /** Allow auto-grow with content */\n 'auto-resize'?: boolean;\n /** Maximum character count; shows a counter when set */\n maxlength?: number;\n /** Disable a manual resize handle */\n 'no-resize'?: boolean;\n /**\n * JS-only callback fired with the inner `<textarea>` element when it mounts,\n * and with `null` when it unmounts. Intended for composed components that\n * need imperative access to the raw element.\n * Set as a JS property: `bitTextarea.ref = (el) => { ... }`.\n */\n ref?: ((el: HTMLTextAreaElement | null) => void) | null;\n /** Resize direction override */\n resize?: 'none' | 'horizontal' | 'both' | 'vertical';\n /** Number of visible text rows */\n rows?: number;\n};\n\n/**\n * A multi-line text input with label, helper text, character counter, and auto-resize.\n *\n * @element ore-textarea\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current value\n * @attr {string} placeholder - Placeholder text\n * @attr {string} name - Form field name\n * @attr {number} rows - Visible row count\n * @attr {number} maxlength - Max character count (shows counter)\n * @attr {string} helper - Helper text below the textarea\n * @attr {string} error - Error message\n * @attr {boolean} disabled - Disable interaction\n * @attr {boolean} readonly - Read-only mode\n * @attr {boolean} required - Required field\n * @attr {boolean} no-resize - Disable manual resize\n * @attr {boolean} auto-resize - Grow with content\n * @attr {string} resize - Resize direction: 'none' | 'horizontal' | 'both' | 'vertical'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires input - Fired on every keystroke with current value. detail: { value: string; originalEvent: Event }\n * @fires change - Fired on blur with changed value. detail: { value: string; originalEvent: Event }\n *\n * @slot helper - Complex helper content\n *\n * @cssprop --textarea-bg - Background color\n * @cssprop --textarea-border-color - Border color\n * @cssprop --textarea-placeholder-color - Placeholder text color\n * @cssprop --textarea-radius - Border radius\n * @cssprop --textarea-padding - Inner padding (block inline)\n * @cssprop --textarea-gap - Gap between label and field\n * @cssprop --textarea-font-size - Font size\n * @cssprop --textarea-min-height - Minimum field height\n * @cssprop --textarea-max-height - Maximum field height (none = unlimited)\n * @cssprop --textarea-resize - CSS resize direction ('vertical' | 'horizontal' | 'both' | 'none')\n * @cssprop --textarea-hover-bg - Field background on hover (flat/ghost variants)\n * @cssprop --textarea-hover-border-color - Field border on hover (flat/bordered variants)\n * @cssprop --textarea-focus-bg - Field background when focused (flat variant)\n * @cssprop --textarea-focus-border-color - Field border when focused (flat variant)\n *\n * @part wrapper - Outer wrapper element.\n * @part label - Label element.\n * @part field - Field container.\n * @part textarea - The native `<textarea>` element.\n * @part helper - The helper text element.\n * @part error - The error text element (`role=\"alert\"`).\n *\n * @example\n * ```html\n * <ore-textarea></ore-textarea>\n * ```\n */\nexport const TEXTAREA_TAG = 'ore-textarea' as const;\ndefine<OreTextareaProps>(TEXTAREA_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n 'auto-resize': prop.bool(false),\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n maxlength: prop.json(undefined as number | undefined),\n name: prop.string(),\n 'no-resize': prop.bool(false),\n placeholder: prop.string(),\n readonly: prop.bool(false),\n ref: prop.json(undefined as ((el: HTMLTextAreaElement | null) => void) | null | undefined),\n required: prop.bool(false),\n resize: prop.string<'none' | 'both' | 'horizontal' | 'vertical'>(),\n rows: prop.json(undefined as number | undefined),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n setup(props) {\n const emit = useEmit<OreTextareaEvents>();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const textareaRef = ref<HTMLTextAreaElement>();\n\n const autoGrow = () => {\n if (!props['auto-resize'].value || !textareaRef.value) return;\n\n const textareaEl = textareaRef.value;\n\n textareaEl.style.height = 'auto';\n textareaEl.style.height = `${textareaEl.scrollHeight}px`;\n };\n\n const abortSignal = lifecycleSignal(onCleanup);\n let _formField: { reportValidity(): void } | null = null;\n const tf = createTextField({\n disabled: fCtxProps.disabled,\n error: props.error,\n getFormField: () => _formField,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n maxLength: props.maxlength,\n onBeforeInput: autoGrow,\n onChange: (event: Event, value: string) => {\n emit('change', { originalEvent: event, value });\n },\n onInput: (event: Event, value: string) => {\n emit('input', { originalEvent: event, value });\n },\n prefix: 'textarea',\n signal: abortSignal,\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n _formField = useField<string>({ disabled: tf.disabled, toFormValue: (v) => v, value: tf.value });\n\n const {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n ariaLabelledBy,\n assistiveId,\n counter,\n errorId,\n errorText,\n fieldId: textareaId,\n helperText,\n labelId,\n labelVisible,\n } = tf;\n\n onElement(textareaRef, (textareaEl) => {\n const unwireEl = tf.wire(textareaEl);\n\n props.ref.value?.(textareaEl);\n\n const sub = rippleWatch(props.ref, (cb) => {\n cb?.(textareaEl);\n });\n\n const stopLayoutEffect = watch(() => {\n textareaEl.style.resize =\n props['auto-resize'].value || props['no-resize'].value ? 'none' : props.resize.value || 'vertical';\n\n if (props['auto-resize'].value) {\n requestAnimationFrame(autoGrow);\n }\n });\n\n return () => {\n sub.dispose();\n props.ref.value?.(null);\n unwireEl();\n stopLayoutEffect();\n };\n });\n\n bind({\n attr: {\n error: () => errorText.value || undefined,\n size: fCtxProps.size,\n variant: fCtxProps.variant,\n },\n });\n\n const counterClass = () =>\n counter?.value.counterAtLimit\n ? 'counter at-limit'\n : counter?.value.counterNearLimit\n ? 'counter near-limit'\n : 'counter';\n const counterHidden = () => !counter;\n const counterText = () => counter?.value.counterText.replace(' / ', '/') ?? '';\n const helperHidden = () => !!errorText.value || !helperText.value;\n const errorHidden = () => !errorText.value;\n\n return html`\n <div class=\"textarea-wrapper\" part=\"wrapper\">\n <label class=\"label\" part=\"label\" for=\"${textareaId}\" id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\"\n >${props.label}</label\n >\n <div class=\"field\" part=\"field\">\n <textarea\n part=\"textarea\"\n ref=\"${textareaRef}\"\n id=\"${textareaId}\"\n :name=\"${props.name}\"\n :placeholder=\"${props.placeholder}\"\n :rows=\"${props.rows}\"\n :maxlength=\"${props.maxlength}\"\n ?disabled=\"${props.disabled}\"\n ?readonly=\"${props.readonly}\"\n ?required=\"${props.required}\"\n :value=\"${live(tf.value)}\"\n :aria-describedby=\"${ariaDescribedBy}\"\n :aria-errormessage=\"${ariaErrorMessage}\"\n :aria-invalid=\"${ariaInvalid}\"\n :aria-labelledby=\"${ariaLabelledBy}\"></textarea>\n </div>\n <span class=\"${counterClass}\" aria-live=\"polite\" ?hidden=\"${counterHidden}\">${counterText}</span>\n <div id=\"${assistiveId}\" class=\"helper-text\" aria-live=\"polite\" part=\"helper\" ?hidden=\"${helperHidden}\">\n ${() => helperText.value}\n </div>\n <div id=\"${errorId}\" class=\"helper-text\" role=\"alert\" part=\"error\" ?hidden=\"${errorHidden}\">\n ${() => errorText.value}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [...fieldMixins, sizeVariantMixin(TEXTAREA_SIZE_PRESET), forcedColorsFocusMixin('textarea'), componentStyles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"textarea.cjs","names":[],"sources":["../src/inputs/textarea/textarea.ts"],"sourcesContent":["import { define, html, inject, prop, ref, bind, onCleanup, onElement, useEmit, watchEffect } from '@vielzeug/ore';\nimport { live } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { watch as rippleWatch } from '@vielzeug/ripple';\n\nimport type { TextFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\n\nimport { counterClassName, createAutoResize, lifecycleSignal, createTextField } from '../../headless';\nimport { disablableBundle, roundableBundle, sizableBundle, TEXTAREA_SIZE_PRESET, themableBundle } from '../../shared';\nimport { fieldMixins, forcedColorsFocusMixin, sizeVariantMixin } from '../../styles';\nimport { errorAttr } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './textarea.css?inline';\n\n/** Textarea component properties */\n\nexport type OreTextareaEvents = {\n change: { originalEvent: Event; value: string };\n input: { originalEvent: Event; value: string };\n};\n\nexport type OreTextareaProps = TextFieldProps<Exclude<VisualVariant, 'frost' | 'text'>> & {\n /** Allow auto-grow with content */\n 'auto-resize'?: boolean;\n /** Maximum character count; shows a counter when set */\n maxlength?: number;\n /** Disable a manual resize handle */\n 'no-resize'?: boolean;\n /**\n * JS-only callback fired with the inner `<textarea>` element when it mounts,\n * and with `null` when it unmounts. Intended for composed components that\n * need imperative access to the raw element.\n * Set as a JS property: `bitTextarea.ref = (el) => { ... }`.\n */\n ref?: ((el: HTMLTextAreaElement | null) => void) | null;\n /** Resize direction override */\n resize?: 'none' | 'horizontal' | 'both' | 'vertical';\n /** Number of visible text rows */\n rows?: number;\n};\n\n/**\n * A multi-line text input with label, helper text, character counter, and auto-resize.\n *\n * @element ore-textarea\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current value\n * @attr {string} placeholder - Placeholder text\n * @attr {string} name - Form field name\n * @attr {number} rows - Visible row count\n * @attr {number} maxlength - Max character count (shows counter)\n * @attr {string} helper - Helper text below the textarea\n * @attr {string} error - Error message\n * @attr {boolean} disabled - Disable interaction\n * @attr {boolean} readonly - Read-only mode\n * @attr {boolean} required - Required field\n * @attr {boolean} no-resize - Disable manual resize\n * @attr {boolean} auto-resize - Grow with content\n * @attr {string} resize - Resize direction: 'none' | 'horizontal' | 'both' | 'vertical'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires input - Fired on every keystroke with current value. detail: { value: string; originalEvent: Event }\n * @fires change - Fired on blur with changed value. detail: { value: string; originalEvent: Event }\n *\n * @slot helper - Complex helper content\n *\n * @cssprop --textarea-bg - Background color\n * @cssprop --textarea-border-color - Border color\n * @cssprop --textarea-placeholder-color - Placeholder text color\n * @cssprop --textarea-radius - Border radius\n * @cssprop --textarea-padding - Inner padding (block inline)\n * @cssprop --textarea-gap - Gap between label and field\n * @cssprop --textarea-font-size - Font size\n * @cssprop --textarea-min-height - Minimum field height\n * @cssprop --textarea-max-height - Maximum field height (none = unlimited)\n * @cssprop --textarea-resize - CSS resize direction ('vertical' | 'horizontal' | 'both' | 'none')\n * @cssprop --textarea-hover-bg - Field background on hover (flat/ghost variants)\n * @cssprop --textarea-hover-border-color - Field border on hover (flat/bordered variants)\n * @cssprop --textarea-focus-bg - Field background when focused (flat variant)\n * @cssprop --textarea-focus-border-color - Field border when focused (flat variant)\n *\n * @part wrapper - Outer wrapper element.\n * @part label - Label element.\n * @part field - Field container.\n * @part textarea - The native `<textarea>` element.\n * @part helper - The helper text element.\n * @part error - The error text element (`role=\"alert\"`).\n *\n * @example\n * ```html\n * <ore-textarea></ore-textarea>\n * ```\n */\nexport const TEXTAREA_TAG = 'ore-textarea' as const;\ndefine<OreTextareaProps>(TEXTAREA_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n 'auto-resize': prop.bool(false),\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n maxlength: prop.json(undefined as number | undefined),\n name: prop.string(),\n 'no-resize': prop.bool(false),\n placeholder: prop.string(),\n readonly: prop.bool(false),\n ref: prop.json(undefined as ((el: HTMLTextAreaElement | null) => void) | null | undefined),\n required: prop.bool(false),\n resize: prop.string<'none' | 'both' | 'horizontal' | 'vertical'>(),\n rows: prop.json(undefined as number | undefined),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n setup(props) {\n const emit = useEmit<OreTextareaEvents>();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const textareaRef = ref<HTMLTextAreaElement>();\n const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n\n const abortSignal = lifecycleSignal(onCleanup);\n const tf = createTextField({\n disabled: fCtxProps.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n maxLength: props.maxlength,\n onChange: (event: Event, value: string) => {\n emit('change', { originalEvent: event, value });\n },\n onInput: (event: Event, value: string) => {\n emit('input', { originalEvent: event, value });\n },\n prefix: 'textarea',\n readonly: props.readonly,\n required: props.required,\n signal: abortSignal,\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n tf.attachFormField(\n useField<string>({\n disabled: tf.disabled,\n onReset: () => {\n tf.reset();\n requestAnimationFrame(() => autoResize.recompute());\n },\n toFormValue: (v) => v,\n validationMessage: tf.validationMessage,\n validity: tf.validity,\n value: tf.value,\n }),\n );\n\n const {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n ariaLabelledBy,\n assistiveId,\n counter,\n errorId,\n errorText,\n fieldId: textareaId,\n helperText,\n labelId,\n labelVisible,\n } = tf;\n\n onElement(textareaRef, (textareaEl) => {\n const unwireEl = tf.wire(textareaEl);\n const unwireAutoResize = autoResize.wire(textareaEl);\n\n props.ref.value?.(textareaEl);\n\n const sub = rippleWatch(props.ref, (cb) => {\n cb?.(textareaEl);\n });\n\n const stopLayoutEffect = watch(() => {\n textareaEl.style.resize =\n props['auto-resize'].value || props['no-resize'].value ? 'none' : props.resize.value || 'vertical';\n\n // Deferred a frame: on mount (or right after `auto-resize` flips true) the browser\n // hasn't necessarily finished laying out the textarea yet, so `scrollHeight` read\n // synchronously here could be stale.\n requestAnimationFrame(() => autoResize.recompute());\n });\n\n return () => {\n sub.dispose();\n props.ref.value?.(null);\n unwireEl();\n unwireAutoResize();\n stopLayoutEffect();\n };\n });\n\n bind({\n attr: {\n error: errorAttr(errorText),\n size: fCtxProps.size,\n variant: fCtxProps.variant,\n },\n });\n\n const counterClass = () => counterClassName(counter?.value);\n const counterHidden = () => !counter;\n const counterText = () => counter?.value.counterText.replace(' / ', '/') ?? '';\n const helperHidden = () => !!errorText.value || !helperText.value;\n const errorHidden = () => !errorText.value;\n\n return html`\n <div class=\"textarea-wrapper\" part=\"wrapper\">\n <label class=\"label\" part=\"label\" for=\"${textareaId}\" id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\"\n >${props.label}</label\n >\n <div class=\"field\" part=\"field\">\n <textarea\n part=\"textarea\"\n ref=\"${textareaRef}\"\n id=\"${textareaId}\"\n :name=\"${props.name}\"\n :placeholder=\"${props.placeholder}\"\n :rows=\"${props.rows}\"\n :maxlength=\"${props.maxlength}\"\n ?disabled=\"${props.disabled}\"\n ?readonly=\"${props.readonly}\"\n ?required=\"${props.required}\"\n :value=\"${live(tf.value)}\"\n :aria-describedby=\"${ariaDescribedBy}\"\n :aria-errormessage=\"${ariaErrorMessage}\"\n :aria-invalid=\"${ariaInvalid}\"\n :aria-labelledby=\"${ariaLabelledBy}\"></textarea>\n </div>\n <span class=\"${counterClass}\" aria-live=\"polite\" ?hidden=\"${counterHidden}\">${counterText}</span>\n <div id=\"${assistiveId}\" class=\"helper-text\" aria-live=\"polite\" part=\"helper\" ?hidden=\"${helperHidden}\">\n ${() => helperText.value}\n </div>\n <div id=\"${errorId}\" class=\"helper-text\" role=\"alert\" part=\"error\" ?hidden=\"${errorHidden}\">\n ${() => errorText.value}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [...fieldMixins, sizeVariantMixin(TEXTAREA_SIZE_PRESET), forcedColorsFocusMixin('textarea'), componentStyles],\n});\n"],"mappings":"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"}
package/dist/textarea.js CHANGED
@@ -6,54 +6,51 @@ import { fieldMixins as r } from "./styles/index.js";
6
6
  import { disablableBundle as i, roundableBundle as a, sizableBundle as o, themableBundle as s } from "./shared/prop-bundles.js";
7
7
  import { TEXTAREA_SIZE_PRESET as c } from "./shared/size-presets.js";
8
8
  import { lifecycleSignal as l } from "./headless/ore.js";
9
- import { createTextField as u } from "./headless/text-field.js";
10
- import { useField as d } from "./ore/dist/forms/field.js";
9
+ import { createAutoResize as u } from "./headless/auto-resize.js";
10
+ import { counterClassName as d } from "./headless/field-base.js";
11
+ import { createTextField as f } from "./headless/text-field.js";
12
+ import { useField as p } from "./ore/dist/forms/field.js";
11
13
  import "./ore/dist/forms.js";
12
- import { FORM_CTX as f, useFormContext as p } from "./inputs/shared/form-context.js";
13
- import m from "./inputs/textarea/textarea.css.js";
14
- import { bind as h, define as g, html as _, inject as v, onCleanup as y, onElement as b, prop as x, ref as S, useEmit as C, watchEffect as w } from "@vielzeug/ore";
15
- import { watch as T } from "@vielzeug/ripple";
14
+ import { errorAttr as m } from "./inputs/shared/field-binding.js";
15
+ import { FORM_CTX as h, useFormContext as g } from "./inputs/shared/form-context.js";
16
+ import _ from "./inputs/textarea/textarea.css.js";
17
+ import { bind as v, define as y, html as b, inject as x, onCleanup as S, onElement as C, prop as w, ref as T, useEmit as E, watchEffect as D } from "@vielzeug/ore";
18
+ import { watch as O } from "@vielzeug/ripple";
16
19
  //#region src/inputs/textarea/textarea.ts
17
- var E = "ore-textarea";
18
- g(E, {
20
+ var k = "ore-textarea";
21
+ y(k, {
19
22
  formAssociated: !0,
20
23
  props: {
21
24
  ...s,
22
25
  ...o,
23
26
  ...i,
24
27
  ...a,
25
- "auto-resize": x.bool(!1),
26
- error: x.string(),
27
- fullwidth: x.bool(!1),
28
- helper: x.string(),
29
- label: x.string(),
30
- "label-placement": x.oneOf(["inset", "outside"], "inset"),
31
- maxlength: x.json(void 0),
32
- name: x.string(),
33
- "no-resize": x.bool(!1),
34
- placeholder: x.string(),
35
- readonly: x.bool(!1),
36
- ref: x.json(void 0),
37
- required: x.bool(!1),
38
- resize: x.string(),
39
- rows: x.json(void 0),
40
- value: x.string(),
41
- variant: x.string()
28
+ "auto-resize": w.bool(!1),
29
+ error: w.string(),
30
+ fullwidth: w.bool(!1),
31
+ helper: w.string(),
32
+ label: w.string(),
33
+ "label-placement": w.oneOf(["inset", "outside"], "inset"),
34
+ maxlength: w.json(void 0),
35
+ name: w.string(),
36
+ "no-resize": w.bool(!1),
37
+ placeholder: w.string(),
38
+ readonly: w.bool(!1),
39
+ ref: w.json(void 0),
40
+ required: w.bool(!1),
41
+ resize: w.string(),
42
+ rows: w.json(void 0),
43
+ value: w.string(),
44
+ variant: w.string()
42
45
  },
43
46
  setup(t) {
44
- let n = C(), r = w, i = v(f), a = p(t, i), o = S(), s = () => {
45
- if (!t["auto-resize"].value || !o.value) return;
46
- let e = o.value;
47
- e.style.height = "auto", e.style.height = `${e.scrollHeight}px`;
48
- }, c = l(y), m = null, g = u({
47
+ let n = E(), r = D, i = x(h), a = g(t, i), o = T(), s = u({ enabled: t["auto-resize"] }), c = l(S), _ = f({
49
48
  disabled: a.disabled,
50
49
  error: t.error,
51
- getFormField: () => m,
52
50
  helper: t.helper,
53
51
  label: t.label,
54
52
  labelPlacement: t["label-placement"],
55
53
  maxLength: t.maxlength,
56
- onBeforeInput: s,
57
54
  onChange: (e, t) => {
58
55
  n("change", {
59
56
  originalEvent: e,
@@ -67,41 +64,48 @@ g(E, {
67
64
  });
68
65
  },
69
66
  prefix: "textarea",
67
+ readonly: t.readonly,
68
+ required: t.required,
70
69
  signal: c,
71
70
  validateOn: i?.validateOn,
72
71
  value: t.value
73
72
  });
74
- m = d({
75
- disabled: g.disabled,
73
+ _.attachFormField(p({
74
+ disabled: _.disabled,
75
+ onReset: () => {
76
+ _.reset(), requestAnimationFrame(() => s.recompute());
77
+ },
76
78
  toFormValue: (e) => e,
77
- value: g.value
78
- });
79
- let { ariaDescribedBy: x, ariaErrorMessage: E, ariaInvalid: D, ariaLabelledBy: O, assistiveId: k, counter: A, errorId: j, errorText: M, fieldId: N, helperText: P, labelId: F, labelVisible: I } = g;
80
- return b(o, (e) => {
81
- let n = g.wire(e);
79
+ validationMessage: _.validationMessage,
80
+ validity: _.validity,
81
+ value: _.value
82
+ }));
83
+ let { ariaDescribedBy: y, ariaErrorMessage: w, ariaInvalid: k, ariaLabelledBy: A, assistiveId: j, counter: M, errorId: N, errorText: P, fieldId: F, helperText: I, labelId: L, labelVisible: R } = _;
84
+ return C(o, (e) => {
85
+ let n = _.wire(e), i = s.wire(e);
82
86
  t.ref.value?.(e);
83
- let i = T(t.ref, (t) => {
87
+ let a = O(t.ref, (t) => {
84
88
  t?.(e);
85
- }), a = r(() => {
86
- e.style.resize = t["auto-resize"].value || t["no-resize"].value ? "none" : t.resize.value || "vertical", t["auto-resize"].value && requestAnimationFrame(s);
89
+ }), o = r(() => {
90
+ e.style.resize = t["auto-resize"].value || t["no-resize"].value ? "none" : t.resize.value || "vertical", requestAnimationFrame(() => s.recompute());
87
91
  });
88
92
  return () => {
89
- i.dispose(), t.ref.value?.(null), n(), a();
93
+ a.dispose(), t.ref.value?.(null), n(), i(), o();
90
94
  };
91
- }), h({ attr: {
92
- error: () => M.value || void 0,
95
+ }), v({ attr: {
96
+ error: m(P),
93
97
  size: a.size,
94
98
  variant: a.variant
95
- } }), _`
99
+ } }), b`
96
100
  <div class="textarea-wrapper" part="wrapper">
97
- <label class="label" part="label" for="${N}" id="${F}" ?hidden="${() => !I.value}"
101
+ <label class="label" part="label" for="${F}" id="${L}" ?hidden="${() => !R.value}"
98
102
  >${t.label}</label
99
103
  >
100
104
  <div class="field" part="field">
101
105
  <textarea
102
106
  part="textarea"
103
107
  ref="${o}"
104
- id="${N}"
108
+ id="${F}"
105
109
  :name="${t.name}"
106
110
  :placeholder="${t.placeholder}"
107
111
  :rows="${t.rows}"
@@ -109,18 +113,18 @@ g(E, {
109
113
  ?disabled="${t.disabled}"
110
114
  ?readonly="${t.readonly}"
111
115
  ?required="${t.required}"
112
- :value="${e(g.value)}"
113
- :aria-describedby="${x}"
114
- :aria-errormessage="${E}"
115
- :aria-invalid="${D}"
116
- :aria-labelledby="${O}"></textarea>
116
+ :value="${e(_.value)}"
117
+ :aria-describedby="${y}"
118
+ :aria-errormessage="${w}"
119
+ :aria-invalid="${k}"
120
+ :aria-labelledby="${A}"></textarea>
117
121
  </div>
118
- <span class="${() => A?.value.counterAtLimit ? "counter at-limit" : A?.value.counterNearLimit ? "counter near-limit" : "counter"}" aria-live="polite" ?hidden="${() => !A}">${() => A?.value.counterText.replace(" / ", "/") ?? ""}</span>
119
- <div id="${k}" class="helper-text" aria-live="polite" part="helper" ?hidden="${() => !!M.value || !P.value}">
120
- ${() => P.value}
122
+ <span class="${() => d(M?.value)}" aria-live="polite" ?hidden="${() => !M}">${() => M?.value.counterText.replace(" / ", "/") ?? ""}</span>
123
+ <div id="${j}" class="helper-text" aria-live="polite" part="helper" ?hidden="${() => !!P.value || !I.value}">
124
+ ${() => I.value}
121
125
  </div>
122
- <div id="${j}" class="helper-text" role="alert" part="error" ?hidden="${() => !M.value}">
123
- ${() => M.value}
126
+ <div id="${N}" class="helper-text" role="alert" part="error" ?hidden="${() => !P.value}">
127
+ ${() => P.value}
124
128
  </div>
125
129
  </div>
126
130
  `;
@@ -130,10 +134,10 @@ g(E, {
130
134
  ...r,
131
135
  t(c),
132
136
  n("textarea"),
133
- m
137
+ _
134
138
  ]
135
139
  });
136
140
  //#endregion
137
- export { E as TEXTAREA_TAG };
141
+ export { k as TEXTAREA_TAG };
138
142
 
139
143
  //# sourceMappingURL=textarea.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.js","names":[],"sources":["../src/inputs/textarea/textarea.ts"],"sourcesContent":["import { define, html, inject, prop, ref, bind, onCleanup, onElement, useEmit, watchEffect } from '@vielzeug/ore';\nimport { live } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { watch as rippleWatch } from '@vielzeug/ripple';\n\nimport type { TextFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\n\nimport { lifecycleSignal, createTextField } from '../../headless';\nimport { disablableBundle, roundableBundle, sizableBundle, TEXTAREA_SIZE_PRESET, themableBundle } from '../../shared';\nimport { fieldMixins, forcedColorsFocusMixin, sizeVariantMixin } from '../../styles';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './textarea.css?inline';\n\n/** Textarea component properties */\n\nexport type OreTextareaEvents = {\n change: { originalEvent: Event; value: string };\n input: { originalEvent: Event; value: string };\n};\n\nexport type OreTextareaProps = TextFieldProps<Exclude<VisualVariant, 'frost' | 'text'>> & {\n /** Allow auto-grow with content */\n 'auto-resize'?: boolean;\n /** Maximum character count; shows a counter when set */\n maxlength?: number;\n /** Disable a manual resize handle */\n 'no-resize'?: boolean;\n /**\n * JS-only callback fired with the inner `<textarea>` element when it mounts,\n * and with `null` when it unmounts. Intended for composed components that\n * need imperative access to the raw element.\n * Set as a JS property: `bitTextarea.ref = (el) => { ... }`.\n */\n ref?: ((el: HTMLTextAreaElement | null) => void) | null;\n /** Resize direction override */\n resize?: 'none' | 'horizontal' | 'both' | 'vertical';\n /** Number of visible text rows */\n rows?: number;\n};\n\n/**\n * A multi-line text input with label, helper text, character counter, and auto-resize.\n *\n * @element ore-textarea\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current value\n * @attr {string} placeholder - Placeholder text\n * @attr {string} name - Form field name\n * @attr {number} rows - Visible row count\n * @attr {number} maxlength - Max character count (shows counter)\n * @attr {string} helper - Helper text below the textarea\n * @attr {string} error - Error message\n * @attr {boolean} disabled - Disable interaction\n * @attr {boolean} readonly - Read-only mode\n * @attr {boolean} required - Required field\n * @attr {boolean} no-resize - Disable manual resize\n * @attr {boolean} auto-resize - Grow with content\n * @attr {string} resize - Resize direction: 'none' | 'horizontal' | 'both' | 'vertical'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires input - Fired on every keystroke with current value. detail: { value: string; originalEvent: Event }\n * @fires change - Fired on blur with changed value. detail: { value: string; originalEvent: Event }\n *\n * @slot helper - Complex helper content\n *\n * @cssprop --textarea-bg - Background color\n * @cssprop --textarea-border-color - Border color\n * @cssprop --textarea-placeholder-color - Placeholder text color\n * @cssprop --textarea-radius - Border radius\n * @cssprop --textarea-padding - Inner padding (block inline)\n * @cssprop --textarea-gap - Gap between label and field\n * @cssprop --textarea-font-size - Font size\n * @cssprop --textarea-min-height - Minimum field height\n * @cssprop --textarea-max-height - Maximum field height (none = unlimited)\n * @cssprop --textarea-resize - CSS resize direction ('vertical' | 'horizontal' | 'both' | 'none')\n * @cssprop --textarea-hover-bg - Field background on hover (flat/ghost variants)\n * @cssprop --textarea-hover-border-color - Field border on hover (flat/bordered variants)\n * @cssprop --textarea-focus-bg - Field background when focused (flat variant)\n * @cssprop --textarea-focus-border-color - Field border when focused (flat variant)\n *\n * @part wrapper - Outer wrapper element.\n * @part label - Label element.\n * @part field - Field container.\n * @part textarea - The native `<textarea>` element.\n * @part helper - The helper text element.\n * @part error - The error text element (`role=\"alert\"`).\n *\n * @example\n * ```html\n * <ore-textarea></ore-textarea>\n * ```\n */\nexport const TEXTAREA_TAG = 'ore-textarea' as const;\ndefine<OreTextareaProps>(TEXTAREA_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n 'auto-resize': prop.bool(false),\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n maxlength: prop.json(undefined as number | undefined),\n name: prop.string(),\n 'no-resize': prop.bool(false),\n placeholder: prop.string(),\n readonly: prop.bool(false),\n ref: prop.json(undefined as ((el: HTMLTextAreaElement | null) => void) | null | undefined),\n required: prop.bool(false),\n resize: prop.string<'none' | 'both' | 'horizontal' | 'vertical'>(),\n rows: prop.json(undefined as number | undefined),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n setup(props) {\n const emit = useEmit<OreTextareaEvents>();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const textareaRef = ref<HTMLTextAreaElement>();\n\n const autoGrow = () => {\n if (!props['auto-resize'].value || !textareaRef.value) return;\n\n const textareaEl = textareaRef.value;\n\n textareaEl.style.height = 'auto';\n textareaEl.style.height = `${textareaEl.scrollHeight}px`;\n };\n\n const abortSignal = lifecycleSignal(onCleanup);\n let _formField: { reportValidity(): void } | null = null;\n const tf = createTextField({\n disabled: fCtxProps.disabled,\n error: props.error,\n getFormField: () => _formField,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n maxLength: props.maxlength,\n onBeforeInput: autoGrow,\n onChange: (event: Event, value: string) => {\n emit('change', { originalEvent: event, value });\n },\n onInput: (event: Event, value: string) => {\n emit('input', { originalEvent: event, value });\n },\n prefix: 'textarea',\n signal: abortSignal,\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n _formField = useField<string>({ disabled: tf.disabled, toFormValue: (v) => v, value: tf.value });\n\n const {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n ariaLabelledBy,\n assistiveId,\n counter,\n errorId,\n errorText,\n fieldId: textareaId,\n helperText,\n labelId,\n labelVisible,\n } = tf;\n\n onElement(textareaRef, (textareaEl) => {\n const unwireEl = tf.wire(textareaEl);\n\n props.ref.value?.(textareaEl);\n\n const sub = rippleWatch(props.ref, (cb) => {\n cb?.(textareaEl);\n });\n\n const stopLayoutEffect = watch(() => {\n textareaEl.style.resize =\n props['auto-resize'].value || props['no-resize'].value ? 'none' : props.resize.value || 'vertical';\n\n if (props['auto-resize'].value) {\n requestAnimationFrame(autoGrow);\n }\n });\n\n return () => {\n sub.dispose();\n props.ref.value?.(null);\n unwireEl();\n stopLayoutEffect();\n };\n });\n\n bind({\n attr: {\n error: () => errorText.value || undefined,\n size: fCtxProps.size,\n variant: fCtxProps.variant,\n },\n });\n\n const counterClass = () =>\n counter?.value.counterAtLimit\n ? 'counter at-limit'\n : counter?.value.counterNearLimit\n ? 'counter near-limit'\n : 'counter';\n const counterHidden = () => !counter;\n const counterText = () => counter?.value.counterText.replace(' / ', '/') ?? '';\n const helperHidden = () => !!errorText.value || !helperText.value;\n const errorHidden = () => !errorText.value;\n\n return html`\n <div class=\"textarea-wrapper\" part=\"wrapper\">\n <label class=\"label\" part=\"label\" for=\"${textareaId}\" id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\"\n >${props.label}</label\n >\n <div class=\"field\" part=\"field\">\n <textarea\n part=\"textarea\"\n ref=\"${textareaRef}\"\n id=\"${textareaId}\"\n :name=\"${props.name}\"\n :placeholder=\"${props.placeholder}\"\n :rows=\"${props.rows}\"\n :maxlength=\"${props.maxlength}\"\n ?disabled=\"${props.disabled}\"\n ?readonly=\"${props.readonly}\"\n ?required=\"${props.required}\"\n :value=\"${live(tf.value)}\"\n :aria-describedby=\"${ariaDescribedBy}\"\n :aria-errormessage=\"${ariaErrorMessage}\"\n :aria-invalid=\"${ariaInvalid}\"\n :aria-labelledby=\"${ariaLabelledBy}\"></textarea>\n </div>\n <span class=\"${counterClass}\" aria-live=\"polite\" ?hidden=\"${counterHidden}\">${counterText}</span>\n <div id=\"${assistiveId}\" class=\"helper-text\" aria-live=\"polite\" part=\"helper\" ?hidden=\"${helperHidden}\">\n ${() => helperText.value}\n </div>\n <div id=\"${errorId}\" class=\"helper-text\" role=\"alert\" part=\"error\" ?hidden=\"${errorHidden}\">\n ${() => errorText.value}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [...fieldMixins, sizeVariantMixin(TEXTAREA_SIZE_PRESET), forcedColorsFocusMixin('textarea'), componentStyles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"textarea.js","names":[],"sources":["../src/inputs/textarea/textarea.ts"],"sourcesContent":["import { define, html, inject, prop, ref, bind, onCleanup, onElement, useEmit, watchEffect } from '@vielzeug/ore';\nimport { live } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { watch as rippleWatch } from '@vielzeug/ripple';\n\nimport type { TextFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\n\nimport { counterClassName, createAutoResize, lifecycleSignal, createTextField } from '../../headless';\nimport { disablableBundle, roundableBundle, sizableBundle, TEXTAREA_SIZE_PRESET, themableBundle } from '../../shared';\nimport { fieldMixins, forcedColorsFocusMixin, sizeVariantMixin } from '../../styles';\nimport { errorAttr } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './textarea.css?inline';\n\n/** Textarea component properties */\n\nexport type OreTextareaEvents = {\n change: { originalEvent: Event; value: string };\n input: { originalEvent: Event; value: string };\n};\n\nexport type OreTextareaProps = TextFieldProps<Exclude<VisualVariant, 'frost' | 'text'>> & {\n /** Allow auto-grow with content */\n 'auto-resize'?: boolean;\n /** Maximum character count; shows a counter when set */\n maxlength?: number;\n /** Disable a manual resize handle */\n 'no-resize'?: boolean;\n /**\n * JS-only callback fired with the inner `<textarea>` element when it mounts,\n * and with `null` when it unmounts. Intended for composed components that\n * need imperative access to the raw element.\n * Set as a JS property: `bitTextarea.ref = (el) => { ... }`.\n */\n ref?: ((el: HTMLTextAreaElement | null) => void) | null;\n /** Resize direction override */\n resize?: 'none' | 'horizontal' | 'both' | 'vertical';\n /** Number of visible text rows */\n rows?: number;\n};\n\n/**\n * A multi-line text input with label, helper text, character counter, and auto-resize.\n *\n * @element ore-textarea\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current value\n * @attr {string} placeholder - Placeholder text\n * @attr {string} name - Form field name\n * @attr {number} rows - Visible row count\n * @attr {number} maxlength - Max character count (shows counter)\n * @attr {string} helper - Helper text below the textarea\n * @attr {string} error - Error message\n * @attr {boolean} disabled - Disable interaction\n * @attr {boolean} readonly - Read-only mode\n * @attr {boolean} required - Required field\n * @attr {boolean} no-resize - Disable manual resize\n * @attr {boolean} auto-resize - Grow with content\n * @attr {string} resize - Resize direction: 'none' | 'horizontal' | 'both' | 'vertical'\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n *\n * @fires input - Fired on every keystroke with current value. detail: { value: string; originalEvent: Event }\n * @fires change - Fired on blur with changed value. detail: { value: string; originalEvent: Event }\n *\n * @slot helper - Complex helper content\n *\n * @cssprop --textarea-bg - Background color\n * @cssprop --textarea-border-color - Border color\n * @cssprop --textarea-placeholder-color - Placeholder text color\n * @cssprop --textarea-radius - Border radius\n * @cssprop --textarea-padding - Inner padding (block inline)\n * @cssprop --textarea-gap - Gap between label and field\n * @cssprop --textarea-font-size - Font size\n * @cssprop --textarea-min-height - Minimum field height\n * @cssprop --textarea-max-height - Maximum field height (none = unlimited)\n * @cssprop --textarea-resize - CSS resize direction ('vertical' | 'horizontal' | 'both' | 'none')\n * @cssprop --textarea-hover-bg - Field background on hover (flat/ghost variants)\n * @cssprop --textarea-hover-border-color - Field border on hover (flat/bordered variants)\n * @cssprop --textarea-focus-bg - Field background when focused (flat variant)\n * @cssprop --textarea-focus-border-color - Field border when focused (flat variant)\n *\n * @part wrapper - Outer wrapper element.\n * @part label - Label element.\n * @part field - Field container.\n * @part textarea - The native `<textarea>` element.\n * @part helper - The helper text element.\n * @part error - The error text element (`role=\"alert\"`).\n *\n * @example\n * ```html\n * <ore-textarea></ore-textarea>\n * ```\n */\nexport const TEXTAREA_TAG = 'ore-textarea' as const;\ndefine<OreTextareaProps>(TEXTAREA_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...roundableBundle,\n 'auto-resize': prop.bool(false),\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n maxlength: prop.json(undefined as number | undefined),\n name: prop.string(),\n 'no-resize': prop.bool(false),\n placeholder: prop.string(),\n readonly: prop.bool(false),\n ref: prop.json(undefined as ((el: HTMLTextAreaElement | null) => void) | null | undefined),\n required: prop.bool(false),\n resize: prop.string<'none' | 'both' | 'horizontal' | 'vertical'>(),\n rows: prop.json(undefined as number | undefined),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n setup(props) {\n const emit = useEmit<OreTextareaEvents>();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const textareaRef = ref<HTMLTextAreaElement>();\n const autoResize = createAutoResize({ enabled: props['auto-resize'] });\n\n const abortSignal = lifecycleSignal(onCleanup);\n const tf = createTextField({\n disabled: fCtxProps.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n maxLength: props.maxlength,\n onChange: (event: Event, value: string) => {\n emit('change', { originalEvent: event, value });\n },\n onInput: (event: Event, value: string) => {\n emit('input', { originalEvent: event, value });\n },\n prefix: 'textarea',\n readonly: props.readonly,\n required: props.required,\n signal: abortSignal,\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n tf.attachFormField(\n useField<string>({\n disabled: tf.disabled,\n onReset: () => {\n tf.reset();\n requestAnimationFrame(() => autoResize.recompute());\n },\n toFormValue: (v) => v,\n validationMessage: tf.validationMessage,\n validity: tf.validity,\n value: tf.value,\n }),\n );\n\n const {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n ariaLabelledBy,\n assistiveId,\n counter,\n errorId,\n errorText,\n fieldId: textareaId,\n helperText,\n labelId,\n labelVisible,\n } = tf;\n\n onElement(textareaRef, (textareaEl) => {\n const unwireEl = tf.wire(textareaEl);\n const unwireAutoResize = autoResize.wire(textareaEl);\n\n props.ref.value?.(textareaEl);\n\n const sub = rippleWatch(props.ref, (cb) => {\n cb?.(textareaEl);\n });\n\n const stopLayoutEffect = watch(() => {\n textareaEl.style.resize =\n props['auto-resize'].value || props['no-resize'].value ? 'none' : props.resize.value || 'vertical';\n\n // Deferred a frame: on mount (or right after `auto-resize` flips true) the browser\n // hasn't necessarily finished laying out the textarea yet, so `scrollHeight` read\n // synchronously here could be stale.\n requestAnimationFrame(() => autoResize.recompute());\n });\n\n return () => {\n sub.dispose();\n props.ref.value?.(null);\n unwireEl();\n unwireAutoResize();\n stopLayoutEffect();\n };\n });\n\n bind({\n attr: {\n error: errorAttr(errorText),\n size: fCtxProps.size,\n variant: fCtxProps.variant,\n },\n });\n\n const counterClass = () => counterClassName(counter?.value);\n const counterHidden = () => !counter;\n const counterText = () => counter?.value.counterText.replace(' / ', '/') ?? '';\n const helperHidden = () => !!errorText.value || !helperText.value;\n const errorHidden = () => !errorText.value;\n\n return html`\n <div class=\"textarea-wrapper\" part=\"wrapper\">\n <label class=\"label\" part=\"label\" for=\"${textareaId}\" id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\"\n >${props.label}</label\n >\n <div class=\"field\" part=\"field\">\n <textarea\n part=\"textarea\"\n ref=\"${textareaRef}\"\n id=\"${textareaId}\"\n :name=\"${props.name}\"\n :placeholder=\"${props.placeholder}\"\n :rows=\"${props.rows}\"\n :maxlength=\"${props.maxlength}\"\n ?disabled=\"${props.disabled}\"\n ?readonly=\"${props.readonly}\"\n ?required=\"${props.required}\"\n :value=\"${live(tf.value)}\"\n :aria-describedby=\"${ariaDescribedBy}\"\n :aria-errormessage=\"${ariaErrorMessage}\"\n :aria-invalid=\"${ariaInvalid}\"\n :aria-labelledby=\"${ariaLabelledBy}\"></textarea>\n </div>\n <span class=\"${counterClass}\" aria-live=\"polite\" ?hidden=\"${counterHidden}\">${counterText}</span>\n <div id=\"${assistiveId}\" class=\"helper-text\" aria-live=\"polite\" part=\"helper\" ?hidden=\"${helperHidden}\">\n ${() => helperText.value}\n </div>\n <div id=\"${errorId}\" class=\"helper-text\" role=\"alert\" part=\"error\" ?hidden=\"${errorHidden}\">\n ${() => errorText.value}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [...fieldMixins, sizeVariantMixin(TEXTAREA_SIZE_PRESET), forcedColorsFocusMixin('textarea'), componentStyles],\n});\n"],"mappings":"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"}