aeico-components 0.1.5 → 0.1.7

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 (337) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +0 -0
  3. package/dist/chunks/action-button.cjs +296 -0
  4. package/dist/chunks/action-button.cjs.map +1 -0
  5. package/dist/chunks/action-button.js +297 -0
  6. package/dist/chunks/action-button.js.map +1 -0
  7. package/dist/chunks/alert.cjs +26 -32
  8. package/dist/chunks/alert.cjs.map +1 -1
  9. package/dist/chunks/alert.js +27 -33
  10. package/dist/chunks/alert.js.map +1 -1
  11. package/dist/chunks/badge.cjs +2 -2
  12. package/dist/chunks/badge.cjs.map +1 -1
  13. package/dist/chunks/badge.js +3 -3
  14. package/dist/chunks/badge.js.map +1 -1
  15. package/dist/chunks/breadcrumb-item.cjs +9 -9
  16. package/dist/chunks/breadcrumb-item.cjs.map +1 -1
  17. package/dist/chunks/breadcrumb-item.js +10 -10
  18. package/dist/chunks/breadcrumb-item.js.map +1 -1
  19. package/dist/chunks/button-group.cjs +1 -1
  20. package/dist/chunks/button-group.cjs.map +1 -1
  21. package/dist/chunks/button-group.js +2 -2
  22. package/dist/chunks/button-group.js.map +1 -1
  23. package/dist/chunks/button.cjs +40 -51
  24. package/dist/chunks/button.cjs.map +1 -1
  25. package/dist/chunks/button.js +41 -52
  26. package/dist/chunks/button.js.map +1 -1
  27. package/dist/chunks/card.cjs +2 -2
  28. package/dist/chunks/card.cjs.map +1 -1
  29. package/dist/chunks/card.js +3 -3
  30. package/dist/chunks/card.js.map +1 -1
  31. package/dist/chunks/checkbox.cjs +18 -5
  32. package/dist/chunks/checkbox.cjs.map +1 -1
  33. package/dist/chunks/checkbox.js +18 -5
  34. package/dist/chunks/checkbox.js.map +1 -1
  35. package/dist/chunks/color.cjs +1 -1
  36. package/dist/chunks/color.js +1 -1
  37. package/dist/chunks/copy-button.cjs +171 -0
  38. package/dist/chunks/copy-button.cjs.map +1 -0
  39. package/dist/chunks/copy-button.js +172 -0
  40. package/dist/chunks/copy-button.js.map +1 -0
  41. package/dist/chunks/detail-group.cjs +65 -0
  42. package/dist/chunks/detail-group.cjs.map +1 -0
  43. package/dist/chunks/detail-group.js +66 -0
  44. package/dist/chunks/detail-group.js.map +1 -0
  45. package/dist/chunks/detail.cjs +7 -4
  46. package/dist/chunks/detail.cjs.map +1 -1
  47. package/dist/chunks/detail.js +8 -5
  48. package/dist/chunks/detail.js.map +1 -1
  49. package/dist/chunks/dialog.cjs +2 -2
  50. package/dist/chunks/dialog.cjs.map +1 -1
  51. package/dist/chunks/dialog.js +3 -3
  52. package/dist/chunks/dialog.js.map +1 -1
  53. package/dist/chunks/divider.cjs +2 -2
  54. package/dist/chunks/divider.cjs.map +1 -1
  55. package/dist/chunks/divider.js +3 -3
  56. package/dist/chunks/divider.js.map +1 -1
  57. package/dist/chunks/drawer.cjs +180 -0
  58. package/dist/chunks/drawer.cjs.map +1 -0
  59. package/dist/chunks/drawer.js +181 -0
  60. package/dist/chunks/drawer.js.map +1 -0
  61. package/dist/chunks/dropdown-button.cjs +10 -11
  62. package/dist/chunks/dropdown-button.cjs.map +1 -1
  63. package/dist/chunks/dropdown-button.js +14 -15
  64. package/dist/chunks/dropdown-button.js.map +1 -1
  65. package/dist/chunks/icon.cjs +57 -11
  66. package/dist/chunks/icon.cjs.map +1 -1
  67. package/dist/chunks/icon.js +58 -12
  68. package/dist/chunks/icon.js.map +1 -1
  69. package/dist/chunks/menu.cjs +396 -0
  70. package/dist/chunks/menu.cjs.map +1 -0
  71. package/dist/chunks/menu.js +397 -0
  72. package/dist/chunks/menu.js.map +1 -0
  73. package/dist/chunks/navbar.cjs +2 -2
  74. package/dist/chunks/navbar.cjs.map +1 -1
  75. package/dist/chunks/navbar.js +3 -3
  76. package/dist/chunks/navbar.js.map +1 -1
  77. package/dist/chunks/number-input.cjs +110 -0
  78. package/dist/chunks/number-input.cjs.map +1 -0
  79. package/dist/chunks/number-input.js +111 -0
  80. package/dist/chunks/number-input.js.map +1 -0
  81. package/dist/chunks/pagination.cjs +475 -0
  82. package/dist/chunks/pagination.cjs.map +1 -0
  83. package/dist/chunks/pagination.js +476 -0
  84. package/dist/chunks/pagination.js.map +1 -0
  85. package/dist/chunks/progress-bar.cjs +101 -0
  86. package/dist/chunks/progress-bar.cjs.map +1 -0
  87. package/dist/chunks/progress-bar.js +102 -0
  88. package/dist/chunks/progress-bar.js.map +1 -0
  89. package/dist/chunks/radio.cjs +11 -7
  90. package/dist/chunks/radio.cjs.map +1 -1
  91. package/dist/chunks/radio.js +11 -7
  92. package/dist/chunks/radio.js.map +1 -1
  93. package/dist/chunks/select.cjs +97 -66
  94. package/dist/chunks/select.cjs.map +1 -1
  95. package/dist/chunks/select.js +97 -66
  96. package/dist/chunks/select.js.map +1 -1
  97. package/dist/chunks/size.cjs +1 -1
  98. package/dist/chunks/size.js +1 -1
  99. package/dist/chunks/slider.cjs +35 -75
  100. package/dist/chunks/slider.cjs.map +1 -1
  101. package/dist/chunks/slider.js +35 -75
  102. package/dist/chunks/slider.js.map +1 -1
  103. package/dist/chunks/spinner.cjs +102 -0
  104. package/dist/chunks/spinner.cjs.map +1 -0
  105. package/dist/chunks/spinner.js +103 -0
  106. package/dist/chunks/spinner.js.map +1 -0
  107. package/dist/chunks/switch.cjs +110 -16
  108. package/dist/chunks/switch.cjs.map +1 -1
  109. package/dist/chunks/switch.js +111 -17
  110. package/dist/chunks/switch.js.map +1 -1
  111. package/dist/chunks/tab-panel.cjs +14 -5
  112. package/dist/chunks/tab-panel.cjs.map +1 -1
  113. package/dist/chunks/tab-panel.js +15 -6
  114. package/dist/chunks/tab-panel.js.map +1 -1
  115. package/dist/chunks/tag.cjs +2 -2
  116. package/dist/chunks/tag.cjs.map +1 -1
  117. package/dist/chunks/tag.js +3 -3
  118. package/dist/chunks/tag.js.map +1 -1
  119. package/dist/chunks/text-input.cjs +11 -16
  120. package/dist/chunks/text-input.cjs.map +1 -1
  121. package/dist/chunks/text-input.js +11 -16
  122. package/dist/chunks/text-input.js.map +1 -1
  123. package/dist/chunks/textarea.cjs +137 -0
  124. package/dist/chunks/textarea.cjs.map +1 -0
  125. package/dist/chunks/textarea.js +138 -0
  126. package/dist/chunks/textarea.js.map +1 -0
  127. package/dist/chunks/tooltip.cjs +292 -0
  128. package/dist/chunks/tooltip.cjs.map +1 -0
  129. package/dist/chunks/tooltip.js +293 -0
  130. package/dist/chunks/tooltip.js.map +1 -0
  131. package/dist/chunks/tree.cjs +468 -0
  132. package/dist/chunks/tree.cjs.map +1 -0
  133. package/dist/chunks/tree.js +469 -0
  134. package/dist/chunks/tree.js.map +1 -0
  135. package/dist/chunks/variables.cjs +273 -249
  136. package/dist/chunks/variables.cjs.map +1 -1
  137. package/dist/chunks/variables.js +273 -249
  138. package/dist/chunks/variables.js.map +1 -1
  139. package/dist/copy-button.cjs +6 -0
  140. package/dist/copy-button.cjs.map +1 -0
  141. package/dist/copy-button.js +6 -0
  142. package/dist/copy-button.js.map +1 -0
  143. package/dist/detail-group.cjs +6 -0
  144. package/dist/detail-group.cjs.map +1 -0
  145. package/dist/detail-group.js +6 -0
  146. package/dist/detail-group.js.map +1 -0
  147. package/dist/drawer.cjs +6 -0
  148. package/dist/drawer.cjs.map +1 -0
  149. package/dist/drawer.js +6 -0
  150. package/dist/drawer.js.map +1 -0
  151. package/dist/dropdown.js +4 -4
  152. package/dist/index.cjs +24 -99
  153. package/dist/index.cjs.map +1 -1
  154. package/dist/index.js +41 -116
  155. package/dist/index.js.map +1 -1
  156. package/dist/menu.cjs +6 -0
  157. package/dist/menu.cjs.map +1 -0
  158. package/dist/menu.js +6 -0
  159. package/dist/menu.js.map +1 -0
  160. package/dist/number-input.cjs +6 -0
  161. package/dist/number-input.cjs.map +1 -0
  162. package/dist/number-input.js +6 -0
  163. package/dist/number-input.js.map +1 -0
  164. package/dist/pagination.cjs +6 -0
  165. package/dist/pagination.cjs.map +1 -0
  166. package/dist/pagination.js +6 -0
  167. package/dist/pagination.js.map +1 -0
  168. package/dist/progress-bar.cjs +6 -0
  169. package/dist/progress-bar.cjs.map +1 -0
  170. package/dist/progress-bar.js +6 -0
  171. package/dist/progress-bar.js.map +1 -0
  172. package/dist/select.cjs +2 -2
  173. package/dist/select.cjs.map +1 -1
  174. package/dist/select.js +3 -3
  175. package/dist/select.js.map +1 -1
  176. package/dist/spinner.cjs +6 -0
  177. package/dist/spinner.cjs.map +1 -0
  178. package/dist/spinner.js +6 -0
  179. package/dist/spinner.js.map +1 -0
  180. package/dist/textarea.cjs +5 -0
  181. package/dist/textarea.cjs.map +1 -0
  182. package/dist/textarea.js +5 -0
  183. package/dist/textarea.js.map +1 -0
  184. package/dist/tooltip.cjs +6 -0
  185. package/dist/tooltip.cjs.map +1 -0
  186. package/dist/tooltip.js +6 -0
  187. package/dist/tooltip.js.map +1 -0
  188. package/dist/tree.cjs +6 -0
  189. package/dist/tree.cjs.map +1 -0
  190. package/dist/tree.js +6 -0
  191. package/dist/tree.js.map +1 -0
  192. package/dist/types/aeico-field.d.ts +52 -0
  193. package/dist/types/alert/alert.d.ts +1 -0
  194. package/dist/types/card/defines.d.ts +1 -1
  195. package/dist/types/copy-button/copy-button.d.ts +32 -0
  196. package/dist/types/copy-button/defines.d.ts +1 -0
  197. package/dist/types/copy-button/index.d.ts +3 -0
  198. package/dist/types/detail/defines.d.ts +2 -1
  199. package/dist/types/detail/detail.d.ts +3 -1
  200. package/dist/types/detail/index.d.ts +1 -1
  201. package/dist/types/detail-group/detail-group.d.ts +39 -0
  202. package/dist/types/detail-group/index.d.ts +2 -0
  203. package/dist/types/drawer/defines.d.ts +1 -0
  204. package/dist/types/drawer/drawer.d.ts +31 -0
  205. package/dist/types/drawer/index.d.ts +3 -0
  206. package/dist/types/icon/built-in-icons.d.ts +1 -0
  207. package/dist/types/icon/defines.d.ts +5 -1
  208. package/dist/types/icon/icon.d.ts +1 -0
  209. package/dist/types/icon/index.d.ts +1 -1
  210. package/dist/types/icon/registry.d.ts +8 -0
  211. package/dist/types/index.d.ts +17 -0
  212. package/dist/types/menu/defines.d.ts +15 -0
  213. package/dist/types/menu/index.d.ts +5 -0
  214. package/dist/types/menu/menu-item.d.ts +63 -0
  215. package/dist/types/menu/menu.d.ts +34 -0
  216. package/dist/types/number-input/index.d.ts +2 -0
  217. package/dist/types/number-input/number-input.d.ts +35 -0
  218. package/dist/types/pagination/defines.d.ts +2 -0
  219. package/dist/types/pagination/index.d.ts +3 -0
  220. package/dist/types/pagination/pagination.d.ts +77 -0
  221. package/dist/types/select/select.d.ts +2 -2
  222. package/dist/types/spinner/defines.d.ts +3 -0
  223. package/dist/types/spinner/index.d.ts +3 -0
  224. package/dist/types/spinner/spinner.d.ts +35 -0
  225. package/dist/types/switch/defines.d.ts +1 -0
  226. package/dist/types/switch/switch.d.ts +8 -4
  227. package/dist/types/tabs/defines.d.ts +1 -0
  228. package/dist/types/tabs/tabs.d.ts +3 -0
  229. package/dist/types/text-input/text-input.d.ts +0 -4
  230. package/dist/types/textarea/index.d.ts +1 -0
  231. package/dist/types/textarea/textarea.d.ts +26 -0
  232. package/dist/types/tooltip/defines.d.ts +2 -0
  233. package/dist/types/tooltip/index.d.ts +4 -0
  234. package/dist/types/tooltip/tooltip.d.ts +48 -0
  235. package/dist/types/tree/defines.d.ts +23 -0
  236. package/dist/types/tree/index.d.ts +5 -0
  237. package/dist/types/tree/tree-item.d.ts +54 -0
  238. package/dist/types/tree/tree.d.ts +64 -0
  239. package/package.json +5 -5
  240. package/src/aeico-field.ts +142 -7
  241. package/src/alert/alert.ts +3 -2
  242. package/src/card/defines.ts +1 -1
  243. package/src/checkbox/checkbox.ts +17 -2
  244. package/src/copy-button/copy-button.ts +150 -0
  245. package/src/copy-button/defines.ts +5 -0
  246. package/src/copy-button/index.ts +3 -0
  247. package/src/detail/defines.ts +2 -1
  248. package/src/detail/detail.ts +5 -1
  249. package/src/detail/index.ts +1 -1
  250. package/src/detail-group/detail-group.ts +104 -0
  251. package/src/detail-group/index.ts +2 -0
  252. package/src/drawer/defines.ts +1 -0
  253. package/src/drawer/drawer.ts +157 -0
  254. package/src/drawer/index.ts +3 -0
  255. package/src/icon/built-in-icons.ts +21 -0
  256. package/src/icon/defines.ts +6 -1
  257. package/src/icon/icon.ts +29 -10
  258. package/src/icon/index.ts +1 -1
  259. package/src/icon/registry.ts +23 -1
  260. package/src/index.ts +30 -0
  261. package/src/menu/defines.ts +17 -0
  262. package/src/menu/index.ts +5 -0
  263. package/src/menu/menu-item.ts +315 -0
  264. package/src/menu/menu.ts +81 -0
  265. package/src/number-input/index.ts +2 -0
  266. package/src/number-input/number-input.ts +137 -0
  267. package/src/pagination/defines.ts +2 -0
  268. package/src/pagination/index.ts +3 -0
  269. package/src/pagination/pagination.ts +310 -0
  270. package/src/radio-group/radio-group.ts +11 -4
  271. package/src/select/select.ts +111 -70
  272. package/src/slider/slider.ts +9 -2
  273. package/src/spinner/defines.ts +12 -0
  274. package/src/spinner/index.ts +3 -0
  275. package/src/spinner/spinner.ts +81 -0
  276. package/src/styles/color.css +91 -91
  277. package/src/styles/components/action-button.css +37 -0
  278. package/src/styles/components/alert.css +22 -28
  279. package/src/styles/components/badge.css +13 -20
  280. package/src/styles/components/breadcrumb-item.css +7 -7
  281. package/src/styles/components/button.css +39 -50
  282. package/src/styles/components/card.css +45 -30
  283. package/src/styles/components/checkbox.css +9 -36
  284. package/src/styles/components/copy-button.css +108 -0
  285. package/src/styles/components/detail-group.css +10 -0
  286. package/src/styles/components/detail.css +56 -35
  287. package/src/styles/components/dialog.css +11 -11
  288. package/src/styles/components/divider.css +3 -3
  289. package/src/styles/components/drawer.css +152 -0
  290. package/src/styles/components/dropdown-item.css +7 -8
  291. package/src/styles/components/dropdown.css +3 -4
  292. package/src/styles/components/field-label.css +120 -0
  293. package/src/styles/components/icon-button.css +25 -36
  294. package/src/styles/components/icon.css +1 -1
  295. package/src/styles/components/menu-item.css +167 -0
  296. package/src/styles/components/menu.css +17 -0
  297. package/src/styles/components/navbar.css +16 -16
  298. package/src/styles/components/number-input.css +158 -0
  299. package/src/styles/components/pagination.css +205 -0
  300. package/src/styles/components/progress-bar.css +2 -3
  301. package/src/styles/components/radio-group.css +69 -106
  302. package/src/styles/components/select-option.css +5 -5
  303. package/src/styles/components/select.css +30 -69
  304. package/src/styles/components/slider.css +26 -71
  305. package/src/styles/components/spinner.css +78 -0
  306. package/src/styles/components/switch.css +64 -27
  307. package/src/styles/components/tab-panel.css +1 -1
  308. package/src/styles/components/tab.css +4 -5
  309. package/src/styles/components/tabs.css +49 -1
  310. package/src/styles/components/tag.css +14 -21
  311. package/src/styles/components/text-input.css +17 -64
  312. package/src/styles/components/textarea.css +66 -0
  313. package/src/styles/components/tooltip.css +102 -0
  314. package/src/styles/components/tree-item.css +152 -0
  315. package/src/styles/components/tree.css +10 -0
  316. package/src/styles/layout.css +457 -25
  317. package/src/styles/radius.css +10 -0
  318. package/src/styles/size.css +7 -7
  319. package/src/styles/variables.css +271 -247
  320. package/src/switch/defines.ts +1 -0
  321. package/src/switch/switch.ts +61 -12
  322. package/src/tabs/defines.ts +1 -0
  323. package/src/tabs/tabs.ts +12 -0
  324. package/src/text-input/text-input.ts +10 -15
  325. package/src/textarea/index.ts +1 -0
  326. package/src/textarea/textarea.ts +107 -0
  327. package/src/tooltip/defines.ts +11 -0
  328. package/src/tooltip/index.ts +4 -0
  329. package/src/tooltip/tooltip.ts +183 -0
  330. package/src/tree/defines.ts +26 -0
  331. package/src/tree/index.ts +5 -0
  332. package/src/tree/tree-item.ts +258 -0
  333. package/src/tree/tree.ts +237 -0
  334. package/dist/chunks/aeico-field.cjs +0 -179
  335. package/dist/chunks/aeico-field.cjs.map +0 -1
  336. package/dist/chunks/aeico-field.js +0 -180
  337. package/dist/chunks/aeico-field.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import type { InferProps, Props, Watchers } from 'aeico';
