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.
Files changed (122) hide show
  1. package/FIGMA_COMPONENT_AUDIT.md +154 -0
  2. package/README.md +72 -0
  3. package/ng-package.json +7 -0
  4. package/package.json +29 -0
  5. package/src/lib/components/accordion/accordion.component.html +34 -0
  6. package/src/lib/components/accordion/accordion.component.ts +38 -0
  7. package/src/lib/components/alert/alert.component.html +52 -0
  8. package/src/lib/components/alert/alert.component.ts +25 -0
  9. package/src/lib/components/alert-dialog/alert-dialog.component.html +41 -0
  10. package/src/lib/components/alert-dialog/alert-dialog.component.ts +45 -0
  11. package/src/lib/components/aspect-ratio/aspect-ratio.component.html +11 -0
  12. package/src/lib/components/aspect-ratio/aspect-ratio.component.ts +18 -0
  13. package/src/lib/components/avatar/avatar.component.html +21 -0
  14. package/src/lib/components/avatar/avatar.component.ts +32 -0
  15. package/src/lib/components/badge/badge.component.html +28 -0
  16. package/src/lib/components/badge/badge.component.ts +23 -0
  17. package/src/lib/components/breadcrumb/breadcrumb.component.html +39 -0
  18. package/src/lib/components/breadcrumb/breadcrumb.component.ts +26 -0
  19. package/src/lib/components/button/button.component.html +15 -0
  20. package/src/lib/components/button/button.component.ts +84 -0
  21. package/src/lib/components/button-group/button-group.component.html +39 -0
  22. package/src/lib/components/button-group/button-group.component.ts +15 -0
  23. package/src/lib/components/calendar/calendar.component.html +73 -0
  24. package/src/lib/components/calendar/calendar.component.ts +78 -0
  25. package/src/lib/components/card/card.component.html +77 -0
  26. package/src/lib/components/card/card.component.ts +39 -0
  27. package/src/lib/components/carousel/carousel.component.html +86 -0
  28. package/src/lib/components/carousel/carousel.component.ts +100 -0
  29. package/src/lib/components/chart/chart.component.html +143 -0
  30. package/src/lib/components/chart/chart.component.ts +147 -0
  31. package/src/lib/components/checkbox/checkbox.component.html +38 -0
  32. package/src/lib/components/checkbox/checkbox.component.ts +32 -0
  33. package/src/lib/components/collapsible/collapsible.component.html +26 -0
  34. package/src/lib/components/collapsible/collapsible.component.ts +29 -0
  35. package/src/lib/components/combobox/combobox.component.html +42 -0
  36. package/src/lib/components/combobox/combobox.component.ts +32 -0
  37. package/src/lib/components/command/command.component.html +55 -0
  38. package/src/lib/components/command/command.component.ts +67 -0
  39. package/src/lib/components/context-menu/context-menu.component.html +47 -0
  40. package/src/lib/components/context-menu/context-menu.component.ts +67 -0
  41. package/src/lib/components/data-table/data-table.component.html +63 -0
  42. package/src/lib/components/data-table/data-table.component.ts +78 -0
  43. package/src/lib/components/date-picker/date-picker.component.html +38 -0
  44. package/src/lib/components/date-picker/date-picker.component.ts +34 -0
  45. package/src/lib/components/dialog/dialog.component.html +78 -0
  46. package/src/lib/components/dialog/dialog.component.ts +55 -0
  47. package/src/lib/components/drawer/drawer.component.html +56 -0
  48. package/src/lib/components/drawer/drawer.component.ts +43 -0
  49. package/src/lib/components/dropdown-menu/dropdown-menu.component.html +56 -0
  50. package/src/lib/components/dropdown-menu/dropdown-menu.component.ts +126 -0
  51. package/src/lib/components/empty/empty.component.html +29 -0
  52. package/src/lib/components/empty/empty.component.ts +35 -0
  53. package/src/lib/components/field/field.component.html +22 -0
  54. package/src/lib/components/field/field.component.ts +28 -0
  55. package/src/lib/components/hover-card/hover-card.component.html +24 -0
  56. package/src/lib/components/hover-card/hover-card.component.ts +36 -0
  57. package/src/lib/components/icon/icon.component.html +286 -0
  58. package/src/lib/components/icon/icon.component.ts +133 -0
  59. package/src/lib/components/input/input.component.html +22 -0
  60. package/src/lib/components/input/input.component.ts +33 -0
  61. package/src/lib/components/input-group/input-group.component.html +31 -0
  62. package/src/lib/components/input-group/input-group.component.ts +26 -0
  63. package/src/lib/components/input-otp/input-otp.component.html +25 -0
  64. package/src/lib/components/input-otp/input-otp.component.ts +146 -0
  65. package/src/lib/components/input-password/input-password.component.html +64 -0
  66. package/src/lib/components/input-password/input-password.component.ts +46 -0
  67. package/src/lib/components/item/item.component.html +10 -0
  68. package/src/lib/components/item/item.component.ts +12 -0
  69. package/src/lib/components/kbd/kbd.component.html +3 -0
  70. package/src/lib/components/kbd/kbd.component.ts +10 -0
  71. package/src/lib/components/label/label.component.html +7 -0
  72. package/src/lib/components/label/label.component.ts +12 -0
  73. package/src/lib/components/menubar/menubar.component.html +16 -0
  74. package/src/lib/components/menubar/menubar.component.ts +29 -0
  75. package/src/lib/components/native-select/native-select.component.html +17 -0
  76. package/src/lib/components/native-select/native-select.component.ts +28 -0
  77. package/src/lib/components/navigation-menu/navigation-menu.component.html +15 -0
  78. package/src/lib/components/navigation-menu/navigation-menu.component.ts +17 -0
  79. package/src/lib/components/pagination/pagination.component.html +30 -0
  80. package/src/lib/components/pagination/pagination.component.ts +37 -0
  81. package/src/lib/components/popover/popover.component.html +6 -0
  82. package/src/lib/components/popover/popover.component.ts +40 -0
  83. package/src/lib/components/progress/progress.component.html +9 -0
  84. package/src/lib/components/progress/progress.component.ts +20 -0
  85. package/src/lib/components/radio-group/radio-group.component.html +25 -0
  86. package/src/lib/components/radio-group/radio-group.component.ts +30 -0
  87. package/src/lib/components/scroll-area/scroll-area.component.html +5 -0
  88. package/src/lib/components/scroll-area/scroll-area.component.ts +11 -0
  89. package/src/lib/components/select/select.component.html +14 -0
  90. package/src/lib/components/select/select.component.ts +27 -0
  91. package/src/lib/components/separator/separator.component.html +5 -0
  92. package/src/lib/components/separator/separator.component.ts +16 -0
  93. package/src/lib/components/sheet/sheet.component.html +10 -0
  94. package/src/lib/components/sheet/sheet.component.ts +28 -0
  95. package/src/lib/components/sidebar/sidebar.component.html +3 -0
  96. package/src/lib/components/sidebar/sidebar.component.ts +11 -0
  97. package/src/lib/components/skeleton/skeleton.component.html +1 -0
  98. package/src/lib/components/skeleton/skeleton.component.ts +10 -0
  99. package/src/lib/components/slider/slider.component.html +15 -0
  100. package/src/lib/components/slider/slider.component.ts +31 -0
  101. package/src/lib/components/sonner/sonner.component.html +10 -0
  102. package/src/lib/components/sonner/sonner.component.ts +25 -0
  103. package/src/lib/components/spinner/spinner.component.html +6 -0
  104. package/src/lib/components/spinner/spinner.component.ts +11 -0
  105. package/src/lib/components/switch/switch.component.html +14 -0
  106. package/src/lib/components/switch/switch.component.ts +20 -0
  107. package/src/lib/components/table/table.component.html +5 -0
  108. package/src/lib/components/table/table.component.ts +10 -0
  109. package/src/lib/components/tabs/tabs.component.html +21 -0
  110. package/src/lib/components/tabs/tabs.component.ts +26 -0
  111. package/src/lib/components/textarea/textarea.component.html +21 -0
  112. package/src/lib/components/textarea/textarea.component.ts +28 -0
  113. package/src/lib/components/toggle/toggle.component.html +16 -0
  114. package/src/lib/components/toggle/toggle.component.ts +29 -0
  115. package/src/lib/components/toggle-group/toggle-group.component.html +17 -0
  116. package/src/lib/components/toggle-group/toggle-group.component.ts +26 -0
  117. package/src/lib/components/tooltip/tooltip.component.html +6 -0
  118. package/src/lib/components/tooltip/tooltip.component.ts +20 -0
  119. package/src/lib/pdm-ui-kit.module.ts +126 -0
  120. package/src/public-api.ts +58 -0
  121. package/tsconfig.lib.json +17 -0
  122. package/tsconfig.lib.prod.json +9 -0
