aeico-components 0.1.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 (174) hide show
  1. package/README.md +0 -0
  2. package/dist/index.cjs +4226 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.js +4226 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/types/aeico-component.d.ts +8 -0
  7. package/dist/types/aeico-field.d.ts +132 -0
  8. package/dist/types/alert/alert.d.ts +49 -0
  9. package/dist/types/alert/defines.d.ts +3 -0
  10. package/dist/types/alert/index.d.ts +3 -0
  11. package/dist/types/badge/badge.d.ts +34 -0
  12. package/dist/types/badge/defines.d.ts +3 -0
  13. package/dist/types/badge/index.d.ts +3 -0
  14. package/dist/types/breadcrumb/breadcrumb-item.d.ts +31 -0
  15. package/dist/types/breadcrumb/breadcrumb.d.ts +60 -0
  16. package/dist/types/breadcrumb/defines.d.ts +1 -0
  17. package/dist/types/breadcrumb/index.d.ts +5 -0
  18. package/dist/types/button/button.d.ts +60 -0
  19. package/dist/types/button/defines.d.ts +3 -0
  20. package/dist/types/button/index.d.ts +3 -0
  21. package/dist/types/button-group/button-group.d.ts +56 -0
  22. package/dist/types/button-group/index.d.ts +2 -0
  23. package/dist/types/card/card.d.ts +19 -0
  24. package/dist/types/card/defines.d.ts +2 -0
  25. package/dist/types/card/index.d.ts +3 -0
  26. package/dist/types/checkbox/checkbox.d.ts +37 -0
  27. package/dist/types/checkbox/defines.d.ts +1 -0
  28. package/dist/types/checkbox/index.d.ts +3 -0
  29. package/dist/types/detail/defines.d.ts +2 -0
  30. package/dist/types/detail/detail.d.ts +40 -0
  31. package/dist/types/detail/index.d.ts +3 -0
  32. package/dist/types/dialog/dialog.d.ts +29 -0
  33. package/dist/types/dialog/index.d.ts +2 -0
  34. package/dist/types/divider/divider.d.ts +34 -0
  35. package/dist/types/divider/index.d.ts +2 -0
  36. package/dist/types/dropdown/defines.d.ts +1 -0
  37. package/dist/types/dropdown/dropdown-button.d.ts +60 -0
  38. package/dist/types/dropdown/dropdown-item.d.ts +56 -0
  39. package/dist/types/dropdown/dropdown.d.ts +84 -0
  40. package/dist/types/dropdown/index.d.ts +7 -0
  41. package/dist/types/icon/defines.d.ts +10 -0
  42. package/dist/types/icon/icon.d.ts +21 -0
  43. package/dist/types/icon/index.d.ts +4 -0
  44. package/dist/types/icon/registry.d.ts +8 -0
  45. package/dist/types/icon-button/icon-button.d.ts +32 -0
  46. package/dist/types/icon-button/index.d.ts +2 -0
  47. package/dist/types/index.d.ts +74 -0
  48. package/dist/types/navbar/defines.d.ts +2 -0
  49. package/dist/types/navbar/index.d.ts +3 -0
  50. package/dist/types/navbar/navbar.d.ts +73 -0
  51. package/dist/types/radio-group/defines.d.ts +6 -0
  52. package/dist/types/radio-group/index.d.ts +5 -0
  53. package/dist/types/radio-group/radio-group.d.ts +41 -0
  54. package/dist/types/radio-group/radio.d.ts +47 -0
  55. package/dist/types/select/defines.d.ts +8 -0
  56. package/dist/types/select/index.d.ts +5 -0
  57. package/dist/types/select/select-option.d.ts +20 -0
  58. package/dist/types/select/select.d.ts +60 -0
  59. package/dist/types/slider/defines.d.ts +31 -0
  60. package/dist/types/slider/index.d.ts +3 -0
  61. package/dist/types/slider/slider.d.ts +45 -0
  62. package/dist/types/switch/index.d.ts +2 -0
  63. package/dist/types/switch/switch.d.ts +35 -0
  64. package/dist/types/tabs/defines.d.ts +1 -0
  65. package/dist/types/tabs/index.d.ts +3 -0
  66. package/dist/types/tabs/tab-panel.d.ts +11 -0
  67. package/dist/types/tabs/tab.d.ts +18 -0
  68. package/dist/types/tabs/tabs.d.ts +24 -0
  69. package/dist/types/tag/defines.d.ts +3 -0
  70. package/dist/types/tag/index.d.ts +3 -0
  71. package/dist/types/tag/tag.d.ts +36 -0
  72. package/dist/types/text-input/index.d.ts +2 -0
  73. package/dist/types/text-input/text-input.d.ts +26 -0
  74. package/dist/types/utils.d.ts +2 -0
  75. package/package.json +63 -0
  76. package/src/aeico-component.ts +17 -0
  77. package/src/aeico-field.ts +228 -0
  78. package/src/alert/alert.ts +107 -0
  79. package/src/alert/defines.ts +11 -0
  80. package/src/alert/index.ts +3 -0
  81. package/src/badge/badge.ts +62 -0
  82. package/src/badge/defines.ts +12 -0
  83. package/src/badge/index.ts +3 -0
  84. package/src/breadcrumb/breadcrumb-item.ts +61 -0
  85. package/src/breadcrumb/breadcrumb.ts +138 -0
  86. package/src/breadcrumb/defines.ts +10 -0
  87. package/src/breadcrumb/index.ts +5 -0
  88. package/src/button/button.ts +147 -0
  89. package/src/button/defines.ts +12 -0
  90. package/src/button/index.ts +3 -0
  91. package/src/button-group/button-group.ts +140 -0
  92. package/src/button-group/index.ts +2 -0
  93. package/src/card/card.ts +57 -0
  94. package/src/card/defines.ts +11 -0
  95. package/src/card/index.ts +3 -0
  96. package/src/checkbox/checkbox.ts +90 -0
  97. package/src/checkbox/defines.ts +1 -0
  98. package/src/checkbox/index.ts +3 -0
  99. package/src/detail/defines.ts +11 -0
  100. package/src/detail/detail.ts +122 -0
  101. package/src/detail/index.ts +3 -0
  102. package/src/dialog/dialog.ts +149 -0
  103. package/src/dialog/index.ts +2 -0
  104. package/src/divider/divider.ts +56 -0
  105. package/src/divider/index.ts +2 -0
  106. package/src/dropdown/defines.ts +13 -0
  107. package/src/dropdown/dropdown-button.ts +130 -0
  108. package/src/dropdown/dropdown-item.ts +136 -0
  109. package/src/dropdown/dropdown.ts +211 -0
  110. package/src/dropdown/index.ts +7 -0
  111. package/src/icon/defines.ts +21 -0
  112. package/src/icon/icon.ts +84 -0
  113. package/src/icon/index.ts +4 -0
  114. package/src/icon/registry.ts +25 -0
  115. package/src/icon-button/icon-button.ts +64 -0
  116. package/src/icon-button/index.ts +2 -0
  117. package/src/index.ts +85 -0
  118. package/src/navbar/defines.ts +11 -0
  119. package/src/navbar/index.ts +3 -0
  120. package/src/navbar/navbar.ts +162 -0
  121. package/src/radio-group/defines.ts +5 -0
  122. package/src/radio-group/index.ts +5 -0
  123. package/src/radio-group/radio-group.ts +227 -0
  124. package/src/radio-group/radio.ts +58 -0
  125. package/src/select/defines.ts +12 -0
  126. package/src/select/index.ts +5 -0
  127. package/src/select/select-option.ts +59 -0
  128. package/src/select/select.ts +387 -0
  129. package/src/slider/defines.ts +33 -0
  130. package/src/slider/index.ts +3 -0
  131. package/src/slider/slider.ts +364 -0
  132. package/src/styles/color.css +117 -0
  133. package/src/styles/components/alert.css +104 -0
  134. package/src/styles/components/badge.css +67 -0
  135. package/src/styles/components/breadcrumb-item.css +59 -0
  136. package/src/styles/components/breadcrumb.css +19 -0
  137. package/src/styles/components/button-group.css +25 -0
  138. package/src/styles/components/button.css +213 -0
  139. package/src/styles/components/card.css +64 -0
  140. package/src/styles/components/checkbox.css +78 -0
  141. package/src/styles/components/detail.css +127 -0
  142. package/src/styles/components/dialog.css +103 -0
  143. package/src/styles/components/divider.css +18 -0
  144. package/src/styles/components/dropdown-item.css +91 -0
  145. package/src/styles/components/dropdown.css +179 -0
  146. package/src/styles/components/icon-button.css +116 -0
  147. package/src/styles/components/icon.css +29 -0
  148. package/src/styles/components/navbar.css +250 -0
  149. package/src/styles/components/radio-group.css +360 -0
  150. package/src/styles/components/select-option.css +43 -0
  151. package/src/styles/components/select.css +222 -0
  152. package/src/styles/components/slider.css +326 -0
  153. package/src/styles/components/switch.css +117 -0
  154. package/src/styles/components/tab-panel.css +8 -0
  155. package/src/styles/components/tab.css +44 -0
  156. package/src/styles/components/tabs.css +16 -0
  157. package/src/styles/components/tag.css +107 -0
  158. package/src/styles/components/text-input.css +110 -0
  159. package/src/styles/layout.css +43 -0
  160. package/src/styles/size.css +7 -0
  161. package/src/styles/variables.css +368 -0
  162. package/src/switch/index.ts +2 -0
  163. package/src/switch/switch.ts +88 -0
  164. package/src/tabs/defines.ts +1 -0
  165. package/src/tabs/index.ts +3 -0
  166. package/src/tabs/tab-panel.ts +23 -0
  167. package/src/tabs/tab.ts +62 -0
  168. package/src/tabs/tabs.ts +134 -0
  169. package/src/tag/defines.ts +12 -0
  170. package/src/tag/index.ts +3 -0
  171. package/src/tag/tag.ts +85 -0
  172. package/src/text-input/index.ts +2 -0
  173. package/src/text-input/text-input.ts +75 -0
  174. package/src/utils.ts +6 -0
@@ -0,0 +1,364 @@
1
+ import AeicoField from '../aeico-field';
2
+ import type { InferProps } from 'aeico';
3
+ import { html, tags } from 'aeico';
4
+ import type { NormalizedOption, SliderMarks, SliderOption, SliderOptions } from './defines';
5
+ import style from '../styles/components/slider.css?inline';
6
+ import variables from '../styles/variables.css?inline';
7
+ import sizeCSS from '../styles/size.css?inline';
8
+ import colorCSS from '../styles/color.css?inline';
9
+ import { prop } from 'aeico';
10
+
11
+ class Slider extends AeicoField {
12
+ protected fieldElement: HTMLInputElement | null = null;
13
+ private _valueLabel: HTMLSpanElement | null = null;
14
+ private _numberInput: HTMLInputElement | null = null;
15
+
16
+ private _boundOnRangeInput: () => void;
17
+ private _boundOnNumberInput: () => void;
18
+
19
+ static tagName = 'slider';
20
+
21
+ @prop({ type: Array })
22
+ accessor options: SliderOptions = [];
23
+
24
+ @prop({ type: Boolean })
25
+ accessor percentage = false;
26
+
27
+ @prop({ type: Number })
28
+ accessor min = 0;
29
+
30
+ @prop({ type: Number })
31
+ accessor max = 100;
32
+
33
+ @prop({ type: Number })
34
+ accessor step = 1;
35
+
36
+ @prop({ type: Boolean })
37
+ accessor editable = false;
38
+
39
+ @prop({ type: Boolean })
40
+ accessor tracked = false;
41
+
42
+ @prop({
43
+ type: Array,
44
+ // bare attribute (<ae-slider marks>) → true; JSON array → MarkItem[]
45
+ parser: (value: string | null) => {
46
+ if (value === null) return undefined;
47
+ if (value === '' || value === 'true') return true;
48
+ if (value === 'false') return false;
49
+ try {
50
+ return JSON.parse(value);
51
+ } catch {
52
+ return true;
53
+ }
54
+ },
55
+ })
56
+ accessor marks: SliderMarks | undefined;
57
+
58
+ protected static styles = [variables, sizeCSS, colorCSS, style];
59
+
60
+ constructor() {
61
+ super();
62
+ this._boundOnRangeInput = this._onRangeInput.bind(this);
63
+ this._boundOnNumberInput = this._onNumberInput.bind(this);
64
+ }
65
+
66
+ private _normalizeOptions(): NormalizedOption[] | null {
67
+ if (!Array.isArray(this.options) || this.options.length === 0) return null;
68
+
69
+ const opts = this.options.map((opt) =>
70
+ this._isSliderOption(opt)
71
+ ? { label: opt.label, value: String(opt.value) }
72
+ : { label: String(opt), value: String(opt) },
73
+ );
74
+
75
+ // Sort by numeric value if all values are numeric; otherwise keep original order
76
+ const allNumeric = opts.every((o) => o.value !== '' && !isNaN(Number(o.value)));
77
+
78
+ if (allNumeric) {
79
+ return [...opts]
80
+ .sort((a, b) => Number(a.value) - Number(b.value))
81
+ .map((o) => ({ ...o, rangeValue: Number(o.value) }));
82
+ } else {
83
+ return opts.map((o, i) => ({ ...o, rangeValue: i }));
84
+ }
85
+ }
86
+
87
+ private _getRangeAttrs(normalized: NormalizedOption[] | null): {
88
+ min: string;
89
+ max: string;
90
+ step: string;
91
+ inOptionsMode: boolean;
92
+ } {
93
+ if (normalized && normalized.length >= 1) {
94
+ const vals = normalized.map((o) => o.rangeValue);
95
+ const min = Math.min(...vals);
96
+ const max = Math.max(...vals);
97
+
98
+ // Compute step from minimum gap between adjacent sorted values
99
+ const sorted = [...vals].sort((a, b) => a - b);
100
+ let minGap = Infinity;
101
+ for (let i = 1; i < sorted.length; i++) {
102
+ const d = sorted[i] - sorted[i - 1];
103
+ if (d > 0 && d < minGap) minGap = d;
104
+ }
105
+
106
+ return {
107
+ min: String(min),
108
+ max: String(max),
109
+ step: minGap === Infinity ? '1' : String(minGap),
110
+ inOptionsMode: true,
111
+ };
112
+ }
113
+
114
+ return {
115
+ min: String(this.min),
116
+ max: String(this.max),
117
+ step: String(this.step),
118
+ inOptionsMode: false,
119
+ };
120
+ }
121
+
122
+ private _toRangeValue(value: string | undefined, normalized: NormalizedOption[] | null): string {
123
+ if (value == null || value === '') return '';
124
+ if (normalized) {
125
+ const found = normalized.find((o) => o.value === value);
126
+
127
+ return found !== undefined ? String(found.rangeValue) : '';
128
+ }
129
+
130
+ return value;
131
+ }
132
+
133
+ private _fromRangeValue(rv: string, normalized: NormalizedOption[] | null): string {
134
+ if (normalized) {
135
+ const n = Number(rv);
136
+
137
+ return normalized.find((o) => o.rangeValue === n)?.value ?? normalized[0]?.value ?? rv;
138
+ }
139
+ return rv;
140
+ }
141
+
142
+ private _displayLabel(value: string | undefined, normalized: NormalizedOption[] | null): string {
143
+ if (value == null || value === '') return '';
144
+ const label = normalized ? (normalized.find((o) => o.value === value)?.label ?? value) : value;
145
+
146
+ return this.percentage ? `${label}%` : label;
147
+ }
148
+
149
+ private _maxValueLabelWidth(
150
+ normalized: NormalizedOption[] | null,
151
+ attrs: { min: string; max: string },
152
+ ): string {
153
+ const candidates = normalized
154
+ ? normalized.map((o) => this._displayLabel(o.value, normalized))
155
+ : [this._displayLabel(attrs.min, null), this._displayLabel(attrs.max, null)];
156
+ const maxLen = Math.max(...candidates.map((l) => l.length), 1);
157
+
158
+ return `${maxLen}ch`;
159
+ }
160
+
161
+ private _updateTrackFill(): void {
162
+ if (!this.tracked || !this.fieldElement) return;
163
+ const min = Number(this.fieldElement.min);
164
+ const max = Number(this.fieldElement.max);
165
+ const val = Number(this.fieldElement.value);
166
+ const range = max - min || 1;
167
+ const pct = Math.max(0, Math.min(100, ((val - min) / range) * 100));
168
+ this.style.setProperty('--fill-pct', `${pct}%`);
169
+ }
170
+
171
+ private _getMarksData(
172
+ normalized: NormalizedOption[] | null,
173
+ attrs: { min: string; max: string; inOptionsMode: boolean },
174
+ ): Array<{ value: string; label: string; pct: number }> {
175
+ const minVal = Number(attrs.min);
176
+ const maxVal = Number(attrs.max);
177
+ const range = maxVal - minVal || 1;
178
+
179
+ const marks = this.marks;
180
+
181
+ // Custom marks array — purely visual, no snapping effect
182
+ if (Array.isArray(marks)) {
183
+ const result: Array<{ value: string; label: string; pct: number }> = [];
184
+ for (const m of marks) {
185
+ const isObj = m !== null && typeof m === 'object';
186
+ const numVal = isObj ? (m as { value: number }).value : m;
187
+ if (numVal < minVal || numVal > maxVal) continue;
188
+ const rawLabel = isObj ? (m.label ?? String(numVal)) : String(numVal);
189
+ result.push({
190
+ value: String(numVal),
191
+ label: this.percentage ? `${rawLabel}%` : rawLabel,
192
+ pct: ((numVal - minVal) / range) * 100,
193
+ });
194
+ }
195
+
196
+ return result;
197
+ }
198
+
199
+ // marks === true — auto-generate from options or free-mode endpoints
200
+ if (normalized) {
201
+ return normalized.map((o) => ({
202
+ value: o.value,
203
+ label: this.percentage ? `${o.label}%` : o.label,
204
+ pct: ((o.rangeValue - minVal) / range) * 100,
205
+ }));
206
+ }
207
+
208
+ // Free mode — show min and max endpoints only
209
+ return [
210
+ { value: attrs.min, label: this.percentage ? `${minVal}%` : String(minVal), pct: 0 },
211
+ { value: attrs.max, label: this.percentage ? `${maxVal}%` : String(maxVal), pct: 100 },
212
+ ];
213
+ }
214
+
215
+ protected writeValue(value: string): void {
216
+ const normalized = this._normalizeOptions();
217
+ const rv = this._toRangeValue(value, normalized);
218
+
219
+ if (this.fieldElement && rv !== '' && this.fieldElement.value !== rv) {
220
+ this.fieldElement.value = rv;
221
+ }
222
+
223
+ if (this._valueLabel) {
224
+ this._valueLabel.textContent = this._displayLabel(value, normalized);
225
+ }
226
+
227
+ // Sync number input only in free mode (options mode disables it)
228
+ if (this._numberInput && !normalized && this._numberInput.value !== rv) {
229
+ this._numberInput.value = rv;
230
+ }
231
+
232
+ this._updateTrackFill();
233
+ }
234
+
235
+ protected getValue(): string {
236
+ if (!this.fieldElement) return '';
237
+
238
+ return this._fromRangeValue(this.fieldElement.value, this._normalizeOptions());
239
+ }
240
+
241
+ render() {
242
+ const normalized = this._normalizeOptions();
243
+ const attrs = this._getRangeAttrs(normalized);
244
+
245
+ return html(({ div, input, span }) => {
246
+ div({ className: 'range-container' }, () => {
247
+ // Wrap range + optional marks in a column so marks don't push siblings
248
+ div({ key: 'range-wrapper', className: 'range-wrapper' }, () => {
249
+ this.fieldElement = input({
250
+ key: 'range',
251
+ type: 'range',
252
+ min: attrs.min,
253
+ max: attrs.max,
254
+ step: attrs.step,
255
+ '@input': this._boundOnRangeInput,
256
+ '@change': this.boundOnChange,
257
+ });
258
+
259
+ if (this.marks) {
260
+ const marksData = this._getMarksData(normalized, attrs);
261
+ div({ key: 'marks', className: 'marks-container' }, () => {
262
+ for (const m of marksData) {
263
+ tags.span(
264
+ {
265
+ key: `mark-${m.value}`,
266
+ className: 'mark',
267
+ style: { left: `${m.pct}%` },
268
+ },
269
+ () => {
270
+ tags.span({ className: 'mark-label', textContent: m.label });
271
+ },
272
+ );
273
+ }
274
+ });
275
+ }
276
+ });
277
+
278
+ this._valueLabel = span({
279
+ key: 'label',
280
+ className: 'value-label',
281
+ style: { minWidth: this._maxValueLabelWidth(normalized, attrs) },
282
+ textContent: this._displayLabel(this.value, normalized),
283
+ });
284
+
285
+ // Action buttons first so the number input can be toggled without disrupting button reuse
286
+ this.renderActionButtons();
287
+
288
+ if (this.editable) {
289
+ this._numberInput = input({
290
+ key: 'number',
291
+ type: 'number',
292
+ className: 'value-input',
293
+ min: attrs.min,
294
+ max: attrs.max,
295
+ step: attrs.step,
296
+ // Disabled in options mode: valid values are discrete, free text makes no sense
297
+ disabled: attrs.inOptionsMode,
298
+ '@input': this._boundOnNumberInput,
299
+ });
300
+ } else {
301
+ this._numberInput = null;
302
+ }
303
+ });
304
+
305
+ if (this.value != null) this.writeValue(this.value);
306
+ });
307
+ }
308
+
309
+ private _onRangeInput(): void {
310
+ if (!this.fieldElement) return;
311
+
312
+ const normalized = this._normalizeOptions();
313
+ const actualValue = this._fromRangeValue(this.fieldElement.value, normalized);
314
+
315
+ if (this._valueLabel) {
316
+ this._valueLabel.textContent = this._displayLabel(actualValue, normalized);
317
+ }
318
+ // Keep number input in sync during drag
319
+ if (this._numberInput && !normalized) {
320
+ this._numberInput.value = this.fieldElement.value;
321
+ }
322
+
323
+ this._updateTrackFill();
324
+ }
325
+
326
+ private _onNumberInput(): void {
327
+ if (!this._numberInput || !this.fieldElement) return;
328
+ const v = this._numberInput.value;
329
+
330
+ if (this.fieldElement.value === v) return;
331
+
332
+ this.fieldElement.value = v;
333
+ if (this._valueLabel) {
334
+ this._valueLabel.textContent = this._displayLabel(v, null);
335
+ }
336
+ }
337
+
338
+ public clear(options?: { silent?: boolean }): void {
339
+ const normalized = this._normalizeOptions();
340
+ const attrs = this._getRangeAttrs(normalized);
341
+ // Reset to the option whose rangeValue === min, or to attrs.min in free mode
342
+ const clearTo = normalized
343
+ ? (normalized.find((o) => String(o.rangeValue) === attrs.min)?.value ??
344
+ normalized[0]?.value ??
345
+ attrs.min)
346
+ : attrs.min;
347
+ this.setValue(clearTo, { ...options, action: 'clear' });
348
+ }
349
+
350
+ private _isSliderOption(opt: unknown): opt is SliderOption {
351
+ return opt !== null && typeof opt === 'object' && 'label' in opt && 'value' in opt;
352
+ }
353
+ }
354
+
355
+ Slider.register();
356
+
357
+ declare global {
358
+ interface HTMLElementTagNameMap {
359
+ 'ae-slider': Slider;
360
+ }
361
+ }
362
+
363
+ export default Slider;
364
+ export type SliderProps = InferProps<typeof Slider>;
@@ -0,0 +1,117 @@
1
+ :host {
2
+ --color-solid: var(--color-gray);
3
+ --color-solid-hover: color-mix(in srgb, var(--color-solid), var(--color-mix-hover));
4
+ --color-solid-active: color-mix(in srgb, var(--color-solid), var(--color-mix-active));
5
+ --color-on-solid: var(--color-text-main);
6
+ --color-border: var(--color-gray);
7
+ --color-border-hover: color-mix(in srgb, var(--color-border), var(--color-mix-hover));
8
+ --color-accent: var(--color-text-muted);
9
+ --color-accent-hover: var(--color-text-main);
10
+ --color-subtle: var(--border-subtle);
11
+ --color-subtle-hover: var(--border-default);
12
+ --color-bg-subtle: var(--border-subtle);
13
+ --color-text-subtle: var(--color-text-muted);
14
+ --color-border-subtle: var(--border-default);
15
+ }
16
+
17
+ :host([color="primary"]) {
18
+ --color-solid: var(--color-primary);
19
+ --color-on-solid: white;
20
+ --color-border: var(--color-primary);
21
+ --color-accent: var(--color-text-link);
22
+ --color-accent-hover: var(--color-text-link-hover);
23
+ --color-subtle: var(--color-primary-bg-subtle);
24
+ --color-subtle-hover: rgb(from var(--blue) r g b / 0.15);
25
+ --color-bg-subtle: var(--color-primary-bg-subtle);
26
+ --color-text-subtle: var(--color-primary-text-emphasis);
27
+ --color-border-subtle: var(--color-primary-border-subtle);
28
+ }
29
+
30
+ :host([color="secondary"]) {
31
+ --color-solid: var(--color-secondary);
32
+ --color-on-solid: white;
33
+ --color-border: var(--color-secondary-hover);
34
+ --color-accent: var(--color-secondary-text-emphasis);
35
+ --color-accent-hover: var(--color-text-main);
36
+ --color-subtle: rgb(from var(--slate) r g b / 0.06);
37
+ --color-subtle-hover: rgb(from var(--slate) r g b / 0.12);
38
+ --color-bg-subtle: var(--color-secondary-bg-subtle);
39
+ --color-text-subtle: var(--color-secondary-text-emphasis);
40
+ --color-border-subtle: var(--color-secondary-border-subtle);
41
+ }
42
+
43
+ :host([color="success"]) {
44
+ --color-solid: var(--color-success);
45
+ --color-on-solid: white;
46
+ --color-border: var(--color-success);
47
+ --color-accent: var(--color-success-text-emphasis);
48
+ --color-accent-hover: var(--color-success-hover);
49
+ --color-subtle: var(--color-success-bg-subtle);
50
+ --color-subtle-hover: rgb(from var(--green) r g b / 0.15);
51
+ --color-bg-subtle: var(--color-success-bg-subtle);
52
+ --color-text-subtle: var(--color-success-text-emphasis);
53
+ --color-border-subtle: var(--color-success-border-subtle);
54
+ }
55
+
56
+ :host([color="danger"]) {
57
+ --color-solid: var(--color-danger);
58
+ --color-on-solid: white;
59
+ --color-border: var(--color-danger);
60
+ --color-accent: var(--color-danger-text-emphasis);
61
+ --color-accent-hover: var(--color-danger-hover);
62
+ --color-subtle: var(--color-danger-bg-subtle);
63
+ --color-subtle-hover: rgb(from var(--red) r g b / 0.15);
64
+ --color-bg-subtle: var(--color-danger-bg-subtle);
65
+ --color-text-subtle: var(--color-danger-text-emphasis);
66
+ --color-border-subtle: var(--color-danger-border-subtle);
67
+ }
68
+
69
+ :host([color="warning"]) {
70
+ --color-solid: var(--color-warning);
71
+ --color-on-solid: var(--gray-900);
72
+ --color-border: var(--color-warning);
73
+ --color-accent: var(--color-warning-text-emphasis);
74
+ --color-accent-hover: var(--color-warning-hover);
75
+ --color-subtle: var(--color-warning-bg-subtle);
76
+ --color-subtle-hover: rgb(from var(--yellow) r g b / 0.15);
77
+ --color-bg-subtle: var(--color-warning-bg-subtle);
78
+ --color-text-subtle: var(--color-warning-text-emphasis);
79
+ --color-border-subtle: var(--color-warning-border-subtle);
80
+ }
81
+
82
+ :host([color="info"]) {
83
+ --color-solid: var(--color-info);
84
+ --color-on-solid: white;
85
+ --color-border: var(--color-info);
86
+ --color-accent: var(--color-info-text-emphasis);
87
+ --color-accent-hover: var(--color-info-hover);
88
+ --color-subtle: var(--color-info-bg-subtle);
89
+ --color-subtle-hover: rgb(from var(--cyan) r g b / 0.15);
90
+ --color-bg-subtle: var(--color-info-bg-subtle);
91
+ --color-text-subtle: var(--color-info-text-emphasis);
92
+ --color-border-subtle: var(--color-info-border-subtle);
93
+ }
94
+
95
+ :host([color="light"]) {
96
+ --color-solid: var(--light);
97
+ --color-on-solid: var(--gray-900);
98
+ --color-border: var(--border-hover);
99
+ --color-subtle: var(--color-light-bg-subtle);
100
+ --color-subtle-hover: var(--color-light-border-subtle);
101
+ --color-bg-subtle: var(--color-light-bg-subtle);
102
+ --color-text-subtle: var(--color-text-muted);
103
+ --color-border-subtle: var(--color-light-border-subtle);
104
+ }
105
+
106
+ :host([color="dark"]) {
107
+ --color-solid: var(--dark-500);
108
+ --color-on-solid: white;
109
+ --color-border: var(--dark-500);
110
+ --color-accent: var(--color-text-main);
111
+ --color-accent-hover: var(--color-text-main);
112
+ --color-subtle: var(--color-dark-bg-subtle);
113
+ --color-subtle-hover: var(--color-dark-border-subtle);
114
+ --color-bg-subtle: var(--color-dark-bg-subtle);
115
+ --color-text-subtle: var(--color-text-main);
116
+ --color-border-subtle: var(--color-dark-border-subtle);
117
+ }
@@ -0,0 +1,104 @@
1
+ .alert {
2
+ position: relative;
3
+ padding: 12px 16px;
4
+ margin-bottom: 12px;
5
+ border-radius: 4px;
6
+ font-size: 12px;
7
+ line-height: 1.6;
8
+ background-color: var(--alert-solid-bg);
9
+ color: var(--alert-solid-color);
10
+ border: 1px solid var(--alert-solid-bg);
11
+ }
12
+
13
+ .alert:last-child {
14
+ margin-bottom: 0;
15
+ }
16
+
17
+ :host {
18
+ --color-solid: var(--color-primary);
19
+ --color-on-solid: var(--color-text-main);
20
+ --color-border: var(--color-primary);
21
+ --color-bg-subtle: var(--color-primary-bg-subtle);
22
+ --color-text-subtle: var(--color-primary-text-emphasis);
23
+ --color-border-subtle: var(--color-primary-border-subtle);
24
+
25
+ --alert-solid-bg: var(--color-solid);
26
+ --alert-solid-color: var(--color-on-solid);
27
+ --alert-border: var(--color-border);
28
+ --alert-subtle-bg: var(--color-bg-subtle);
29
+ --alert-subtle-color: var(--color-text-subtle);
30
+ --alert-subtle-border:var(--color-border-subtle);
31
+ }
32
+
33
+
34
+ :host([variant="faint"]) .alert {
35
+ background-color: var(--alert-subtle-bg);
36
+ color: var(--alert-subtle-color);
37
+ border-color: var(--alert-subtle-border);
38
+ }
39
+
40
+ :host([variant="subtle"]) .alert {
41
+ background-color: var(--alert-subtle-bg);
42
+ color: var(--alert-subtle-color);
43
+ border-color: transparent;
44
+ }
45
+
46
+ :host([variant="filled"]) .alert {
47
+ background-color: var(--alert-solid-bg);
48
+ color: var(--alert-solid-color);
49
+ border-color: var(--alert-solid-bg);
50
+ }
51
+
52
+ :host([variant="outlined"]) .alert {
53
+ background-color: transparent;
54
+ color: var(--alert-subtle-color);
55
+ border-color: var(--alert-border);
56
+ }
57
+
58
+ :host([dismissible]) .alert {
59
+ padding-right: 40px;
60
+ }
61
+
62
+ :host([icon]) .alert {
63
+ display: flex;
64
+ align-items: flex-start;
65
+ gap: 10px;
66
+ }
67
+
68
+ :host([icon]) .alert::before {
69
+ content: '';
70
+ flex-shrink: 0;
71
+ width: 16px;
72
+ height: 16px;
73
+ margin-top: 2px;
74
+ }
75
+
76
+ .alert-close {
77
+ position: absolute;
78
+ top: 50%;
79
+ right: 12px;
80
+ transform: translateY(-50%);
81
+ background: transparent;
82
+ border: none;
83
+ color: inherit;
84
+ opacity: 0.6;
85
+ cursor: pointer;
86
+ padding: 4px;
87
+ font-size: 18px;
88
+ line-height: 1;
89
+ transition: opacity 0.15s;
90
+ }
91
+
92
+ .alert-close:hover {
93
+ opacity: 1;
94
+ }
95
+
96
+ :host([size="sm"]) .alert {
97
+ padding: 8px 12px;
98
+ font-size: 11px;
99
+ }
100
+
101
+ :host([size="lg"]) .alert {
102
+ padding: 16px 20px;
103
+ font-size: 13px;
104
+ }
@@ -0,0 +1,67 @@
1
+ :host {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ --badge-solid-bg: var(--color-solid);
5
+ --badge-solid-color: var(--color-on-solid);
6
+ --badge-border: var(--color-border);
7
+ --badge-accent: var(--color-accent);
8
+ --badge-subtle-bg: var(--color-bg-subtle);
9
+ --badge-subtle-color: var(--color-text-subtle);
10
+ --badge-subtle-border: var(--color-border-subtle);
11
+ }
12
+
13
+ .badge {
14
+ display: inline-flex;
15
+ align-items: center;
16
+ gap: 4px;
17
+ font-family: inherit;
18
+ font-weight: 500;
19
+ white-space: nowrap;
20
+ vertical-align: middle;
21
+ line-height: 1.2;
22
+ font-size: 1em;
23
+ padding: 0.15em 0.6em;
24
+ border-radius: 4px;
25
+ border: 1px solid var(--badge-solid-bg);
26
+ background: var(--badge-solid-bg);
27
+ color: var(--badge-solid-color);
28
+ }
29
+
30
+ /* Default (no [variant]) = filled */
31
+
32
+ /* --- Variants --- */
33
+
34
+ :host([variant="outlined"]) .badge {
35
+ background: transparent;
36
+ border-color: var(--badge-border);
37
+ color: var(--badge-accent);
38
+ }
39
+
40
+ :host([variant="faint"]) .badge {
41
+ background: var(--badge-subtle-bg);
42
+ border-color: var(--badge-subtle-border);
43
+ color: var(--badge-subtle-color);
44
+ }
45
+
46
+ :host([variant="subtle"]) .badge {
47
+ background: var(--badge-subtle-bg);
48
+ border-color: transparent;
49
+ color: var(--badge-subtle-color);
50
+ }
51
+
52
+ :host([variant="text"]) .badge {
53
+ background: transparent;
54
+ border-color: transparent;
55
+ color: var(--badge-accent);
56
+ padding-left: 0;
57
+ padding-right: 0;
58
+ }
59
+
60
+ :host([pill]) .badge {
61
+ border-radius: 999px;
62
+ }
63
+
64
+ /* Icon slots */
65
+ ::slotted(ae-icon) {
66
+ font-size: 1.1em;
67
+ }
@@ -0,0 +1,59 @@
1
+ :host {
2
+ display: contents;
3
+ }
4
+
5
+ .item {
6
+ display: inline-flex;
7
+ align-items: center;
8
+ list-style: none;
9
+ white-space: nowrap;
10
+ }
11
+
12
+ /* Separator — uses a fixed muted color that does NOT follow the breadcrumb's color prop */
13
+ .sep {
14
+ display: inline-flex;
15
+ align-items: center;
16
+ color: var(--color-text-muted);
17
+ padding: 0 0.35em;
18
+ user-select: none;
19
+ pointer-events: none;
20
+ flex-shrink: 0;
21
+ font-size: 0.85em;
22
+ }
23
+
24
+ /* Hide separator on the first item */
25
+ :host(:first-child) .sep,
26
+ :host(:first-of-type) .sep {
27
+ display: none;
28
+ }
29
+
30
+ .label {
31
+ display: inline-flex;
32
+ align-items: center;
33
+ color: var(--color-text-muted);
34
+ }
35
+
36
+ /* Current page — last item */
37
+ :host([aria-current="page"]) .label {
38
+ color: var(--color-text-main);
39
+ font-weight: 500;
40
+ cursor: default;
41
+ }
42
+
43
+ /* Link items */
44
+ .label a {
45
+ color: var(--color-accent, var(--color-text-link));
46
+ text-decoration: none;
47
+ outline: none;
48
+ }
49
+
50
+ .label a:hover {
51
+ color: var(--color-accent-hover, var(--color-text-link-hover));
52
+ text-decoration: underline;
53
+ }
54
+
55
+ .label a:focus-visible {
56
+ outline: 2px solid var(--color-accent, var(--focus-ring-color));
57
+ outline-offset: 2px;
58
+ border-radius: 2px;
59
+ }