pdm-ui-kit 0.1.0
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/FIGMA_COMPONENT_AUDIT.md +154 -0
- package/README.md +72 -0
- package/ng-package.json +7 -0
- package/package.json +29 -0
- package/src/lib/components/accordion/accordion.component.html +34 -0
- package/src/lib/components/accordion/accordion.component.ts +38 -0
- package/src/lib/components/alert/alert.component.html +52 -0
- package/src/lib/components/alert/alert.component.ts +25 -0
- package/src/lib/components/alert-dialog/alert-dialog.component.html +41 -0
- package/src/lib/components/alert-dialog/alert-dialog.component.ts +45 -0
- package/src/lib/components/aspect-ratio/aspect-ratio.component.html +11 -0
- package/src/lib/components/aspect-ratio/aspect-ratio.component.ts +18 -0
- package/src/lib/components/avatar/avatar.component.html +21 -0
- package/src/lib/components/avatar/avatar.component.ts +32 -0
- package/src/lib/components/badge/badge.component.html +28 -0
- package/src/lib/components/badge/badge.component.ts +23 -0
- package/src/lib/components/breadcrumb/breadcrumb.component.html +39 -0
- package/src/lib/components/breadcrumb/breadcrumb.component.ts +26 -0
- package/src/lib/components/button/button.component.html +15 -0
- package/src/lib/components/button/button.component.ts +84 -0
- package/src/lib/components/button-group/button-group.component.html +39 -0
- package/src/lib/components/button-group/button-group.component.ts +15 -0
- package/src/lib/components/calendar/calendar.component.html +73 -0
- package/src/lib/components/calendar/calendar.component.ts +78 -0
- package/src/lib/components/card/card.component.html +77 -0
- package/src/lib/components/card/card.component.ts +39 -0
- package/src/lib/components/carousel/carousel.component.html +86 -0
- package/src/lib/components/carousel/carousel.component.ts +100 -0
- package/src/lib/components/chart/chart.component.html +143 -0
- package/src/lib/components/chart/chart.component.ts +147 -0
- package/src/lib/components/checkbox/checkbox.component.html +38 -0
- package/src/lib/components/checkbox/checkbox.component.ts +32 -0
- package/src/lib/components/collapsible/collapsible.component.html +26 -0
- package/src/lib/components/collapsible/collapsible.component.ts +29 -0
- package/src/lib/components/combobox/combobox.component.html +42 -0
- package/src/lib/components/combobox/combobox.component.ts +32 -0
- package/src/lib/components/command/command.component.html +55 -0
- package/src/lib/components/command/command.component.ts +67 -0
- package/src/lib/components/context-menu/context-menu.component.html +47 -0
- package/src/lib/components/context-menu/context-menu.component.ts +67 -0
- package/src/lib/components/data-table/data-table.component.html +63 -0
- package/src/lib/components/data-table/data-table.component.ts +78 -0
- package/src/lib/components/date-picker/date-picker.component.html +38 -0
- package/src/lib/components/date-picker/date-picker.component.ts +34 -0
- package/src/lib/components/dialog/dialog.component.html +78 -0
- package/src/lib/components/dialog/dialog.component.ts +55 -0
- package/src/lib/components/drawer/drawer.component.html +56 -0
- package/src/lib/components/drawer/drawer.component.ts +43 -0
- package/src/lib/components/dropdown-menu/dropdown-menu.component.html +56 -0
- package/src/lib/components/dropdown-menu/dropdown-menu.component.ts +126 -0
- package/src/lib/components/empty/empty.component.html +29 -0
- package/src/lib/components/empty/empty.component.ts +35 -0
- package/src/lib/components/field/field.component.html +22 -0
- package/src/lib/components/field/field.component.ts +28 -0
- package/src/lib/components/hover-card/hover-card.component.html +24 -0
- package/src/lib/components/hover-card/hover-card.component.ts +36 -0
- package/src/lib/components/icon/icon.component.html +286 -0
- package/src/lib/components/icon/icon.component.ts +133 -0
- package/src/lib/components/input/input.component.html +22 -0
- package/src/lib/components/input/input.component.ts +33 -0
- package/src/lib/components/input-group/input-group.component.html +31 -0
- package/src/lib/components/input-group/input-group.component.ts +26 -0
- package/src/lib/components/input-otp/input-otp.component.html +25 -0
- package/src/lib/components/input-otp/input-otp.component.ts +146 -0
- package/src/lib/components/input-password/input-password.component.html +64 -0
- package/src/lib/components/input-password/input-password.component.ts +46 -0
- package/src/lib/components/item/item.component.html +10 -0
- package/src/lib/components/item/item.component.ts +12 -0
- package/src/lib/components/kbd/kbd.component.html +3 -0
- package/src/lib/components/kbd/kbd.component.ts +10 -0
- package/src/lib/components/label/label.component.html +7 -0
- package/src/lib/components/label/label.component.ts +12 -0
- package/src/lib/components/menubar/menubar.component.html +16 -0
- package/src/lib/components/menubar/menubar.component.ts +29 -0
- package/src/lib/components/native-select/native-select.component.html +17 -0
- package/src/lib/components/native-select/native-select.component.ts +28 -0
- package/src/lib/components/navigation-menu/navigation-menu.component.html +15 -0
- package/src/lib/components/navigation-menu/navigation-menu.component.ts +17 -0
- package/src/lib/components/pagination/pagination.component.html +30 -0
- package/src/lib/components/pagination/pagination.component.ts +37 -0
- package/src/lib/components/popover/popover.component.html +6 -0
- package/src/lib/components/popover/popover.component.ts +40 -0
- package/src/lib/components/progress/progress.component.html +9 -0
- package/src/lib/components/progress/progress.component.ts +20 -0
- package/src/lib/components/radio-group/radio-group.component.html +25 -0
- package/src/lib/components/radio-group/radio-group.component.ts +30 -0
- package/src/lib/components/scroll-area/scroll-area.component.html +5 -0
- package/src/lib/components/scroll-area/scroll-area.component.ts +11 -0
- package/src/lib/components/select/select.component.html +14 -0
- package/src/lib/components/select/select.component.ts +27 -0
- package/src/lib/components/separator/separator.component.html +5 -0
- package/src/lib/components/separator/separator.component.ts +16 -0
- package/src/lib/components/sheet/sheet.component.html +10 -0
- package/src/lib/components/sheet/sheet.component.ts +28 -0
- package/src/lib/components/sidebar/sidebar.component.html +3 -0
- package/src/lib/components/sidebar/sidebar.component.ts +11 -0
- package/src/lib/components/skeleton/skeleton.component.html +1 -0
- package/src/lib/components/skeleton/skeleton.component.ts +10 -0
- package/src/lib/components/slider/slider.component.html +15 -0
- package/src/lib/components/slider/slider.component.ts +31 -0
- package/src/lib/components/sonner/sonner.component.html +10 -0
- package/src/lib/components/sonner/sonner.component.ts +25 -0
- package/src/lib/components/spinner/spinner.component.html +6 -0
- package/src/lib/components/spinner/spinner.component.ts +11 -0
- package/src/lib/components/switch/switch.component.html +14 -0
- package/src/lib/components/switch/switch.component.ts +20 -0
- package/src/lib/components/table/table.component.html +5 -0
- package/src/lib/components/table/table.component.ts +10 -0
- package/src/lib/components/tabs/tabs.component.html +21 -0
- package/src/lib/components/tabs/tabs.component.ts +26 -0
- package/src/lib/components/textarea/textarea.component.html +21 -0
- package/src/lib/components/textarea/textarea.component.ts +28 -0
- package/src/lib/components/toggle/toggle.component.html +16 -0
- package/src/lib/components/toggle/toggle.component.ts +29 -0
- package/src/lib/components/toggle-group/toggle-group.component.html +17 -0
- package/src/lib/components/toggle-group/toggle-group.component.ts +26 -0
- package/src/lib/components/tooltip/tooltip.component.html +6 -0
- package/src/lib/components/tooltip/tooltip.component.ts +20 -0
- package/src/lib/pdm-ui-kit.module.ts +126 -0
- package/src/public-api.ts +58 -0
- package/tsconfig.lib.json +17 -0
- package/tsconfig.lib.prod.json +9 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
export type PdmCarouselVariant = 'default' | 'sizes' | 'orientation' | 'api';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'pdm-carousel',
|
|
7
|
+
templateUrl: './carousel.component.html',
|
|
8
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
9
|
+
})
|
|
10
|
+
export class PdmCarouselComponent {
|
|
11
|
+
@Input() variant: PdmCarouselVariant = 'default';
|
|
12
|
+
@Input() className = '';
|
|
13
|
+
@Input() slides: number[] = [1, 2, 3, 4, 5];
|
|
14
|
+
@Input() startIndex = 0;
|
|
15
|
+
@Input() loop = false;
|
|
16
|
+
|
|
17
|
+
@Output() indexChange = new EventEmitter<number>();
|
|
18
|
+
|
|
19
|
+
private _index = 0;
|
|
20
|
+
|
|
21
|
+
ngOnInit(): void {
|
|
22
|
+
this._index = this.normalizeIndex(this.startIndex);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get index(): number {
|
|
26
|
+
return this._index;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get canPrev(): boolean {
|
|
30
|
+
return this.loop || this._index > 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get canNext(): boolean {
|
|
34
|
+
return this.loop || this._index < this.maxIndex;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get maxIndex(): number {
|
|
38
|
+
if (this.slides.length === 0) {
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
if (this.variant === 'sizes') {
|
|
42
|
+
return Math.max(0, this.slides.length - 3);
|
|
43
|
+
}
|
|
44
|
+
if (this.variant === 'orientation') {
|
|
45
|
+
return Math.max(0, this.slides.length - 2);
|
|
46
|
+
}
|
|
47
|
+
return Math.max(0, this.slides.length - 1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get visibleSlides(): number[] {
|
|
51
|
+
if (this.slides.length === 0) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (this.variant === 'sizes') {
|
|
56
|
+
return this.sliceWindow(3);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (this.variant === 'orientation') {
|
|
60
|
+
return this.sliceWindow(2);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return [this.slides[this._index]];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
onPrev(): void {
|
|
67
|
+
if (!this.canPrev) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const next = this._index - 1;
|
|
72
|
+
this.setIndex(this.loop && next < 0 ? this.maxIndex : next);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
onNext(): void {
|
|
76
|
+
if (!this.canNext) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const next = this._index + 1;
|
|
81
|
+
this.setIndex(this.loop && next > this.maxIndex ? 0 : next);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private setIndex(value: number): void {
|
|
85
|
+
const normalized = this.normalizeIndex(value);
|
|
86
|
+
this._index = normalized;
|
|
87
|
+
this.indexChange.emit(this._index);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private normalizeIndex(value: number): number {
|
|
91
|
+
if (this.slides.length === 0) {
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
return Math.min(this.maxIndex, Math.max(0, value));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private sliceWindow(size: number): number[] {
|
|
98
|
+
return this.slides.slice(this._index, this._index + size);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<section [ngClass]="['w-full rounded-[14px] border border-[hsl(var(--border))] bg-[hsl(var(--card))] text-[hsl(var(--card-foreground))]', className]">
|
|
2
|
+
<div class="flex w-full items-stretch border-b border-[hsl(var(--border))]">
|
|
3
|
+
<div class="flex min-w-0 flex-1 flex-col gap-1 px-6 pb-3 pt-4">
|
|
4
|
+
<h3 class="m-0 text-sm font-semibold leading-5">{{ title }}</h3>
|
|
5
|
+
<p class="m-0 text-sm leading-5 text-[hsl(var(--muted-foreground))]">{{ description }}</p>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div class="flex h-[92px]">
|
|
9
|
+
<div class="flex h-full w-[171px] flex-col justify-center border-l border-[hsl(var(--border))] bg-[hsl(var(--muted))] px-6 py-4">
|
|
10
|
+
<span class="text-xs leading-4 text-[hsl(var(--muted-foreground))]">{{ desktopLabel }}</span>
|
|
11
|
+
<span class="mt-1 text-[28px] font-semibold leading-8">{{ desktopValue }}</span>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="flex h-full w-[171px] flex-col justify-center px-6 py-4">
|
|
14
|
+
<span class="text-xs leading-4 text-[hsl(var(--muted-foreground))]">{{ mobileLabel }}</span>
|
|
15
|
+
<span class="mt-1 text-[28px] font-semibold leading-8">{{ mobileValue }}</span>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="w-full p-6">
|
|
21
|
+
<ng-container [ngSwitch]="type">
|
|
22
|
+
<div *ngSwitchCase="'bar'" class="flex h-[236px] w-full flex-col justify-end gap-2">
|
|
23
|
+
<div class="relative h-[183px] w-full overflow-hidden">
|
|
24
|
+
<div class="absolute bottom-0 left-0 right-0 border-t border-[hsl(var(--border))] opacity-50"></div>
|
|
25
|
+
<div class="absolute left-0 right-0 top-[54px] border-t border-[hsl(var(--border))] opacity-50"></div>
|
|
26
|
+
<div class="absolute left-0 right-0 top-[108px] border-t border-[hsl(var(--border))] opacity-50"></div>
|
|
27
|
+
<div class="absolute left-0 right-0 top-[162px] border-t border-[hsl(var(--border))] opacity-50"></div>
|
|
28
|
+
<div class="absolute inset-x-0 bottom-0 flex h-[183px] items-end gap-[2px] px-0.5">
|
|
29
|
+
<div *ngFor="let bar of normalizedBars" class="w-[5px] rounded-[2px] bg-[hsl(var(--primary))]" [style.height.px]="bar"></div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="flex h-4 w-full items-center justify-between gap-2">
|
|
33
|
+
<span *ngFor="let label of labels" class="truncate text-xs leading-4 text-[hsl(var(--muted-foreground))]">{{ label }}</span>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div *ngSwitchCase="'line'" class="flex h-[236px] w-full flex-col gap-2">
|
|
38
|
+
<svg viewBox="0 0 320 180" class="h-[183px] w-full">
|
|
39
|
+
<line x1="0" y1="180" x2="320" y2="180" stroke="hsl(var(--border))" stroke-width="1"></line>
|
|
40
|
+
<line x1="0" y1="135" x2="320" y2="135" stroke="hsl(var(--border))" stroke-width="1" opacity="0.5"></line>
|
|
41
|
+
<line x1="0" y1="90" x2="320" y2="90" stroke="hsl(var(--border))" stroke-width="1" opacity="0.5"></line>
|
|
42
|
+
<line x1="0" y1="45" x2="320" y2="45" stroke="hsl(var(--border))" stroke-width="1" opacity="0.5"></line>
|
|
43
|
+
<path [attr.d]="linePath" fill="none" stroke="hsl(var(--primary))" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
44
|
+
</svg>
|
|
45
|
+
<div class="flex h-4 w-full items-center justify-between gap-2">
|
|
46
|
+
<span *ngFor="let label of labels" class="truncate text-xs leading-4 text-[hsl(var(--muted-foreground))]">{{ label }}</span>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div *ngSwitchCase="'area'" class="flex h-[236px] w-full flex-col gap-2">
|
|
51
|
+
<svg viewBox="0 0 320 180" class="h-[183px] w-full">
|
|
52
|
+
<defs>
|
|
53
|
+
<linearGradient id="pdm-chart-area-gradient" x1="0" y1="0" x2="0" y2="1">
|
|
54
|
+
<stop offset="0%" stop-color="hsl(var(--primary))" stop-opacity="0.35"></stop>
|
|
55
|
+
<stop offset="100%" stop-color="hsl(var(--primary))" stop-opacity="0.02"></stop>
|
|
56
|
+
</linearGradient>
|
|
57
|
+
</defs>
|
|
58
|
+
<line x1="0" y1="180" x2="320" y2="180" stroke="hsl(var(--border))" stroke-width="1"></line>
|
|
59
|
+
<line x1="0" y1="135" x2="320" y2="135" stroke="hsl(var(--border))" stroke-width="1" opacity="0.5"></line>
|
|
60
|
+
<line x1="0" y1="90" x2="320" y2="90" stroke="hsl(var(--border))" stroke-width="1" opacity="0.5"></line>
|
|
61
|
+
<line x1="0" y1="45" x2="320" y2="45" stroke="hsl(var(--border))" stroke-width="1" opacity="0.5"></line>
|
|
62
|
+
<path [attr.d]="areaPath" fill="url(#pdm-chart-area-gradient)"></path>
|
|
63
|
+
<path [attr.d]="linePath" fill="none" stroke="hsl(var(--primary))" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
64
|
+
</svg>
|
|
65
|
+
<div class="flex h-4 w-full items-center justify-between gap-2">
|
|
66
|
+
<span *ngFor="let label of labels" class="truncate text-xs leading-4 text-[hsl(var(--muted-foreground))]">{{ label }}</span>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div *ngSwitchCase="'pie'" class="flex items-center gap-6">
|
|
71
|
+
<div class="relative h-40 w-40 rounded-full" [style.background]="pieGradient">
|
|
72
|
+
<div class="absolute inset-7 rounded-full bg-[hsl(var(--card))]"></div>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="grid flex-1 gap-2">
|
|
75
|
+
<div *ngFor="let segment of pieSegments" class="flex items-center justify-between rounded-md border border-[hsl(var(--border))] px-3 py-2">
|
|
76
|
+
<div class="flex items-center gap-2">
|
|
77
|
+
<span class="size-2.5 rounded-full" [style.background]="segment.color"></span>
|
|
78
|
+
<span class="text-sm leading-5">{{ segment.label }}</span>
|
|
79
|
+
</div>
|
|
80
|
+
<span class="text-sm font-medium leading-5">{{ segment.size | number: '1.0-0' }}%</span>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div *ngSwitchCase="'radar'" class="flex items-center gap-6">
|
|
86
|
+
<svg viewBox="0 0 160 160" class="h-44 w-44">
|
|
87
|
+
<polygon points="80,8 144,44 144,116 80,152 16,116 16,44" fill="none" stroke="hsl(var(--border))"></polygon>
|
|
88
|
+
<polygon points="80,28 126,54 126,106 80,132 34,106 34,54" fill="none" stroke="hsl(var(--border))" opacity="0.75"></polygon>
|
|
89
|
+
<polygon points="80,48 109,64 109,96 80,112 51,96 51,64" fill="none" stroke="hsl(var(--border))" opacity="0.55"></polygon>
|
|
90
|
+
<polygon [attr.points]="radarPoints" fill="hsl(var(--primary))" fill-opacity="0.22" stroke="hsl(var(--primary))" stroke-width="2"></polygon>
|
|
91
|
+
</svg>
|
|
92
|
+
<div class="grid flex-1 gap-2">
|
|
93
|
+
<div *ngFor="let item of radar; index as i" class="flex items-center justify-between rounded-md border border-[hsl(var(--border))] px-3 py-2 text-sm">
|
|
94
|
+
<span class="text-[hsl(var(--muted-foreground))]">Metric {{ i + 1 }}</span>
|
|
95
|
+
<span class="font-medium">{{ item }}</span>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div *ngSwitchCase="'radial'" class="flex items-center justify-between gap-6">
|
|
101
|
+
<div class="relative flex size-44 items-center justify-center">
|
|
102
|
+
<svg viewBox="0 0 96 96" class="size-40 -rotate-90">
|
|
103
|
+
<circle cx="48" cy="48" r="42" fill="none" stroke="hsl(var(--muted))" stroke-width="10"></circle>
|
|
104
|
+
<circle
|
|
105
|
+
cx="48"
|
|
106
|
+
cy="48"
|
|
107
|
+
r="42"
|
|
108
|
+
fill="none"
|
|
109
|
+
stroke="hsl(var(--primary))"
|
|
110
|
+
stroke-width="10"
|
|
111
|
+
stroke-linecap="round"
|
|
112
|
+
stroke-dasharray="263.89"
|
|
113
|
+
[attr.stroke-dashoffset]="radialStrokeOffset"
|
|
114
|
+
></circle>
|
|
115
|
+
</svg>
|
|
116
|
+
<div class="absolute text-center">
|
|
117
|
+
<div class="text-[30px] font-semibold leading-9">{{ radialValue }}%</div>
|
|
118
|
+
<div class="text-xs text-[hsl(var(--muted-foreground))]">{{ radialLabel }}</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="flex-1">
|
|
122
|
+
<p class="m-0 text-sm font-medium leading-5">{{ radialLabel }}</p>
|
|
123
|
+
<p class="m-0 mt-1 text-sm leading-5 text-[hsl(var(--muted-foreground))]">{{ radialDescription }}</p>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<div *ngSwitchCase="'tooltips'" class="grid gap-3">
|
|
128
|
+
<div class="inline-flex w-fit min-w-[220px] flex-col gap-2 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-3 shadow-sm">
|
|
129
|
+
<p class="m-0 text-xs font-medium leading-4 text-[hsl(var(--muted-foreground))]">{{ tooltipTitle }}</p>
|
|
130
|
+
<div class="flex items-center justify-between text-sm">
|
|
131
|
+
<span>{{ tooltipPrimaryLabel }}</span>
|
|
132
|
+
<span class="font-semibold">{{ tooltipPrimaryValue }}</span>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="flex items-center justify-between text-sm">
|
|
135
|
+
<span>{{ tooltipSecondaryLabel }}</span>
|
|
136
|
+
<span class="font-semibold">{{ tooltipSecondaryValue }}</span>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
<p class="m-0 text-xs text-[hsl(var(--muted-foreground))]">Tooltip style preparado para charts de línea, área y barras.</p>
|
|
140
|
+
</div>
|
|
141
|
+
</ng-container>
|
|
142
|
+
</div>
|
|
143
|
+
</section>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
export type PdmChartType = 'area' | 'bar' | 'line' | 'pie' | 'radar' | 'radial' | 'tooltips';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'pdm-chart',
|
|
7
|
+
templateUrl: './chart.component.html',
|
|
8
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
9
|
+
})
|
|
10
|
+
export class PdmChartComponent {
|
|
11
|
+
@Input() type: PdmChartType = 'bar';
|
|
12
|
+
@Input() className = '';
|
|
13
|
+
@Input() title = 'Bar Chart - Interactive';
|
|
14
|
+
@Input() description = 'Showing total visitors for the last 3 months';
|
|
15
|
+
|
|
16
|
+
@Input() desktopLabel = 'Desktop';
|
|
17
|
+
@Input() desktopValue = '24,828';
|
|
18
|
+
@Input() mobileLabel = 'Mobile';
|
|
19
|
+
@Input() mobileValue = '25,010';
|
|
20
|
+
|
|
21
|
+
@Input() labels: string[] = ['Apr 9', 'Apr 19', 'Apr 29', 'May 9', 'May 19', 'May 29', 'Jun 9', 'Jun 19', 'Jun 30'];
|
|
22
|
+
|
|
23
|
+
@Input() bars: number[] = [
|
|
24
|
+
48, 21, 53, 69, 56, 77, 12, 54, 61, 59, 66, 27, 24, 27, 84, 62, 44, 18, 45, 57,
|
|
25
|
+
45, 72, 56, 15, 73, 25, 59, 86, 49, 59, 69, 93, 96, 72, 30, 58, 75, 67, 43, 43,
|
|
26
|
+
84, 89, 62, 96, 59, 72, 82, 34, 34, 70, 62, 42, 40, 80, 60, 33, 67, 34, 34, 88,
|
|
27
|
+
22, 84, 19, 62, 56, 43, 95, 70, 67, 89, 84, 31, 18, 92, 16, 87, 54, 44, 91, 23,
|
|
28
|
+
67, 77, 49, 59, 93, 26, 29, 81, 84, 30, 22, 84
|
|
29
|
+
];
|
|
30
|
+
@Input() line: number[] = [40, 28, 56, 49, 73, 67, 81, 58, 92];
|
|
31
|
+
@Input() pie: number[] = [35, 28, 20, 17];
|
|
32
|
+
@Input() radar: number[] = [72, 58, 88, 64, 79, 70];
|
|
33
|
+
@Input() radialValue = 76;
|
|
34
|
+
@Input() radialLabel = 'Completion';
|
|
35
|
+
@Input() radialDescription = 'Updated monthly';
|
|
36
|
+
@Input() tooltipTitle = 'June 2024';
|
|
37
|
+
@Input() tooltipPrimaryLabel = 'Desktop';
|
|
38
|
+
@Input() tooltipPrimaryValue = '12,450';
|
|
39
|
+
@Input() tooltipSecondaryLabel = 'Mobile';
|
|
40
|
+
@Input() tooltipSecondaryValue = '8,110';
|
|
41
|
+
@Input() pieLabels: string[] = ['Desktop', 'Mobile', 'Tablet', 'Other'];
|
|
42
|
+
|
|
43
|
+
get normalizedBars(): number[] {
|
|
44
|
+
if (!this.bars.length) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const max = Math.max(...this.bars);
|
|
49
|
+
if (max === 0) {
|
|
50
|
+
return this.bars.map(() => 0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return this.bars.map((value) => Math.max(8, Math.round((value / max) * 183)));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get normalizedLine(): number[] {
|
|
57
|
+
if (!this.line.length) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const max = Math.max(...this.line);
|
|
62
|
+
if (max === 0) {
|
|
63
|
+
return this.line.map(() => 170);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return this.line.map((value) => 180 - Math.round((value / max) * 150));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get linePath(): string {
|
|
70
|
+
const points = this.normalizedLine;
|
|
71
|
+
if (!points.length) {
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const step = points.length > 1 ? 320 / (points.length - 1) : 0;
|
|
76
|
+
return points
|
|
77
|
+
.map((y, index) => `${index === 0 ? 'M' : 'L'} ${Math.round(index * step)} ${y}`)
|
|
78
|
+
.join(' ');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get areaPath(): string {
|
|
82
|
+
const linePath = this.linePath;
|
|
83
|
+
if (!linePath) {
|
|
84
|
+
return '';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return `${linePath} L 320 180 L 0 180 Z`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get pieSegments(): { color: string; size: number; offset: number; label: string }[] {
|
|
91
|
+
const total = this.pie.reduce((acc, value) => acc + Math.max(value, 0), 0);
|
|
92
|
+
if (total === 0) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let offset = 0;
|
|
97
|
+
const colors = ['#0090ff', '#34d399', '#f59e0b', '#f43f5e'];
|
|
98
|
+
return this.pie.map((value, index) => {
|
|
99
|
+
const size = (Math.max(value, 0) / total) * 100;
|
|
100
|
+
const segment = {
|
|
101
|
+
color: colors[index % colors.length],
|
|
102
|
+
size,
|
|
103
|
+
offset,
|
|
104
|
+
label: this.pieLabels[index] ?? `Series ${index + 1}`
|
|
105
|
+
};
|
|
106
|
+
offset += size;
|
|
107
|
+
return segment;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
get pieGradient(): string {
|
|
112
|
+
const segments = this.pieSegments;
|
|
113
|
+
if (!segments.length) {
|
|
114
|
+
return 'conic-gradient(#e5e5e5 0 100%)';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return `conic-gradient(${segments
|
|
118
|
+
.map((segment) => `${segment.color} ${segment.offset}% ${segment.offset + segment.size}%`)
|
|
119
|
+
.join(', ')})`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get radarPoints(): string {
|
|
123
|
+
const values = this.radar.slice(0, 6);
|
|
124
|
+
if (!values.length) {
|
|
125
|
+
return '';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const max = Math.max(...values, 1);
|
|
129
|
+
const radius = 72;
|
|
130
|
+
const center = 80;
|
|
131
|
+
return values
|
|
132
|
+
.map((value, index) => {
|
|
133
|
+
const angle = ((Math.PI * 2) / values.length) * index - Math.PI / 2;
|
|
134
|
+
const valueRadius = (Math.max(value, 0) / max) * radius;
|
|
135
|
+
const x = center + Math.cos(angle) * valueRadius;
|
|
136
|
+
const y = center + Math.sin(angle) * valueRadius;
|
|
137
|
+
return `${x},${y}`;
|
|
138
|
+
})
|
|
139
|
+
.join(' ');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
get radialStrokeOffset(): number {
|
|
143
|
+
const bounded = Math.max(0, Math.min(this.radialValue, 100));
|
|
144
|
+
const circumference = 2 * Math.PI * 42;
|
|
145
|
+
return circumference - (bounded / 100) * circumference;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<button
|
|
2
|
+
type="button"
|
|
3
|
+
role="checkbox"
|
|
4
|
+
[attr.id]="id || null"
|
|
5
|
+
[attr.aria-checked]="checked"
|
|
6
|
+
[attr.aria-disabled]="disabled"
|
|
7
|
+
[disabled]="disabled"
|
|
8
|
+
[ngClass]="[
|
|
9
|
+
'w-full border-0 bg-transparent p-0 text-left outline-none focus:outline-none focus-visible:outline-none',
|
|
10
|
+
variant === 'card'
|
|
11
|
+
? checked
|
|
12
|
+
? 'flex items-start gap-3 rounded-[10px] border border-foreground bg-muted p-3'
|
|
13
|
+
: 'flex items-start gap-3 rounded-[10px] border border-border bg-background p-3'
|
|
14
|
+
: hasDescription
|
|
15
|
+
? 'flex items-start gap-3'
|
|
16
|
+
: 'flex items-center gap-3',
|
|
17
|
+
disabled ? 'opacity-50' : '',
|
|
18
|
+
className
|
|
19
|
+
]"
|
|
20
|
+
(click)="onToggle()"
|
|
21
|
+
>
|
|
22
|
+
<span
|
|
23
|
+
[ngClass]="[
|
|
24
|
+
'mt-0.5 inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-[6px] border shadow-[0_1px_2px_rgba(0,0,0,0.1)]',
|
|
25
|
+
checked ? 'border-foreground bg-foreground text-background' : 'border-input bg-background text-transparent'
|
|
26
|
+
]"
|
|
27
|
+
aria-hidden="true"
|
|
28
|
+
>
|
|
29
|
+
<svg viewBox="0 0 16 16" class="h-3 w-3" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
30
|
+
<path d="M3 8.5L6.4 12L13 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
31
|
+
</svg>
|
|
32
|
+
</span>
|
|
33
|
+
|
|
34
|
+
<span [ngClass]="['min-w-0 flex-1', hasDescription ? 'flex flex-col gap-2' : 'flex items-center']">
|
|
35
|
+
<span class="text-[14px] font-medium leading-5 text-foreground">{{ label }}</span>
|
|
36
|
+
<span *ngIf="hasDescription" class="text-[14px] leading-5 text-muted-foreground">{{ description }}</span>
|
|
37
|
+
</span>
|
|
38
|
+
</button>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
export type PdmCheckboxVariant = 'default' | 'subtext' | 'card';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'pdm-checkbox',
|
|
7
|
+
templateUrl: './checkbox.component.html',
|
|
8
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
9
|
+
})
|
|
10
|
+
export class PdmCheckboxComponent {
|
|
11
|
+
@Input() id = '';
|
|
12
|
+
@Input() checked = false;
|
|
13
|
+
@Input() disabled = false;
|
|
14
|
+
@Input() variant: PdmCheckboxVariant = 'default';
|
|
15
|
+
@Input() label = 'Accept terms and conditions';
|
|
16
|
+
@Input() description = '';
|
|
17
|
+
@Input() className = '';
|
|
18
|
+
|
|
19
|
+
@Output() checkedChange = new EventEmitter<boolean>();
|
|
20
|
+
|
|
21
|
+
get hasDescription(): boolean {
|
|
22
|
+
return this.variant === 'subtext' || this.variant === 'card' ? !!this.description : false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onToggle(): void {
|
|
26
|
+
if (this.disabled) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.checkedChange.emit(!this.checked);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<div [ngClass]="['flex w-[350px] flex-col gap-2', className]">
|
|
2
|
+
<button
|
|
3
|
+
type="button"
|
|
4
|
+
[disabled]="disabled"
|
|
5
|
+
class="flex w-full items-center justify-between px-4 text-left"
|
|
6
|
+
[attr.aria-expanded]="open"
|
|
7
|
+
(click)="toggle()"
|
|
8
|
+
>
|
|
9
|
+
<span class="text-[14px] font-semibold leading-5 text-[#0a0a0a]">{{ title }}</span>
|
|
10
|
+
<span class="inline-flex h-8 w-8 items-center justify-center text-[#0a0a0a]">
|
|
11
|
+
<svg viewBox="0 0 24 24" class="h-4 w-4" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
12
|
+
<path d="M7 15L12 20L17 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
13
|
+
<path d="M17 9L12 4L7 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
14
|
+
</svg>
|
|
15
|
+
</span>
|
|
16
|
+
</button>
|
|
17
|
+
|
|
18
|
+
<div class="flex w-full flex-col gap-2">
|
|
19
|
+
<div
|
|
20
|
+
*ngFor="let item of visibleItems"
|
|
21
|
+
class="w-full rounded-[8px] border border-[#e5e5e5] bg-white px-4 py-2"
|
|
22
|
+
>
|
|
23
|
+
<span class="font-mono text-[14px] leading-5 text-[#0a0a0a]">{{ item }}</span>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Component({
|
|
4
|
+
selector: 'pdm-collapsible',
|
|
5
|
+
templateUrl: './collapsible.component.html',
|
|
6
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
7
|
+
})
|
|
8
|
+
export class PdmCollapsibleComponent {
|
|
9
|
+
@Input() title = '@peduarte starred 3 repositories';
|
|
10
|
+
@Input() open = false;
|
|
11
|
+
@Input() disabled = false;
|
|
12
|
+
@Input() items: string[] = ['@radix-ui/primitives', '@radix-ui/colors', '@stitches/react'];
|
|
13
|
+
@Input() className = '';
|
|
14
|
+
|
|
15
|
+
@Output() openChange = new EventEmitter<boolean>();
|
|
16
|
+
|
|
17
|
+
get visibleItems(): string[] {
|
|
18
|
+
if (!this.items.length) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
return this.open ? this.items : this.items.slice(0, 1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
toggle(): void {
|
|
25
|
+
if (!this.disabled) {
|
|
26
|
+
this.openChange.emit(!this.open);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<div [ngClass]="['flex flex-col gap-1', className]" [style.width.px]="width">
|
|
2
|
+
<button
|
|
3
|
+
type="button"
|
|
4
|
+
class="flex h-9 w-full items-center gap-2 rounded-[8px] border border-[#e5e5e5] bg-white px-3 py-2 shadow-[0_1px_2px_rgba(0,0,0,0.1)]"
|
|
5
|
+
[attr.aria-expanded]="open"
|
|
6
|
+
(click)="toggle()"
|
|
7
|
+
>
|
|
8
|
+
<span class="min-w-0 flex-1 truncate text-left text-[14px] font-medium leading-5 text-[#0a0a0a]">{{ selectedLabel }}</span>
|
|
9
|
+
<svg viewBox="0 0 24 24" class="h-4 w-4 text-[#0a0a0a]" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
10
|
+
<path d="M7 15L12 20L17 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
11
|
+
<path d="M17 9L12 4L7 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
12
|
+
</svg>
|
|
13
|
+
</button>
|
|
14
|
+
|
|
15
|
+
<div
|
|
16
|
+
*ngIf="open"
|
|
17
|
+
class="w-full rounded-[8px] border border-[#e5e5e5] bg-white shadow-[0_2px_4px_-2px_rgba(0,0,0,0.1),0_4px_6px_-1px_rgba(0,0,0,0.1)]"
|
|
18
|
+
>
|
|
19
|
+
<div class="flex items-center gap-2 border-b border-[#e5e5e5] px-3">
|
|
20
|
+
<svg viewBox="0 0 24 24" class="h-4 w-4 text-[#737373]" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
21
|
+
<circle cx="11" cy="11" r="7" stroke="currentColor" stroke-width="1.5"></circle>
|
|
22
|
+
<path d="M20 20L16.6 16.6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
|
|
23
|
+
</svg>
|
|
24
|
+
<div class="h-9 flex-1 py-2 text-[14px] leading-5 text-[#737373]">{{ searchPlaceholder }}</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="p-1">
|
|
28
|
+
<button
|
|
29
|
+
*ngFor="let option of options"
|
|
30
|
+
type="button"
|
|
31
|
+
class="flex w-full items-center gap-2 rounded-[4px] px-2 py-[6px] text-left"
|
|
32
|
+
[ngClass]="option === value ? 'bg-[#f5f5f5]' : ''"
|
|
33
|
+
(click)="select(option)"
|
|
34
|
+
>
|
|
35
|
+
<span [ngClass]="['min-w-0 flex-1 truncate text-[14px] leading-5', option === value ? 'text-[#171717]' : 'text-[#0a0a0a]']">{{ option }}</span>
|
|
36
|
+
<svg *ngIf="option === value" viewBox="0 0 24 24" class="h-4 w-4 text-[#0a0a0a]" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
37
|
+
<path d="M5 12.5L9.2 16.7L19 7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
38
|
+
</svg>
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Component({
|
|
4
|
+
selector: 'pdm-combobox',
|
|
5
|
+
templateUrl: './combobox.component.html',
|
|
6
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
7
|
+
})
|
|
8
|
+
export class PdmComboboxComponent {
|
|
9
|
+
@Input() open = false;
|
|
10
|
+
@Input() placeholder = 'Select framework...';
|
|
11
|
+
@Input() searchPlaceholder = 'Search framework';
|
|
12
|
+
@Input() className = '';
|
|
13
|
+
@Input() options: string[] = ['Next.js', 'SvelteKit', 'Nuxt.js', 'Remix', 'Astro'];
|
|
14
|
+
@Input() value = '';
|
|
15
|
+
@Input() width = 200;
|
|
16
|
+
|
|
17
|
+
@Output() openChange = new EventEmitter<boolean>();
|
|
18
|
+
@Output() valueChange = new EventEmitter<string>();
|
|
19
|
+
|
|
20
|
+
get selectedLabel(): string {
|
|
21
|
+
return this.value || this.placeholder;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
toggle(): void {
|
|
25
|
+
this.openChange.emit(!this.open);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
select(option: string): void {
|
|
29
|
+
this.valueChange.emit(option);
|
|
30
|
+
this.openChange.emit(false);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<div [ngClass]="['w-full', className]">
|
|
2
|
+
<div *ngIf="!open" class="flex items-center gap-1">
|
|
3
|
+
<span class="text-[14px] font-medium leading-5 text-[#737373]">{{ hintLabel }}</span>
|
|
4
|
+
<button
|
|
5
|
+
type="button"
|
|
6
|
+
class="inline-flex h-5 items-center gap-0.5 rounded-[6px] border border-[#e5e5e5] bg-[#f5f5f5] px-1.5"
|
|
7
|
+
(click)="toggleOpen()"
|
|
8
|
+
>
|
|
9
|
+
<pdm-icon name="command" [size]="12" className="text-[#737373]" [decorative]="true"></pdm-icon>
|
|
10
|
+
<span class="text-[12px] leading-4 text-[#737373]">{{ hintKey }}</span>
|
|
11
|
+
</button>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<section
|
|
15
|
+
*ngIf="open"
|
|
16
|
+
class="flex w-full flex-col overflow-hidden rounded-[10px] border border-[#e5e5e5] bg-white shadow-[0_2px_4px_-2px_rgba(0,0,0,0.1),0_4px_6px_-1px_rgba(0,0,0,0.1)]"
|
|
17
|
+
>
|
|
18
|
+
<div class="flex items-center gap-2 border-b border-[#e5e5e5] px-3">
|
|
19
|
+
<pdm-icon name="search" [size]="16" className="text-[#737373]" [decorative]="true"></pdm-icon>
|
|
20
|
+
<input
|
|
21
|
+
type="text"
|
|
22
|
+
[placeholder]="placeholder"
|
|
23
|
+
[value]="query"
|
|
24
|
+
(input)="onQueryChange($event)"
|
|
25
|
+
class="h-10 w-full bg-transparent py-3 text-[14px] leading-5 text-[#0a0a0a] outline-none placeholder:text-[#737373]"
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div class="max-h-[300px] overflow-y-auto p-1">
|
|
30
|
+
<ng-container *ngFor="let group of groupedItems; let groupIndex = index">
|
|
31
|
+
<div *ngIf="group.name" class="px-2 py-[6px] text-[12px] leading-4 text-[#737373]">{{ group.name }}</div>
|
|
32
|
+
<button
|
|
33
|
+
*ngFor="let item of group.items"
|
|
34
|
+
type="button"
|
|
35
|
+
[disabled]="item.disabled"
|
|
36
|
+
class="flex w-full items-center gap-2 rounded-[6px] px-2 py-[6px] text-left"
|
|
37
|
+
[ngClass]="[
|
|
38
|
+
item.disabled ? 'opacity-50' : '',
|
|
39
|
+
item.label === 'Calendar' ? 'bg-[#f5f5f5]' : ''
|
|
40
|
+
]"
|
|
41
|
+
(click)="select(item.value)"
|
|
42
|
+
>
|
|
43
|
+
<span class="inline-flex h-4 w-4 items-center justify-center text-[#0a0a0a]">
|
|
44
|
+
<pdm-icon *ngIf="item.icon" [name]="item.icon" [size]="16" [decorative]="true"></pdm-icon>
|
|
45
|
+
</span>
|
|
46
|
+
<span class="min-w-0 flex-1 text-[14px] leading-5 text-[#0a0a0a]">{{ item.label }}</span>
|
|
47
|
+
<span *ngIf="item.shortcut" class="text-[12px] leading-4 text-[#737373]">{{ item.shortcut }}</span>
|
|
48
|
+
</button>
|
|
49
|
+
<div *ngIf="groupIndex === 0 && groupedItems.length > 1" class="my-1 border-t border-[#e5e5e5]"></div>
|
|
50
|
+
</ng-container>
|
|
51
|
+
|
|
52
|
+
<p *ngIf="filteredItems.length === 0" class="py-6 text-center text-[14px] text-[#737373]">{{ emptyMessage }}</p>
|
|
53
|
+
</div>
|
|
54
|
+
</section>
|
|
55
|
+
</div>
|