2
2
  import { tags } from 'aeico';
3
3
  import AeicoComponent from './aeico-component';
4
- import { t } from 'aeico-localize';
5
4
 
6
5
  export type FieldAction = 'clear' | 'reset' | 'change';
7
6
  export type FieldElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
7
+ export type ActionButtonStyle = 'standalone' | 'integrated';
8
8
 
9
9
  /**
10
10
  * Base class for form field components
@@ -25,10 +25,18 @@ class AeicoField<TValue = string> extends AeicoComponent {
25
25
  defaultValue: { type: String },
26
26
  resettable: { type: Boolean },
27
27
  resetText: { type: String },
28
+ resetTitle: { type: String },
28
29
  clearable: { type: Boolean },
29
30
  clearText: { type: String },
31
+ clearTitle: { type: String },
32
+ actionButtonStyle: { type: String },
30
33
  size: { type: String },
31
34
  disabled: { type: Boolean },
35
+ label: { type: String },
36
+ labelPlacement: { type: String },
37
+ required: { type: Boolean },
38
+ helperText: { type: String },
39
+ error: { type: String },
32
40
  };
33
41
 
34
42
  /**
@@ -36,8 +44,17 @@ class AeicoField<TValue = string> extends AeicoComponent {
36
44
  */
37
45
  static watchers: Watchers = {
38
46
  disabled: 'onDisabledChanged',
47
+ error: 'onErrorChanged',
39
48
  };
