aeico-components 0.1.4 → 0.1.6

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 (299) 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 +4 -4
  8. package/dist/chunks/alert.cjs.map +1 -1
  9. package/dist/chunks/alert.js +5 -5
  10. package/dist/chunks/alert.js.map +1 -1
  11. package/dist/chunks/badge.cjs +1 -1
  12. package/dist/chunks/badge.cjs.map +1 -1
  13. package/dist/chunks/badge.js +2 -2
  14. package/dist/chunks/badge.js.map +1 -1
  15. package/dist/chunks/breadcrumb-item.cjs +2 -2
  16. package/dist/chunks/breadcrumb-item.cjs.map +1 -1
  17. package/dist/chunks/breadcrumb-item.js +3 -3
  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 +12 -15
  24. package/dist/chunks/button.cjs.map +1 -1
  25. package/dist/chunks/button.js +13 -16
  26. package/dist/chunks/button.js.map +1 -1
  27. package/dist/chunks/card.cjs +1 -1
  28. package/dist/chunks/card.cjs.map +1 -1
  29. package/dist/chunks/card.js +2 -2
  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/copy-button.cjs +168 -0
  36. package/dist/chunks/copy-button.cjs.map +1 -0
  37. package/dist/chunks/copy-button.js +169 -0
  38. package/dist/chunks/copy-button.js.map +1 -0
  39. package/dist/chunks/detail.cjs +7 -4
  40. package/dist/chunks/detail.cjs.map +1 -1
  41. package/dist/chunks/detail.js +8 -5
  42. package/dist/chunks/detail.js.map +1 -1
  43. package/dist/chunks/dialog.cjs +1 -1
  44. package/dist/chunks/dialog.cjs.map +1 -1
  45. package/dist/chunks/dialog.js +2 -2
  46. package/dist/chunks/dialog.js.map +1 -1
  47. package/dist/chunks/divider.cjs +1 -1
  48. package/dist/chunks/divider.cjs.map +1 -1
  49. package/dist/chunks/divider.js +2 -2
  50. package/dist/chunks/divider.js.map +1 -1
  51. package/dist/chunks/drawer.cjs +180 -0
  52. package/dist/chunks/drawer.cjs.map +1 -0
  53. package/dist/chunks/drawer.js +181 -0
  54. package/dist/chunks/drawer.js.map +1 -0
  55. package/dist/chunks/dropdown-button.cjs +2 -2
  56. package/dist/chunks/dropdown-button.cjs.map +1 -1
  57. package/dist/chunks/dropdown-button.js +6 -6
  58. package/dist/chunks/dropdown-button.js.map +1 -1
  59. package/dist/chunks/icon.cjs +31 -1
  60. package/dist/chunks/icon.cjs.map +1 -1
  61. package/dist/chunks/icon.js +32 -2
  62. package/dist/chunks/icon.js.map +1 -1
  63. package/dist/chunks/menu.cjs +396 -0
  64. package/dist/chunks/menu.cjs.map +1 -0
  65. package/dist/chunks/menu.js +397 -0
  66. package/dist/chunks/menu.js.map +1 -0
  67. package/dist/chunks/navbar.cjs +2 -3
  68. package/dist/chunks/navbar.cjs.map +1 -1
  69. package/dist/chunks/navbar.js +3 -4
  70. package/dist/chunks/navbar.js.map +1 -1
  71. package/dist/chunks/pagination.cjs +475 -0
  72. package/dist/chunks/pagination.cjs.map +1 -0
  73. package/dist/chunks/pagination.js +476 -0
  74. package/dist/chunks/pagination.js.map +1 -0
  75. package/dist/chunks/progress-bar.cjs +101 -0
  76. package/dist/chunks/progress-bar.cjs.map +1 -0
  77. package/dist/chunks/progress-bar.js +102 -0
  78. package/dist/chunks/progress-bar.js.map +1 -0
  79. package/dist/chunks/radio.cjs +11 -7
  80. package/dist/chunks/radio.cjs.map +1 -1
  81. package/dist/chunks/radio.js +11 -7
  82. package/dist/chunks/radio.js.map +1 -1
  83. package/dist/chunks/select.cjs +97 -66
  84. package/dist/chunks/select.cjs.map +1 -1
  85. package/dist/chunks/select.js +97 -66
  86. package/dist/chunks/select.js.map +1 -1
  87. package/dist/chunks/slider.cjs +9 -46
  88. package/dist/chunks/slider.cjs.map +1 -1
  89. package/dist/chunks/slider.js +9 -46
  90. package/dist/chunks/slider.js.map +1 -1
  91. package/dist/chunks/spinner.cjs +102 -0
  92. package/dist/chunks/spinner.cjs.map +1 -0
  93. package/dist/chunks/spinner.js +103 -0
  94. package/dist/chunks/spinner.js.map +1 -0
  95. package/dist/chunks/switch.cjs +110 -16
  96. package/dist/chunks/switch.cjs.map +1 -1
  97. package/dist/chunks/switch.js +111 -17
  98. package/dist/chunks/switch.js.map +1 -1
  99. package/dist/chunks/tab-panel.cjs +6 -7
  100. package/dist/chunks/tab-panel.cjs.map +1 -1
  101. package/dist/chunks/tab-panel.js +7 -8
  102. package/dist/chunks/tab-panel.js.map +1 -1
  103. package/dist/chunks/tag.cjs +1 -1
  104. package/dist/chunks/tag.cjs.map +1 -1
  105. package/dist/chunks/tag.js +2 -2
  106. package/dist/chunks/tag.js.map +1 -1
  107. package/dist/chunks/text-input.cjs +11 -16
  108. package/dist/chunks/text-input.cjs.map +1 -1
  109. package/dist/chunks/text-input.js +11 -16
  110. package/dist/chunks/text-input.js.map +1 -1
  111. package/dist/chunks/textarea.cjs +137 -0
  112. package/dist/chunks/textarea.cjs.map +1 -0
  113. package/dist/chunks/textarea.js +138 -0
  114. package/dist/chunks/textarea.js.map +1 -0
  115. package/dist/chunks/tooltip.cjs +293 -0
  116. package/dist/chunks/tooltip.cjs.map +1 -0
  117. package/dist/chunks/tooltip.js +294 -0
  118. package/dist/chunks/tooltip.js.map +1 -0
  119. package/dist/chunks/tree.cjs +468 -0
  120. package/dist/chunks/tree.cjs.map +1 -0
  121. package/dist/chunks/tree.js +469 -0
  122. package/dist/chunks/tree.js.map +1 -0
  123. package/dist/chunks/variables.cjs +2 -2
  124. package/dist/chunks/variables.js +2 -2
  125. package/dist/copy-button.cjs +6 -0
  126. package/dist/copy-button.cjs.map +1 -0
  127. package/dist/copy-button.js +6 -0
  128. package/dist/copy-button.js.map +1 -0
  129. package/dist/drawer.cjs +6 -0
  130. package/dist/drawer.cjs.map +1 -0
  131. package/dist/drawer.js +6 -0
  132. package/dist/drawer.js.map +1 -0
  133. package/dist/dropdown.js +4 -4
  134. package/dist/index.cjs +186 -0
  135. package/dist/index.cjs.map +1 -1
  136. package/dist/index.js +201 -15
  137. package/dist/index.js.map +1 -1
  138. package/dist/menu.cjs +6 -0
  139. package/dist/menu.cjs.map +1 -0
  140. package/dist/menu.js +6 -0
  141. package/dist/menu.js.map +1 -0
  142. package/dist/pagination.cjs +6 -0
  143. package/dist/pagination.cjs.map +1 -0
  144. package/dist/pagination.js +6 -0
  145. package/dist/pagination.js.map +1 -0
  146. package/dist/progress-bar.cjs +6 -0
  147. package/dist/progress-bar.cjs.map +1 -0
  148. package/dist/progress-bar.js +6 -0
  149. package/dist/progress-bar.js.map +1 -0
  150. package/dist/select.cjs +1 -1
  151. package/dist/select.cjs.map +1 -1
  152. package/dist/select.js +2 -2
  153. package/dist/select.js.map +1 -1
  154. package/dist/spinner.cjs +6 -0
  155. package/dist/spinner.cjs.map +1 -0
  156. package/dist/spinner.js +6 -0
  157. package/dist/spinner.js.map +1 -0
  158. package/dist/textarea.cjs +5 -0
  159. package/dist/textarea.cjs.map +1 -0
  160. package/dist/textarea.js +5 -0
  161. package/dist/textarea.js.map +1 -0
  162. package/dist/tooltip.cjs +6 -0
  163. package/dist/tooltip.cjs.map +1 -0
  164. package/dist/tooltip.js +6 -0
  165. package/dist/tooltip.js.map +1 -0
  166. package/dist/tree.cjs +6 -0
  167. package/dist/tree.cjs.map +1 -0
  168. package/dist/tree.js +6 -0
  169. package/dist/tree.js.map +1 -0
  170. package/dist/types/aeico-field.d.ts +57 -5
  171. package/dist/types/alert/alert.d.ts +1 -0
  172. package/dist/types/button/button.d.ts +2 -1
  173. package/dist/types/checkbox/checkbox.d.ts +5 -5
  174. package/dist/types/copy-button/copy-button.d.ts +32 -0
  175. package/dist/types/copy-button/defines.d.ts +1 -0
  176. package/dist/types/copy-button/index.d.ts +3 -0
  177. package/dist/types/detail/defines.d.ts +1 -0
  178. package/dist/types/detail/detail.d.ts +3 -1
  179. package/dist/types/detail/index.d.ts +1 -1
  180. package/dist/types/detail-group/detail-group.d.ts +39 -0
  181. package/dist/types/detail-group/index.d.ts +2 -0
  182. package/dist/types/drawer/defines.d.ts +1 -0
  183. package/dist/types/drawer/drawer.d.ts +31 -0
  184. package/dist/types/drawer/index.d.ts +3 -0
  185. package/dist/types/icon/built-in-icons.d.ts +1 -0
  186. package/dist/types/icon/icon.d.ts +1 -0
  187. package/dist/types/icon/registry.d.ts +8 -0
  188. package/dist/types/index.d.ts +19 -0
  189. package/dist/types/menu/defines.d.ts +15 -0
  190. package/dist/types/menu/index.d.ts +5 -0
  191. package/dist/types/menu/menu-item.d.ts +63 -0
  192. package/dist/types/menu/menu.d.ts +34 -0
  193. package/dist/types/number-input/index.d.ts +2 -0
  194. package/dist/types/number-input/number-input.d.ts +35 -0
  195. package/dist/types/pagination/defines.d.ts +2 -0
  196. package/dist/types/pagination/index.d.ts +3 -0
  197. package/dist/types/pagination/pagination.d.ts +77 -0
  198. package/dist/types/progress-bar/defines.d.ts +1 -0
  199. package/dist/types/progress-bar/index.d.ts +3 -0
  200. package/dist/types/progress-bar/progress-bar.d.ts +37 -0
  201. package/dist/types/radio-group/radio-group.d.ts +1 -1
  202. package/dist/types/select/select.d.ts +3 -3
  203. package/dist/types/spinner/defines.d.ts +3 -0
  204. package/dist/types/spinner/index.d.ts +3 -0
  205. package/dist/types/spinner/spinner.d.ts +35 -0
  206. package/dist/types/switch/defines.d.ts +1 -0
  207. package/dist/types/switch/switch.d.ts +13 -9
  208. package/dist/types/text-input/text-input.d.ts +0 -4
  209. package/dist/types/textarea/index.d.ts +1 -0
  210. package/dist/types/textarea/textarea.d.ts +26 -0
  211. package/dist/types/tooltip/defines.d.ts +2 -0
  212. package/dist/types/tooltip/index.d.ts +4 -0
  213. package/dist/types/tooltip/tooltip.d.ts +48 -0
  214. package/dist/types/tree/defines.d.ts +23 -0
  215. package/dist/types/tree/index.d.ts +5 -0
  216. package/dist/types/tree/tree-item.d.ts +54 -0
  217. package/dist/types/tree/tree.d.ts +64 -0
  218. package/package.json +6 -6
  219. package/src/aeico-field.ts +154 -15
  220. package/src/alert/alert.ts +3 -2
  221. package/src/button/button.ts +11 -13
  222. package/src/checkbox/checkbox.ts +21 -6
  223. package/src/copy-button/copy-button.ts +146 -0
  224. package/src/copy-button/defines.ts +5 -0
  225. package/src/copy-button/index.ts +3 -0
  226. package/src/detail/defines.ts +1 -0
  227. package/src/detail/detail.ts +5 -1
  228. package/src/detail/index.ts +1 -1
  229. package/src/detail-group/detail-group.ts +104 -0
  230. package/src/detail-group/index.ts +2 -0
  231. package/src/drawer/defines.ts +1 -0
  232. package/src/drawer/drawer.ts +157 -0
  233. package/src/drawer/index.ts +3 -0
  234. package/src/icon/built-in-icons.ts +21 -0
  235. package/src/icon/icon.ts +1 -0
  236. package/src/icon/registry.ts +22 -0
  237. package/src/index.ts +32 -0
  238. package/src/menu/defines.ts +17 -0
  239. package/src/menu/index.ts +5 -0
  240. package/src/menu/menu-item.ts +315 -0
  241. package/src/menu/menu.ts +81 -0
  242. package/src/navbar/navbar.ts +1 -3
  243. package/src/number-input/index.ts +2 -0
  244. package/src/number-input/number-input.ts +137 -0
  245. package/src/pagination/defines.ts +2 -0
  246. package/src/pagination/index.ts +3 -0
  247. package/src/pagination/pagination.ts +310 -0
  248. package/src/progress-bar/defines.ts +8 -0
  249. package/src/progress-bar/index.ts +3 -0
  250. package/src/progress-bar/progress-bar.ts +80 -0
  251. package/src/radio-group/radio-group.ts +12 -5
  252. package/src/select/select.ts +112 -71
  253. package/src/slider/slider.ts +9 -2
  254. package/src/spinner/defines.ts +12 -0
  255. package/src/spinner/index.ts +3 -0
  256. package/src/spinner/spinner.ts +81 -0
  257. package/src/styles/components/action-button.css +37 -0
  258. package/src/styles/components/checkbox.css +4 -26
  259. package/src/styles/components/copy-button.css +119 -0
  260. package/src/styles/components/detail-group.css +10 -0
  261. package/src/styles/components/detail.css +10 -1
  262. package/src/styles/components/drawer.css +161 -0
  263. package/src/styles/components/field-label.css +120 -0
  264. package/src/styles/components/menu-item.css +168 -0
  265. package/src/styles/components/menu.css +17 -0
  266. package/src/styles/components/number-input.css +167 -0
  267. package/src/styles/components/pagination.css +205 -0
  268. package/src/styles/components/progress-bar.css +44 -0
  269. package/src/styles/components/radio-group.css +0 -23
  270. package/src/styles/components/select.css +12 -39
  271. package/src/styles/components/slider.css +0 -42
  272. package/src/styles/components/spinner.css +80 -0
  273. package/src/styles/components/switch.css +68 -19
  274. package/src/styles/components/tab-panel.css +1 -1
  275. package/src/styles/components/tabs.css +1 -0
  276. package/src/styles/components/text-input.css +7 -45
  277. package/src/styles/components/textarea.css +75 -0
  278. package/src/styles/components/tooltip.css +103 -0
  279. package/src/styles/components/tree-item.css +152 -0
  280. package/src/styles/components/tree.css +10 -0
  281. package/src/styles/layout.css +457 -25
  282. package/src/switch/defines.ts +1 -0
  283. package/src/switch/switch.ts +65 -16
  284. package/src/tabs/tab.ts +1 -1
  285. package/src/tabs/tabs.ts +1 -2
  286. package/src/text-input/text-input.ts +10 -15
  287. package/src/textarea/index.ts +1 -0
  288. package/src/textarea/textarea.ts +107 -0
  289. package/src/tooltip/defines.ts +11 -0
  290. package/src/tooltip/index.ts +4 -0
  291. package/src/tooltip/tooltip.ts +183 -0
  292. package/src/tree/defines.ts +26 -0
  293. package/src/tree/index.ts +5 -0
  294. package/src/tree/tree-item.ts +258 -0
  295. package/src/tree/tree.ts +237 -0
  296. package/dist/chunks/aeico-field.cjs +0 -179
  297. package/dist/chunks/aeico-field.cjs.map +0 -1
  298. package/dist/chunks/aeico-field.js +0 -180
  299. package/dist/chunks/aeico-field.js.map +0 -1