@@ -0,0 +1,56 @@
1
+ <div class="relative inline-block" [ngClass]="className">
2
+ <button
3
+ type="button"
4
+ class="inline-flex h-9 items-center justify-center rounded-[10px] border border-[#e5e5e5] bg-white px-4 text-[14px] font-medium text-[#0a0a0a] shadow-[0_1px_2px_rgba(0,0,0,0.1)]"
5
+ [attr.aria-expanded]="open"
6
+ (click)="toggle()"
7
+ >
8
+ {{ triggerText }}
9
+ </button>
10
+
11
+ <div
12
+ *ngIf="open"
13
+ [ngClass]="[
14
+ 'absolute left-0 top-full z-50 mt-2 min-w-[224px] overflow-hidden rounded-[8px] border border-[#e5e5e5] bg-white p-1 text-[#0a0a0a] shadow-[0_2px_4px_-2px_rgba(0,0,0,0.1),0_4px_6px_-1px_rgba(0,0,0,0.1)]',
15
+ panelClassName
16
+ ]"
17
+ >
18
+ <ng-container *ngFor="let item of resolvedItems">
19
+ <div *ngIf="item.type === 'separator'" class="my-[5px] h-px bg-[#f1f5f9]"></div>
20
+
21
+ <div
22
+ *ngIf="item.type === 'label'"
23
+ [ngClass]="['px-2 py-1.5 text-[14px] font-semibold leading-5 text-[#0a0a0a]', item.inset ? 'pl-8' : '']"
24
+ >
25
+ {{ item.label }}
26
+ </div>
27
+
28
+ <button
29
+ *ngIf="!item.type || item.type === 'item'"
30
+ type="button"
31
+ [disabled]="item.disabled"
32
+ [ngClass]="[
33
+ 'relative flex w-full items-center gap-2 rounded-[6px] px-2 py-1.5 text-left text-[14px] leading-5 text-[#0a0a0a] transition-colors hover:bg-[#f5f5f5] disabled:pointer-events-none disabled:opacity-50',
34
+ item.inset ? 'pl-8' : ''
35
+ ]"
36
+ (click)="select(item)"
37
+ >
38
+ <span class="inline-flex h-4 w-4 shrink-0 items-center justify-center">
39
+ <svg
40
+ *ngIf="item.checked"
41
+ viewBox="0 0 24 24"
42
+ class="h-3.5 w-3.5 text-[#0a0a0a]"
43
+ fill="none"
44
+ xmlns="http://www.w3.org/2000/svg"
45
+ >
46
+ <path d="M5 12.5L9.2 16.7L19 7" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"></path>
47
+ </svg>
48
+ <span *ngIf="item.radioSelected" class="h-2 w-2 rounded-full bg-[#0a0a0a]"></span>
49
+ </span>
50
+ <span class="min-w-0 flex-1 truncate">{{ item.label }}</span>
51
+ <span *ngIf="item.shortcut" class="text-[12px] leading-5 text-[#64748b]">{{ item.shortcut }}</span>
52
+ <span *ngIf="item.showChevron" class="text-[16px] leading-none text-[#475569]">›</span>
53
+ </button>
54
+ </ng-container>
55
+ </div>
56
+ </div>
@@ -0,0 +1,126 @@
1
+ import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, HostListener, Input, Output } from '@angular/core';
2
+
3
+ export type PdmDropdownMenuVariant = 'default' | 'checkboxes' | 'radio-group';
4
+
5
+ export interface PdmMenuItem {
6
+ type?: 'item' | 'label' | 'separator';
7
+ label?: string;
8
+ value?: string;
9
+ shortcut?: string;
10
+ disabled?: boolean;
11
+ inset?: boolean;
12
+ showChevron?: boolean;
13
+ checked?: boolean;
14
+ radioSelected?: boolean;
15
+ }
16
+
17
+ @Component({
18
+ selector: 'pdm-dropdown-menu',
19
+ templateUrl: './dropdown-menu.component.html',
20
+ changeDetection: ChangeDetectionStrategy.OnPush
21
+ })
22
+ export class PdmDropdownMenuComponent {
23
+ @Input() triggerText = 'Open';
24
+ @Input() variant: PdmDropdownMenuVariant = 'default';
25
+ @Input() items: PdmMenuItem[] = [];
26
+ @Input() closeOnSelect = true;
27
+ @Input() panelClassName = '';
28
+ @Input() className = '';
29
+
30
+ @Output() itemSelect = new EventEmitter<string>();
31
+ @Output() itemsChange = new EventEmitter<PdmMenuItem[]>();
32
+
33
+ open = false;
34
+
35
+ constructor(private readonly elementRef: ElementRef<HTMLElement>) {}
36
+
37
+ toggle(): void {
38
+ this.open = !this.open;
39
+ }
40
+
41
+ get resolvedItems(): PdmMenuItem[] {
42
+ if (this.items.length) {
43
+ return this.items;
44
+ }
45
+
46
+ if (this.variant === 'checkboxes') {
47
+ return [
48
+ { type: 'label', label: 'Appearance' },
49
+ { type: 'separator' },
50
+ { type: 'item', label: 'Status Bar', value: 'status-bar', checked: true },
51
+ { type: 'item', label: 'Activity Bar', value: 'activity-bar', checked: false },
52
+ { type: 'item', label: 'Panel', value: 'panel', checked: false }
53
+ ];
54
+ }
55
+
56
+ if (this.variant === 'radio-group') {
57
+ return [
58
+ { type: 'label', label: 'Panel Position' },
59
+ { type: 'separator' },
60
+ { type: 'item', label: 'Top', value: 'top', radioSelected: true },
61
+ { type: 'item', label: 'Bottom', value: 'bottom', radioSelected: false },
62
+ { type: 'item', label: 'Right', value: 'right', radioSelected: false }
63
+ ];
64
+ }
65
+
66
+ return [
67
+ { type: 'label', label: 'My Account', inset: true },
68
+ { type: 'separator' },
69
+ { type: 'item', label: 'Profile', value: 'profile', shortcut: '⇧⌘P' },
70
+ { type: 'item', label: 'Billing', value: 'billing', shortcut: '⌘B' },
71
+ { type: 'item', label: 'Settings', value: 'settings', shortcut: '⌘S' },
72
+ { type: 'item', label: 'Keyboard shortcuts', value: 'shortcuts', shortcut: '⌘K' },
73
+ { type: 'separator' },
74
+ { type: 'item', label: 'Team', value: 'team' },
75
+ { type: 'item', label: 'Invite users', value: 'invite', showChevron: true },
76
+ { type: 'item', label: 'New Team', value: 'new-team', shortcut: '⌘+T' },
77
+ { type: 'separator' },
78
+ { type: 'item', label: 'GitHub', value: 'github' },
79
+ { type: 'item', label: 'Support', value: 'support' },
80
+ { type: 'item', label: 'API', value: 'api', disabled: true },
81
+ { type: 'separator' },
82
+ { type: 'item', label: 'Log out', value: 'logout', shortcut: '⇧⌘Q' }
83
+ ];
84
+ }
85
+
86
+ select(item: PdmMenuItem): void {
87
+ if (item.disabled || item.type === 'separator' || item.type === 'label' || !item.value) return;
88
+
89
+ if (this.variant === 'checkboxes') {
90
+ const updated = this.resolvedItems.map((entry) =>
91
+ entry.value === item.value ? { ...entry, checked: !entry.checked } : entry
92
+ );
93
+ this.itemsChange.emit(updated);
94
+ }
95
+
96
+ if (this.variant === 'radio-group') {
97
+ const updated = this.resolvedItems.map((entry) =>
98
+ entry.type === 'item'
99
+ ? { ...entry, radioSelected: entry.value === item.value }
100
+ : entry
101
+ );
102
+ this.itemsChange.emit(updated);
103
+ }
104
+
105
+ this.itemSelect.emit(item.value);
106
+
107
+ const shouldClose = this.variant === 'default' ? this.closeOnSelect : false;
108
+ if (shouldClose) {
109
+ this.open = false;
110
+ }
111
+ }
112
+
113
+ @HostListener('document:keydown.escape')
114
+ onEsc(): void {
115
+ this.open = false;
116
+ }
117
+
118
+ @HostListener('document:click', ['$event'])
119
+ onDocumentClick(event: MouseEvent): void {
120
+ if (!this.open) return;
121
+ const target = event.target as Node | null;
122
+ if (target && !this.elementRef.nativeElement.contains(target)) {
123
+ this.open = false;
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,29 @@
1
+ <section [ngClass]="['flex flex-col items-center justify-center gap-4 px-6 py-10 text-center text-[hsl(var(--foreground))]', containerClass, className]">
2
+ <div class="flex h-10 w-10 items-center justify-center rounded-[10px] bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))]">
3
+ <pdm-icon [name]="iconName" [size]="20"></pdm-icon>
4
+ </div>
5
+
6
+ <div class="space-y-2">
7
+ <h3 class="text-2xl font-semibold leading-none tracking-tight">{{ title }}</h3>
8
+ <p class="mx-auto max-w-[420px] text-sm leading-relaxed text-[hsl(var(--muted-foreground))]">
9
+ {{ description }}
10
+ </p>
11
+ </div>
12
+
13
+ <div *ngIf="primaryActionLabel || secondaryActionLabel" class="mt-1 flex flex-wrap items-center justify-center gap-3">
14
+ <pdm-button *ngIf="primaryActionLabel" variant="default" (pressed)="primaryAction.emit()">{{ primaryActionLabel }}</pdm-button>
15
+ <pdm-button *ngIf="secondaryActionLabel" variant="outline" (pressed)="secondaryAction.emit()">{{ secondaryActionLabel }}</pdm-button>
16
+ </div>
17
+
18
+ <button
19
+ *ngIf="linkLabel"
20
+ type="button"
21
+ class="mt-1 inline-flex items-center gap-2 text-[15px] font-medium text-[hsl(var(--muted-foreground))] underline-offset-4 hover:underline"
22
+ (click)="linkAction.emit()"
23
+ >
24
+ <span>{{ linkLabel }}</span>
25
+ <pdm-icon name="arrow-up-right" [size]="16"></pdm-icon>
26
+ </button>
27
+
28
+ <ng-content></ng-content>
29
+ </section>
@@ -0,0 +1,35 @@
1
+ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
2
+
3
+ export type PdmEmptyVariant = 'default' | 'outline' | 'background';
4
+
5
+ @Component({
6
+ selector: 'pdm-empty',
7
+ templateUrl: './empty.component.html',
8
+ changeDetection: ChangeDetectionStrategy.OnPush
9
+ })
10
+ export class PdmEmptyComponent {
11
+ @Input() variant: PdmEmptyVariant = 'default';
12
+ @Input() title = 'No Projects Yet';
13
+ @Input() description = "You haven't created any projects yet. Get started by creating your first project.";
14
+ @Input() iconName = 'folder';
15
+ @Input() primaryActionLabel = '';
16
+ @Input() secondaryActionLabel = '';
17
+ @Input() linkLabel = '';
18
+ @Input() className = '';
19
+
20
+ @Output() primaryAction = new EventEmitter<void>();
21
+ @Output() secondaryAction = new EventEmitter<void>();
22
+ @Output() linkAction = new EventEmitter<void>();
23
+
24
+ get containerClass(): string {
25
+ if (this.variant === 'outline') {
26
+ return 'border border-dashed border-[hsl(var(--border))] rounded-[12px]';
27
+ }
28
+
29
+ if (this.variant === 'background') {
30
+ return 'rounded-[12px] bg-[hsl(var(--muted))]';
31
+ }
32
+
33
+ return '';
34
+ }
35
+ }
@@ -0,0 +1,22 @@
1
+ <div [ngClass]="[rootClass, className]">
2
+ <div class="grid gap-2">
3
+ <label
4
+ *ngIf="label"
5
+ [attr.for]="id"
6
+ [ngClass]="['text-sm font-medium leading-5 text-[hsl(var(--foreground))]', disabled ? 'opacity-70' : '', labelClassName]"
7
+ >
8
+ {{ label }}
9
+ <span *ngIf="required" class="text-[hsl(var(--destructive))]">*</span>
10
+ </label>
11
+
12
+ <p *ngIf="description && !invalid" class="text-sm leading-5 text-[hsl(var(--muted-foreground))]">{{ description }}</p>
13
+ <p *ngIf="invalid && error" class="text-sm leading-5 text-[hsl(var(--destructive))]">{{ error }}</p>
14
+
15
+ <ng-content select="[pdmFieldDescription]"></ng-content>
16
+ </div>
17
+
18
+ <div [ngClass]="controlClassName">
19
+ <ng-content></ng-content>
20
+ <ng-content select="[pdmFieldControl]"></ng-content>
21
+ </div>
22
+ </div>
@@ -0,0 +1,28 @@
1
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2
+
3
+ export type PdmFieldOrientation = 'vertical' | 'horizontal';
4
+
5
+ @Component({
6
+ selector: 'pdm-field',
7
+ templateUrl: './field.component.html',
8
+ changeDetection: ChangeDetectionStrategy.OnPush
9
+ })
10
+ export class PdmFieldComponent {
11
+ @Input() id = '';
12
+ @Input() label = '';
13
+ @Input() description = '';
14
+ @Input() error = '';
15
+ @Input() required = false;
16
+ @Input() disabled = false;
17
+ @Input() invalid = false;
18
+ @Input() orientation: PdmFieldOrientation = 'vertical';
19
+ @Input() className = '';
20
+ @Input() labelClassName = '';
21
+ @Input() controlClassName = '';
22
+
23
+ get rootClass(): string {
24
+ return this.orientation === 'horizontal'
25
+ ? 'grid items-start gap-3 sm:grid-cols-[200px_minmax(0,1fr)] sm:gap-4'
26
+ : 'grid w-full gap-3';
27
+ }
28
+ }
@@ -0,0 +1,24 @@
1
+ <div
2
+ class="relative inline-flex"
3
+ [ngClass]="className"
4
+ (mouseenter)="open = true"
5
+ (mouseleave)="open = false"
6
+ (focusin)="open = true"
7
+ (focusout)="open = false"
8
+ >
9
+ <div>
10
+ <ng-content select="[pdmHoverTrigger]"></ng-content>
11
+ </div>
12
+
13
+ <section
14
+ *ngIf="open"
15
+ [style.width.px]="panelWidth"
16
+ [ngClass]="[
17
+ 'absolute z-30 rounded-[10px] border border-[hsl(var(--border))] bg-[hsl(var(--popover))] p-4 text-[hsl(var(--popover-foreground))] shadow-[0_10px_30px_rgba(15,23,42,0.15)]',
18
+ positionClass,
19
+ panelClassName
20
+ ]"
21
+ >
22
+ <ng-content select="[pdmHoverContent]"></ng-content>
23
+ </section>
24
+ </div>
@@ -0,0 +1,36 @@
1
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2
+
3
+ export type PdmHoverCardSide = 'top' | 'right' | 'bottom' | 'left';
4
+ export type PdmHoverCardAlign = 'start' | 'center' | 'end';
5
+
6
+ @Component({
7
+ selector: 'pdm-hover-card',
8
+ templateUrl: './hover-card.component.html',
9
+ changeDetection: ChangeDetectionStrategy.OnPush
10
+ })
11
+ export class PdmHoverCardComponent {
12
+ @Input() className = '';
13
+ @Input() panelClassName = '';
14
+ @Input() side: PdmHoverCardSide = 'bottom';
15
+ @Input() align: PdmHoverCardAlign = 'start';
16
+ @Input() panelWidth = 304;
17
+
18
+ open = false;
19
+
20
+ get positionClass(): string {
21
+ const sideClassMap: Record<PdmHoverCardSide, string> = {
22
+ top: 'bottom-full mb-2',
23
+ right: 'left-full ml-2',
24
+ bottom: 'top-full mt-2',
25
+ left: 'right-full mr-2'
26
+ };
27
+
28
+ const alignClassMap: Record<PdmHoverCardAlign, string> = {
29
+ start: this.side === 'top' || this.side === 'bottom' ? 'left-0' : 'top-0',
30
+ center: this.side === 'top' || this.side === 'bottom' ? 'left-1/2 -translate-x-1/2' : 'top-1/2 -translate-y-1/2',
31
+ end: this.side === 'top' || this.side === 'bottom' ? 'right-0' : 'bottom-0'
32
+ };
33
+
34
+ return `${sideClassMap[this.side]} ${alignClassMap[this.align]}`;
35
+ }
36
+ }