40
49
 
50
+ private static _fieldIdCounter = 0;
51
+ private readonly _fieldId: string;
52
+
53
+ constructor() {
54
+ super();
55
+ this._fieldId = `ae-field-${++AeicoField._fieldIdCounter}`;
56
+ }
57
+
41
58
  /**
42
59
  * The underlying form control element (input, select, etc.)
43
60
  * Subclasses should set this to their specific element
@@ -58,10 +75,18 @@ class AeicoField<TValue = string> extends AeicoComponent {
58
75
  declare defaultValue?: TValue | string;
59
76
  declare resettable?: boolean;
60
77
  declare resetText?: string;
78
+ declare resetTitle?: string;
61
79
  declare clearable?: boolean;
62
80
  declare clearText?: string;
81
+ declare clearTitle?: string;
82
+ declare actionButtonStyle?: ActionButtonStyle;
63
83
  declare size?: string;
64
84
  declare disabled?: boolean;
85
+ declare label?: string;
86
+ declare labelPlacement?: 'top' | 'left';
87
+ declare required?: boolean;
88
+ declare helperText?: string;
89
+ declare error?: string;
65
90
 
66
91
  /**
67
92
  * Lifecycle: Component connected to DOM
@@ -82,18 +107,30 @@ class AeicoField<TValue = string> extends AeicoComponent {
82
107
  * Must be called from within a build() callback.
83
108
  */
