@foxeltech/angular-ui 0.2.0 → 0.3.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/components.css +2 -17
- package/fesm2022/foxeltech-angular-ui.mjs +509 -243
- package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
- package/package.json +15 -15
- package/src/lib/styles/components.css +2 -17
- package/src/lib/styles/tailwind.css +11 -8
- package/src/lib/styles/tokens.css +131 -0
- package/src/lib/styles/utilities.css +24 -0
- package/src/lib/ui/components/button/button.component.html +4 -1
- package/src/lib/ui/components/checkbox/checkbox.component.html +3 -2
- package/src/lib/ui/components/datetime-picker/datetime-picker.component.html +10 -5
- package/src/lib/ui/components/datetime-picker/datetime-picker.component.scss +3 -0
- package/src/lib/ui/components/drawer/drawer.component.html +78 -61
- package/src/lib/ui/components/drawer/drawer.component.scss +3 -0
- package/src/lib/ui/components/input/input.component.html +15 -3
- package/src/lib/ui/components/kanban-board/kanban-board.component.scss +3 -0
- package/src/lib/ui/components/loading-panel/loading-panel.component.html +53 -53
- package/src/lib/ui/components/master-detail/master-detail.component.css +11 -0
- package/src/lib/ui/components/master-detail/master-detail.component.html +70 -0
- package/src/lib/ui/components/meter/meter.component.html +8 -1
- package/src/lib/ui/components/radio-button/radio-button.component.html +33 -27
- package/src/lib/ui/components/radio-button-toggle/radio-button-toggle.component.html +8 -3
- package/src/lib/ui/components/rich-text-area/rich-text-area.component.scss +3 -0
- package/src/lib/ui/components/search-bar/search-bar.component.html +11 -10
- package/src/lib/ui/components/select/select.component.html +17 -10
- package/src/lib/ui/components/severity-badges/severity-badges.component.html +5 -4
- package/src/lib/ui/components/skeleton-list/skeleton-list.component.html +2 -2
- package/src/lib/ui/components/slider/slider.component.html +20 -15
- package/src/lib/ui/components/switch/switch.component.html +11 -6
- package/src/lib/ui/components/tab-group/tab-group.component.html +44 -27
- package/src/lib/ui/components/tab-group/tab-group.component.scss +3 -0
- package/src/lib/ui/components/tag/tag.component.html +6 -1
- package/src/lib/ui/components/toast/toast.component.html +38 -35
- package/src/lib/ui/components/tree-diagram/tree-diagram.component.html +21 -19
- package/src/lib/ui/dialogs/confirmation/confirmation.component.html +1 -1
- package/tailwind.css +11 -8
- package/tokens.css +131 -0
- package/{index.d.ts → types/foxeltech-angular-ui.d.ts} +123 -34
- package/utilities.css +24 -0
- package/tailwind-config.d.ts +0 -4
- package/tailwind.config.js +0 -113
|
@@ -1,57 +1,57 @@
|
|
|
1
1
|
<!-- Full-screen glass overlay -->
|
|
2
|
-
|
|
3
|
-
*ngIf="show"
|
|
4
|
-
class="fixed inset-0 z-50 flex items-center justify-center"
|
|
5
|
-
aria-hidden="false"
|
|
6
|
-
role="alert"
|
|
7
|
-
aria-busy="true"
|
|
8
|
-
>
|
|
9
|
-
<div class="absolute inset-0 bg-bg-primary/30 backdrop-blur-xl" aria-hidden="true"></div>
|
|
10
|
-
|
|
2
|
+
@if (show) {
|
|
11
3
|
<div
|
|
12
|
-
class="
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
4
|
+
class="fixed inset-0 z-50 flex items-center justify-center"
|
|
5
|
+
aria-hidden="false"
|
|
6
|
+
role="alert"
|
|
7
|
+
aria-busy="true"
|
|
8
|
+
>
|
|
9
|
+
<div class="absolute inset-0 bg-bg-primary/30 backdrop-blur-xl" aria-hidden="true"></div>
|
|
10
|
+
<div
|
|
11
|
+
class="relative z-10 flex flex-col items-center gap-4 p-6 rounded-2xl shadow-xl bg-bg-primary/70 border border-border-strong backdrop-bg-primary/70"
|
|
12
|
+
style="min-width: 220px; max-width: 90%"
|
|
13
|
+
>
|
|
14
|
+
<svg fill="url(#spinner-gradient)" width="50" height="50" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
15
|
+
<defs>
|
|
16
|
+
<linearGradient id="spinner-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
17
|
+
<stop offset="0%" style="stop-color: rgb(var(--gradient-primary-start)); stop-opacity: 1" />
|
|
18
|
+
<stop offset="100%" style="stop-color: rgb(var(--gradient-primary-end)); stop-opacity: 1" />
|
|
19
|
+
</linearGradient>
|
|
20
|
+
</defs>
|
|
21
|
+
<circle cx="4" cy="12" r="0">
|
|
22
|
+
<animate begin="0;spinner_z0Or.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
|
|
23
|
+
<animate begin="spinner_OLMs.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
|
|
24
|
+
<animate begin="spinner_UHR2.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
|
|
25
|
+
<animate id="spinner_lo66" begin="spinner_Aguh.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
|
|
26
|
+
<animate id="spinner_z0Or" begin="spinner_lo66.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
|
|
27
|
+
</circle>
|
|
28
|
+
<circle cx="4" cy="12" r="3">
|
|
29
|
+
<animate begin="0;spinner_z0Or.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
|
|
30
|
+
<animate begin="spinner_OLMs.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
|
|
31
|
+
<animate id="spinner_JsnR" begin="spinner_UHR2.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
|
|
32
|
+
<animate id="spinner_Aguh" begin="spinner_JsnR.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
|
|
33
|
+
<animate begin="spinner_Aguh.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
|
|
34
|
+
</circle>
|
|
35
|
+
<circle cx="12" cy="12" r="3">
|
|
36
|
+
<animate begin="0;spinner_z0Or.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
|
|
37
|
+
<animate id="spinner_hSjk" begin="spinner_OLMs.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
|
|
38
|
+
<animate id="spinner_UHR2" begin="spinner_hSjk.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
|
|
39
|
+
<animate begin="spinner_UHR2.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
|
|
40
|
+
<animate begin="spinner_Aguh.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
|
|
41
|
+
</circle>
|
|
42
|
+
<circle cx="20" cy="12" r="3">
|
|
43
|
+
<animate id="spinner_4v5M" begin="0;spinner_z0Or.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
|
|
44
|
+
<animate id="spinner_OLMs" begin="spinner_4v5M.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
|
|
45
|
+
<animate begin="spinner_OLMs.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
|
|
46
|
+
<animate begin="spinner_UHR2.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
|
|
47
|
+
<animate begin="spinner_Aguh.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
|
|
48
|
+
</circle>
|
|
49
|
+
</svg>
|
|
50
|
+
@if (message) {
|
|
51
|
+
<div class="text-sm text-text-primary text-center">
|
|
52
|
+
{{ message }}
|
|
53
|
+
</div>
|
|
54
|
+
}
|
|
55
55
|
</div>
|
|
56
56
|
</div>
|
|
57
|
-
|
|
57
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<div class="flex flex-col lg:flex-row items-start gap-xl" [style.--fx-md-list-width]="listWidth">
|
|
2
|
+
<!-- Master: list pane -->
|
|
3
|
+
<div class="fx-md-list card-common">
|
|
4
|
+
<ng-content select="[mdHeader]"></ng-content>
|
|
5
|
+
<ng-content select="[mdFilters]"></ng-content>
|
|
6
|
+
<div
|
|
7
|
+
class="flex flex-col gap-normal overflow-auto pr-xs"
|
|
8
|
+
[style.maxHeight]="maxListHeight"
|
|
9
|
+
role="listbox"
|
|
10
|
+
[attr.aria-label]="listLabel || null"
|
|
11
|
+
>
|
|
12
|
+
@if (table.loading) {
|
|
13
|
+
<fx-ui-skeleton-list [rows]="table.pageSize"></fx-ui-skeleton-list>
|
|
14
|
+
} @else {
|
|
15
|
+
@for (row of table.dataSource.data; track table.idOf(row); let i = $index) {
|
|
16
|
+
<button
|
|
17
|
+
type="button"
|
|
18
|
+
role="option"
|
|
19
|
+
[id]="optionId(i)"
|
|
20
|
+
[attr.aria-selected]="isSelected(row)"
|
|
21
|
+
[tabindex]="isSelected(row) ? 0 : -1"
|
|
22
|
+
(click)="table.selectItem(row)"
|
|
23
|
+
(keydown)="onKeydown($event, i)"
|
|
24
|
+
class="text-left rounded-lg border p-semi transition-colors"
|
|
25
|
+
[ngClass]="
|
|
26
|
+
isSelected(row)
|
|
27
|
+
? 'border-gradient-primary-start bg-gradient-primary-start/5'
|
|
28
|
+
: 'border-border-default hover:border-border-interactive bg-bg-primary'
|
|
29
|
+
"
|
|
30
|
+
>
|
|
31
|
+
<ng-container
|
|
32
|
+
[ngTemplateOutlet]="rowTemplate"
|
|
33
|
+
[ngTemplateOutletContext]="{ $implicit: row, selected: isSelected(row) }"
|
|
34
|
+
></ng-container>
|
|
35
|
+
</button>
|
|
36
|
+
}
|
|
37
|
+
@if (!table.dataSource.data.length) {
|
|
38
|
+
<fx-ui-empty-state [message]="emptyMessage"></fx-ui-empty-state>
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
</div>
|
|
42
|
+
<!-- No (page) binding: ngAfterViewInit hands this paginator to the table,
|
|
43
|
+
whose own BaseTable subscription (running after this child's hook)
|
|
44
|
+
wires page events — a binding here would double-fetch every change. -->
|
|
45
|
+
<mat-paginator
|
|
46
|
+
class="mt-normal"
|
|
47
|
+
[length]="table.total"
|
|
48
|
+
[pageSize]="table.pageSize"
|
|
49
|
+
[pageSizeOptions]="pageSizeOptions"
|
|
50
|
+
></mat-paginator>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<!-- Detail pane -->
|
|
54
|
+
<div class="w-full lg:flex-1 min-w-0">
|
|
55
|
+
@if (table.selected) {
|
|
56
|
+
@if (table.detailBusy) {
|
|
57
|
+
<fx-ui-skeleton-detail [cards]="skeletonCards"></fx-ui-skeleton-detail>
|
|
58
|
+
} @else {
|
|
59
|
+
<ng-container
|
|
60
|
+
[ngTemplateOutlet]="detailTemplate"
|
|
61
|
+
[ngTemplateOutletContext]="{ $implicit: table.detail, selected: table.selected }"
|
|
62
|
+
></ng-container>
|
|
63
|
+
}
|
|
64
|
+
} @else {
|
|
65
|
+
<div class="card-common flex items-center justify-center h-[300px]">
|
|
66
|
+
<fx-ui-empty-state [message]="emptyMessage"></fx-ui-empty-state>
|
|
67
|
+
</div>
|
|
68
|
+
}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
@@ -9,7 +9,14 @@
|
|
|
9
9
|
}
|
|
10
10
|
</div>
|
|
11
11
|
}
|
|
12
|
-
<div
|
|
12
|
+
<div
|
|
13
|
+
class="h-2 rounded-full bg-bg-hover overflow-hidden"
|
|
14
|
+
role="progressbar"
|
|
15
|
+
[attr.aria-valuemin]="0"
|
|
16
|
+
[attr.aria-valuemax]="max"
|
|
17
|
+
[attr.aria-valuenow]="value"
|
|
18
|
+
[attr.aria-label]="label || null"
|
|
19
|
+
>
|
|
13
20
|
<div class="h-full rounded-full" [ngClass]="fillClass" [style.width.%]="percent"></div>
|
|
14
21
|
</div>
|
|
15
22
|
</div>
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
<div
|
|
1
|
+
<div
|
|
2
|
+
class="w-full rounded-lg overflow-hidden"
|
|
3
|
+
[class]="class"
|
|
4
|
+
role="radiogroup"
|
|
5
|
+
[attr.aria-labelledby]="label ? groupId + '-label' : null"
|
|
6
|
+
>
|
|
7
|
+
<!-- A radiogroup has no single control a <label> could point at — name it via aria-labelledby. -->
|
|
2
8
|
@if (label) {
|
|
3
|
-
<
|
|
9
|
+
<span class="txt-field-label block" [id]="groupId + '-label'">
|
|
4
10
|
{{ label }}
|
|
5
11
|
@if (required) {
|
|
6
|
-
<span class="txt-required">*</span>
|
|
12
|
+
<span class="txt-required" aria-hidden="true">*</span>
|
|
7
13
|
}
|
|
8
14
|
@if (tooltip) {
|
|
9
15
|
<fx-ui-hero-icon
|
|
@@ -15,36 +21,36 @@
|
|
|
15
21
|
matTooltipClass="text-text-primary text-xs font-semibold"
|
|
16
22
|
></fx-ui-hero-icon>
|
|
17
23
|
}
|
|
18
|
-
</
|
|
24
|
+
</span>
|
|
19
25
|
}
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
@for (opt of options; track opt) {
|
|
28
|
+
<button
|
|
29
|
+
type="button"
|
|
30
|
+
role="radio"
|
|
31
|
+
[attr.aria-checked]="value === opt.value"
|
|
32
|
+
(click)="onSelectValue(opt.value)"
|
|
33
|
+
[disabled]="disabled"
|
|
34
|
+
[class.text-gray-900]="value !== opt.value"
|
|
35
|
+
class="flex-1 px-normal w-full flex items-center gap-normal h-10 rounded-md hover:bg-bg-hover transition-all duration-150 disabled:opacity-60 disabled:cursor-not-allowed"
|
|
36
|
+
[matTooltip]="opt.tooltip || ''"
|
|
37
|
+
matTooltipPosition="right"
|
|
38
|
+
matTooltipClass="text-text-primary text-xs font-semibold"
|
|
39
|
+
>
|
|
40
|
+
<span
|
|
35
41
|
[ngClass]="{
|
|
36
42
|
'radio-checked': value === opt.value,
|
|
37
43
|
'radio-uncheck': value !== opt.value,
|
|
38
44
|
}"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
class="radio-container"
|
|
46
|
+
>
|
|
47
|
+
@if (value === opt.value) {
|
|
48
|
+
<span class="radio-dot"></span>
|
|
49
|
+
}
|
|
50
|
+
</span>
|
|
51
|
+
<span class="txt-default mb-0">{{ opt.label }}</span>
|
|
52
|
+
</button>
|
|
53
|
+
}
|
|
48
54
|
@if (invalid) {
|
|
49
55
|
<div class="txt-invalid">
|
|
50
56
|
{{ getErrorMessage(label) }}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
<div class="flex flex-col text-left mb-4 w-full" [class]="class">
|
|
2
|
-
|
|
2
|
+
<!-- A radiogroup has no single control a <label> could point at — name it via aria-labelledby. -->
|
|
3
|
+
@if(label){<span class="txt-field-label" [id]="groupId + '-label'">
|
|
3
4
|
{{ label }}
|
|
4
|
-
@if(required) {<span class="txt-required">*</span>} </
|
|
5
|
+
@if(required) {<span class="txt-required" aria-hidden="true">*</span>} </span
|
|
5
6
|
>}
|
|
6
7
|
|
|
7
|
-
<div
|
|
8
|
+
<div
|
|
9
|
+
class="inline-flex w-full rounded-lg overflow-hidden"
|
|
10
|
+
role="radiogroup"
|
|
11
|
+
[attr.aria-labelledby]="label ? groupId + '-label' : null"
|
|
12
|
+
>
|
|
8
13
|
@for(opt of options; track opt){<button
|
|
9
14
|
type="button"
|
|
10
15
|
role="radio"
|
|
@@ -8,15 +8,16 @@
|
|
|
8
8
|
[value]="value"
|
|
9
9
|
(input)="onValueChange($any($event.target).value)"
|
|
10
10
|
(keydown.enter)="onEnterKey()"
|
|
11
|
-
|
|
11
|
+
/>
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
@if (value && !disabled) {
|
|
14
|
+
<button
|
|
15
|
+
type="button"
|
|
16
|
+
class="absolute inset-y-0 right-2 flex items-center text-text-primary hover:text-text-secondary"
|
|
17
|
+
(click)="clear()"
|
|
18
|
+
aria-label="Clear"
|
|
19
|
+
>
|
|
20
|
+
<i class="material-symbols-outlined text-lg select-none">close</i>
|
|
21
|
+
</button>
|
|
22
|
+
}
|
|
22
23
|
</div>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<div class="mb-4 text-left w-full" [class]="class">
|
|
2
2
|
@if (label) {
|
|
3
|
-
<label class="block txt-field-label">
|
|
3
|
+
<label class="block txt-field-label" [attr.for]="fieldId">
|
|
4
4
|
{{ label }}
|
|
5
5
|
@if (required) {
|
|
6
|
-
<span class="txt-required">*</span>
|
|
6
|
+
<span class="txt-required" aria-hidden="true">*</span>
|
|
7
7
|
}
|
|
8
8
|
@if (tooltip) {
|
|
9
9
|
<fx-ui-hero-icon
|
|
@@ -22,8 +22,12 @@
|
|
|
22
22
|
[attr.invalid]="invalid || null"
|
|
23
23
|
class="fx-select-field w-full shadow-sm"
|
|
24
24
|
appearance="fill"
|
|
25
|
-
|
|
25
|
+
>
|
|
26
26
|
<mat-select
|
|
27
|
+
[id]="fieldId"
|
|
28
|
+
[required]="!!required"
|
|
29
|
+
[attr.aria-invalid]="invalid || null"
|
|
30
|
+
[attr.aria-describedby]="invalid ? fieldId + '-error' : null"
|
|
27
31
|
[placeholder]="placeholder"
|
|
28
32
|
[disabled]="disabled"
|
|
29
33
|
[multiple]="multiple"
|
|
@@ -31,27 +35,30 @@
|
|
|
31
35
|
(selectionChange)="onSelectionChange($event.value)"
|
|
32
36
|
(openedChange)="onOpened($event)"
|
|
33
37
|
(blur)="onTouched()"
|
|
34
|
-
|
|
38
|
+
>
|
|
35
39
|
@if (enableSearch) {
|
|
36
40
|
<div class="px-semi w-full">
|
|
37
41
|
<input
|
|
38
42
|
type="text"
|
|
43
|
+
aria-label="Search"
|
|
39
44
|
[placeholder]="'Search'"
|
|
40
45
|
[(ngModel)]="searchTerm"
|
|
41
46
|
(ngModelChange)="onSearchChange($event)"
|
|
42
47
|
(click)="$event.stopPropagation()"
|
|
43
|
-
class="text-text-primary placeholder:text-text-placeholder w-full bg-bg-primary border-b px-semi py-normal border-border-default focus:outline-none focus:border-gradient-primary-start text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out"
|
|
44
|
-
|
|
48
|
+
class="text-text-primary placeholder:text-text-placeholder w-full bg-bg-primary border-b px-semi py-normal border-border-default focus:outline-none focus:border-gradient-primary-start focus-visible:ring-2 focus-visible:ring-primary/50 text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out"
|
|
49
|
+
/>
|
|
45
50
|
</div>
|
|
46
51
|
}
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
|
|
52
|
+
@for (option of filteredOptions; track option) {
|
|
53
|
+
<mat-option [value]="option.value">
|
|
54
|
+
<span class="txt-default">{{ option.label }}</span>
|
|
55
|
+
</mat-option>
|
|
56
|
+
}
|
|
50
57
|
</mat-select>
|
|
51
58
|
</mat-form-field>
|
|
52
59
|
|
|
53
60
|
@if (invalid) {
|
|
54
|
-
<div class="txt-invalid text-left">
|
|
61
|
+
<div class="txt-invalid text-left" role="alert" [id]="fieldId + '-error'">
|
|
55
62
|
{{ getErrorMessage(label) }}
|
|
56
63
|
</div>
|
|
57
64
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<div class="flex items-center gap-small">
|
|
2
2
|
@for (badge of badges; track badge.key) {
|
|
3
|
-
<
|
|
4
|
-
|
|
3
|
+
<button
|
|
4
|
+
type="button"
|
|
5
|
+
class="px-2 py-0.5 rounded-full txt-utility-caption font-medium cursor-pointer focus-visible:ring-2 focus-visible:ring-primary/50"
|
|
5
6
|
[ngClass]="badgeClass(badge)"
|
|
7
|
+
[attr.aria-label]="badge.key + ': ' + badge.label"
|
|
6
8
|
(click)="badgeClick.emit(badge.key)"
|
|
7
|
-
|
|
8
|
-
>
|
|
9
|
+
>{{ badge.label }}</button>
|
|
9
10
|
}
|
|
10
11
|
</div>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div class="flex flex-col gap-normal animate-pulse">
|
|
2
2
|
@for (r of range(rows); track r) {
|
|
3
|
-
<div class="rounded-
|
|
3
|
+
<div class="rounded-lg border border-border-default p-semi">
|
|
4
4
|
<div class="flex items-center gap-normal mb-small">
|
|
5
|
-
<div class="w-8 h-8 rounded-
|
|
5
|
+
<div class="w-8 h-8 rounded-lg bg-bg-hover shrink-0"></div>
|
|
6
6
|
<div class="h-4 flex-1 rounded bg-bg-hover"></div>
|
|
7
7
|
<div class="h-5 w-12 rounded-full bg-bg-hover shrink-0"></div>
|
|
8
8
|
</div>
|
|
@@ -3,34 +3,38 @@
|
|
|
3
3
|
[class.cursor-not-allowed]="disabled"
|
|
4
4
|
[class.opacity-60]="disabled"
|
|
5
5
|
role="group"
|
|
6
|
-
>
|
|
6
|
+
>
|
|
7
|
+
<!-- Track click is a pointer shortcut; the thumb button below owns keyboard/focus. -->
|
|
8
|
+
<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->
|
|
7
9
|
<div
|
|
8
10
|
#track
|
|
9
11
|
class="relative touch-none py-4"
|
|
10
12
|
(click)="onTrackClick($event)"
|
|
11
13
|
[attr.aria-disabled]="disabled"
|
|
12
14
|
[attr.aria-orientation]="vertical ? 'vertical' : 'horizontal'"
|
|
13
|
-
|
|
15
|
+
>
|
|
14
16
|
<div
|
|
15
17
|
class="relative flex items-center"
|
|
16
18
|
[ngClass]="{ 'flex-col h-48': vertical }"
|
|
17
|
-
|
|
19
|
+
>
|
|
18
20
|
<div
|
|
19
21
|
class="relative flex-1 rounded-full bg-gray-600 h-2"
|
|
20
22
|
[ngClass]="{ 'w-1 rotate-0 h-full w-auto bg-border-gray-700': vertical }"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
>
|
|
24
|
+
@if (!vertical) {
|
|
25
|
+
<div
|
|
26
|
+
class="absolute left-0 top-0 h-full rounded-full bg-primary"
|
|
27
|
+
[style.width.%]="percent"
|
|
28
|
+
></div>
|
|
29
|
+
}
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
@if (vertical) {
|
|
32
|
+
<div
|
|
33
|
+
class="absolute bottom-0 left-0 w-full rounded-full bg-primary"
|
|
34
|
+
[style.height.%]="percent"
|
|
35
|
+
style="transform-origin: bottom;"
|
|
36
|
+
></div>
|
|
37
|
+
}
|
|
34
38
|
|
|
35
39
|
<button
|
|
36
40
|
#thumb
|
|
@@ -38,6 +42,7 @@
|
|
|
38
42
|
class="absolute -mt-1 -ml-1.5 w-4 h-4 rounded-full shadow focus:ring-primary bg-primary border-2 border-text-primary"
|
|
39
43
|
[ngStyle]="thumbStyle()"
|
|
40
44
|
role="slider"
|
|
45
|
+
aria-label="Slider"
|
|
41
46
|
[attr.aria-valuemin]="min"
|
|
42
47
|
[attr.aria-valuemax]="max"
|
|
43
48
|
[attr.aria-valuenow]="value"
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
1
|
+
<button
|
|
2
|
+
type="button"
|
|
3
|
+
role="switch"
|
|
4
|
+
[attr.aria-checked]="!!value"
|
|
5
|
+
[attr.aria-label]="ariaLabel || null"
|
|
6
|
+
[disabled]="disabled"
|
|
7
|
+
class="relative flex items-center w-10 h-5 rounded-full transition-colors duration-300 cursor-pointer disabled:cursor-not-allowed focus-visible:ring-2 focus-visible:ring-primary/50"
|
|
3
8
|
[ngClass]="{
|
|
4
9
|
'bg-success': value && !disabled,
|
|
5
10
|
'bg-button-default': !value && !disabled,
|
|
@@ -8,14 +13,14 @@
|
|
|
8
13
|
(click)="toggle()"
|
|
9
14
|
>
|
|
10
15
|
@if(value) {
|
|
11
|
-
<i style="font-size: 20px;" class="material-symbols-outlined absolute left-0.5 top-0 text-text-inverse">check</i>
|
|
12
|
-
|
|
16
|
+
<i style="font-size: 20px;" class="material-symbols-outlined absolute left-0.5 top-0 text-text-inverse" aria-hidden="true">check</i>
|
|
17
|
+
|
|
13
18
|
}
|
|
14
19
|
@if(!value) {
|
|
15
|
-
<i style="font-size: 20px;" class="material-symbols-outlined absolute right-0.5 top-0 text-text-inverse">close</i>
|
|
20
|
+
<i style="font-size: 20px;" class="material-symbols-outlined absolute right-0.5 top-0 text-text-inverse" aria-hidden="true">close</i>
|
|
16
21
|
}
|
|
17
22
|
<div
|
|
18
23
|
class="absolute top-0.5 left-0.5 w-4 h-4 bg-text-inverse rounded-full shadow-md transform transition-transform duration-300"
|
|
19
24
|
[ngClass]="{ 'translate-x-5': value }"
|
|
20
25
|
></div>
|
|
21
|
-
</
|
|
26
|
+
</button>
|