@@ -1,7 +1,6 @@
1
1
  import AeicoField from '../aeico-field';
2
2
  import type { InferProps } from 'aeico';
3
3
  import { html, tags } from 'aeico';
4
- import { t } from 'aeico-localize';
5
4
  import type {
6
5
  SelectOptionValue,
7
6
  SelectOption,
@@ -12,6 +11,8 @@ import type {
12
11
  import style from '../styles/components/select.css?inline';
13
12
  import variables from '../styles/variables.css?inline';
14
13
  import sizeCSS from '../styles/size.css?inline';
14
+ import fieldLabelCSS from '../styles/components/field-label.css?inline';
15
+ import actionButtonCSS from '../styles/components/action-button.css?inline';
15
16
  import SelectOptionElement from './select-option';
16
17
  import '../tag/tag';
17
18
  import { prop } from 'aeico';
@@ -28,7 +29,7 @@ import { prop } from 'aeico';
28
29
  *
29
30
  */
30
31
  class Select extends AeicoField<SelectOptionValue | SelectMultiValue> {
31
- protected fieldElement = null;
32
+ protected fieldElement: HTMLInputElement | null = null;
32
33
  private _isOpen = false;
33
34
  private _triggerEl: HTMLElement | null = null;
34
35
  private _dropdownEl: HTMLElement | null = null;
@@ -101,13 +102,13 @@ class Select extends AeicoField<SelectOptionValue | SelectMultiValue> {
101
102
  })
102
103
  override defaultValue: SelectOptionValue | SelectMultiValue | undefined = undefined;
103
104
 
104
- protected static styles = [variables, sizeCSS, style];
105
+ protected static styles = [variables, sizeCSS, fieldLabelCSS, actionButtonCSS, style];
105
106
 
106
107
  protected writeValue(_value: SelectOptionValue | SelectMultiValue): void {
107
108
  // Reactive re-render via this.value prop change handles the display update
108
109
  }
109
110
 
110
- protected getValue(): any {
111
+ protected getValue(): SelectOptionValue | SelectMultiValue {
111
112
  if (this.multiple) return this._getMultiValues();
112
113
 
113
114
  return this.value || '';
@@ -124,7 +125,8 @@ class Select extends AeicoField<SelectOptionValue | SelectMultiValue> {
124
125
  // disabled is a reactive prop — render() already picks it up automatically
125
126
  }
126
127
 
127
- protected onUpdated(_changedProps: Map<string, unknown>): void {
128
+ protected onUpdated(changedProps: Map<string, unknown>): void {
129
+ super.onUpdated(changedProps);
128
130
  if (!this.multiple || this.expandable) {
129
131
  if (this._expanded) this._expanded = false;
130
132
  return;
@@ -140,7 +142,7 @@ class Select extends AeicoField<SelectOptionValue | SelectMultiValue> {
140
142
  if (Array.isArray(this.options)) {
141
143
  for (const opt of this.options) {
142
144
  if (this._isSelectOption(opt)) {
143
- if (String(opt.value) === strVal) return t(opt.label, opt.label);
145
+ if (String(opt.value) === strVal) return opt.label;
144
146
  } else {
145
147
  if (String(opt) === strVal) return strVal;
146
148
  }
@@ -261,74 +263,113 @@ class Select extends AeicoField<SelectOptionValue | SelectMultiValue> {
261
263
  this._syncSlotOptionsSelected();
262
264
 
263
265
  return html(({ div, span, slot }) => {
264
- div({ className: 'container' }, () => {
265
- this._triggerEl = div(
266
- {
267
- className: `trigger${this._isOpen ? ' open' : ''}${isDisabled ? ' disabled' : ''}`,
268
- '@click': () => {
269
- if (isDisabled) return;
270
-
271
- this._toggleDropdown();
266
+ const id = this.getFieldId();
267
+ this.renderLabel(id);
268
+ div(
269
+ {
270
+ id,
271
+ 'aria-labelledby': this.label ? `${id}-label` : undefined,
272
+ className: 'container field-body',
273
+ },
274
+ () => {
275
+ this._triggerEl = div(
276
+ {
277
+ className: `trigger${this._isOpen ? ' open' : ''}${isDisabled ? ' disabled' : ''}`,
278
+ '@click': () => {
279
+ if (isDisabled) return;
280
+
281
+ this._toggleDropdown();
282
+ },
272
283
  },
273
- },
274
- () => {
275
- if (this.multiple) {
276
- if (hasMultiSelection) {
277
- this._selectedListEl = div(
278
- {
279
- className: `selected-list${!this.expandable ? ' selected-list--clipped' : ''}`,
280
- },
281
- () => {
282
- for (const v of multiValues) {
283
- const lbl = this._findLabel(v);
284
- tags.aeTag({
285
- key: `sel-${v}`,
286
- color: 'default',
287
- variant: 'faint',
288
- dismissible: true,
289
- disabled: isDisabled,
290
- textContent: lbl,
291
- '@dismiss': (e: Event) => {
292
- e.stopPropagation();
293
- if (isDisabled) return;
294
-
295
- const next = multiValues.filter((item) => String(item) !== String(v));
296
- this.setValue(next, { silent: false, action: 'change' });
297
- },
298
- });
299
- }
300
- },
301
- );
302
- if (!this.expandable && this._expanded) {
303
- span({ className: 'overflow-indicator', textContent: '…' });
284
+ () => {
285
+ if (this.multiple) {
286
+ if (hasMultiSelection) {
287
+ this._selectedListEl = div(
288
+ {
289
+ className: `selected-list${!this.expandable ? ' selected-list--clipped' : ''}`,
290
+ },
291
+ () => {
292
+ for (const v of multiValues) {
293
+ const lbl = this._findLabel(v);
294
+ tags.aeTag({
295
+ key: `sel-${v}`,
296
+ color: 'default',
297
+ variant: 'faint',
298
+ dismissible: true,
299
+ disabled: isDisabled,
300
+ textContent: lbl,
301
+ '@dismiss': (e: Event) => {
302
+ e.stopPropagation();
303
+ if (isDisabled) return;
304
+
305
+ const next = multiValues.filter((item) => String(item) !== String(v));
306
+ this.setValue(next, { silent: false, action: 'change' });
307
+ },
308
+ });
309
+ }
310
+ },
311
+ );
312
+ if (!this.expandable && this._expanded) {
313
+ span({ className: 'overflow-indicator', textContent: '…' });
314
+ }
315
+ } else {
316
+ span({ className: 'value placeholder', textContent: this.placeholder || '' });
304
317
  }
305
318
  } else {
306
- span({ className: 'value placeholder', textContent: this.placeholder || '' });
307
- }
308
- } else {
309
- if (selectedLabel) {
310
- span({ className: 'value', textContent: selectedLabel });
311
- } else {
312
- span({ className: 'value placeholder', textContent: this.placeholder || '' });
319
+ if (selectedLabel) {
320
+ span({ className: 'value', textContent: selectedLabel });
321
+ } else {
322
+ span({ className: 'value placeholder', textContent: this.placeholder || '' });
323
+ }
313
324
  }
314
- }
315
- span({ className: 'arrow', textContent: '▾' });
316
- },
317
- );
318
-
319
- this._dropdownEl = div(
320
- {
321
- className: `dropdown position-${position}${this._isOpen ? ' open' : ''}`,
322
- },
323
- () => {
324
- this._renderProgrammaticOptions();
325
- this._slotEl = slot({
326
- '@slotchange': () => this._onSlotChange(),
327
- });
328
- },
329
- );
330
-
331
- this.renderActionButtons();
325
+ span({ className: 'arrow', textContent: '▾' });
326
+ },
327
+ );
328
+
329
+ this._dropdownEl = div(
330
+ {
331
+ className: `dropdown position-${position}${this._isOpen ? ' open' : ''}`,
332
+ },
333
+ () => {
334
+ this._renderProgrammaticOptions();
335
+ this._slotEl = slot({
336
+ '@slotchange': () => this._onSlotChange(),
337
+ });
338
+ },
339
+ );
340
+
341
+ this.renderActionButtons();
342
+ },
343
+ );
344
+ this.renderHelperText();
345
+ this.renderError();
346
+
347
+ // Visually-hidden input so native form constraint validation works for `required`.
348
+ // type="text" (not "hidden") is required — type="hidden" is exempt from constraint validation.
349
+ const currentValue =
350
+ this.value != null &&
351
+ this.value !== '' &&
352
+ !(Array.isArray(this.value) && this.value.length === 0)
353
+ ? String(this.value)
354
+ : '';
355
+ this.fieldElement = tags.input({
356
+ key: 'validation-input',
357
+ type: 'text',
358
+ 'aria-hidden': 'true',
359
+ tabIndex: -1,
360
+ required: Boolean(this.required),
361
+ value: currentValue,
362
+ style: {
363
+ position: 'absolute',
364
+ width: '0',
365
+ height: '0',
366
+ opacity: '0',
367
+ margin: '0',
368
+ padding: '0',
369
+ border: '0',
370
+ pointerEvents: 'none',
371
+ overflow: 'hidden',
372
+ },
332
373
  });
333
374
  });
334
375
  }
@@ -347,7 +388,7 @@ class Select extends AeicoField<SelectOptionValue | SelectMultiValue> {
347
388
  key: `opt-${opt.value}`,
348
389
  value: String(opt.value),
349
390
  label: opt.label,
350
- textContent: t(opt.label, opt.label),
391
+ textContent: opt.label,
351
392
  selected: isSelected ? true : undefined,
352
393
  });
353
394
  } else {
@@ -6,6 +6,8 @@ import style from '../styles/components/slider.css?inline';
6
6
  import variables from '../styles/variables.css?inline';
7
7
  import sizeCSS from '../styles/size.css?inline';
8
8
  import colorCSS from '../styles/color.css?inline';
9
+ import fieldLabelCSS from '../styles/components/field-label.css?inline';
10
+ import actionButtonCSS from '../styles/components/action-button.css?inline';
9
11
  import { prop } from 'aeico';
10
12
 
11
13
  class Slider extends AeicoField {
@@ -55,7 +57,7 @@ class Slider extends AeicoField {
55
57
  })
56
58
  accessor marks: SliderMarks | undefined;
57
59
 
58
- protected static styles = [variables, sizeCSS, colorCSS, style];
60
+ protected static styles = [variables, sizeCSS, colorCSS, fieldLabelCSS, actionButtonCSS, style];
59
61
 
60
62
  constructor() {
61
63
  super();
@@ -243,11 +245,14 @@ class Slider extends AeicoField {
243
245
  const attrs = this._getRangeAttrs(normalized);
244
246
 
245
247
  return html(({ div, input, span }) => {
246
- div({ className: 'range-container' }, () => {
248
+ const id = this.getFieldId();
249
+ this.renderLabel(id);
250
+ div({ className: 'range-container field-body' }, () => {
247
251
  // Wrap range + optional marks in a column so marks don't push siblings
248
252
  div({ key: 'range-wrapper', className: 'range-wrapper' }, () => {
249
253
  this.fieldElement = input({
250
254
  key: 'range',
255
+ id,
251
256
  type: 'range',
252
257
  min: attrs.min,
253
258
  max: attrs.max,
@@ -303,6 +308,8 @@ class Slider extends AeicoField {
303
308
  });
304
309
 
305
310
  if (this.value != null) this.writeValue(this.value);
311
+ this.renderHelperText();
312
+ this.renderError();
306
313
  });
307
314
  }
308
315
 
@@ -0,0 +1,12 @@
1
+ export type SpinnerVariant = 'border' | 'dots';
2
+ export type SpinnerSize = 'xs' | 'sm' | 'md' | 'lg';
3
+ export type SpinnerColor =
4
+ | 'default'
5
+ | 'primary'
6
+ | 'secondary'
7
+ | 'success'
8
+ | 'danger'
9
+ | 'warning'
10
+ | 'info'
11
+ | 'light'
12
+ | 'dark';
@@ -0,0 +1,3 @@
1
+ export { default, default as Spinner } from './spinner';
2
+ export type { SpinnerProps } from './spinner';
3
+ export type { SpinnerColor, SpinnerSize, SpinnerVariant } from './defines';
@@ -0,0 +1,81 @@
1
+ import AeicoComponent from '../aeico-component';
2
+ import type { InferProps } from 'aeico';
3
+ import { html, prop } from 'aeico';
4
+ import styleVariables from '../styles/variables.css?inline';
5
+ import sizeCSS from '../styles/size.css?inline';
6
+ import colorCSS from '../styles/color.css?inline';
7
+ import style from '../styles/components/spinner.css?inline';
8
+ import type { SpinnerColor, SpinnerSize, SpinnerVariant } from './defines';
9
+
10
+ /**
11
+ * Spinner — animated loading indicator.
12
+ *
13
+ * Supports two visual variants: a rotating ring (`border`, default) and
14
+ * three bouncing dots (`dots`). Size and colour are driven by the shared
15
+ * design-token system.
16
+ *
17
+ * @example
18
+ * ```html
19
+ * <ae-spinner></ae-spinner>
20
+ * <ae-spinner variant="dots" color="primary" size="lg"></ae-spinner>
21
+ * <ae-spinner color="success" speed="0.5s" label="Saving…"></ae-spinner>
22
+ * ```
23
+ */
24
+ class Spinner extends AeicoComponent {
25
+ static tagName = 'spinner';
26
+
27
+ @prop({ type: String })
28
+ accessor variant: SpinnerVariant = 'border';
29
+
30
+ @prop({ type: String })
31
+ accessor size: SpinnerSize = 'md';
32
+
33
+ @prop({ type: String })
34
+ accessor color: SpinnerColor = 'default';
35
+
36
+ @prop({ type: String })
37
+ accessor label: string = 'Loading…';
38
+
39
+ @prop({ type: String })
40
+ accessor speed: string | undefined;
41
+
42
+ protected static styles = [styleVariables, sizeCSS, colorCSS, style];
43
+
44
+ connectedCallback() {
45
+ super.connectedCallback();
46
+ this.setAttribute('role', 'status');
47
+ }
48
+
49
+ protected render() {
50
+ this.setAttribute('aria-label', this.label);
51
+
52
+ if (this.speed) {
53
+ this.style.setProperty('--spinner-speed', this.speed);
54
+ } else {
55
+ this.style.removeProperty('--spinner-speed');
56
+ }
57
+
58
+ const isDots = this.variant === 'dots';
59
+
60
+ return html(({ span }) => {
61
+ span({ part: 'track', className: 'track', 'aria-hidden': 'true' }, () => {
62
+ if (isDots) {
63
+ span({ className: 'dot' });
64
+ span({ className: 'dot' });
65
+ span({ className: 'dot' });
66
+ }
67
+ });
68
+ });
69
+ }
70
+ }
71
+
72
+ Spinner.register();
73
+
74
+ declare global {
75
+ interface HTMLElementTagNameMap {
76
+ 'ae-spinner': Spinner;
77
+ }
78
+ }
79
+
80
+ export default Spinner;
81
+ export type SpinnerProps = InferProps<typeof Spinner>;
@@ -0,0 +1,37 @@
1
+ /* Action button base styles — shared by clear-btn and reset-btn (standalone mode) */
2
+ .clear-btn:not(.action-btn),
3
+ .reset-btn:not(.action-btn) {
4
+ width: 1.333em;
5
+ height: 1.333em;
6
+ border: none;
7
+ cursor: pointer;
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ flex-shrink: 0;
12
+ line-height: 1;
13
+ }
14
+
15
+ .clear-btn:not(.action-btn) {
16
+ border-radius: var(--clear-btn-border-radius);
17
+ background: var(--clear-btn-bg);
18
+ color: var(--clear-btn-color);
19
+ transition: var(--clear-btn-transition);
20
+ }
21
+
22
+ .reset-btn:not(.action-btn) {
23
+ border-radius: var(--reset-btn-border-radius);
24
+ background: var(--reset-btn-bg);
25
+ color: var(--reset-btn-color);
26
+ transition: var(--reset-btn-transition);
27
+ }
28
+
29
+ .clear-btn:not(.action-btn):hover {
30
+ background: var(--clear-btn-bg-hover);
31
+ color: var(--clear-btn-color-hover);
32
+ }
33
+
34
+ .reset-btn:not(.action-btn):hover {
35
+ background: var(--reset-btn-bg-hover);
36
+ color: var(--reset-btn-color-hover);
37
+ }
@@ -49,30 +49,8 @@
49
49
  cursor: not-allowed;
50
50
  }
51
51
 
52
- /* action buttons */
53
- .reset-btn,
54
- .clear-btn {
55
- width: 1.333em;
56
- height: 1.333em;
57
- border: none;
58
- border-radius: var(--reset-btn-border-radius);
59
- cursor: pointer;
60
- display: flex;
61
- align-items: center;
62
- justify-content: center;
63
- background: var(--reset-btn-bg);
64
- color: var(--reset-btn-color);
65
- transition: var(--reset-btn-transition);
66
- flex-shrink: 0;
67
- line-height: 1;
68
- }
69
-
70
- .reset-btn:hover {
71
- background: var(--reset-btn-bg-hover);
72
- color: var(--reset-btn-color-hover);
73
- }
74
-
75
- .clear-btn:hover {
76
- background: var(--clear-btn-bg-hover);
77
- color: var(--clear-btn-color-hover);
52
+ /* Error state */
53
+ :host([error]) .field-input {
54
+ outline: 2px solid var(--field-error-color, var(--red));
55
+ outline-offset: 1px;
78
56
  }
@@ -0,0 +1,119 @@
1
+ :host {
2
+ display: inline-block;
3
+ --btn-solid-bg: var(--color-solid);
4
+ --btn-solid-bg-hover: var(--color-solid-hover);
5
+ --btn-solid-bg-active: var(--color-solid-active);
6
+ --btn-solid-color: var(--color-on-solid);
7
+ --btn-solid-color-hover: var(--color-on-solid-hover);
8
+ --btn-border: var(--color-border);
9
+ --btn-border-hover: var(--color-border-hover);
10
+ --btn-accent: var(--color-accent);
11
+ --btn-accent-hover: var(--color-accent-hover);
12
+ --btn-subtle-bg: var(--color-subtle);
13
+ --btn-subtle-bg-hover: var(--color-subtle-hover);
14
+ }
15
+
16
+ button {
17
+ display: inline-flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ gap: 6px;
21
+ font-family: inherit;
22
+ font-weight: 400;
23
+ text-align: center;
24
+ white-space: nowrap;
25
+ vertical-align: middle;
26
+ user-select: none;
27
+ cursor: pointer;
28
+ transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
29
+ outline: none;
30
+ position: relative;
31
+ padding: 0.429em 0.571em;
32
+ font-size: 1em;
33
+ line-height: 1.5;
34
+ border-radius: 4px;
35
+ height: 2.286em;
36
+ width: 2.286em;
37
+ min-width: unset;
38
+
39
+ background: var(--btn-solid-bg);
40
+ border: 1px solid var(--btn-solid-bg);
41
+ color: var(--btn-solid-color);
42
+ }
43
+
44
+ button:focus { outline: none; }
45
+ button:active { transform: translateY(1px); }
46
+
47
+ button:hover:not(:disabled) {
48
+ background: var(--btn-solid-bg-hover);
49
+ border-color: var(--btn-border-hover);
50
+ color: var(--btn-solid-color-hover, var(--btn-solid-color));
51
+ }
52
+
53
+ button:active:not(:disabled) { background: var(--btn-solid-bg-active); }
54
+
55
+ button:disabled {
56
+ opacity: 0.5;
57
+ cursor: not-allowed;
58
+ }
59
+
60
+ /* Hide the fallback-text slot */
61
+ slot { display: none; }
62
+
63
+ /* Icon visibility */
64
+ .icon-copy,
65
+ .icon-check {
66
+ position: absolute;
67
+ inset: 0;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ transition: opacity 0.15s ease, transform 0.15s ease;
72
+ }
73
+
74
+ .icon-copy { opacity: 1; transform: scale(1); }
75
+ .icon-check { opacity: 0; transform: scale(0.6); }
76
+
77
+ :host([copied]) .icon-copy { opacity: 0; transform: scale(0.6); }
78
+ :host([copied]) .icon-check { opacity: 1; transform: scale(1); }
79
+
80
+ /* Variants */
81
+ :host([variant="outlined"]) button {
82
+ background: transparent;
83
+ border-color: var(--btn-border);
84
+ color: var(--btn-accent);
85
+ }
86
+ :host([variant="outlined"]) button:hover:not(:disabled) {
87
+ background: var(--btn-subtle-bg);
88
+ border-color: var(--btn-border-hover);
89
+ color: var(--btn-accent-hover);
90
+ }
91
+
92
+ :host([variant="faint"]) button {
93
+ background: var(--btn-subtle-bg);
94
+ border-color: transparent;
95
+ color: var(--btn-accent);
96
+ }
97
+ :host([variant="faint"]) button:hover:not(:disabled) {
98
+ background: var(--btn-subtle-bg-hover);
99
+ color: var(--btn-accent-hover);
100
+ }
101
+
102
+ :host([variant="subtle"]) button {
103
+ background: transparent;
104
+ border-color: transparent;
105
+ color: var(--btn-accent);
106
+ }
107
+ :host([variant="subtle"]) button:hover:not(:disabled) {
108
+ background: var(--btn-subtle-bg);
109
+ color: var(--btn-accent-hover);
110
+ }
111
+
112
+ :host([variant="text"]) button {
113
+ background: transparent;
114
+ border-color: transparent;
115
+ color: var(--btn-accent);
116
+ }
117
+ :host([variant="text"]) button:hover:not(:disabled) {
118
+ color: var(--btn-accent-hover);
119
+ }
@@ -0,0 +1,10 @@
1
+ :host {
2
+ display: block;
3
+ }
4
+
5
+ /* Raise hovered/focused item so its border shows above neighbours */
6
+ ::slotted(ae-detail:hover),
7
+ ::slotted(ae-detail:focus-within) {
8
+ position: relative;
9
+ z-index: 1;
10
+ }
@@ -43,7 +43,11 @@
43
43
 
44
44
  .detail {
45
45
  border: 1px solid var(--detail-border);
46
- border-radius: var(--detail-radius);
46
+ border-radius:
47
+ var(--detail-r-tl, var(--detail-radius))
48
+ var(--detail-r-tr, var(--detail-radius))
49
+ var(--detail-r-br, var(--detail-radius))
50
+ var(--detail-r-bl, var(--detail-radius));
47
51
  color: var(--detail-color);
48
52
  background: var(--detail-bg);
49
53
  overflow: hidden;
@@ -93,6 +97,11 @@ slot[name="collapse"] {
93
97
  flex-shrink: 0;
94
98
  }
95
99
 
100
+ :host([icon-placement="start"]) slot[name="expand"],
101
+ :host([icon-placement="start"]) slot[name="collapse"] {
102
+ order: -1;
103
+ }
104
+
96
105
  slot[name="collapse"] {
97
106
  display: none;
98
107
  }