84
109
  protected renderActionButtons(force: boolean = false) {
85
- this.renderClearButton(force);
86
- this.renderResetButton(force);
110
+ const style = this.actionButtonStyle || 'integrated';
111
+ const hasActions = force || this.clearable || this.resettable;
112
+
113
+ if (hasActions && style === 'integrated') {
114
+ tags.div({ className: 'action-controls' }, () => {
115
+ this.renderClearButton(force);
116
+ this.renderResetButton(force);
117
+ });
118
+ } else {
119
+ this.renderClearButton(force);
120
+ this.renderResetButton(force);
121
+ }
87
122
  }
88
123
 
89
124
  protected renderResetButton(force: boolean = false) {
90
125
  const { button } = tags;
126
+ const style = this.actionButtonStyle || 'integrated';
91
127
 
92
128
  if (force || this.resettable) {
129
+ const className = style === 'integrated' ? 'reset-btn action-btn' : 'reset-btn';
93
130
  this.resetBtn = button({
94
- className: 'reset-btn',
131
+ className,
95
132
  textContent: this.resetText || '↺',
96
- title: t('buttons.reset', '↺'),
133
+ title: this.resetTitle || '↺',
97
134
  '@click': this.boundOnReset,
98
135
  });
99
136
  }
@@ -101,17 +138,63 @@ class AeicoField<TValue = string> extends AeicoComponent {
101
138
 
102
139
  protected renderClearButton(force: boolean = false) {
103
140
  const { button } = tags;
141
+ const style = this.actionButtonStyle || 'integrated';
104
142
 
105
143
  if (force || this.clearable) {
144
+ const className = style === 'integrated' ? 'clear-btn action-btn' : 'clear-btn';
106
145
  this.clearBtn = button({
107
- className: 'clear-btn',
146
+ className,
108
147
  textContent: this.clearText || '✕',
109
- title: t('buttons.clear', '✕'),
148
+ title: this.clearTitle || '✕',
110
149
  '@click': this.boundOnClear,
111
150
  });
112
151
  }
113
152
  }
114
153
 
154
+ /**
155
+ * Returns a stable unique ID for this field instance,
156
+ * used to associate <label htmlFor> with the underlying input.
157
+ */
158
+ protected getFieldId(): string {
159
+ return this._fieldId;
160
+ }
161
+
162
+ /**
163
+ * Renders a <label> element when the `label` prop is set.
164
+ * Call this as the first statement inside the render() html() callback.
165
+ * @param fieldId - The id to set on the underlying form control element (pass to input via id prop)
166
+ */
167
+ protected renderLabel(fieldId: string): void {
168
+ if (!this.label) return;
169
+ const { span } = tags;
170
+ tags.label({ id: `${fieldId}-label`, className: 'field-label', for: fieldId }, () => {
171
+ span({ textContent: this.label! });
172
+ if (this.required) {
173
+ span({ className: 'field-required', 'aria-hidden': 'true', textContent: ' *' });
174
+ }
175
+ });
176
+ }
177
+
178
+ /**
179
+ * Renders helper text below the field. Hidden when `error` is set.
180
+ * Call this after the field-body div in render().
181
+ */
182
+ protected renderHelperText(): void {
183
+ if (!this.helperText || this.error) return;
184
+ const { span } = tags;
185
+ span({ className: 'field-helper', textContent: this.helperText });
186
+ }
187
+
188
+ /**
189
+ * Renders an error message below the field when `error` is set.
190
+ * Call this after renderHelperText() in render().
191
+ */
192
+ protected renderError(): void {
193
+ if (!this.error) return;
194
+ const { span } = tags;
195
+ span({ className: 'field-error', textContent: this.error });
196
+ }
197
+
115
198
  /**
116
199
  * Watcher for disabled property
117
200
  */
@@ -121,6 +204,58 @@ class AeicoField<TValue = string> extends AeicoComponent {
121
204
  }
122
205
  }
123
206
 
207
+ /**
208
+ * Watcher for error property — syncs aria-invalid on the field element
209
+ */
210
+ protected onErrorChanged(newValue: string | undefined): void {
211
+ if (this.fieldElement) {
212
+ if (newValue) {
213
+ this.fieldElement.setAttribute('aria-invalid', 'true');
214
+ } else {
215
+ this.fieldElement.removeAttribute('aria-invalid');
216
+ }
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Lifecycle: called after every render update.
222
+ * Keeps aria-invalid on fieldElement in sync regardless of watcher timing.
223
+ */
224
+ protected onUpdated(_changedProps: Map<string, unknown>): void {
225
+ if (!this.fieldElement) return;
226
+ if (this.error) {
227
+ this.fieldElement.setAttribute('aria-invalid', 'true');
228
+ } else {
229
+ this.fieldElement.removeAttribute('aria-invalid');
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Returns true if the field passes constraint validation.
235
+ * Delegates to the underlying fieldElement when available;
236
+ * falls back to a manual required-check otherwise.
237
+ */
238
+ public checkValidity(): boolean {
239
+ if (this.fieldElement) {
240
+ return this.fieldElement.checkValidity();
241
+ }
242
+ if (this.required) {
243
+ const v = this.value;
244
+ return v !== undefined && v !== '' && v !== null;
245
+ }
246
+ return true;
247
+ }
248
+
249
+ /**
250
+ * Reports validity, showing the browser's built-in validation UI when possible.
251
+ */
252
+ public reportValidity(): boolean {
253
+ if (this.fieldElement) {
254
+ return this.fieldElement.reportValidity();
255
+ }
256
+ return this.checkValidity();
257
+ }
258
+
124
259
  /**
125
260
  * Render the field component
126
261
  * Override in subclass to provide custom rendering
@@ -4,7 +4,6 @@ import colorCSS from '../styles/color.css?inline';
4
4
  import alertStyle from '../styles/components/alert.css?inline';
5
5
  import AeicoComponent from '../aeico-component';
6
6
  import { html } from 'aeico';
7
- import { t } from 'aeico-localize';
8
7
  import type { AlertColor, AlertSize, AlertVariant } from './defines';
9
8
 
10
9
  /**
@@ -39,6 +38,7 @@ class Alert extends AeicoComponent {
39
38
  size: { type: String },
40
39
  dismissible: { type: Boolean },
41
40
  invisible: { type: Boolean },
41
+ closeText: { type: String },
42
42
  };
43
43
 
44
44
  protected static useStyles = ['alert'];
@@ -49,6 +49,7 @@ class Alert extends AeicoComponent {
49
49
  declare size?: AlertSize;
50
50
  declare dismissible?: boolean;
51
51
  declare invisible?: boolean;
52
+ declare closeText?: string;
52
53
 
53
54
  protected render() {
54
55
  return html(({ div, slot, button, span }) => {
@@ -67,7 +68,7 @@ class Alert extends AeicoComponent {
67
68
  {
68
69
  className: 'alert-close',
69
70
  '@click': () => this._handleClose(),
70
- title: t('alert.close', 'Close alert'),
71
+ title: this.closeText || 'Close alert',
71
72
  },
72
73
  () => {
73
74
  span({ 'aria-hidden': 'true', textContent: '\u00d7' });
@@ -1,4 +1,4 @@
1
- export type CardVariant = 'subtle' | 'faint' | 'filled' | 'outlined';
1
+ export type CardVariant = 'subtle' | 'faint' | 'filled' | 'outlined' | 'text';
2
2
  export type CardColor =
3
3
  | 'default'
4
4
  | 'primary'
@@ -4,6 +4,8 @@ import { html } from 'aeico';
4
4
  import styleVariables from '../styles/variables.css?inline';
5
5
  import sizeCSS from '../styles/size.css?inline';
6
6
  import colorCSS from '../styles/color.css?inline';
7
+ import fieldLabelCSS from '../styles/components/field-label.css?inline';
8
+ import actionButtonCSS from '../styles/components/action-button.css?inline';
7
9
  import styles from '../styles/components/checkbox.css?inline';
8
10
  import { CheckboxVariant } from './defines';
9
11
 
@@ -22,7 +24,14 @@ class Checkbox extends AeicoField<boolean> {
22
24
  declare defaultChecked?: boolean;
23
25
  declare variant?: CheckboxVariant;
24
26
 
25
- protected static styles = [styleVariables, sizeCSS, colorCSS, styles];
27
+ protected static styles = [
28
+ styleVariables,
29
+ sizeCSS,
30
+ colorCSS,
31
+ fieldLabelCSS,
32
+ actionButtonCSS,
33
+ styles,
34
+ ];
26
35
 
27
36
  protected getValue(): boolean {
28
37
  return this.fieldElement?.checked ?? false;
@@ -62,18 +71,24 @@ class Checkbox extends AeicoField<boolean> {
62
71
 
63
72
  render() {
64
73
  return html(({ div, input }) => {
65
- div({ className: 'checkbox-container', variant: this.variant }, () => {
74
+ const id = this.getFieldId();
75
+ this.renderLabel(id);
76
+ div({ className: 'checkbox-container field-body', variant: this.variant }, () => {
66
77
  div({ className: 'checkbox-wrapper' }, () => {
67
78
  this.fieldElement = input({
79
+ id,
68
80
  type: 'checkbox',
69
81
  className: 'field-input',
70
82
  checked: Boolean(this.checked),
71
83
  disabled: Boolean(this.disabled),
84
+ required: Boolean(this.required),
72
85
  '@change': this.boundOnChange,
73
86
  });
74
87
  });
75
88
  this.renderActionButtons();
76
89
  });
90
+ this.renderHelperText();
91
+ this.renderError();
77
92
  });
78
93
  }
79
94
  }
@@ -0,0 +1,150 @@
1
+ import type { InferProps } from 'aeico';
2
+ import styleVariables from '../styles/variables.css?inline';
3
+ import sizeCSS from '../styles/size.css?inline';
4
+ import colorCSS from '../styles/color.css?inline';
5
+ import copyButtonStyle from '../styles/components/copy-button.css?inline';
6
+ import AeicoComponent from '../aeico-component';
7
+ import { html } from 'aeico';
8
+ import { prop } from 'aeico';
9
+ // Ensure ae-icon and ae-tooltip are registered
10
+ import '../icon/icon';
11
+ import '../tooltip/tooltip';
12
+ import type Tooltip from '../tooltip/tooltip';
13
+ import type { TooltipPlacement } from '../tooltip/defines';
14
+ import type { CopyButtonColor, CopyButtonSize, CopyButtonVariant } from './defines';
15
+
16
+ class CopyButton extends AeicoComponent {
17
+ protected static styles = [styleVariables, sizeCSS, colorCSS, copyButtonStyle];
18
+
19
+ @prop({ type: String })
20
+ accessor text: string | undefined;
21
+
22
+ @prop({ type: String })
23
+ accessor color: CopyButtonColor | undefined;
24
+
25
+ @prop({ type: String })
26
+ accessor variant: CopyButtonVariant | undefined;
27
+
28
+ @prop({ type: String })
29
+ accessor size: CopyButtonSize | undefined;
30
+
31
+ @prop({ type: Boolean })
32
+ accessor disabled: boolean | undefined;
33
+
34
+ @prop({ type: Number })
35
+ accessor duration: number | undefined;
36
+
37
+ @prop({ type: String })
38
+ accessor tooltip: string = 'Copy';
39
+
40
+ @prop({ type: String })
41
+ accessor tooltipCopied: string = 'Copied!';
42
+
43
+ @prop({ type: String })
44
+ accessor tooltipPlacement: TooltipPlacement = 'top';
45
+
46
+ private _slotElement: HTMLSlotElement | null = null;
47
+ private _tooltipEl: Tooltip | null = null;
48
+ private _resetTimer: ReturnType<typeof setTimeout> | null = null;
49
+
50
+ private _getTextToCopy(): string {
51
+ if (this.text != null) return this.text;
52
+
53
+ const nodes = this._slotElement?.assignedNodes({ flatten: true }) ?? [];
54
+
55
+ return nodes
56
+ .reduce<string>((text, n) => {
57
+ if (n.nodeType === Node.TEXT_NODE) return text + (n.textContent ?? '');
58
+
59
+ return text;
60
+ }, '')
61
+ .trim();
62
+ }
63
+
64
+ private _handleClick = () => {
65
+ if (this.disabled) return;
66
+
67
+ const textToCopy = this._getTextToCopy();
68
+
69
+ void navigator.clipboard.writeText(textToCopy).then(() => {
70
+ this.setAttribute('copied', '');
71
+
72
+ if (this._tooltipEl) {
73
+ this._tooltipEl.content = this.tooltipCopied;
74
+ this._tooltipEl.open = true;
75
+ }
76
+
77
+ if (this._resetTimer !== null) {
78
+ clearTimeout(this._resetTimer);
79
+ }
80
+ const duration = this.duration ?? 2000;
81
+ this._resetTimer = setTimeout(() => {
82
+ this.removeAttribute('copied');
83
+ if (this._tooltipEl) {
84
+ this._tooltipEl.content = this.tooltip;
85
+ this._tooltipEl.open = false;
86
+ }
87
+ this._resetTimer = null;
88
+ }, duration);
89
+
90
+ this.dispatchEvent(
91
+ new CustomEvent('copy', {
92
+ bubbles: true,
93
+ composed: true,
94
+ detail: { text: textToCopy },
95
+ }),
96
+ );
97
+ });
98
+ };
99
+
100
+ protected onUnmounted() {
101
+ if (this._resetTimer !== null) {
102
+ clearTimeout(this._resetTimer);
103
+ this._resetTimer = null;
104
+ }
105
+ }
106
+
107
+ protected render() {
108
+ return html(({ aeTooltip, button, span, slot, aeIcon }) => {
109
+ this._tooltipEl = aeTooltip(
110
+ {
111
+ content: this.tooltip,
112
+ placement: this.tooltipPlacement,
113
+ disabled: this.disabled,
114
+ },
115
+ () => {
116
+ button(
117
+ {
118
+ type: 'button',
119
+ disabled: this.disabled,
120
+ part: 'button',
121
+ 'aria-label': this.tooltip,
122
+ 'aria-disabled': this.disabled,
123
+ '@click': this._handleClick,
124
+ },
125
+ () => {
126
+ span({ className: 'icon-copy' }, () => {
127
+ aeIcon({ name: 'copy', size: this.size });
128
+ });
129
+ span({ className: 'icon-check' }, () => {
130
+ aeIcon({ name: 'check', size: this.size });
131
+ });
132
+ this._slotElement = slot();
133
+ },
134
+ );
135
+ },
136
+ );
137
+ });
138
+ }
139
+ }
140
+
141
+ CopyButton.register();
142
+
143
+ declare global {
144
+ interface HTMLElementTagNameMap {
145
+ 'ae-copy-button': CopyButton;
146
+ }
147
+ }
148
+
149
+ export default CopyButton;
150
+ export type CopyButtonProps = InferProps<typeof CopyButton>;
@@ -0,0 +1,5 @@
1
+ export type {
2
+ ButtonColor as CopyButtonColor,
3
+ ButtonVariant as CopyButtonVariant,
4
+ ButtonSize as CopyButtonSize,
5
+ } from '../button/defines';
@@ -0,0 +1,3 @@
1
+ export { default, default as CopyButton } from './copy-button';
2
+ export type { CopyButtonProps } from './copy-button';
3
+ export type { CopyButtonColor, CopyButtonSize, CopyButtonVariant } from './defines';
@@ -1,4 +1,5 @@
1
- export type DetailVariant = 'subtle' | 'faint' | 'filled' | 'outlined';
1
+ export type DetailVariant = 'subtle' | 'faint' | 'filled' | 'outlined' | 'text';
2
+ export type DetailIconPlacement = 'start' | 'end';
2
3
  export type DetailColor =
3
4
  | 'default'
4
5
  | 'primary'
@@ -5,7 +5,7 @@ import detailStyle from '../styles/components/detail.css?inline';
5
5
  import AeicoComponent from '../aeico-component';
6
6
  import { html } from 'aeico';
7
7
  import { prop } from 'aeico';
8
- import type { DetailColor, DetailVariant } from './defines';
8
+ import type { DetailColor, DetailVariant, DetailIconPlacement } from './defines';
9
9
 
10
10
  /**
11
11
  * Detail component that can be used to show/hide additional content.
@@ -35,6 +35,9 @@ class Detail extends AeicoComponent {
35
35
  @prop({ type: Boolean })
36
36
  accessor disabled: boolean = false;
37
37
 
38
+ @prop({ type: String })
39
+ accessor iconPlacement: DetailIconPlacement = 'end';
40
+
38
41
  private _open: boolean = false;
39
42
 
40
43
  /** Opens the detail panel. */
@@ -120,3 +123,4 @@ declare global {
120
123
 
121
124
  export default Detail;
122
125
  export type DetailProps = InferProps<typeof Detail>;
126
+ export type { DetailIconPlacement };
@@ -1,3 +1,3 @@
1
1
  export { default, default as Detail } from './detail';
2
2
  export type { DetailProps } from './detail';
3
- export type { DetailVariant, DetailColor } from './defines';
3
+ export type { DetailVariant, DetailColor, DetailIconPlacement } from './defines';
@@ -0,0 +1,104 @@
1
+ import type { InferProps, Props } from 'aeico';
2
+ import styleVariables from '../styles/variables.css?inline';
3
+ import detailGroupStyle from '../styles/components/detail-group.css?inline';
4
+ import AeicoComponent from '../aeico-component';
5
+ import { html } from 'aeico';
6
+ import type Detail from '../detail/detail';
7
+ import type { DetailColor, DetailVariant } from '../detail/defines';
8
+
9
+ /**
10
+ * DetailGroup component — wraps multiple `ae-detail` elements into an
11
+ * accordion-style group. By default only one item can be open at a time;
12
+ * set `multiple` to allow several items open simultaneously.
13
+ *
14
+ * @example
15
+ * ```html
16
+ * <ae-detail-group>
17
+ * <ae-detail summary="Section 1">Content 1</ae-detail>
18
+ * <ae-detail summary="Section 2">Content 2</ae-detail>
19
+ * <ae-detail summary="Section 3">Content 3</ae-detail>
20
+ * </ae-detail-group>
21
+ * ```
22
+ */
23
+ class DetailGroup extends AeicoComponent {
24
+ static tagName = 'detail-group';
25
+
26
+ static props: Props = {
27
+ multiple: { type: Boolean },
28
+ variant: { type: String },
29
+ color: { type: String },
30
+ };
31
+
32
+ protected static styles = [styleVariables, detailGroupStyle];
33
+
34
+ declare multiple?: boolean;
35
+ declare variant?: DetailVariant;
36
+ declare color?: DetailColor;
37
+
38
+ private slotEl: HTMLSlotElement | null = null;
39
+
40
+ private readonly DETAIL_RADIUS = 6;
41
+
42
+ connectedCallback() {
43
+ super.connectedCallback();
44
+ this.listen('open', this._handleOpen);
45
+ }
46
+
47
+ private _getDetails(): Detail[] {
48
+ if (!this.slotEl) return [];
49
+ return (this.slotEl.assignedElements({ flatten: true }) as Detail[]).filter(
50
+ (el) => el.tagName.toLowerCase() === 'ae-detail',
51
+ );
52
+ }
53
+
54
+ private _syncChildren(): void {
55
+ const details = this._getDetails();
56
+ const r = this.DETAIL_RADIUS;
57
+
58
+ details.forEach((detail, i) => {
59
+ const isFirst = i === 0;
60
+ const isLast = i === details.length - 1;
61
+
62
+ // Collapse borders between adjacent items
63
+ detail.style.marginTop = isFirst ? '' : '-1px';
64
+
65
+ // Adjust per-corner radius so only the outer edges of the group keep it
66
+ detail.style.setProperty('--detail-r-tl', isFirst ? `${r}px` : '0');
67
+ detail.style.setProperty('--detail-r-tr', isFirst ? `${r}px` : '0');
68
+ detail.style.setProperty('--detail-r-br', isLast ? `${r}px` : '0');
69
+ detail.style.setProperty('--detail-r-bl', isLast ? `${r}px` : '0');
70
+
71
+ // Propagate variant / color only when explicitly set on the group
72
+ if (this.variant !== undefined) detail.variant = this.variant;
73
+ if (this.color !== undefined) detail.color = this.color;
74
+ });
75
+ }
76
+
77
+ private _handleOpen = (event: Event): void => {
78
+ if (this.multiple) return;
79
+ const opened = event.target as Element;
80
+ this._getDetails().forEach((detail) => {
81
+ if (detail !== opened) detail.close();
82
+ });
83
+ };
84
+
85
+ protected render() {
86
+ return html(({ slot }) => {
87
+ this.slotEl = slot({
88
+ '@slotchange': () => this._syncChildren(),
89
+ });
90
+ this._syncChildren();
91
+ });
92
+ }
93
+ }
94
+
95
+ DetailGroup.register();
96
+
97
+ declare global {
98
+ interface HTMLElementTagNameMap {
99
+ 'ae-detail-group': DetailGroup;
100
+ }
101
+ }
102
+
103
+ export default DetailGroup;
104
+ export type DetailGroupProps = InferProps<typeof DetailGroup>;
@@ -0,0 +1,2 @@
1
+ export { default, default as DetailGroup } from './detail-group';
2
+ export type { DetailGroupProps } from './detail-group';
@@ -0,0 +1 @@
1
+ export type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom';