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.
- package/README.md +0 -0
- package/dist/index.cjs +4226 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +4226 -0
- package/dist/index.js.map +1 -0
- package/dist/types/aeico-component.d.ts +8 -0
- package/dist/types/aeico-field.d.ts +132 -0
- package/dist/types/alert/alert.d.ts +49 -0
- package/dist/types/alert/defines.d.ts +3 -0
- package/dist/types/alert/index.d.ts +3 -0
- package/dist/types/badge/badge.d.ts +34 -0
- package/dist/types/badge/defines.d.ts +3 -0
- package/dist/types/badge/index.d.ts +3 -0
- package/dist/types/breadcrumb/breadcrumb-item.d.ts +31 -0
- package/dist/types/breadcrumb/breadcrumb.d.ts +60 -0
- package/dist/types/breadcrumb/defines.d.ts +1 -0
- package/dist/types/breadcrumb/index.d.ts +5 -0
- package/dist/types/button/button.d.ts +60 -0
- package/dist/types/button/defines.d.ts +3 -0
- package/dist/types/button/index.d.ts +3 -0
- package/dist/types/button-group/button-group.d.ts +56 -0
- package/dist/types/button-group/index.d.ts +2 -0
- package/dist/types/card/card.d.ts +19 -0
- package/dist/types/card/defines.d.ts +2 -0
- package/dist/types/card/index.d.ts +3 -0
- package/dist/types/checkbox/checkbox.d.ts +37 -0
- package/dist/types/checkbox/defines.d.ts +1 -0
- package/dist/types/checkbox/index.d.ts +3 -0
- package/dist/types/detail/defines.d.ts +2 -0
- package/dist/types/detail/detail.d.ts +40 -0
- package/dist/types/detail/index.d.ts +3 -0
- package/dist/types/dialog/dialog.d.ts +29 -0
- package/dist/types/dialog/index.d.ts +2 -0
- package/dist/types/divider/divider.d.ts +34 -0
- package/dist/types/divider/index.d.ts +2 -0
- package/dist/types/dropdown/defines.d.ts +1 -0
- package/dist/types/dropdown/dropdown-button.d.ts +60 -0
- package/dist/types/dropdown/dropdown-item.d.ts +56 -0
- package/dist/types/dropdown/dropdown.d.ts +84 -0
- package/dist/types/dropdown/index.d.ts +7 -0
- package/dist/types/icon/defines.d.ts +10 -0
- package/dist/types/icon/icon.d.ts +21 -0
- package/dist/types/icon/index.d.ts +4 -0
- package/dist/types/icon/registry.d.ts +8 -0
- package/dist/types/icon-button/icon-button.d.ts +32 -0
- package/dist/types/icon-button/index.d.ts +2 -0
- package/dist/types/index.d.ts +74 -0
- package/dist/types/navbar/defines.d.ts +2 -0
- package/dist/types/navbar/index.d.ts +3 -0
- package/dist/types/navbar/navbar.d.ts +73 -0
- package/dist/types/radio-group/defines.d.ts +6 -0
- package/dist/types/radio-group/index.d.ts +5 -0
- package/dist/types/radio-group/radio-group.d.ts +41 -0
- package/dist/types/radio-group/radio.d.ts +47 -0
- package/dist/types/select/defines.d.ts +8 -0
- package/dist/types/select/index.d.ts +5 -0
- package/dist/types/select/select-option.d.ts +20 -0
- package/dist/types/select/select.d.ts +60 -0
- package/dist/types/slider/defines.d.ts +31 -0
- package/dist/types/slider/index.d.ts +3 -0
- package/dist/types/slider/slider.d.ts +45 -0
- package/dist/types/switch/index.d.ts +2 -0
- package/dist/types/switch/switch.d.ts +35 -0
- package/dist/types/tabs/defines.d.ts +1 -0
- package/dist/types/tabs/index.d.ts +3 -0
- package/dist/types/tabs/tab-panel.d.ts +11 -0
- package/dist/types/tabs/tab.d.ts +18 -0
- package/dist/types/tabs/tabs.d.ts +24 -0
- package/dist/types/tag/defines.d.ts +3 -0
- package/dist/types/tag/index.d.ts +3 -0
- package/dist/types/tag/tag.d.ts +36 -0
- package/dist/types/text-input/index.d.ts +2 -0
- package/dist/types/text-input/text-input.d.ts +26 -0
- package/dist/types/utils.d.ts +2 -0
- package/package.json +63 -0
- package/src/aeico-component.ts +17 -0
- package/src/aeico-field.ts +228 -0
- package/src/alert/alert.ts +107 -0
- package/src/alert/defines.ts +11 -0
- package/src/alert/index.ts +3 -0
- package/src/badge/badge.ts +62 -0
- package/src/badge/defines.ts +12 -0
- package/src/badge/index.ts +3 -0
- package/src/breadcrumb/breadcrumb-item.ts +61 -0
- package/src/breadcrumb/breadcrumb.ts +138 -0
- package/src/breadcrumb/defines.ts +10 -0
- package/src/breadcrumb/index.ts +5 -0
- package/src/button/button.ts +147 -0
- package/src/button/defines.ts +12 -0
- package/src/button/index.ts +3 -0
- package/src/button-group/button-group.ts +140 -0
- package/src/button-group/index.ts +2 -0
- package/src/card/card.ts +57 -0
- package/src/card/defines.ts +11 -0
- package/src/card/index.ts +3 -0
- package/src/checkbox/checkbox.ts +90 -0
- package/src/checkbox/defines.ts +1 -0
- package/src/checkbox/index.ts +3 -0
- package/src/detail/defines.ts +11 -0
- package/src/detail/detail.ts +122 -0
- package/src/detail/index.ts +3 -0
- package/src/dialog/dialog.ts +149 -0
- package/src/dialog/index.ts +2 -0
- package/src/divider/divider.ts +56 -0
- package/src/divider/index.ts +2 -0
- package/src/dropdown/defines.ts +13 -0
- package/src/dropdown/dropdown-button.ts +130 -0
- package/src/dropdown/dropdown-item.ts +136 -0
- package/src/dropdown/dropdown.ts +211 -0
- package/src/dropdown/index.ts +7 -0
- package/src/icon/defines.ts +21 -0
- package/src/icon/icon.ts +84 -0
- package/src/icon/index.ts +4 -0
- package/src/icon/registry.ts +25 -0
- package/src/icon-button/icon-button.ts +64 -0
- package/src/icon-button/index.ts +2 -0
- package/src/index.ts +85 -0
- package/src/navbar/defines.ts +11 -0
- package/src/navbar/index.ts +3 -0
- package/src/navbar/navbar.ts +162 -0
- package/src/radio-group/defines.ts +5 -0
- package/src/radio-group/index.ts +5 -0
- package/src/radio-group/radio-group.ts +227 -0
- package/src/radio-group/radio.ts +58 -0
- package/src/select/defines.ts +12 -0
- package/src/select/index.ts +5 -0
- package/src/select/select-option.ts +59 -0
- package/src/select/select.ts +387 -0
- package/src/slider/defines.ts +33 -0
- package/src/slider/index.ts +3 -0
- package/src/slider/slider.ts +364 -0
- package/src/styles/color.css +117 -0
- package/src/styles/components/alert.css +104 -0
- package/src/styles/components/badge.css +67 -0
- package/src/styles/components/breadcrumb-item.css +59 -0
- package/src/styles/components/breadcrumb.css +19 -0
- package/src/styles/components/button-group.css +25 -0
- package/src/styles/components/button.css +213 -0
- package/src/styles/components/card.css +64 -0
- package/src/styles/components/checkbox.css +78 -0
- package/src/styles/components/detail.css +127 -0
- package/src/styles/components/dialog.css +103 -0
- package/src/styles/components/divider.css +18 -0
- package/src/styles/components/dropdown-item.css +91 -0
- package/src/styles/components/dropdown.css +179 -0
- package/src/styles/components/icon-button.css +116 -0
- package/src/styles/components/icon.css +29 -0
- package/src/styles/components/navbar.css +250 -0
- package/src/styles/components/radio-group.css +360 -0
- package/src/styles/components/select-option.css +43 -0
- package/src/styles/components/select.css +222 -0
- package/src/styles/components/slider.css +326 -0
- package/src/styles/components/switch.css +117 -0
- package/src/styles/components/tab-panel.css +8 -0
- package/src/styles/components/tab.css +44 -0
- package/src/styles/components/tabs.css +16 -0
- package/src/styles/components/tag.css +107 -0
- package/src/styles/components/text-input.css +110 -0
- package/src/styles/layout.css +43 -0
- package/src/styles/size.css +7 -0
- package/src/styles/variables.css +368 -0
- package/src/switch/index.ts +2 -0
- package/src/switch/switch.ts +88 -0
- package/src/tabs/defines.ts +1 -0
- package/src/tabs/index.ts +3 -0
- package/src/tabs/tab-panel.ts +23 -0
- package/src/tabs/tab.ts +62 -0
- package/src/tabs/tabs.ts +134 -0
- package/src/tag/defines.ts +12 -0
- package/src/tag/index.ts +3 -0
- package/src/tag/tag.ts +85 -0
- package/src/text-input/index.ts +2 -0
- package/src/text-input/text-input.ts +75 -0
- package/src/utils.ts +6 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { InferProps } from 'aeico';
|
|
2
|
+
import styleVariables from '../styles/variables.css?inline';
|
|
3
|
+
import style from '../styles/components/breadcrumb-item.css?inline';
|
|
4
|
+
import AeicoComponent from '../aeico-component';
|
|
5
|
+
import { html } from 'aeico';
|
|
6
|
+
import { prop } from 'aeico';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* BreadcrumbItem Component
|
|
10
|
+
*
|
|
11
|
+
* A single item in the `ae-breadcrumb` navigation trail.
|
|
12
|
+
* Renders as a link (`<a>`) when `href` is provided, otherwise as plain text.
|
|
13
|
+
* The separator is injected by the parent `ae-breadcrumb`.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```html
|
|
17
|
+
* <ae-breadcrumb>
|
|
18
|
+
* <ae-breadcrumb-item href="/">Home</ae-breadcrumb-item>
|
|
19
|
+
* <ae-breadcrumb-item href="/docs">Docs</ae-breadcrumb-item>
|
|
20
|
+
* <ae-breadcrumb-item>Current Page</ae-breadcrumb-item>
|
|
21
|
+
* </ae-breadcrumb>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
class BreadcrumbItem extends AeicoComponent {
|
|
25
|
+
static tagName = 'breadcrumb-item';
|
|
26
|
+
|
|
27
|
+
protected static styles = [styleVariables, style];
|
|
28
|
+
|
|
29
|
+
@prop({ type: String })
|
|
30
|
+
accessor href: string | undefined;
|
|
31
|
+
|
|
32
|
+
protected render() {
|
|
33
|
+
return html(({ li, span, slot, a }) => {
|
|
34
|
+
li({ part: 'item', className: 'item' }, () => {
|
|
35
|
+
span({ part: 'separator', className: 'sep', 'aria-hidden': 'true' }, () => {
|
|
36
|
+
slot({ name: 'separator' });
|
|
37
|
+
});
|
|
38
|
+
span({ part: 'label', className: 'label' }, () => {
|
|
39
|
+
if (this.href) {
|
|
40
|
+
a({ href: this.href, part: 'link' }, () => {
|
|
41
|
+
slot();
|
|
42
|
+
});
|
|
43
|
+
} else {
|
|
44
|
+
slot();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
BreadcrumbItem.register();
|
|
53
|
+
|
|
54
|
+
declare global {
|
|
55
|
+
interface HTMLElementTagNameMap {
|
|
56
|
+
'ae-breadcrumb-item': BreadcrumbItem;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default BreadcrumbItem;
|
|
61
|
+
export type BreadcrumbItemProps = InferProps<typeof BreadcrumbItem>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { InferProps } from 'aeico';
|
|
2
|
+
import styleVariables from '../styles/variables.css?inline';
|
|
3
|
+
import colorCSS from '../styles/color.css?inline';
|
|
4
|
+
import style from '../styles/components/breadcrumb.css?inline';
|
|
5
|
+
import AeicoComponent from '../aeico-component';
|
|
6
|
+
import { html } from 'aeico';
|
|
7
|
+
import { prop } from 'aeico';
|
|
8
|
+
import type BreadcrumbItem from './breadcrumb-item';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Breadcrumb Component
|
|
12
|
+
*
|
|
13
|
+
* A navigation trail that shows the user's location in a hierarchy.
|
|
14
|
+
* Automatically injects separators between items and marks the last item
|
|
15
|
+
* as `aria-current="page"`.
|
|
16
|
+
*
|
|
17
|
+
* The separator is configurable via:
|
|
18
|
+
* - `separator` attribute (text, default `/`) — simple and concise
|
|
19
|
+
* - `slot="separator"` (any element, e.g. `ae-icon`) — takes priority over the attribute
|
|
20
|
+
*
|
|
21
|
+
* Supports `color` for theming item link colors. The separator intentionally
|
|
22
|
+
* uses a fixed muted color and does NOT respond to the `color` prop.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```html
|
|
26
|
+
* <!-- Default separator "/" -->
|
|
27
|
+
* <ae-breadcrumb>
|
|
28
|
+
* <ae-breadcrumb-item href="/">Home</ae-breadcrumb-item>
|
|
29
|
+
* <ae-breadcrumb-item href="/docs">Docs</ae-breadcrumb-item>
|
|
30
|
+
* <ae-breadcrumb-item>Getting Started</ae-breadcrumb-item>
|
|
31
|
+
* </ae-breadcrumb>
|
|
32
|
+
*
|
|
33
|
+
* <!-- Custom text separator -->
|
|
34
|
+
* <ae-breadcrumb separator=">">
|
|
35
|
+
* <ae-breadcrumb-item href="/">Home</ae-breadcrumb-item>
|
|
36
|
+
* <ae-breadcrumb-item>Current</ae-breadcrumb-item>
|
|
37
|
+
* </ae-breadcrumb>
|
|
38
|
+
*
|
|
39
|
+
* <!-- Icon separator (slot takes priority over separator attribute) -->
|
|
40
|
+
* <ae-breadcrumb>
|
|
41
|
+
* <ae-icon slot="separator" name="chevron-right" size="xs"></ae-icon>
|
|
42
|
+
* <ae-breadcrumb-item href="/">Home</ae-breadcrumb-item>
|
|
43
|
+
* <ae-breadcrumb-item>Current</ae-breadcrumb-item>
|
|
44
|
+
* </ae-breadcrumb>
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
class Breadcrumb extends AeicoComponent {
|
|
48
|
+
static tagName = 'breadcrumb';
|
|
49
|
+
|
|
50
|
+
protected static styles = [styleVariables, colorCSS, style];
|
|
51
|
+
|
|
52
|
+
/** Text separator shown between items. Ignored when `slot="separator"` is provided. */
|
|
53
|
+
@prop({ type: String })
|
|
54
|
+
accessor separator: string = '/';
|
|
55
|
+
|
|
56
|
+
@prop({ type: String })
|
|
57
|
+
accessor color: string | undefined;
|
|
58
|
+
|
|
59
|
+
private _itemsSlot: HTMLSlotElement | null = null;
|
|
60
|
+
private _sepSlot: HTMLSlotElement | null = null;
|
|
61
|
+
|
|
62
|
+
protected render() {
|
|
63
|
+
return html(({ nav, ol, slot }) => {
|
|
64
|
+
nav({ 'aria-label': 'breadcrumb', part: 'nav' }, () => {
|
|
65
|
+
ol({ part: 'list', className: 'list' }, () => {
|
|
66
|
+
this._itemsSlot = slot({
|
|
67
|
+
'@slotchange': () => this._syncSeparators(),
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
this._sepSlot = slot({
|
|
72
|
+
name: 'separator',
|
|
73
|
+
className: 'sep-template',
|
|
74
|
+
'@slotchange': () => this._syncSeparators(),
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
protected onUpdated() {
|
|
80
|
+
this._syncSeparators();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private _getItems(): BreadcrumbItem[] {
|
|
84
|
+
return (this._itemsSlot?.assignedElements() ?? []) as BreadcrumbItem[];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private _getSepElement(): Element | null {
|
|
88
|
+
return this._sepSlot?.assignedElements()[0] ?? null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private _syncSeparators = () => {
|
|
92
|
+
const items = this._getItems();
|
|
93
|
+
const sepEl = this._getSepElement();
|
|
94
|
+
|
|
95
|
+
items.forEach((item, i) => {
|
|
96
|
+
// Remove previously injected separators to avoid duplicates
|
|
97
|
+
item.querySelectorAll('[data-ae-sep]').forEach((n) => n.remove());
|
|
98
|
+
|
|
99
|
+
const isLast = i === items.length - 1;
|
|
100
|
+
|
|
101
|
+
// Mark the last item as the current page for accessibility
|
|
102
|
+
if (isLast) {
|
|
103
|
+
item.setAttribute('aria-current', 'page');
|
|
104
|
+
} else {
|
|
105
|
+
item.removeAttribute('aria-current');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// First item gets no separator
|
|
109
|
+
if (i === 0) return;
|
|
110
|
+
|
|
111
|
+
const wrapper = document.createElement('span');
|
|
112
|
+
wrapper.setAttribute('slot', 'separator');
|
|
113
|
+
wrapper.setAttribute('data-ae-sep', '');
|
|
114
|
+
wrapper.setAttribute('aria-hidden', 'true');
|
|
115
|
+
|
|
116
|
+
if (sepEl) {
|
|
117
|
+
// Clone the slotted separator element (e.g. ae-icon)
|
|
118
|
+
wrapper.appendChild(sepEl.cloneNode(true));
|
|
119
|
+
} else {
|
|
120
|
+
// Fall back to text separator
|
|
121
|
+
wrapper.textContent = this.separator;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
item.prepend(wrapper);
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
Breadcrumb.register();
|
|
130
|
+
|
|
131
|
+
declare global {
|
|
132
|
+
interface HTMLElementTagNameMap {
|
|
133
|
+
'ae-breadcrumb': Breadcrumb;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default Breadcrumb;
|
|
138
|
+
export type BreadcrumbProps = InferProps<typeof Breadcrumb>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default, default as Breadcrumb } from './breadcrumb';
|
|
2
|
+
export { default as BreadcrumbItem } from './breadcrumb-item';
|
|
3
|
+
export type { BreadcrumbProps } from './breadcrumb';
|
|
4
|
+
export type { BreadcrumbItemProps } from './breadcrumb-item';
|
|
5
|
+
export type { BreadcrumbColor } from './defines';
|
|
@@ -0,0 +1,147 @@
|
|
|
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 buttonStyle from '../styles/components/button.css?inline';
|
|
6
|
+
import AeicoComponent from '../aeico-component';
|
|
7
|
+
import { html } from 'aeico';
|
|
8
|
+
import { ButtonColor, ButtonSize, ButtonVariant } from './defines';
|
|
9
|
+
import { prop } from 'aeico';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Button Component
|
|
13
|
+
*
|
|
14
|
+
* A customizable button component with multiple variants and sizes.
|
|
15
|
+
* Supports theme and internationalization through mixins.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // Using the static create method
|
|
20
|
+
* const button = Button.create({
|
|
21
|
+
* variant: 'primary',
|
|
22
|
+
* size: 'md'
|
|
23
|
+
* })
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```html
|
|
28
|
+
* <!-- Using as Web Component -->
|
|
29
|
+
* <ae-button variant="primary" size="md">Save</ae-button>
|
|
30
|
+
* <ae-button variant="danger" size="sm">Delete</ae-button>
|
|
31
|
+
* <ae-button variant="subtle">Cancel</ae-button>
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
class Button extends AeicoComponent {
|
|
35
|
+
protected static styles = [styleVariables, sizeCSS, colorCSS, buttonStyle];
|
|
36
|
+
|
|
37
|
+
@prop({ type: String })
|
|
38
|
+
color?: ButtonColor;
|
|
39
|
+
|
|
40
|
+
@prop({ type: String })
|
|
41
|
+
variant?: ButtonVariant;
|
|
42
|
+
|
|
43
|
+
@prop({ type: String })
|
|
44
|
+
size?: ButtonSize;
|
|
45
|
+
|
|
46
|
+
@prop({ type: Boolean })
|
|
47
|
+
disabled?: boolean;
|
|
48
|
+
|
|
49
|
+
@prop({ type: String })
|
|
50
|
+
type?: 'button' | 'submit' | 'reset';
|
|
51
|
+
|
|
52
|
+
@prop({ type: Boolean })
|
|
53
|
+
active?: boolean;
|
|
54
|
+
|
|
55
|
+
@prop({ type: Boolean })
|
|
56
|
+
block?: boolean;
|
|
57
|
+
|
|
58
|
+
private buttonElement: HTMLButtonElement | null = null;
|
|
59
|
+
private _autoAriaLabel = false;
|
|
60
|
+
|
|
61
|
+
protected onMounted() {
|
|
62
|
+
const slot = this.shadowRoot?.querySelector('slot:not([name])');
|
|
63
|
+
if (slot) this.listen(slot, 'slotchange', this._handleSlotChange);
|
|
64
|
+
this._handleSlotChange();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private _handleSlotChange = () => {
|
|
68
|
+
const slot = this.shadowRoot?.querySelector('slot:not([name])') as HTMLSlotElement | null;
|
|
69
|
+
const nodes = slot?.assignedNodes() ?? [];
|
|
70
|
+
// Icon-only: exactly one element (ae-icon) and no meaningful text nodes
|
|
71
|
+
const elements = nodes.filter((n): n is Element => n.nodeType === Node.ELEMENT_NODE);
|
|
72
|
+
const hasText = nodes.some(
|
|
73
|
+
(n) => n.nodeType === Node.TEXT_NODE && n.textContent!.trim() !== '',
|
|
74
|
+
);
|
|
75
|
+
const isIconOnly =
|
|
76
|
+
!hasText && elements.length === 1 && elements[0].tagName.toLowerCase() === 'ae-icon';
|
|
77
|
+
|
|
78
|
+
if (isIconOnly) {
|
|
79
|
+
this.setAttribute('icon-only', '');
|
|
80
|
+
if (!this.hasAttribute('aria-label') || this._autoAriaLabel) {
|
|
81
|
+
this.setAttribute('aria-label', elements[0].getAttribute('name') ?? '');
|
|
82
|
+
this._autoAriaLabel = true;
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
this.removeAttribute('icon-only');
|
|
86
|
+
if (this._autoAriaLabel) {
|
|
87
|
+
this.removeAttribute('aria-label');
|
|
88
|
+
this._autoAriaLabel = false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
protected render() {
|
|
94
|
+
return html(({ button, slot }) => {
|
|
95
|
+
this.buttonElement = button(
|
|
96
|
+
{
|
|
97
|
+
type: this.type || 'button',
|
|
98
|
+
disabled: this.disabled,
|
|
99
|
+
part: 'button',
|
|
100
|
+
'aria-pressed': this.active,
|
|
101
|
+
'aria-disabled': this.disabled,
|
|
102
|
+
},
|
|
103
|
+
() => {
|
|
104
|
+
slot();
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Programmatically click the button
|
|
112
|
+
*/
|
|
113
|
+
click() {
|
|
114
|
+
if (!this.disabled && this.buttonElement) {
|
|
115
|
+
this.buttonElement.click();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Focus the button
|
|
121
|
+
*/
|
|
122
|
+
focus() {
|
|
123
|
+
if (this.buttonElement) {
|
|
124
|
+
this.buttonElement.focus();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Blur the button
|
|
130
|
+
*/
|
|
131
|
+
blur() {
|
|
132
|
+
if (this.buttonElement) {
|
|
133
|
+
this.buttonElement.blur();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
Button.register();
|
|
139
|
+
|
|
140
|
+
declare global {
|
|
141
|
+
interface HTMLElementTagNameMap {
|
|
142
|
+
'ae-button': Button;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export default Button;
|
|
147
|
+
export type ButtonProps = InferProps<typeof Button>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type ButtonColor =
|
|
2
|
+
| 'default'
|
|
3
|
+
| 'primary'
|
|
4
|
+
| 'secondary'
|
|
5
|
+
| 'success'
|
|
6
|
+
| 'danger'
|
|
7
|
+
| 'warning'
|
|
8
|
+
| 'info'
|
|
9
|
+
| 'light'
|
|
10
|
+
| 'dark';
|
|
11
|
+
export type ButtonVariant = 'filled' | 'outlined' | 'faint' | 'subtle' | 'text';
|
|
12
|
+
export type ButtonSize = '3xs' | '2xs' | 'xs' | 'sm' | 'md' | 'lg';
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { InferProps, Props } from 'aeico';
|
|
2
|
+
import styleVariables from '../styles/variables.css?inline';
|
|
3
|
+
import buttonGroupStyle from '../styles/components/button-group.css?inline';
|
|
4
|
+
import AeicoComponent from '../aeico-component';
|
|
5
|
+
import { html } from 'aeico';
|
|
6
|
+
import type { ButtonColor, ButtonVariant, ButtonSize } from '../button';
|
|
7
|
+
import Button from '../button/button';
|
|
8
|
+
import DropdownButton from '../dropdown/dropdown-button';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ButtonGroup Component
|
|
12
|
+
*
|
|
13
|
+
* Groups multiple `ae-button` elements, propagating shared `variant`, `color`,
|
|
14
|
+
* `size`, and `disabled` props to each child. Supports a `compact` mode that
|
|
15
|
+
* joins buttons into a seamless connected strip (like Bootstrap's button group).
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```html
|
|
19
|
+
* <!-- Loose group (gap between buttons) -->
|
|
20
|
+
* <ae-button-group variant="outlined" color="primary">
|
|
21
|
+
* <ae-button>One</ae-button>
|
|
22
|
+
* <ae-button>Two</ae-button>
|
|
23
|
+
* <ae-button>Three</ae-button>
|
|
24
|
+
* </ae-button-group>
|
|
25
|
+
*
|
|
26
|
+
* <!-- Compact — joined strip -->
|
|
27
|
+
* <ae-button-group compact color="primary">
|
|
28
|
+
* <ae-button>Left</ae-button>
|
|
29
|
+
* <ae-button>Middle</ae-button>
|
|
30
|
+
* <ae-button>Right</ae-button>
|
|
31
|
+
* </ae-button-group>
|
|
32
|
+
*
|
|
33
|
+
* <!-- Full-width -->
|
|
34
|
+
* <ae-button-group block color="danger" variant="outlined">
|
|
35
|
+
* <ae-button>Delete</ae-button>
|
|
36
|
+
* <ae-button>Archive</ae-button>
|
|
37
|
+
* </ae-button-group>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
class ButtonGroup extends AeicoComponent {
|
|
41
|
+
static props: Props = {
|
|
42
|
+
variant: { type: String },
|
|
43
|
+
color: { type: String },
|
|
44
|
+
size: { type: String },
|
|
45
|
+
compact: { type: Boolean },
|
|
46
|
+
block: { type: Boolean },
|
|
47
|
+
disabled: { type: Boolean },
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
protected static styles = [styleVariables, buttonGroupStyle];
|
|
51
|
+
|
|
52
|
+
declare variant?: ButtonVariant;
|
|
53
|
+
declare color?: ButtonColor;
|
|
54
|
+
declare size?: ButtonSize;
|
|
55
|
+
declare compact?: boolean;
|
|
56
|
+
declare block?: boolean;
|
|
57
|
+
declare disabled?: boolean;
|
|
58
|
+
|
|
59
|
+
private slotEl: HTMLSlotElement | null = null;
|
|
60
|
+
|
|
61
|
+
connectedCallback() {
|
|
62
|
+
super.connectedCallback();
|
|
63
|
+
|
|
64
|
+
if (this.variant === undefined) this.variant = 'filled';
|
|
65
|
+
if (this.color === undefined) this.color = 'default';
|
|
66
|
+
if (this.size === undefined) this.size = 'md';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected render() {
|
|
70
|
+
return html(({ slot }) => {
|
|
71
|
+
this.slotEl = slot({
|
|
72
|
+
'@slotchange': () => this._syncChildren(),
|
|
73
|
+
});
|
|
74
|
+
this._syncChildren();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private _getButtons(): Array<Button | DropdownButton> {
|
|
79
|
+
if (!this.slotEl) return [];
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
this.slotEl.assignedElements({ flatten: true }) as Array<Button | DropdownButton>
|
|
83
|
+
).filter((el) => {
|
|
84
|
+
const tag = el.tagName.toLowerCase();
|
|
85
|
+
return tag === 'ae-button' || tag === 'ae-dropdown-button';
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private _syncChildren() {
|
|
90
|
+
const buttons = this._getButtons();
|
|
91
|
+
const r = this.size === 'xs' || this.size === 'sm' ? 3 : 4;
|
|
92
|
+
|
|
93
|
+
buttons.forEach((btn: Button | DropdownButton, i) => {
|
|
94
|
+
btn.variant = this.variant;
|
|
95
|
+
btn.color = this.color;
|
|
96
|
+
btn.size = this.size;
|
|
97
|
+
|
|
98
|
+
if (this.disabled) {
|
|
99
|
+
btn.disabled = true;
|
|
100
|
+
} else {
|
|
101
|
+
btn.disabled = false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (this.compact) {
|
|
105
|
+
const isFirst = i === 0;
|
|
106
|
+
const isLast = i === buttons.length - 1;
|
|
107
|
+
|
|
108
|
+
// Overlap adjacent borders by pulling non-first buttons left 1px
|
|
109
|
+
btn.style.marginLeft = isFirst ? '' : '-1px';
|
|
110
|
+
|
|
111
|
+
// Shape corners: only the outer edges of the strip keep radius
|
|
112
|
+
btn.style.setProperty('--_btn-r-tl', isFirst ? `${r}px` : '0');
|
|
113
|
+
btn.style.setProperty('--_btn-r-bl', isFirst ? `${r}px` : '0');
|
|
114
|
+
btn.style.setProperty('--_btn-r-tr', isLast ? `${r}px` : '0');
|
|
115
|
+
btn.style.setProperty('--_btn-r-br', isLast ? `${r}px` : '0');
|
|
116
|
+
} else {
|
|
117
|
+
btn.style.marginLeft = '';
|
|
118
|
+
this._clearRadius(btn);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private _clearRadius(btn: HTMLElement) {
|
|
124
|
+
btn.style.removeProperty('--_btn-r-tl');
|
|
125
|
+
btn.style.removeProperty('--_btn-r-tr');
|
|
126
|
+
btn.style.removeProperty('--_btn-r-br');
|
|
127
|
+
btn.style.removeProperty('--_btn-r-bl');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
ButtonGroup.register();
|
|
132
|
+
|
|
133
|
+
declare global {
|
|
134
|
+
interface HTMLElementTagNameMap {
|
|
135
|
+
'ae-button-group': ButtonGroup;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export default ButtonGroup;
|
|
140
|
+
export type ButtonGroupProps = InferProps<typeof ButtonGroup>;
|
package/src/card/card.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { InferProps } from 'aeico';
|
|
2
|
+
import styleVariables from '../styles/variables.css?inline';
|
|
3
|
+
import colorCSS from '../styles/color.css?inline';
|
|
4
|
+
import cardStyle from '../styles/components/card.css?inline';
|
|
5
|
+
import AeicoComponent from '../aeico-component';
|
|
6
|
+
import { html } from 'aeico';
|
|
7
|
+
import type { CardVariant, CardColor } from './defines';
|
|
8
|
+
import { prop } from 'aeico';
|
|
9
|
+
|
|
10
|
+
class Card extends AeicoComponent {
|
|
11
|
+
static tagName = 'card';
|
|
12
|
+
|
|
13
|
+
protected static styles = [styleVariables, colorCSS, cardStyle];
|
|
14
|
+
|
|
15
|
+
@prop({ type: String })
|
|
16
|
+
accessor color: CardColor = 'default';
|
|
17
|
+
|
|
18
|
+
@prop({ type: String })
|
|
19
|
+
accessor variant: CardVariant = 'filled';
|
|
20
|
+
|
|
21
|
+
protected render() {
|
|
22
|
+
return html(({ div, header, footer, slot }) => {
|
|
23
|
+
div({ className: 'card', part: 'card' }, () => {
|
|
24
|
+
header({ className: 'header', part: 'header' }, () => {
|
|
25
|
+
slot({ name: 'header', '@slotchange': (e: Event) => this._onHeaderSlotChange(e) });
|
|
26
|
+
});
|
|
27
|
+
div({ className: 'body', part: 'body' }, () => {
|
|
28
|
+
slot();
|
|
29
|
+
});
|
|
30
|
+
footer({ className: 'footer', part: 'footer' }, () => {
|
|
31
|
+
slot({ name: 'footer', '@slotchange': (e: Event) => this._onFooterSlotChange(e) });
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private _onHeaderSlotChange(e: Event) {
|
|
38
|
+
const slot = e.target as HTMLSlotElement;
|
|
39
|
+
this.toggleAttribute('has-header', slot.assignedNodes({ flatten: true }).length > 0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private _onFooterSlotChange(e: Event) {
|
|
43
|
+
const slot = e.target as HTMLSlotElement;
|
|
44
|
+
this.toggleAttribute('has-footer', slot.assignedNodes({ flatten: true }).length > 0);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Card.register();
|
|
49
|
+
|
|
50
|
+
declare global {
|
|
51
|
+
interface HTMLElementTagNameMap {
|
|
52
|
+
'ae-card': Card;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default Card;
|
|
57
|
+
export type CardProps = InferProps<typeof Card>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import AeicoField from '../aeico-field';
|
|
2
|
+
import type { InferProps, Props } from 'aeico';
|
|
3
|
+
import { html } 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 styles from '../styles/components/checkbox.css?inline';
|
|
8
|
+
import { CheckboxVariant } from './defines';
|
|
9
|
+
|
|
10
|
+
class Checkbox extends AeicoField {
|
|
11
|
+
protected fieldElement: HTMLInputElement | null = null;
|
|
12
|
+
|
|
13
|
+
static tagName = 'checkbox';
|
|
14
|
+
|
|
15
|
+
static props: Props = {
|
|
16
|
+
checked: { type: Boolean },
|
|
17
|
+
defaultChecked: { type: Boolean },
|
|
18
|
+
variant: { type: String },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
declare checked?: boolean;
|
|
22
|
+
declare defaultChecked?: boolean;
|
|
23
|
+
declare variant?: CheckboxVariant;
|
|
24
|
+
|
|
25
|
+
protected static styles = [styleVariables, sizeCSS, colorCSS, styles];
|
|
26
|
+
|
|
27
|
+
protected getValue(): boolean {
|
|
28
|
+
return this.fieldElement?.checked ?? false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
protected writeValue(checked: boolean): void {
|
|
32
|
+
if (this.fieldElement) {
|
|
33
|
+
this.fieldElement.checked = Boolean(checked);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
protected getEventPayload(checked: boolean, oldChecked: boolean, action: any) {
|
|
38
|
+
return { checked, oldChecked, action };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
protected setValue(checked: boolean, options?: { silent?: boolean; action?: any }): void {
|
|
42
|
+
const oldChecked = this.getValue();
|
|
43
|
+
this.checked = checked;
|
|
44
|
+
this.writeValue(checked);
|
|
45
|
+
if (options?.silent === false) {
|
|
46
|
+
this.emit('change', {
|
|
47
|
+
detail: this.getEventPayload(checked, oldChecked, options.action || 'change'),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public reset(checked?: boolean, options?: { silent?: boolean }): void {
|
|
53
|
+
this.setValue(checked !== undefined ? checked : (this.defaultChecked ?? false), {
|
|
54
|
+
...options,
|
|
55
|
+
action: 'reset',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public clear(options?: { silent?: boolean }): void {
|
|
60
|
+
this.setValue(false, { ...options, action: 'clear' });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
render() {
|
|
64
|
+
return html(({ div, input }) => {
|
|
65
|
+
div({ className: 'checkbox-container', variant: this.variant }, () => {
|
|
66
|
+
div({ className: 'checkbox-wrapper' }, () => {
|
|
67
|
+
this.fieldElement = input({
|
|
68
|
+
type: 'checkbox',
|
|
69
|
+
className: 'field-input',
|
|
70
|
+
checked: Boolean(this.checked),
|
|
71
|
+
disabled: Boolean(this.disabled),
|
|
72
|
+
'@change': this.boundOnChange,
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
this.renderActionButtons();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Checkbox.register();
|
|
82
|
+
|
|
83
|
+
declare global {
|
|
84
|
+
interface HTMLElementTagNameMap {
|
|
85
|
+
'ae-checkbox': Checkbox;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default Checkbox;
|
|
90
|
+
export type CheckboxProps = InferProps<typeof Checkbox>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type CheckboxVariant = 'checkbox';
|