aeico-components 0.1.5 → 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.
- package/LICENSE +21 -0
- package/README.md +0 -0
- package/dist/chunks/action-button.cjs +296 -0
- package/dist/chunks/action-button.cjs.map +1 -0
- package/dist/chunks/action-button.js +297 -0
- package/dist/chunks/action-button.js.map +1 -0
- package/dist/chunks/alert.cjs +4 -4
- package/dist/chunks/alert.cjs.map +1 -1
- package/dist/chunks/alert.js +5 -5
- package/dist/chunks/alert.js.map +1 -1
- package/dist/chunks/badge.cjs +1 -1
- package/dist/chunks/badge.cjs.map +1 -1
- package/dist/chunks/badge.js +2 -2
- package/dist/chunks/badge.js.map +1 -1
- package/dist/chunks/breadcrumb-item.cjs +2 -2
- package/dist/chunks/breadcrumb-item.cjs.map +1 -1
- package/dist/chunks/breadcrumb-item.js +3 -3
- package/dist/chunks/breadcrumb-item.js.map +1 -1
- package/dist/chunks/button-group.cjs +1 -1
- package/dist/chunks/button-group.cjs.map +1 -1
- package/dist/chunks/button-group.js +2 -2
- package/dist/chunks/button-group.js.map +1 -1
- package/dist/chunks/button.cjs +1 -1
- package/dist/chunks/button.cjs.map +1 -1
- package/dist/chunks/button.js +2 -2
- package/dist/chunks/button.js.map +1 -1
- package/dist/chunks/card.cjs +1 -1
- package/dist/chunks/card.cjs.map +1 -1
- package/dist/chunks/card.js +2 -2
- package/dist/chunks/card.js.map +1 -1
- package/dist/chunks/checkbox.cjs +18 -5
- package/dist/chunks/checkbox.cjs.map +1 -1
- package/dist/chunks/checkbox.js +18 -5
- package/dist/chunks/checkbox.js.map +1 -1
- package/dist/chunks/copy-button.cjs +168 -0
- package/dist/chunks/copy-button.cjs.map +1 -0
- package/dist/chunks/copy-button.js +169 -0
- package/dist/chunks/copy-button.js.map +1 -0
- package/dist/chunks/detail.cjs +7 -4
- package/dist/chunks/detail.cjs.map +1 -1
- package/dist/chunks/detail.js +8 -5
- package/dist/chunks/detail.js.map +1 -1
- package/dist/chunks/dialog.cjs +1 -1
- package/dist/chunks/dialog.cjs.map +1 -1
- package/dist/chunks/dialog.js +2 -2
- package/dist/chunks/dialog.js.map +1 -1
- package/dist/chunks/divider.cjs +1 -1
- package/dist/chunks/divider.cjs.map +1 -1
- package/dist/chunks/divider.js +2 -2
- package/dist/chunks/divider.js.map +1 -1
- package/dist/chunks/drawer.cjs +180 -0
- package/dist/chunks/drawer.cjs.map +1 -0
- package/dist/chunks/drawer.js +181 -0
- package/dist/chunks/drawer.js.map +1 -0
- package/dist/chunks/dropdown-button.cjs +2 -2
- package/dist/chunks/dropdown-button.cjs.map +1 -1
- package/dist/chunks/dropdown-button.js +3 -3
- package/dist/chunks/dropdown-button.js.map +1 -1
- package/dist/chunks/icon.cjs +31 -1
- package/dist/chunks/icon.cjs.map +1 -1
- package/dist/chunks/icon.js +32 -2
- package/dist/chunks/icon.js.map +1 -1
- package/dist/chunks/menu.cjs +396 -0
- package/dist/chunks/menu.cjs.map +1 -0
- package/dist/chunks/menu.js +397 -0
- package/dist/chunks/menu.js.map +1 -0
- package/dist/chunks/navbar.cjs +1 -1
- package/dist/chunks/navbar.cjs.map +1 -1
- package/dist/chunks/navbar.js +2 -2
- package/dist/chunks/navbar.js.map +1 -1
- package/dist/chunks/pagination.cjs +475 -0
- package/dist/chunks/pagination.cjs.map +1 -0
- package/dist/chunks/pagination.js +476 -0
- package/dist/chunks/pagination.js.map +1 -0
- package/dist/chunks/progress-bar.cjs +101 -0
- package/dist/chunks/progress-bar.cjs.map +1 -0
- package/dist/chunks/progress-bar.js +102 -0
- package/dist/chunks/progress-bar.js.map +1 -0
- package/dist/chunks/radio.cjs +11 -7
- package/dist/chunks/radio.cjs.map +1 -1
- package/dist/chunks/radio.js +11 -7
- package/dist/chunks/radio.js.map +1 -1
- package/dist/chunks/select.cjs +97 -66
- package/dist/chunks/select.cjs.map +1 -1
- package/dist/chunks/select.js +97 -66
- package/dist/chunks/select.js.map +1 -1
- package/dist/chunks/slider.cjs +9 -46
- package/dist/chunks/slider.cjs.map +1 -1
- package/dist/chunks/slider.js +9 -46
- package/dist/chunks/slider.js.map +1 -1
- package/dist/chunks/spinner.cjs +102 -0
- package/dist/chunks/spinner.cjs.map +1 -0
- package/dist/chunks/spinner.js +103 -0
- package/dist/chunks/spinner.js.map +1 -0
- package/dist/chunks/switch.cjs +110 -16
- package/dist/chunks/switch.cjs.map +1 -1
- package/dist/chunks/switch.js +111 -17
- package/dist/chunks/switch.js.map +1 -1
- package/dist/chunks/tab-panel.cjs +3 -3
- package/dist/chunks/tab-panel.cjs.map +1 -1
- package/dist/chunks/tab-panel.js +4 -4
- package/dist/chunks/tab-panel.js.map +1 -1
- package/dist/chunks/tag.cjs +1 -1
- package/dist/chunks/tag.cjs.map +1 -1
- package/dist/chunks/tag.js +2 -2
- package/dist/chunks/tag.js.map +1 -1
- package/dist/chunks/text-input.cjs +11 -16
- package/dist/chunks/text-input.cjs.map +1 -1
- package/dist/chunks/text-input.js +11 -16
- package/dist/chunks/text-input.js.map +1 -1
- package/dist/chunks/textarea.cjs +137 -0
- package/dist/chunks/textarea.cjs.map +1 -0
- package/dist/chunks/textarea.js +138 -0
- package/dist/chunks/textarea.js.map +1 -0
- package/dist/chunks/tooltip.cjs +293 -0
- package/dist/chunks/tooltip.cjs.map +1 -0
- package/dist/chunks/tooltip.js +294 -0
- package/dist/chunks/tooltip.js.map +1 -0
- package/dist/chunks/tree.cjs +468 -0
- package/dist/chunks/tree.cjs.map +1 -0
- package/dist/chunks/tree.js +469 -0
- package/dist/chunks/tree.js.map +1 -0
- package/dist/chunks/variables.cjs +2 -2
- package/dist/chunks/variables.js +2 -2
- package/dist/copy-button.cjs +6 -0
- package/dist/copy-button.cjs.map +1 -0
- package/dist/copy-button.js +6 -0
- package/dist/copy-button.js.map +1 -0
- package/dist/drawer.cjs +6 -0
- package/dist/drawer.cjs.map +1 -0
- package/dist/drawer.js +6 -0
- package/dist/drawer.js.map +1 -0
- package/dist/index.cjs +175 -88
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +186 -99
- package/dist/index.js.map +1 -1
- package/dist/menu.cjs +6 -0
- package/dist/menu.cjs.map +1 -0
- package/dist/menu.js +6 -0
- package/dist/menu.js.map +1 -0
- package/dist/pagination.cjs +6 -0
- package/dist/pagination.cjs.map +1 -0
- package/dist/pagination.js +6 -0
- package/dist/pagination.js.map +1 -0
- package/dist/progress-bar.cjs +6 -0
- package/dist/progress-bar.cjs.map +1 -0
- package/dist/progress-bar.js +6 -0
- package/dist/progress-bar.js.map +1 -0
- package/dist/select.cjs +1 -1
- package/dist/select.cjs.map +1 -1
- package/dist/select.js +2 -2
- package/dist/select.js.map +1 -1
- package/dist/spinner.cjs +6 -0
- package/dist/spinner.cjs.map +1 -0
- package/dist/spinner.js +6 -0
- package/dist/spinner.js.map +1 -0
- package/dist/textarea.cjs +5 -0
- package/dist/textarea.cjs.map +1 -0
- package/dist/textarea.js +5 -0
- package/dist/textarea.js.map +1 -0
- package/dist/tooltip.cjs +6 -0
- package/dist/tooltip.cjs.map +1 -0
- package/dist/tooltip.js +6 -0
- package/dist/tooltip.js.map +1 -0
- package/dist/tree.cjs +6 -0
- package/dist/tree.cjs.map +1 -0
- package/dist/tree.js +6 -0
- package/dist/tree.js.map +1 -0
- package/dist/types/aeico-field.d.ts +52 -0
- package/dist/types/alert/alert.d.ts +1 -0
- package/dist/types/copy-button/copy-button.d.ts +32 -0
- package/dist/types/copy-button/defines.d.ts +1 -0
- package/dist/types/copy-button/index.d.ts +3 -0
- package/dist/types/detail/defines.d.ts +1 -0
- package/dist/types/detail/detail.d.ts +3 -1
- package/dist/types/detail/index.d.ts +1 -1
- package/dist/types/detail-group/detail-group.d.ts +39 -0
- package/dist/types/detail-group/index.d.ts +2 -0
- package/dist/types/drawer/defines.d.ts +1 -0
- package/dist/types/drawer/drawer.d.ts +31 -0
- package/dist/types/drawer/index.d.ts +3 -0
- package/dist/types/icon/built-in-icons.d.ts +1 -0
- package/dist/types/icon/icon.d.ts +1 -0
- package/dist/types/icon/registry.d.ts +8 -0
- package/dist/types/index.d.ts +17 -0
- package/dist/types/menu/defines.d.ts +15 -0
- package/dist/types/menu/index.d.ts +5 -0
- package/dist/types/menu/menu-item.d.ts +63 -0
- package/dist/types/menu/menu.d.ts +34 -0
- package/dist/types/number-input/index.d.ts +2 -0
- package/dist/types/number-input/number-input.d.ts +35 -0
- package/dist/types/pagination/defines.d.ts +2 -0
- package/dist/types/pagination/index.d.ts +3 -0
- package/dist/types/pagination/pagination.d.ts +77 -0
- package/dist/types/select/select.d.ts +2 -2
- package/dist/types/spinner/defines.d.ts +3 -0
- package/dist/types/spinner/index.d.ts +3 -0
- package/dist/types/spinner/spinner.d.ts +35 -0
- package/dist/types/switch/defines.d.ts +1 -0
- package/dist/types/switch/switch.d.ts +8 -4
- package/dist/types/text-input/text-input.d.ts +0 -4
- package/dist/types/textarea/index.d.ts +1 -0
- package/dist/types/textarea/textarea.d.ts +26 -0
- package/dist/types/tooltip/defines.d.ts +2 -0
- package/dist/types/tooltip/index.d.ts +4 -0
- package/dist/types/tooltip/tooltip.d.ts +48 -0
- package/dist/types/tree/defines.d.ts +23 -0
- package/dist/types/tree/index.d.ts +5 -0
- package/dist/types/tree/tree-item.d.ts +54 -0
- package/dist/types/tree/tree.d.ts +64 -0
- package/package.json +5 -5
- package/src/aeico-field.ts +142 -7
- package/src/alert/alert.ts +3 -2
- package/src/checkbox/checkbox.ts +17 -2
- package/src/copy-button/copy-button.ts +146 -0
- package/src/copy-button/defines.ts +5 -0
- package/src/copy-button/index.ts +3 -0
- package/src/detail/defines.ts +1 -0
- package/src/detail/detail.ts +5 -1
- package/src/detail/index.ts +1 -1
- package/src/detail-group/detail-group.ts +104 -0
- package/src/detail-group/index.ts +2 -0
- package/src/drawer/defines.ts +1 -0
- package/src/drawer/drawer.ts +157 -0
- package/src/drawer/index.ts +3 -0
- package/src/icon/built-in-icons.ts +21 -0
- package/src/icon/icon.ts +1 -0
- package/src/icon/registry.ts +22 -0
- package/src/index.ts +30 -0
- package/src/menu/defines.ts +17 -0
- package/src/menu/index.ts +5 -0
- package/src/menu/menu-item.ts +315 -0
- package/src/menu/menu.ts +81 -0
- package/src/number-input/index.ts +2 -0
- package/src/number-input/number-input.ts +137 -0
- package/src/pagination/defines.ts +2 -0
- package/src/pagination/index.ts +3 -0
- package/src/pagination/pagination.ts +310 -0
- package/src/radio-group/radio-group.ts +11 -4
- package/src/select/select.ts +111 -70
- package/src/slider/slider.ts +9 -2
- package/src/spinner/defines.ts +12 -0
- package/src/spinner/index.ts +3 -0
- package/src/spinner/spinner.ts +81 -0
- package/src/styles/components/action-button.css +37 -0
- package/src/styles/components/checkbox.css +4 -26
- package/src/styles/components/copy-button.css +119 -0
- package/src/styles/components/detail-group.css +10 -0
- package/src/styles/components/detail.css +10 -1
- package/src/styles/components/drawer.css +161 -0
- package/src/styles/components/field-label.css +120 -0
- package/src/styles/components/menu-item.css +168 -0
- package/src/styles/components/menu.css +17 -0
- package/src/styles/components/number-input.css +167 -0
- package/src/styles/components/pagination.css +205 -0
- package/src/styles/components/radio-group.css +0 -23
- package/src/styles/components/select.css +12 -39
- package/src/styles/components/slider.css +0 -42
- package/src/styles/components/spinner.css +80 -0
- package/src/styles/components/switch.css +68 -19
- package/src/styles/components/tab-panel.css +1 -1
- package/src/styles/components/tabs.css +1 -0
- package/src/styles/components/text-input.css +7 -45
- package/src/styles/components/textarea.css +75 -0
- package/src/styles/components/tooltip.css +103 -0
- package/src/styles/components/tree-item.css +152 -0
- package/src/styles/components/tree.css +10 -0
- package/src/styles/layout.css +457 -25
- package/src/switch/defines.ts +1 -0
- package/src/switch/switch.ts +61 -12
- package/src/text-input/text-input.ts +10 -15
- package/src/textarea/index.ts +1 -0
- package/src/textarea/textarea.ts +107 -0
- package/src/tooltip/defines.ts +11 -0
- package/src/tooltip/index.ts +4 -0
- package/src/tooltip/tooltip.ts +183 -0
- package/src/tree/defines.ts +26 -0
- package/src/tree/index.ts +5 -0
- package/src/tree/tree-item.ts +258 -0
- package/src/tree/tree.ts +237 -0
- package/dist/chunks/aeico-field.cjs +0 -179
- package/dist/chunks/aeico-field.cjs.map +0 -1
- package/dist/chunks/aeico-field.js +0 -180
- package/dist/chunks/aeico-field.js.map +0 -1
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { html, prop, tags } from 'aeico';
|
|
2
|
+
import type { InferProps } from 'aeico';
|
|
3
|
+
import AeicoComponent from '../aeico-component';
|
|
4
|
+
import styleVariables from '../styles/variables.css?inline';
|
|
5
|
+
import sizeCSS from '../styles/size.css?inline';
|
|
6
|
+
import paginationStyle from '../styles/components/pagination.css?inline';
|
|
7
|
+
import type { PaginationSize, PaginationVariant } from './defines';
|
|
8
|
+
import '../icon/icon';
|
|
9
|
+
|
|
10
|
+
type PageItem = number | 'ellipsis-start' | 'ellipsis-end';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Pagination component for navigating between pages of content.
|
|
14
|
+
*
|
|
15
|
+
* @prop {number} page - Current page number (1-indexed). Default: 1.
|
|
16
|
+
* @prop {number} pageSize - Number of items per page. Default: 10.
|
|
17
|
+
* @prop {number} total - Total number of items. Used to compute pageCount when pageCount is not set.
|
|
18
|
+
* @prop {number} pageCount - Total number of pages. Overrides total/pageSize calculation when set.
|
|
19
|
+
* @prop {number} siblingCount - Number of page buttons to show on each side of the current page. Default: 1.
|
|
20
|
+
* @prop {'xs'|'sm'|'md'|'lg'} size - Size variant.
|
|
21
|
+
* @prop {boolean} disabled - Disables all interactive controls.
|
|
22
|
+
* @prop {boolean} simple - Simple mode: shows only prev/next buttons and a page number input.
|
|
23
|
+
* @prop {boolean} showFirstLast - Show dedicated first-page and last-page jump buttons.
|
|
24
|
+
* @prop {'borderless'|'link'} variant - Visual variant. `borderless` removes borders; `link` renders page numbers as plain text links with no borders or backgrounds.
|
|
25
|
+
*
|
|
26
|
+
* @event {CustomEvent<{page: number}>} change - Fired when the page changes.
|
|
27
|
+
*
|
|
28
|
+
* @slot prev - Custom content for the previous-page button (default: chevron-left icon).
|
|
29
|
+
* @slot next - Custom content for the next-page button (default: chevron-right icon).
|
|
30
|
+
* @slot first - Custom content for the first-page button (default: chevrons-left icon). Requires showFirstLast.
|
|
31
|
+
* @slot last - Custom content for the last-page button (default: chevrons-right icon). Requires showFirstLast.
|
|
32
|
+
*
|
|
33
|
+
* @csspart root - The outer <nav> element.
|
|
34
|
+
* @csspart prev - The previous-page button.
|
|
35
|
+
* @csspart next - The next-page button.
|
|
36
|
+
* @csspart first - The first-page button (visible when showFirstLast).
|
|
37
|
+
* @csspart last - The last-page button (visible when showFirstLast).
|
|
38
|
+
* @csspart item - A page number button.
|
|
39
|
+
* @csspart ellipsis - An ellipsis span between page groups.
|
|
40
|
+
* @csspart page-input - The page number input in simple mode.
|
|
41
|
+
* @csspart page-total - The "/ N" label in simple mode.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```html
|
|
45
|
+
* <ae-pagination total="100" page-size="10" page="1"></ae-pagination>
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
class Pagination extends AeicoComponent {
|
|
49
|
+
static tagName = 'pagination';
|
|
50
|
+
|
|
51
|
+
protected static styles = [styleVariables, sizeCSS, paginationStyle];
|
|
52
|
+
|
|
53
|
+
@prop({ type: Number })
|
|
54
|
+
accessor page: number = 1;
|
|
55
|
+
|
|
56
|
+
@prop({ type: Number })
|
|
57
|
+
accessor pageSize: number = 10;
|
|
58
|
+
|
|
59
|
+
@prop({ type: Number })
|
|
60
|
+
accessor total: number = 0;
|
|
61
|
+
|
|
62
|
+
@prop({ type: Number })
|
|
63
|
+
accessor pageCount: number | undefined;
|
|
64
|
+
|
|
65
|
+
@prop({ type: Number })
|
|
66
|
+
accessor siblingCount: number = 1;
|
|
67
|
+
|
|
68
|
+
@prop({ type: String })
|
|
69
|
+
accessor size: PaginationSize | undefined;
|
|
70
|
+
|
|
71
|
+
@prop({ type: Boolean })
|
|
72
|
+
accessor disabled: boolean = false;
|
|
73
|
+
|
|
74
|
+
@prop({ type: Boolean })
|
|
75
|
+
accessor simple: boolean = false;
|
|
76
|
+
|
|
77
|
+
@prop({ type: Boolean })
|
|
78
|
+
accessor showFirstLast: boolean = false;
|
|
79
|
+
|
|
80
|
+
@prop({ type: String })
|
|
81
|
+
accessor variant: PaginationVariant | undefined;
|
|
82
|
+
|
|
83
|
+
private _simpleInput: HTMLInputElement | null = null;
|
|
84
|
+
|
|
85
|
+
private get _computedPageCount(): number {
|
|
86
|
+
if (this.pageCount != null) return Math.max(1, this.pageCount);
|
|
87
|
+
const ps = this.pageSize > 0 ? this.pageSize : 10;
|
|
88
|
+
return Math.max(1, Math.ceil(this.total / ps));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private get _currentPage(): number {
|
|
92
|
+
const count = this._computedPageCount;
|
|
93
|
+
return Math.max(1, Math.min(this.page ?? 1, count));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private _getPageItems(): PageItem[] {
|
|
97
|
+
const count = this._computedPageCount;
|
|
98
|
+
const page = this._currentPage;
|
|
99
|
+
const sc = Math.max(0, this.siblingCount ?? 1);
|
|
100
|
+
|
|
101
|
+
const rangeStart = Math.max(2, page - sc);
|
|
102
|
+
const rangeEnd = Math.min(count - 1, page + sc);
|
|
103
|
+
|
|
104
|
+
const showLeftEllipsis = rangeStart > 2;
|
|
105
|
+
const showRightEllipsis = rangeEnd < count - 1;
|
|
106
|
+
|
|
107
|
+
const items: PageItem[] = [];
|
|
108
|
+
|
|
109
|
+
// First page always shown
|
|
110
|
+
items.push(1);
|
|
111
|
+
|
|
112
|
+
if (count <= 1) return items;
|
|
113
|
+
|
|
114
|
+
// Left side
|
|
115
|
+
if (showLeftEllipsis) {
|
|
116
|
+
items.push('ellipsis-start');
|
|
117
|
+
} else {
|
|
118
|
+
for (let i = 2; i < rangeStart; i++) items.push(i);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Sibling range (bounded to 2..count-1)
|
|
122
|
+
for (let i = rangeStart; i <= rangeEnd; i++) items.push(i);
|
|
123
|
+
|
|
124
|
+
// Right side
|
|
125
|
+
if (showRightEllipsis) {
|
|
126
|
+
items.push('ellipsis-end');
|
|
127
|
+
} else {
|
|
128
|
+
for (let i = rangeEnd + 1; i < count; i++) items.push(i);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Last page always shown
|
|
132
|
+
items.push(count);
|
|
133
|
+
|
|
134
|
+
return items;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private _goToPage(page: number): void {
|
|
138
|
+
const count = this._computedPageCount;
|
|
139
|
+
const next = Math.max(1, Math.min(count, page));
|
|
140
|
+
if (next === this._currentPage) return;
|
|
141
|
+
this.page = next;
|
|
142
|
+
this.emit('change', { detail: { page: this.page } });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private _handleItemClick = (e: Event): void => {
|
|
146
|
+
const btn = e.currentTarget as HTMLElement;
|
|
147
|
+
const p = Number(btn.dataset.page);
|
|
148
|
+
if (!isNaN(p)) this._goToPage(p);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
private _handlePrevClick = (): void => {
|
|
152
|
+
this._goToPage(this._currentPage - 1);
|
|
153
|
+
};
|
|
154
|
+
private _handleNextClick = (): void => {
|
|
155
|
+
this._goToPage(this._currentPage + 1);
|
|
156
|
+
};
|
|
157
|
+
private _handleFirstClick = (): void => {
|
|
158
|
+
this._goToPage(1);
|
|
159
|
+
};
|
|
160
|
+
private _handleLastClick = (): void => {
|
|
161
|
+
this._goToPage(this._computedPageCount);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
private _handleSimpleInputChange = (e: Event): void => {
|
|
165
|
+
const input = e.target as HTMLInputElement;
|
|
166
|
+
const val = parseInt(input.value, 10);
|
|
167
|
+
if (!isNaN(val)) {
|
|
168
|
+
this._goToPage(val);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
input.value = String(this._currentPage);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
protected onUpdated(): void {
|
|
175
|
+
if (this._simpleInput) {
|
|
176
|
+
this._simpleInput.value = String(this._currentPage);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private _renderNavBtn(
|
|
181
|
+
name: 'first' | 'prev' | 'next' | 'last',
|
|
182
|
+
iconName: string,
|
|
183
|
+
label: string,
|
|
184
|
+
disabled: boolean,
|
|
185
|
+
onClick: () => void,
|
|
186
|
+
): void {
|
|
187
|
+
const { button, slot, aeIcon } = tags;
|
|
188
|
+
|
|
189
|
+
button(
|
|
190
|
+
{
|
|
191
|
+
key: name,
|
|
192
|
+
part: name,
|
|
193
|
+
className: 'nav-btn',
|
|
194
|
+
disabled,
|
|
195
|
+
'aria-label': label,
|
|
196
|
+
'@click': onClick,
|
|
197
|
+
},
|
|
198
|
+
() => {
|
|
199
|
+
slot({ name }, () => {
|
|
200
|
+
aeIcon({ name: iconName });
|
|
201
|
+
});
|
|
202
|
+
},
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private _renderSimpleMode(page: number, count: number, disabled: boolean): void {
|
|
207
|
+
this._simpleInput = tags.input({
|
|
208
|
+
key: 'page-input',
|
|
209
|
+
part: 'page-input',
|
|
210
|
+
type: 'number',
|
|
211
|
+
min: '1',
|
|
212
|
+
max: String(count),
|
|
213
|
+
value: String(page),
|
|
214
|
+
disabled,
|
|
215
|
+
'aria-label': 'Page number',
|
|
216
|
+
'@change': this._handleSimpleInputChange,
|
|
217
|
+
});
|
|
218
|
+
tags.span({ key: 'page-total', part: 'page-total', textContent: `/ ${count}` });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private _renderPageItems(page: number, disabled: boolean): void {
|
|
222
|
+
for (const item of this._getPageItems()) {
|
|
223
|
+
if (item === 'ellipsis-start' || item === 'ellipsis-end') {
|
|
224
|
+
tags.span({
|
|
225
|
+
key: item,
|
|
226
|
+
part: 'ellipsis',
|
|
227
|
+
className: 'ellipsis',
|
|
228
|
+
'aria-hidden': 'true',
|
|
229
|
+
textContent: '…',
|
|
230
|
+
});
|
|
231
|
+
} else {
|
|
232
|
+
const isActive = item === page;
|
|
233
|
+
tags.button({
|
|
234
|
+
key: `item-${item}`,
|
|
235
|
+
part: isActive ? 'item item-active' : 'item',
|
|
236
|
+
className: isActive ? 'item active' : 'item',
|
|
237
|
+
disabled,
|
|
238
|
+
'aria-current': isActive ? 'page' : undefined,
|
|
239
|
+
'aria-label': `Page ${item}`,
|
|
240
|
+
'data-page': String(item),
|
|
241
|
+
textContent: String(item),
|
|
242
|
+
'@click': this._handleItemClick,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
protected render() {
|
|
249
|
+
const count = this._computedPageCount;
|
|
250
|
+
const page = this._currentPage;
|
|
251
|
+
const disabled = this.disabled;
|
|
252
|
+
const isFirst = page <= 1;
|
|
253
|
+
const isLast = page >= count;
|
|
254
|
+
|
|
255
|
+
return html(({ nav }) => {
|
|
256
|
+
nav({ part: 'root', role: 'navigation', 'aria-label': 'Pagination' }, () => {
|
|
257
|
+
if (this.showFirstLast)
|
|
258
|
+
this._renderNavBtn(
|
|
259
|
+
'first',
|
|
260
|
+
'chevrons-left',
|
|
261
|
+
'First page',
|
|
262
|
+
disabled || isFirst,
|
|
263
|
+
this._handleFirstClick,
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
this._renderNavBtn(
|
|
267
|
+
'prev',
|
|
268
|
+
'chevron-left',
|
|
269
|
+
'Previous page',
|
|
270
|
+
disabled || isFirst,
|
|
271
|
+
this._handlePrevClick,
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
if (this.simple) {
|
|
275
|
+
this._renderSimpleMode(page, count, disabled);
|
|
276
|
+
} else {
|
|
277
|
+
this._renderPageItems(page, disabled);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
this._renderNavBtn(
|
|
281
|
+
'next',
|
|
282
|
+
'chevron-right',
|
|
283
|
+
'Next page',
|
|
284
|
+
disabled || isLast,
|
|
285
|
+
this._handleNextClick,
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
if (this.showFirstLast)
|
|
289
|
+
this._renderNavBtn(
|
|
290
|
+
'last',
|
|
291
|
+
'chevrons-right',
|
|
292
|
+
'Last page',
|
|
293
|
+
disabled || isLast,
|
|
294
|
+
this._handleLastClick,
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
Pagination.register();
|
|
302
|
+
|
|
303
|
+
declare global {
|
|
304
|
+
interface HTMLElementTagNameMap {
|
|
305
|
+
'ae-pagination': Pagination;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export default Pagination;
|
|
310
|
+
export type PaginationProps = InferProps<typeof Pagination>;
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
import type { InferProps, Props } from 'aeico';
|
|
3
3
|
import { html, tags } from 'aeico';
|
|
4
4
|
import type { ButtonColor, ButtonVariant, ButtonSize } from '../button';
|
|
5
|
-
import { t } from 'aeico-localize';
|
|
6
5
|
import type { RadioGroupMode, RadioGroupOption, RadioGroupOptions } from './defines';
|
|
7
6
|
import Radio from './radio';
|
|
8
7
|
import style from '../styles/components/radio-group.css?inline';
|
|
9
8
|
import variables from '../styles/variables.css?inline';
|
|
10
9
|
import sizeCSS from '../styles/size.css?inline';
|
|
11
10
|
import colorCSS from '../styles/color.css?inline';
|
|
11
|
+
import fieldLabelCSS from '../styles/components/field-label.css?inline';
|
|
12
|
+
import actionButtonCSS from '../styles/components/action-button.css?inline';
|
|
12
13
|
|
|
13
14
|
class RadioGroup extends AeicoField {
|
|
14
15
|
protected fieldElement: HTMLInputElement | null = null;
|
|
@@ -37,7 +38,7 @@ class RadioGroup extends AeicoField {
|
|
|
37
38
|
declare size?: ButtonSize;
|
|
38
39
|
declare allowEmpty?: boolean;
|
|
39
40
|
|
|
40
|
-
protected static styles = [variables, sizeCSS, colorCSS, style];
|
|
41
|
+
protected static styles = [variables, sizeCSS, colorCSS, fieldLabelCSS, actionButtonCSS, style];
|
|
41
42
|
|
|
42
43
|
constructor() {
|
|
43
44
|
super();
|
|
@@ -46,7 +47,7 @@ class RadioGroup extends AeicoField {
|
|
|
46
47
|
|
|
47
48
|
private _optLabel(opt: RadioGroupOption): string {
|
|
48
49
|
if (opt !== null && typeof opt === 'object') {
|
|
49
|
-
return
|
|
50
|
+
return String(opt.label);
|
|
50
51
|
}
|
|
51
52
|
return String(opt);
|
|
52
53
|
}
|
|
@@ -137,7 +138,9 @@ class RadioGroup extends AeicoField {
|
|
|
137
138
|
const current = this.value ?? '';
|
|
138
139
|
|
|
139
140
|
return html(({ div, slot }) => {
|
|
140
|
-
|
|
141
|
+
const id = this.getFieldId();
|
|
142
|
+
this.renderLabel(id);
|
|
143
|
+
div({ id, role: 'group', className: 'rg-container field-body' }, () => {
|
|
141
144
|
if (mode === 'default') {
|
|
142
145
|
this._renderRadio(opts, current);
|
|
143
146
|
} else {
|
|
@@ -153,6 +156,9 @@ class RadioGroup extends AeicoField {
|
|
|
153
156
|
style: { display: 'none' },
|
|
154
157
|
'@slotchange': () => this._onSlotChange(),
|
|
155
158
|
});
|
|
159
|
+
|
|
160
|
+
this.renderHelperText();
|
|
161
|
+
this.renderError();
|
|
156
162
|
});
|
|
157
163
|
}
|
|
158
164
|
|
|
@@ -171,6 +177,7 @@ class RadioGroup extends AeicoField {
|
|
|
171
177
|
name: this._groupName,
|
|
172
178
|
value: opt.value,
|
|
173
179
|
disabled: Boolean(this.disabled) || Boolean(opt.disabled),
|
|
180
|
+
required: Boolean(this.required),
|
|
174
181
|
'@click': this._boundOnRadioClick,
|
|
175
182
|
});
|
|
176
183
|
// Sync DOM property directly — setAttribute('checked') doesn't work
|
package/src/select/select.ts
CHANGED
|
@@ -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,7 +102,7 @@ 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
|
|
@@ -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(
|
|
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
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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:
|
|
391
|
+
textContent: opt.label,
|
|
351
392
|
selected: isSelected ? true : undefined,
|
|
352
393
|
});
|
|
353
394
|
} else {
|
package/src/slider/slider.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|