pdm-ui-kit 0.1.49 → 0.2.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 (52) hide show
  1. package/README.md +189 -2
  2. package/esm2020/lib/components/alert-dialog/alert-dialog.component.mjs +25 -7
  3. package/esm2020/lib/components/breadcrumb/breadcrumb.component.mjs +37 -4
  4. package/esm2020/lib/components/calendar/calendar.component.mjs +3 -3
  5. package/esm2020/lib/components/card/card.component.mjs +36 -53
  6. package/esm2020/lib/components/command/command.component.mjs +3 -3
  7. package/esm2020/lib/components/context-menu/context-menu.component.mjs +16 -8
  8. package/esm2020/lib/components/data-table/data-table.component.mjs +214 -16
  9. package/esm2020/lib/components/dialog/dialog.component.mjs +133 -17
  10. package/esm2020/lib/components/draggable-table/draggable-table.component.mjs +300 -0
  11. package/esm2020/lib/components/drawer/drawer.component.mjs +138 -10
  12. package/esm2020/lib/components/dropdown-menu/dropdown-menu.component.mjs +6 -3
  13. package/esm2020/lib/components/hover-card/hover-card.component.mjs +3 -3
  14. package/esm2020/lib/components/menubar/menubar.component.mjs +38 -7
  15. package/esm2020/lib/components/navigation-menu/navigation-menu.component.mjs +25 -3
  16. package/esm2020/lib/components/pagination/pagination.component.mjs +3 -3
  17. package/esm2020/lib/components/popover/popover.component.mjs +19 -11
  18. package/esm2020/lib/components/select/select.component.mjs +8 -4
  19. package/esm2020/lib/components/sheet/sheet.component.mjs +88 -11
  20. package/esm2020/lib/components/sidebar/sidebar.component.mjs +52 -5
  21. package/esm2020/lib/components/table/table.component.mjs +152 -188
  22. package/esm2020/lib/components/tabs/tabs.component.mjs +3 -3
  23. package/esm2020/lib/components/tooltip/tooltip.component.mjs +3 -3
  24. package/esm2020/lib/overlay/pdm-outside-click.directive.mjs +86 -0
  25. package/esm2020/lib/pdm-ui-kit.module.mjs +9 -1
  26. package/esm2020/lib/utils/responsive.mjs +143 -0
  27. package/esm2020/lib/utils/z-index.mjs +93 -0
  28. package/esm2020/public-api.mjs +5 -1
  29. package/fesm2015/pdm-ui-kit.mjs +1625 -370
  30. package/fesm2015/pdm-ui-kit.mjs.map +1 -1
  31. package/fesm2020/pdm-ui-kit.mjs +1620 -367
  32. package/fesm2020/pdm-ui-kit.mjs.map +1 -1
  33. package/lib/components/alert-dialog/alert-dialog.component.d.ts +9 -1
  34. package/lib/components/breadcrumb/breadcrumb.component.d.ts +23 -1
  35. package/lib/components/card/card.component.d.ts +32 -19
  36. package/lib/components/context-menu/context-menu.component.d.ts +6 -3
  37. package/lib/components/data-table/data-table.component.d.ts +172 -14
  38. package/lib/components/dialog/dialog.component.d.ts +35 -1
  39. package/lib/components/draggable-table/draggable-table.component.d.ts +74 -0
  40. package/lib/components/drawer/drawer.component.d.ts +67 -3
  41. package/lib/components/menubar/menubar.component.d.ts +10 -2
  42. package/lib/components/navigation-menu/navigation-menu.component.d.ts +22 -1
  43. package/lib/components/popover/popover.component.d.ts +6 -3
  44. package/lib/components/sheet/sheet.component.d.ts +34 -1
  45. package/lib/components/sidebar/sidebar.component.d.ts +39 -1
  46. package/lib/components/table/table.component.d.ts +46 -25
  47. package/lib/overlay/pdm-outside-click.directive.d.ts +40 -0
  48. package/lib/pdm-ui-kit.module.d.ts +42 -40
  49. package/lib/utils/responsive.d.ts +107 -0
  50. package/lib/utils/z-index.d.ts +73 -0
  51. package/package.json +5 -3
  52. package/public-api.d.ts +4 -0
@@ -1,7 +1,7 @@
1
1
  import * as i1 from '@angular/common';
2
- import { CommonModule } from '@angular/common';
2
+ import { isPlatformBrowser, DOCUMENT, CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { EventEmitter, Component, ChangeDetectionStrategy, Input, Output, HostListener, ViewChild, ViewChildren, Directive, ContentChildren, NgModule } from '@angular/core';
4
+ import { EventEmitter, Component, ChangeDetectionStrategy, Input, Output, HostListener, ViewChild, ViewChildren, ElementRef, PLATFORM_ID, Directive, Inject, ContentChildren, NgModule } from '@angular/core';
5
5
  import * as i1$2 from '@angular/cdk/overlay';
6
6
  import { OverlayModule } from '@angular/cdk/overlay';
7
7
  import { icons } from 'lucide';
@@ -90,35 +90,51 @@ class PdmAlertDialogComponent {
90
90
  this.confirmText = 'Continue';
91
91
  this.cancelText = 'Cancel';
92
92
  this.className = '';
93
+ /** Close when the ESC key is pressed. Default: `true`. */
94
+ this.closeOnEsc = true;
93
95
  this.openChange = new EventEmitter();
94
96
  this.confirm = new EventEmitter();
95
97
  this.cancel = new EventEmitter();
96
98
  }
99
+ /**
100
+ * Returns `true` when at least one consumer listens to `openChange`.
101
+ * - **Controlled** (has observers): parent manages `open` via two-way binding → only emit.
102
+ * - **Uncontrolled** (no observers): we own the `open` state → mutate it locally.
103
+ */
104
+ get isControlled() {
105
+ return this.openChange.observed;
106
+ }
97
107
  onTriggerClick() {
98
- this.open = true;
108
+ if (!this.isControlled) {
109
+ this.open = true;
110
+ }
99
111
  this.openChange.emit(true);
100
112
  }
101
113
  onCancel() {
102
114
  this.cancel.emit();
103
- this.open = false;
115
+ if (!this.isControlled) {
116
+ this.open = false;
117
+ }
104
118
  this.openChange.emit(false);
105
119
  }
106
120
  onConfirm() {
107
121
  this.confirm.emit();
108
- this.open = false;
122
+ if (!this.isControlled) {
123
+ this.open = false;
124
+ }
109
125
  this.openChange.emit(false);
110
126
  }
111
127
  onEsc() {
112
- if (this.open) {
128
+ if (this.open && this.closeOnEsc) {
113
129
  this.onCancel();
114
130
  }
115
131
  }
116
132
  }
117
133
  PdmAlertDialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmAlertDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
118
- PdmAlertDialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmAlertDialogComponent, selector: "pdm-alert-dialog", inputs: { open: "open", showTrigger: "showTrigger", triggerText: "triggerText", title: "title", description: "description", confirmText: "confirmText", cancelText: "cancelText", className: "className" }, outputs: { openChange: "openChange", confirm: "confirm", cancel: "cancel" }, host: { listeners: { "document:keydown.escape": "onEsc()" } }, ngImport: i0, template: "<button\n *ngIf=\"showTrigger && !open\"\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onTriggerClick()\"\n>\n {{ triggerText }}\n</button>\n\n<div *ngIf=\"open\" class=\"fixed inset-0 z-50 flex items-center justify-center p-5\">\n <div class=\"absolute inset-0 bg-foreground/30\" (click)=\"onCancel()\"></div>\n <section\n role=\"alertdialog\"\n aria-modal=\"true\"\n [ngClass]=\"[\n 'relative z-10 w-full max-w-lg rounded-lg border border-border bg-background p-6 text-foreground shadow-lg',\n className\n ]\"\n >\n <div class=\"flex flex-col gap-2\">\n <h2 class=\"m-0 text-lg font-semibold leading-none tracking-tight\">{{ title }}</h2>\n <p *ngIf=\"description\" class=\"m-0 text-sm text-muted-foreground\">{{ description }}</p>\n </div>\n <div class=\"mt-4 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onCancel()\"\n >\n {{ cancelText }}\n </button>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm\"\n (click)=\"onConfirm()\"\n >\n {{ confirmText }}\n </button>\n </div>\n </section>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
134
+ PdmAlertDialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmAlertDialogComponent, selector: "pdm-alert-dialog", inputs: { open: "open", showTrigger: "showTrigger", triggerText: "triggerText", title: "title", description: "description", confirmText: "confirmText", cancelText: "cancelText", className: "className", closeOnEsc: "closeOnEsc" }, outputs: { openChange: "openChange", confirm: "confirm", cancel: "cancel" }, host: { listeners: { "document:keydown.escape": "onEsc()" } }, ngImport: i0, template: "<button\n *ngIf=\"showTrigger && !open\"\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onTriggerClick()\"\n>\n {{ triggerText }}\n</button>\n\n<div *ngIf=\"open\" class=\"fixed inset-0 z-50 flex items-center justify-center p-5\">\n <div class=\"absolute inset-0 bg-foreground/30\" (click)=\"onCancel()\"></div>\n <section\n role=\"alertdialog\"\n aria-modal=\"true\"\n [ngClass]=\"[\n 'relative z-[60] w-full max-w-lg rounded-lg border border-border bg-background p-6 text-foreground shadow-lg',\n className\n ]\"\n >\n <div class=\"flex flex-col gap-2\">\n <h2 class=\"m-0 text-lg font-semibold leading-none tracking-tight\">{{ title }}</h2>\n <p *ngIf=\"description\" class=\"m-0 text-sm text-muted-foreground\">{{ description }}</p>\n </div>\n <div class=\"mt-4 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onCancel()\"\n >\n {{ cancelText }}\n </button>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm\"\n (click)=\"onConfirm()\"\n >\n {{ confirmText }}\n </button>\n </div>\n </section>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
119
135
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmAlertDialogComponent, decorators: [{
120
136
  type: Component,
121
- args: [{ selector: 'pdm-alert-dialog', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n *ngIf=\"showTrigger && !open\"\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onTriggerClick()\"\n>\n {{ triggerText }}\n</button>\n\n<div *ngIf=\"open\" class=\"fixed inset-0 z-50 flex items-center justify-center p-5\">\n <div class=\"absolute inset-0 bg-foreground/30\" (click)=\"onCancel()\"></div>\n <section\n role=\"alertdialog\"\n aria-modal=\"true\"\n [ngClass]=\"[\n 'relative z-10 w-full max-w-lg rounded-lg border border-border bg-background p-6 text-foreground shadow-lg',\n className\n ]\"\n >\n <div class=\"flex flex-col gap-2\">\n <h2 class=\"m-0 text-lg font-semibold leading-none tracking-tight\">{{ title }}</h2>\n <p *ngIf=\"description\" class=\"m-0 text-sm text-muted-foreground\">{{ description }}</p>\n </div>\n <div class=\"mt-4 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onCancel()\"\n >\n {{ cancelText }}\n </button>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm\"\n (click)=\"onConfirm()\"\n >\n {{ confirmText }}\n </button>\n </div>\n </section>\n</div>\n" }]
137
+ args: [{ selector: 'pdm-alert-dialog', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n *ngIf=\"showTrigger && !open\"\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onTriggerClick()\"\n>\n {{ triggerText }}\n</button>\n\n<div *ngIf=\"open\" class=\"fixed inset-0 z-50 flex items-center justify-center p-5\">\n <div class=\"absolute inset-0 bg-foreground/30\" (click)=\"onCancel()\"></div>\n <section\n role=\"alertdialog\"\n aria-modal=\"true\"\n [ngClass]=\"[\n 'relative z-[60] w-full max-w-lg rounded-lg border border-border bg-background p-6 text-foreground shadow-lg',\n className\n ]\"\n >\n <div class=\"flex flex-col gap-2\">\n <h2 class=\"m-0 text-lg font-semibold leading-none tracking-tight\">{{ title }}</h2>\n <p *ngIf=\"description\" class=\"m-0 text-sm text-muted-foreground\">{{ description }}</p>\n </div>\n <div class=\"mt-4 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onCancel()\"\n >\n {{ cancelText }}\n </button>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm\"\n (click)=\"onConfirm()\"\n >\n {{ confirmText }}\n </button>\n </div>\n </section>\n</div>\n" }]
122
138
  }], propDecorators: { open: [{
123
139
  type: Input
124
140
  }], showTrigger: [{
@@ -135,6 +151,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
135
151
  type: Input
136
152
  }], className: [{
137
153
  type: Input
154
+ }], closeOnEsc: [{
155
+ type: Input
138
156
  }], openChange: [{
139
157
  type: Output
140
158
  }], confirm: [{
@@ -237,30 +255,63 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
237
255
  type: Input
238
256
  }] } });
239
257
 
258
+ /**
259
+ * Breadcrumb component con soporte responsive
260
+ *
261
+ * MEJORADO en v0.2.0:
262
+ * - Modo responsive real con overflow-x-auto
263
+ * - Collapse inteligente en mobile
264
+ *
265
+ * @example
266
+ * <pdm-breadcrumb
267
+ * mode="responsive"
268
+ * [items]="['Home', 'Products', 'Electronics', 'Laptops']">
269
+ * </pdm-breadcrumb>
270
+ */
240
271
  class PdmBreadcrumbComponent {
241
272
  constructor() {
242
273
  this.mode = 'link-component';
243
274
  this.items = ['Home', 'Components', 'Breadcrumb'];
244
275
  this.className = '';
276
+ /**
277
+ * Cantidad mínima de items para mostrar en mobile cuando mode="responsive"
278
+ * Default: 2 (primer y último item)
279
+ */
280
+ this.minItemsMobile = 2;
245
281
  }
246
282
  get renderedItems() {
247
- if ((this.mode === 'collapsed' || this.mode === 'responsive') && this.items.length > 3) {
283
+ if (this.mode === 'collapsed' && this.items.length > 3) {
248
284
  return [this.items[0], '...', this.items[this.items.length - 2], this.items[this.items.length - 1]];
249
285
  }
286
+ // Responsive mode: no collapse en el TS, se maneja en el template con CSS
250
287
  return this.items;
251
288
  }
289
+ /**
290
+ * Determina si un item debe estar visible en mobile (modo responsive)
291
+ */
292
+ shouldShowInMobile(index) {
293
+ if (this.mode !== 'responsive')
294
+ return true;
295
+ const totalItems = this.items.length;
296
+ if (totalItems <= this.minItemsMobile)
297
+ return true;
298
+ // Siempre mostrar primero y último
299
+ return index === 0 || index === totalItems - 1;
300
+ }
252
301
  }
253
302
  PdmBreadcrumbComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
254
- PdmBreadcrumbComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmBreadcrumbComponent, selector: "pdm-breadcrumb", inputs: { mode: "mode", items: "items", className: "className" }, ngImport: i0, template: "<nav\n aria-label=\"breadcrumb\"\n [ngClass]=\"['inline-flex items-center gap-1.5 text-sm', className]\"\n>\n <ng-container *ngFor=\"let item of renderedItems; let i = index; let last = last\">\n <span [ngClass]=\"[last ? 'text-foreground' : 'text-muted-foreground']\">{{ item }}</span>\n\n <ng-container *ngIf=\"!last\">\n <span class=\"inline-flex h-6 w-6 items-center justify-center text-muted-foreground\" aria-hidden=\"true\">\n <svg\n *ngIf=\"mode === 'custom-separator' && item !== '...'\"\n viewBox=\"0 0 24 24\"\n class=\"h-4 w-4\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M8 20L16 4\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n <svg\n *ngIf=\"(mode !== 'custom-separator' && item !== '...') || item === '...'\"\n viewBox=\"0 0 24 24\"\n class=\"h-4 w-4\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M9 6L15 12L9 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n </ng-container>\n\n <ng-container *ngIf=\"mode === 'dropdown' && i === 1 && item !== '...' && !last\">\n <span class=\"-ml-2 inline-flex h-6 w-6 items-center justify-center text-muted-foreground\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M7 10L12 15L17 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n </ng-container>\n </ng-container>\n</nav>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
303
+ PdmBreadcrumbComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmBreadcrumbComponent, selector: "pdm-breadcrumb", inputs: { mode: "mode", items: "items", className: "className", minItemsMobile: "minItemsMobile" }, ngImport: i0, template: "<nav\n aria-label=\"breadcrumb\"\n [ngClass]=\"[\n 'inline-flex items-center gap-1.5 text-sm',\n mode === 'responsive' ? 'overflow-x-auto max-w-full pb-1 scrollbar-thin' : '',\n className\n ]\"\n>\n <ng-container *ngFor=\"let item of renderedItems; let i = index; let last = last\">\n <!-- Item text -->\n <span \n [ngClass]=\"[\n last ? 'text-foreground font-medium' : 'text-muted-foreground hover:text-foreground transition-colors cursor-pointer',\n mode === 'responsive' && !shouldShowInMobile(i) ? 'hidden sm:inline' : '',\n 'whitespace-nowrap'\n ]\">\n {{ item }}\n </span>\n\n <!-- Separator -->\n <ng-container *ngIf=\"!last\">\n <span \n [ngClass]=\"[\n 'inline-flex h-6 w-6 flex-shrink-0 items-center justify-center text-muted-foreground',\n mode === 'responsive' && !shouldShowInMobile(i) && !shouldShowInMobile(i + 1) ? 'hidden sm:inline-flex' : ''\n ]\" \n aria-hidden=\"true\">\n <!-- Custom separator (slash) -->\n <svg\n *ngIf=\"mode === 'custom-separator' && item !== '...'\"\n viewBox=\"0 0 24 24\"\n class=\"h-4 w-4\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M8 20L16 4\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n \n <!-- Default separator (chevron) -->\n <svg\n *ngIf=\"(mode !== 'custom-separator' && item !== '...') || item === '...'\"\n viewBox=\"0 0 24 24\"\n class=\"h-4 w-4\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M9 6L15 12L9 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n \n <!-- Ellipsis indicator for responsive mode (only shown when items are hidden) -->\n <span \n *ngIf=\"mode === 'responsive' && !shouldShowInMobile(i) && i === 0\"\n class=\"inline-flex sm:hidden text-muted-foreground\">\n ...\n </span>\n </ng-container>\n\n <!-- Dropdown indicator -->\n <ng-container *ngIf=\"mode === 'dropdown' && i === 1 && item !== '...' && !last\">\n <span class=\"-ml-2 inline-flex h-6 w-6 items-center justify-center text-muted-foreground\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M7 10L12 15L17 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n </ng-container>\n </ng-container>\n</nav>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
255
304
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmBreadcrumbComponent, decorators: [{
256
305
  type: Component,
257
- args: [{ selector: 'pdm-breadcrumb', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav\n aria-label=\"breadcrumb\"\n [ngClass]=\"['inline-flex items-center gap-1.5 text-sm', className]\"\n>\n <ng-container *ngFor=\"let item of renderedItems; let i = index; let last = last\">\n <span [ngClass]=\"[last ? 'text-foreground' : 'text-muted-foreground']\">{{ item }}</span>\n\n <ng-container *ngIf=\"!last\">\n <span class=\"inline-flex h-6 w-6 items-center justify-center text-muted-foreground\" aria-hidden=\"true\">\n <svg\n *ngIf=\"mode === 'custom-separator' && item !== '...'\"\n viewBox=\"0 0 24 24\"\n class=\"h-4 w-4\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M8 20L16 4\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n <svg\n *ngIf=\"(mode !== 'custom-separator' && item !== '...') || item === '...'\"\n viewBox=\"0 0 24 24\"\n class=\"h-4 w-4\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M9 6L15 12L9 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n </ng-container>\n\n <ng-container *ngIf=\"mode === 'dropdown' && i === 1 && item !== '...' && !last\">\n <span class=\"-ml-2 inline-flex h-6 w-6 items-center justify-center text-muted-foreground\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M7 10L12 15L17 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n </ng-container>\n </ng-container>\n</nav>\n" }]
306
+ args: [{ selector: 'pdm-breadcrumb', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav\n aria-label=\"breadcrumb\"\n [ngClass]=\"[\n 'inline-flex items-center gap-1.5 text-sm',\n mode === 'responsive' ? 'overflow-x-auto max-w-full pb-1 scrollbar-thin' : '',\n className\n ]\"\n>\n <ng-container *ngFor=\"let item of renderedItems; let i = index; let last = last\">\n <!-- Item text -->\n <span \n [ngClass]=\"[\n last ? 'text-foreground font-medium' : 'text-muted-foreground hover:text-foreground transition-colors cursor-pointer',\n mode === 'responsive' && !shouldShowInMobile(i) ? 'hidden sm:inline' : '',\n 'whitespace-nowrap'\n ]\">\n {{ item }}\n </span>\n\n <!-- Separator -->\n <ng-container *ngIf=\"!last\">\n <span \n [ngClass]=\"[\n 'inline-flex h-6 w-6 flex-shrink-0 items-center justify-center text-muted-foreground',\n mode === 'responsive' && !shouldShowInMobile(i) && !shouldShowInMobile(i + 1) ? 'hidden sm:inline-flex' : ''\n ]\" \n aria-hidden=\"true\">\n <!-- Custom separator (slash) -->\n <svg\n *ngIf=\"mode === 'custom-separator' && item !== '...'\"\n viewBox=\"0 0 24 24\"\n class=\"h-4 w-4\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M8 20L16 4\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n \n <!-- Default separator (chevron) -->\n <svg\n *ngIf=\"(mode !== 'custom-separator' && item !== '...') || item === '...'\"\n viewBox=\"0 0 24 24\"\n class=\"h-4 w-4\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M9 6L15 12L9 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n \n <!-- Ellipsis indicator for responsive mode (only shown when items are hidden) -->\n <span \n *ngIf=\"mode === 'responsive' && !shouldShowInMobile(i) && i === 0\"\n class=\"inline-flex sm:hidden text-muted-foreground\">\n ...\n </span>\n </ng-container>\n\n <!-- Dropdown indicator -->\n <ng-container *ngIf=\"mode === 'dropdown' && i === 1 && item !== '...' && !last\">\n <span class=\"-ml-2 inline-flex h-6 w-6 items-center justify-center text-muted-foreground\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M7 10L12 15L17 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n </span>\n </ng-container>\n </ng-container>\n</nav>\n" }]
258
307
  }], propDecorators: { mode: [{
259
308
  type: Input
260
309
  }], items: [{
261
310
  type: Input
262
311
  }], className: [{
263
312
  type: Input
313
+ }], minItemsMobile: [{
314
+ type: Input
264
315
  }] } });
265
316
 
266
317
  class PdmButtonGroupComponent {
@@ -1040,10 +1091,10 @@ class PdmCalendarComponent {
1040
1091
  }
1041
1092
  }
1042
1093
  PdmCalendarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCalendarComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1043
- PdmCalendarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmCalendarComponent, selector: "pdm-calendar", inputs: { variant: "variant", className: "className", disabledDates: "disabledDates", minDate: "minDate", maxDate: "maxDate", minYear: "minYear", maxYear: "maxYear", isDateDisabled: "isDateDisabled", allowSameDayRange: "allowSameDayRange", readonly: "readonly", value: "value", rangeValue: "rangeValue", month: "month" }, outputs: { valueChange: "valueChange", rangeValueChange: "rangeValueChange", monthChange: "monthChange", dateClick: "dateClick", disabledDateClick: "disabledDateClick" }, ngImport: i0, template: "<div [ngClass]=\"rootClasses\" [ngStyle]=\"rootStyle\">\n <div *ngFor=\"let month of visibleMonths; let monthIndex = index; trackBy: trackByIndex\" [ngClass]=\"monthPanelClasses(monthIndex)\">\n <div [ngClass]=\"headerClasses(month)\">\n <button\n *ngIf=\"month.showPrevButton; else prevPlaceholder\"\n type=\"button\"\n [ngClass]=\"navButtonClasses()\"\n aria-label=\"Previous month\"\n (click)=\"goToPreviousMonth()\"\n [disabled]=\"readonly\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m15 18-6-6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n <ng-template #prevPlaceholder>\n <div [ngClass]=\"navPlaceholderClasses()\" aria-hidden=\"true\"></div>\n </ng-template>\n\n <ng-container *ngIf=\"month.titleStyle === 'dropdowns'; else plainTitle\">\n <div [ngClass]=\"dropdownWrapClasses()\">\n <div [ngClass]=\"dropdownClasses('w-[72px]')\">\n <select\n [ngClass]=\"dropdownSelectClasses()\"\n [ngStyle]=\"dropdownSelectStyle\"\n [value]=\"singleHeaderMonth\"\n aria-label=\"Month\"\n (change)=\"onSingleMonthChange($any($event.target).value)\"\n >\n <option\n *ngFor=\"let monthOption of monthOptions\"\n [value]=\"monthOption.value\"\n [selected]=\"monthOption.value === singleHeaderMonth\"\n >\n {{ monthOption.label }}\n </option>\n </select>\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3 text-foreground\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </div>\n <div [ngClass]=\"dropdownClasses('w-[82px]')\">\n <select\n [ngClass]=\"dropdownSelectClasses()\"\n [ngStyle]=\"dropdownSelectStyle\"\n [value]=\"singleHeaderYear\"\n aria-label=\"Year\"\n (change)=\"onSingleYearChange($any($event.target).value)\"\n >\n <option\n *ngFor=\"let year of yearOptions\"\n [value]=\"year\"\n [selected]=\"year === singleHeaderYear\"\n >\n {{ year }}\n </option>\n </select>\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3 text-foreground\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </div>\n </div>\n </ng-container>\n\n <ng-template #plainTitle>\n <div class=\"flex min-w-0 flex-1 items-center justify-center\">\n <p class=\"m-0 text-foreground text-center text-sm font-medium leading-5\">\n {{ month.title }}\n </p>\n </div>\n </ng-template>\n\n <button\n *ngIf=\"month.showNextButton; else nextPlaceholder\"\n type=\"button\"\n [ngClass]=\"navButtonClasses()\"\n aria-label=\"Next month\"\n (click)=\"goToNextMonth()\"\n [disabled]=\"readonly\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m9 18 6-6-6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n <ng-template #nextPlaceholder>\n <div [ngClass]=\"navPlaceholderClasses()\" aria-hidden=\"true\"></div>\n </ng-template>\n </div>\n\n <div [ngClass]=\"calendarGridWrapClasses()\">\n <div [ngClass]=\"weekdayRowClasses()\">\n <div *ngFor=\"let day of weekdays; trackBy: trackByIndex\" [ngClass]=\"weekdayCellClasses()\">\n <span>{{ day }}</span>\n </div>\n </div>\n\n <div *ngFor=\"let week of month.weeks; trackBy: trackByIndex\" [ngClass]=\"weekRowClasses()\">\n <div *ngFor=\"let cell of week; trackBy: trackByDate\" [ngClass]=\"dayCellClasses(cell)\">\n <button\n type=\"button\"\n [ngClass]=\"dayButtonClasses(cell)\"\n [disabled]=\"readonly\"\n [attr.aria-selected]=\"cell.selected\"\n [attr.aria-disabled]=\"cell.disabled || readonly\"\n [attr.title]=\"cell.date | date : 'yyyy-MM-dd'\"\n (click)=\"onDatePressed(cell)\"\n >\n <span [ngClass]=\"dayLabelClasses(cell)\">{{ cell.label }}</span>\n </button>\n </div>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1094
+ PdmCalendarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmCalendarComponent, selector: "pdm-calendar", inputs: { variant: "variant", className: "className", disabledDates: "disabledDates", minDate: "minDate", maxDate: "maxDate", minYear: "minYear", maxYear: "maxYear", isDateDisabled: "isDateDisabled", allowSameDayRange: "allowSameDayRange", readonly: "readonly", value: "value", rangeValue: "rangeValue", month: "month" }, outputs: { valueChange: "valueChange", rangeValueChange: "rangeValueChange", monthChange: "monthChange", dateClick: "dateClick", disabledDateClick: "disabledDateClick" }, ngImport: i0, template: "<div [ngClass]=\"rootClasses\" [ngStyle]=\"rootStyle\">\n <div *ngFor=\"let month of visibleMonths; let monthIndex = index; trackBy: trackByIndex\" [ngClass]=\"monthPanelClasses(monthIndex)\">\n <div [ngClass]=\"headerClasses(month)\">\n <button\n *ngIf=\"month.showPrevButton; else prevPlaceholder\"\n type=\"button\"\n [ngClass]=\"navButtonClasses()\"\n aria-label=\"Previous month\"\n (click)=\"goToPreviousMonth()\"\n [disabled]=\"readonly\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m15 18-6-6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n <ng-template #prevPlaceholder>\n <div [ngClass]=\"navPlaceholderClasses()\" aria-hidden=\"true\"></div>\n </ng-template>\n\n <ng-container *ngIf=\"month.titleStyle === 'dropdowns'; else plainTitle\">\n <div [ngClass]=\"dropdownWrapClasses()\">\n <div [ngClass]=\"dropdownClasses('w-16 sm:w-[72px]')\">\n <select\n [ngClass]=\"dropdownSelectClasses()\"\n [ngStyle]=\"dropdownSelectStyle\"\n [value]=\"singleHeaderMonth\"\n aria-label=\"Month\"\n (change)=\"onSingleMonthChange($any($event.target).value)\"\n >\n <option\n *ngFor=\"let monthOption of monthOptions\"\n [value]=\"monthOption.value\"\n [selected]=\"monthOption.value === singleHeaderMonth\"\n >\n {{ monthOption.label }}\n </option>\n </select>\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3 text-foreground\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </div>\n <div [ngClass]=\"dropdownClasses('w-16 sm:w-[82px]')\">\n <select\n [ngClass]=\"dropdownSelectClasses()\"\n [ngStyle]=\"dropdownSelectStyle\"\n [value]=\"singleHeaderYear\"\n aria-label=\"Year\"\n (change)=\"onSingleYearChange($any($event.target).value)\"\n >\n <option\n *ngFor=\"let year of yearOptions\"\n [value]=\"year\"\n [selected]=\"year === singleHeaderYear\"\n >\n {{ year }}\n </option>\n </select>\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3 text-foreground\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </div>\n </div>\n </ng-container>\n\n <ng-template #plainTitle>\n <div class=\"flex min-w-0 flex-1 items-center justify-center\">\n <p class=\"m-0 text-foreground text-center text-sm font-medium leading-5\">\n {{ month.title }}\n </p>\n </div>\n </ng-template>\n\n <button\n *ngIf=\"month.showNextButton; else nextPlaceholder\"\n type=\"button\"\n [ngClass]=\"navButtonClasses()\"\n aria-label=\"Next month\"\n (click)=\"goToNextMonth()\"\n [disabled]=\"readonly\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m9 18 6-6-6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n <ng-template #nextPlaceholder>\n <div [ngClass]=\"navPlaceholderClasses()\" aria-hidden=\"true\"></div>\n </ng-template>\n </div>\n\n <div [ngClass]=\"calendarGridWrapClasses()\">\n <div [ngClass]=\"weekdayRowClasses()\">\n <div *ngFor=\"let day of weekdays; trackBy: trackByIndex\" [ngClass]=\"weekdayCellClasses()\">\n <span>{{ day }}</span>\n </div>\n </div>\n\n <div *ngFor=\"let week of month.weeks; trackBy: trackByIndex\" [ngClass]=\"weekRowClasses()\">\n <div *ngFor=\"let cell of week; trackBy: trackByDate\" [ngClass]=\"dayCellClasses(cell)\">\n <button\n type=\"button\"\n [ngClass]=\"dayButtonClasses(cell)\"\n [disabled]=\"readonly\"\n [attr.aria-selected]=\"cell.selected\"\n [attr.aria-disabled]=\"cell.disabled || readonly\"\n [attr.title]=\"cell.date | date : 'yyyy-MM-dd'\"\n (click)=\"onDatePressed(cell)\"\n >\n <span [ngClass]=\"dayLabelClasses(cell)\">{{ cell.label }}</span>\n </button>\n </div>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1044
1095
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCalendarComponent, decorators: [{
1045
1096
  type: Component,
1046
- args: [{ selector: 'pdm-calendar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [ngClass]=\"rootClasses\" [ngStyle]=\"rootStyle\">\n <div *ngFor=\"let month of visibleMonths; let monthIndex = index; trackBy: trackByIndex\" [ngClass]=\"monthPanelClasses(monthIndex)\">\n <div [ngClass]=\"headerClasses(month)\">\n <button\n *ngIf=\"month.showPrevButton; else prevPlaceholder\"\n type=\"button\"\n [ngClass]=\"navButtonClasses()\"\n aria-label=\"Previous month\"\n (click)=\"goToPreviousMonth()\"\n [disabled]=\"readonly\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m15 18-6-6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n <ng-template #prevPlaceholder>\n <div [ngClass]=\"navPlaceholderClasses()\" aria-hidden=\"true\"></div>\n </ng-template>\n\n <ng-container *ngIf=\"month.titleStyle === 'dropdowns'; else plainTitle\">\n <div [ngClass]=\"dropdownWrapClasses()\">\n <div [ngClass]=\"dropdownClasses('w-[72px]')\">\n <select\n [ngClass]=\"dropdownSelectClasses()\"\n [ngStyle]=\"dropdownSelectStyle\"\n [value]=\"singleHeaderMonth\"\n aria-label=\"Month\"\n (change)=\"onSingleMonthChange($any($event.target).value)\"\n >\n <option\n *ngFor=\"let monthOption of monthOptions\"\n [value]=\"monthOption.value\"\n [selected]=\"monthOption.value === singleHeaderMonth\"\n >\n {{ monthOption.label }}\n </option>\n </select>\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3 text-foreground\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </div>\n <div [ngClass]=\"dropdownClasses('w-[82px]')\">\n <select\n [ngClass]=\"dropdownSelectClasses()\"\n [ngStyle]=\"dropdownSelectStyle\"\n [value]=\"singleHeaderYear\"\n aria-label=\"Year\"\n (change)=\"onSingleYearChange($any($event.target).value)\"\n >\n <option\n *ngFor=\"let year of yearOptions\"\n [value]=\"year\"\n [selected]=\"year === singleHeaderYear\"\n >\n {{ year }}\n </option>\n </select>\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3 text-foreground\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </div>\n </div>\n </ng-container>\n\n <ng-template #plainTitle>\n <div class=\"flex min-w-0 flex-1 items-center justify-center\">\n <p class=\"m-0 text-foreground text-center text-sm font-medium leading-5\">\n {{ month.title }}\n </p>\n </div>\n </ng-template>\n\n <button\n *ngIf=\"month.showNextButton; else nextPlaceholder\"\n type=\"button\"\n [ngClass]=\"navButtonClasses()\"\n aria-label=\"Next month\"\n (click)=\"goToNextMonth()\"\n [disabled]=\"readonly\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m9 18 6-6-6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n <ng-template #nextPlaceholder>\n <div [ngClass]=\"navPlaceholderClasses()\" aria-hidden=\"true\"></div>\n </ng-template>\n </div>\n\n <div [ngClass]=\"calendarGridWrapClasses()\">\n <div [ngClass]=\"weekdayRowClasses()\">\n <div *ngFor=\"let day of weekdays; trackBy: trackByIndex\" [ngClass]=\"weekdayCellClasses()\">\n <span>{{ day }}</span>\n </div>\n </div>\n\n <div *ngFor=\"let week of month.weeks; trackBy: trackByIndex\" [ngClass]=\"weekRowClasses()\">\n <div *ngFor=\"let cell of week; trackBy: trackByDate\" [ngClass]=\"dayCellClasses(cell)\">\n <button\n type=\"button\"\n [ngClass]=\"dayButtonClasses(cell)\"\n [disabled]=\"readonly\"\n [attr.aria-selected]=\"cell.selected\"\n [attr.aria-disabled]=\"cell.disabled || readonly\"\n [attr.title]=\"cell.date | date : 'yyyy-MM-dd'\"\n (click)=\"onDatePressed(cell)\"\n >\n <span [ngClass]=\"dayLabelClasses(cell)\">{{ cell.label }}</span>\n </button>\n </div>\n </div>\n </div>\n </div>\n</div>\n" }]
1097
+ args: [{ selector: 'pdm-calendar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [ngClass]=\"rootClasses\" [ngStyle]=\"rootStyle\">\n <div *ngFor=\"let month of visibleMonths; let monthIndex = index; trackBy: trackByIndex\" [ngClass]=\"monthPanelClasses(monthIndex)\">\n <div [ngClass]=\"headerClasses(month)\">\n <button\n *ngIf=\"month.showPrevButton; else prevPlaceholder\"\n type=\"button\"\n [ngClass]=\"navButtonClasses()\"\n aria-label=\"Previous month\"\n (click)=\"goToPreviousMonth()\"\n [disabled]=\"readonly\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m15 18-6-6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n <ng-template #prevPlaceholder>\n <div [ngClass]=\"navPlaceholderClasses()\" aria-hidden=\"true\"></div>\n </ng-template>\n\n <ng-container *ngIf=\"month.titleStyle === 'dropdowns'; else plainTitle\">\n <div [ngClass]=\"dropdownWrapClasses()\">\n <div [ngClass]=\"dropdownClasses('w-16 sm:w-[72px]')\">\n <select\n [ngClass]=\"dropdownSelectClasses()\"\n [ngStyle]=\"dropdownSelectStyle\"\n [value]=\"singleHeaderMonth\"\n aria-label=\"Month\"\n (change)=\"onSingleMonthChange($any($event.target).value)\"\n >\n <option\n *ngFor=\"let monthOption of monthOptions\"\n [value]=\"monthOption.value\"\n [selected]=\"monthOption.value === singleHeaderMonth\"\n >\n {{ monthOption.label }}\n </option>\n </select>\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3 text-foreground\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </div>\n <div [ngClass]=\"dropdownClasses('w-16 sm:w-[82px]')\">\n <select\n [ngClass]=\"dropdownSelectClasses()\"\n [ngStyle]=\"dropdownSelectStyle\"\n [value]=\"singleHeaderYear\"\n aria-label=\"Year\"\n (change)=\"onSingleYearChange($any($event.target).value)\"\n >\n <option\n *ngFor=\"let year of yearOptions\"\n [value]=\"year\"\n [selected]=\"year === singleHeaderYear\"\n >\n {{ year }}\n </option>\n </select>\n <svg viewBox=\"0 0 24 24\" class=\"h-3 w-3 text-foreground\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </div>\n </div>\n </ng-container>\n\n <ng-template #plainTitle>\n <div class=\"flex min-w-0 flex-1 items-center justify-center\">\n <p class=\"m-0 text-foreground text-center text-sm font-medium leading-5\">\n {{ month.title }}\n </p>\n </div>\n </ng-template>\n\n <button\n *ngIf=\"month.showNextButton; else nextPlaceholder\"\n type=\"button\"\n [ngClass]=\"navButtonClasses()\"\n aria-label=\"Next month\"\n (click)=\"goToNextMonth()\"\n [disabled]=\"readonly\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m9 18 6-6-6-6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n <ng-template #nextPlaceholder>\n <div [ngClass]=\"navPlaceholderClasses()\" aria-hidden=\"true\"></div>\n </ng-template>\n </div>\n\n <div [ngClass]=\"calendarGridWrapClasses()\">\n <div [ngClass]=\"weekdayRowClasses()\">\n <div *ngFor=\"let day of weekdays; trackBy: trackByIndex\" [ngClass]=\"weekdayCellClasses()\">\n <span>{{ day }}</span>\n </div>\n </div>\n\n <div *ngFor=\"let week of month.weeks; trackBy: trackByIndex\" [ngClass]=\"weekRowClasses()\">\n <div *ngFor=\"let cell of week; trackBy: trackByDate\" [ngClass]=\"dayCellClasses(cell)\">\n <button\n type=\"button\"\n [ngClass]=\"dayButtonClasses(cell)\"\n [disabled]=\"readonly\"\n [attr.aria-selected]=\"cell.selected\"\n [attr.aria-disabled]=\"cell.disabled || readonly\"\n [attr.title]=\"cell.date | date : 'yyyy-MM-dd'\"\n (click)=\"onDatePressed(cell)\"\n >\n <span [ngClass]=\"dayLabelClasses(cell)\">{{ cell.label }}</span>\n </button>\n </div>\n </div>\n </div>\n </div>\n</div>\n" }]
1047
1098
  }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { variant: [{
1048
1099
  type: Input
1049
1100
  }], className: [{
@@ -1181,66 +1232,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
1181
1232
  type: Output
1182
1233
  }] } });
1183
1234
 
1235
+ /**
1236
+ * Card component - Visual container primitivo
1237
+ *
1238
+ * BREAKING CHANGE en v0.2.0: variant="login" eliminado.
1239
+ * Card es ahora un componente UI puro sin lógica de negocio.
1240
+ *
1241
+ * Para crear un login form, componer con primitivos:
1242
+ *
1243
+ * @example
1244
+ * <pdm-card>
1245
+ * <div pdmCardHeader>
1246
+ * <h3 class="text-lg font-semibold">Login</h3>
1247
+ * <p class="text-sm text-muted-foreground">Enter your credentials</p>
1248
+ * </div>
1249
+ * <div pdmCardContent>
1250
+ * <form [formGroup]="form">
1251
+ * <pdm-field>
1252
+ * <pdm-label>Email</pdm-label>
1253
+ * <pdm-input type="email" formControlName="email" />
1254
+ * </pdm-field>
1255
+ * <pdm-field>
1256
+ * <pdm-label>Password</pdm-label>
1257
+ * <pdm-input-password formControlName="password" />
1258
+ * </pdm-field>
1259
+ * </form>
1260
+ * </div>
1261
+ * <div pdmCardFooter>
1262
+ * <pdm-button (click)="onLogin()">Login</pdm-button>
1263
+ * </div>
1264
+ * </pdm-card>
1265
+ */
1184
1266
  class PdmCardComponent {
1185
1267
  constructor() {
1186
- this.variant = 'default';
1187
1268
  this.className = '';
1188
- this.title = 'Login to your account';
1189
- this.description = 'Enter your email below to login to your account';
1190
- this.actionText = 'Sign up';
1191
- this.emailLabel = 'Email';
1192
- this.emailPlaceholder = 'm@example.com';
1193
- this.passwordLabel = 'Password';
1194
- this.passwordHint = 'Forgot password?';
1195
- this.primaryActionText = 'Login';
1196
- this.secondaryActionText = 'Login with Google';
1197
- this.primaryAction = new EventEmitter();
1198
- this.secondaryAction = new EventEmitter();
1199
- this.actionPressed = new EventEmitter();
1200
- }
1201
- onPrimaryAction() {
1202
- this.primaryAction.emit();
1203
- }
1204
- onSecondaryAction() {
1205
- this.secondaryAction.emit();
1206
- }
1207
- onActionPressed() {
1208
- this.actionPressed.emit();
1209
1269
  }
1210
1270
  }
1211
1271
  PdmCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1212
- PdmCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmCardComponent, selector: "pdm-card", inputs: { variant: "variant", className: "className", title: "title", description: "description", actionText: "actionText", emailLabel: "emailLabel", emailPlaceholder: "emailPlaceholder", passwordLabel: "passwordLabel", passwordHint: "passwordHint", primaryActionText: "primaryActionText", secondaryActionText: "secondaryActionText" }, outputs: { primaryAction: "primaryAction", secondaryAction: "secondaryAction", actionPressed: "actionPressed" }, ngImport: i0, template: "<section\n [ngClass]=\"[\n 'w-full rounded-lg border border-border bg-background py-6 shadow-sm',\n className\n ]\"\n>\n <ng-container *ngIf=\"variant === 'login'; else defaultCard\">\n <div class=\"flex w-full items-start gap-1.5 px-6\">\n <div class=\"min-w-0 flex-1\">\n <h3 class=\"m-0 text-lg font-semibold leading-none tracking-tight text-foreground\">{{ title }}</h3>\n <p class=\"m-0 mt-1.5 text-sm text-muted-foreground\">{{ description }}</p>\n </div>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border-0 bg-transparent px-4 py-2 text-sm font-medium text-foreground\"\n (click)=\"onActionPressed()\"\n >\n {{ actionText }}\n </button>\n </div>\n\n <div class=\"mt-6 flex flex-col gap-4 px-6\">\n <div class=\"w-full\">\n <label class=\"mb-3 block text-sm font-medium text-foreground\">{{ emailLabel }}</label>\n <input\n type=\"email\"\n [placeholder]=\"emailPlaceholder\"\n class=\"h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm text-foreground shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n />\n </div>\n\n <div class=\"w-full\">\n <div class=\"mb-3 flex w-full items-center justify-between gap-4\">\n <label class=\"text-sm font-medium text-foreground\">{{ passwordLabel }}</label>\n <button type=\"button\" class=\"appearance-none border-0 bg-transparent p-0 text-sm text-foreground\">\n {{ passwordHint }}\n </button>\n </div>\n <input\n type=\"password\"\n class=\"h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm text-foreground shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n />\n </div>\n </div>\n\n <div class=\"mt-6 flex w-full flex-col gap-2 px-6\">\n <button\n type=\"button\"\n class=\"flex h-9 w-full appearance-none items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm\"\n (click)=\"onPrimaryAction()\"\n >\n {{ primaryActionText }}\n </button>\n <button\n type=\"button\"\n class=\"flex h-9 w-full appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onSecondaryAction()\"\n >\n {{ secondaryActionText }}\n </button>\n </div>\n </ng-container>\n\n <ng-template #defaultCard>\n <div class=\"flex w-full flex-col gap-6\">\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardHeader]\"></ng-content>\n </div>\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardContent]\"></ng-content>\n </div>\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardFooter]\"></ng-content>\n </div>\n </div>\n </ng-template>\n</section>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1272
+ PdmCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmCardComponent, selector: "pdm-card", inputs: { className: "className" }, ngImport: i0, template: "<section\n [ngClass]=\"[\n 'w-full rounded-lg border border-border bg-background py-6 shadow-sm',\n className\n ]\"\n>\n <div class=\"flex w-full flex-col gap-6\">\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardHeader]\"></ng-content>\n </div>\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardContent]\"></ng-content>\n </div>\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardFooter]\"></ng-content>\n </div>\n </div>\n</section>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1213
1273
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCardComponent, decorators: [{
1214
1274
  type: Component,
1215
- args: [{ selector: 'pdm-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<section\n [ngClass]=\"[\n 'w-full rounded-lg border border-border bg-background py-6 shadow-sm',\n className\n ]\"\n>\n <ng-container *ngIf=\"variant === 'login'; else defaultCard\">\n <div class=\"flex w-full items-start gap-1.5 px-6\">\n <div class=\"min-w-0 flex-1\">\n <h3 class=\"m-0 text-lg font-semibold leading-none tracking-tight text-foreground\">{{ title }}</h3>\n <p class=\"m-0 mt-1.5 text-sm text-muted-foreground\">{{ description }}</p>\n </div>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border-0 bg-transparent px-4 py-2 text-sm font-medium text-foreground\"\n (click)=\"onActionPressed()\"\n >\n {{ actionText }}\n </button>\n </div>\n\n <div class=\"mt-6 flex flex-col gap-4 px-6\">\n <div class=\"w-full\">\n <label class=\"mb-3 block text-sm font-medium text-foreground\">{{ emailLabel }}</label>\n <input\n type=\"email\"\n [placeholder]=\"emailPlaceholder\"\n class=\"h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm text-foreground shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n />\n </div>\n\n <div class=\"w-full\">\n <div class=\"mb-3 flex w-full items-center justify-between gap-4\">\n <label class=\"text-sm font-medium text-foreground\">{{ passwordLabel }}</label>\n <button type=\"button\" class=\"appearance-none border-0 bg-transparent p-0 text-sm text-foreground\">\n {{ passwordHint }}\n </button>\n </div>\n <input\n type=\"password\"\n class=\"h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm text-foreground shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n />\n </div>\n </div>\n\n <div class=\"mt-6 flex w-full flex-col gap-2 px-6\">\n <button\n type=\"button\"\n class=\"flex h-9 w-full appearance-none items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm\"\n (click)=\"onPrimaryAction()\"\n >\n {{ primaryActionText }}\n </button>\n <button\n type=\"button\"\n class=\"flex h-9 w-full appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onSecondaryAction()\"\n >\n {{ secondaryActionText }}\n </button>\n </div>\n </ng-container>\n\n <ng-template #defaultCard>\n <div class=\"flex w-full flex-col gap-6\">\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardHeader]\"></ng-content>\n </div>\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardContent]\"></ng-content>\n </div>\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardFooter]\"></ng-content>\n </div>\n </div>\n </ng-template>\n</section>\n" }]
1216
- }], propDecorators: { variant: [{
1217
- type: Input
1218
- }], className: [{
1219
- type: Input
1220
- }], title: [{
1221
- type: Input
1222
- }], description: [{
1223
- type: Input
1224
- }], actionText: [{
1225
- type: Input
1226
- }], emailLabel: [{
1227
- type: Input
1228
- }], emailPlaceholder: [{
1229
- type: Input
1230
- }], passwordLabel: [{
1231
- type: Input
1232
- }], passwordHint: [{
1233
- type: Input
1234
- }], primaryActionText: [{
1235
- type: Input
1236
- }], secondaryActionText: [{
1275
+ args: [{ selector: 'pdm-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<section\n [ngClass]=\"[\n 'w-full rounded-lg border border-border bg-background py-6 shadow-sm',\n className\n ]\"\n>\n <div class=\"flex w-full flex-col gap-6\">\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardHeader]\"></ng-content>\n </div>\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardContent]\"></ng-content>\n </div>\n <div class=\"px-6\">\n <ng-content select=\"[pdmCardFooter]\"></ng-content>\n </div>\n </div>\n</section>\n" }]
1276
+ }], propDecorators: { className: [{
1237
1277
  type: Input
1238
- }], primaryAction: [{
1239
- type: Output
1240
- }], secondaryAction: [{
1241
- type: Output
1242
- }], actionPressed: [{
1243
- type: Output
1244
1278
  }] } });
1245
1279
 
1246
1280
  class PdmChartComponent {
@@ -1735,10 +1769,10 @@ class PdmCommandComponent {
1735
1769
  }
1736
1770
  }
1737
1771
  PdmCommandComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCommandComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1738
- PdmCommandComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmCommandComponent, selector: "pdm-command", inputs: { open: "open", hintLabel: "hintLabel", hintKey: "hintKey", placeholder: "placeholder", emptyMessage: "emptyMessage", items: "items", className: "className" }, outputs: { itemSelect: "itemSelect", openChange: "openChange" }, ngImport: i0, template: "<div [ngClass]=\"['w-full', className]\">\n <div *ngIf=\"!open\" class=\"flex items-center gap-1\">\n <span class=\"text-sm font-medium text-muted-foreground\">{{ hintLabel }}</span>\n <button\n type=\"button\"\n class=\"inline-flex h-5 appearance-none items-center gap-0.5 rounded-sm border border-border bg-muted px-1.5\"\n (click)=\"toggleOpen()\"\n >\n <pdm-icon name=\"command\" [size]=\"12\" className=\"text-muted-foreground\" [decorative]=\"true\"></pdm-icon>\n <span class=\"text-xs text-muted-foreground\">{{ hintKey }}</span>\n </button>\n </div>\n\n <section\n *ngIf=\"open\"\n class=\"flex w-full flex-col overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-md\"\n >\n <div class=\"flex items-center gap-2 border-b border-border px-3\">\n <pdm-icon name=\"search\" [size]=\"16\" className=\"text-muted-foreground\" [decorative]=\"true\"></pdm-icon>\n <input\n type=\"text\"\n [placeholder]=\"placeholder\"\n [value]=\"query\"\n (input)=\"onQueryChange($event)\"\n class=\"h-10 w-full bg-transparent py-3 text-sm text-foreground outline-none placeholder:text-muted-foreground\"\n />\n </div>\n\n <div class=\"max-h-72 overflow-y-auto p-1\">\n <ng-container *ngFor=\"let group of groupedItems; let groupIndex = index\">\n <div *ngIf=\"group.name\" class=\"px-2 py-1.5 text-xs text-muted-foreground\">{{ group.name }}</div>\n <button\n *ngFor=\"let item of group.items\"\n type=\"button\"\n [disabled]=\"item.disabled\"\n class=\"flex w-full appearance-none items-center gap-2 rounded-sm border-0 bg-transparent px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground\"\n [ngClass]=\"[\n item.disabled ? 'opacity-50' : '',\n item.label === 'Calendar' ? 'bg-accent text-accent-foreground' : ''\n ]\"\n (click)=\"select(item.value)\"\n >\n <span class=\"inline-flex h-4 w-4 items-center justify-center text-foreground\">\n <pdm-icon *ngIf=\"item.icon\" [name]=\"item.icon\" [size]=\"16\" [decorative]=\"true\"></pdm-icon>\n </span>\n <span class=\"min-w-0 flex-1 text-foreground\">{{ item.label }}</span>\n <span *ngIf=\"item.shortcut\" class=\"text-xs text-muted-foreground\">{{ item.shortcut }}</span>\n </button>\n <div *ngIf=\"groupIndex === 0 && groupedItems.length > 1\" class=\"my-1 border-t border-border\"></div>\n </ng-container>\n\n <p *ngIf=\"filteredItems.length === 0\" class=\"m-0 py-6 text-center text-sm text-muted-foreground\">{{ emptyMessage }}</p>\n </div>\n </section>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PdmIconComponent, selector: "pdm-icon", inputs: ["name", "library", "assetUrl", "size", "strokeWidth", "className", "ariaLabel", "decorative"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1772
+ PdmCommandComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmCommandComponent, selector: "pdm-command", inputs: { open: "open", hintLabel: "hintLabel", hintKey: "hintKey", placeholder: "placeholder", emptyMessage: "emptyMessage", items: "items", className: "className" }, outputs: { itemSelect: "itemSelect", openChange: "openChange" }, ngImport: i0, template: "<div [ngClass]=\"['w-full', className]\">\n <div *ngIf=\"!open\" class=\"flex items-center gap-1\">\n <span class=\"text-sm font-medium text-muted-foreground\">{{ hintLabel }}</span>\n <button\n type=\"button\"\n class=\"inline-flex h-5 appearance-none items-center gap-0.5 rounded-sm border border-border bg-muted px-1.5\"\n (click)=\"toggleOpen()\"\n >\n <pdm-icon name=\"command\" [size]=\"12\" className=\"text-muted-foreground\" [decorative]=\"true\"></pdm-icon>\n <span class=\"text-xs text-muted-foreground\">{{ hintKey }}</span>\n </button>\n </div>\n\n <section\n *ngIf=\"open\"\n class=\"flex w-full flex-col overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-md\"\n >\n <div class=\"flex items-center gap-2 border-b border-border px-3\">\n <pdm-icon name=\"search\" [size]=\"16\" className=\"text-muted-foreground\" [decorative]=\"true\"></pdm-icon>\n <input\n type=\"text\"\n [placeholder]=\"placeholder\"\n [value]=\"query\"\n (input)=\"onQueryChange($event)\"\n class=\"h-10 w-full bg-transparent py-3 text-sm text-foreground outline-none placeholder:text-muted-foreground\"\n />\n </div>\n\n <div class=\"max-h-[50vh] overflow-y-auto p-1 md:max-h-72\">\n <ng-container *ngFor=\"let group of groupedItems; let groupIndex = index\">\n <div *ngIf=\"group.name\" class=\"px-2 py-1.5 text-xs text-muted-foreground\">{{ group.name }}</div>\n <button\n *ngFor=\"let item of group.items\"\n type=\"button\"\n [disabled]=\"item.disabled\"\n class=\"flex w-full appearance-none items-center gap-2 rounded-sm border-0 bg-transparent px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground\"\n [ngClass]=\"[\n item.disabled ? 'opacity-50' : '',\n item.label === 'Calendar' ? 'bg-accent text-accent-foreground' : ''\n ]\"\n (click)=\"select(item.value)\"\n >\n <span class=\"inline-flex h-4 w-4 items-center justify-center text-foreground\">\n <pdm-icon *ngIf=\"item.icon\" [name]=\"item.icon\" [size]=\"16\" [decorative]=\"true\"></pdm-icon>\n </span>\n <span class=\"min-w-0 flex-1 text-foreground\">{{ item.label }}</span>\n <span *ngIf=\"item.shortcut\" class=\"text-xs text-muted-foreground\">{{ item.shortcut }}</span>\n </button>\n <div *ngIf=\"groupIndex === 0 && groupedItems.length > 1\" class=\"my-1 border-t border-border\"></div>\n </ng-container>\n\n <p *ngIf=\"filteredItems.length === 0\" class=\"m-0 py-6 text-center text-sm text-muted-foreground\">{{ emptyMessage }}</p>\n </div>\n </section>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PdmIconComponent, selector: "pdm-icon", inputs: ["name", "library", "assetUrl", "size", "strokeWidth", "className", "ariaLabel", "decorative"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1739
1773
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCommandComponent, decorators: [{
1740
1774
  type: Component,
1741
- args: [{ selector: 'pdm-command', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [ngClass]=\"['w-full', className]\">\n <div *ngIf=\"!open\" class=\"flex items-center gap-1\">\n <span class=\"text-sm font-medium text-muted-foreground\">{{ hintLabel }}</span>\n <button\n type=\"button\"\n class=\"inline-flex h-5 appearance-none items-center gap-0.5 rounded-sm border border-border bg-muted px-1.5\"\n (click)=\"toggleOpen()\"\n >\n <pdm-icon name=\"command\" [size]=\"12\" className=\"text-muted-foreground\" [decorative]=\"true\"></pdm-icon>\n <span class=\"text-xs text-muted-foreground\">{{ hintKey }}</span>\n </button>\n </div>\n\n <section\n *ngIf=\"open\"\n class=\"flex w-full flex-col overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-md\"\n >\n <div class=\"flex items-center gap-2 border-b border-border px-3\">\n <pdm-icon name=\"search\" [size]=\"16\" className=\"text-muted-foreground\" [decorative]=\"true\"></pdm-icon>\n <input\n type=\"text\"\n [placeholder]=\"placeholder\"\n [value]=\"query\"\n (input)=\"onQueryChange($event)\"\n class=\"h-10 w-full bg-transparent py-3 text-sm text-foreground outline-none placeholder:text-muted-foreground\"\n />\n </div>\n\n <div class=\"max-h-72 overflow-y-auto p-1\">\n <ng-container *ngFor=\"let group of groupedItems; let groupIndex = index\">\n <div *ngIf=\"group.name\" class=\"px-2 py-1.5 text-xs text-muted-foreground\">{{ group.name }}</div>\n <button\n *ngFor=\"let item of group.items\"\n type=\"button\"\n [disabled]=\"item.disabled\"\n class=\"flex w-full appearance-none items-center gap-2 rounded-sm border-0 bg-transparent px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground\"\n [ngClass]=\"[\n item.disabled ? 'opacity-50' : '',\n item.label === 'Calendar' ? 'bg-accent text-accent-foreground' : ''\n ]\"\n (click)=\"select(item.value)\"\n >\n <span class=\"inline-flex h-4 w-4 items-center justify-center text-foreground\">\n <pdm-icon *ngIf=\"item.icon\" [name]=\"item.icon\" [size]=\"16\" [decorative]=\"true\"></pdm-icon>\n </span>\n <span class=\"min-w-0 flex-1 text-foreground\">{{ item.label }}</span>\n <span *ngIf=\"item.shortcut\" class=\"text-xs text-muted-foreground\">{{ item.shortcut }}</span>\n </button>\n <div *ngIf=\"groupIndex === 0 && groupedItems.length > 1\" class=\"my-1 border-t border-border\"></div>\n </ng-container>\n\n <p *ngIf=\"filteredItems.length === 0\" class=\"m-0 py-6 text-center text-sm text-muted-foreground\">{{ emptyMessage }}</p>\n </div>\n </section>\n</div>\n" }]
1775
+ args: [{ selector: 'pdm-command', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [ngClass]=\"['w-full', className]\">\n <div *ngIf=\"!open\" class=\"flex items-center gap-1\">\n <span class=\"text-sm font-medium text-muted-foreground\">{{ hintLabel }}</span>\n <button\n type=\"button\"\n class=\"inline-flex h-5 appearance-none items-center gap-0.5 rounded-sm border border-border bg-muted px-1.5\"\n (click)=\"toggleOpen()\"\n >\n <pdm-icon name=\"command\" [size]=\"12\" className=\"text-muted-foreground\" [decorative]=\"true\"></pdm-icon>\n <span class=\"text-xs text-muted-foreground\">{{ hintKey }}</span>\n </button>\n </div>\n\n <section\n *ngIf=\"open\"\n class=\"flex w-full flex-col overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-md\"\n >\n <div class=\"flex items-center gap-2 border-b border-border px-3\">\n <pdm-icon name=\"search\" [size]=\"16\" className=\"text-muted-foreground\" [decorative]=\"true\"></pdm-icon>\n <input\n type=\"text\"\n [placeholder]=\"placeholder\"\n [value]=\"query\"\n (input)=\"onQueryChange($event)\"\n class=\"h-10 w-full bg-transparent py-3 text-sm text-foreground outline-none placeholder:text-muted-foreground\"\n />\n </div>\n\n <div class=\"max-h-[50vh] overflow-y-auto p-1 md:max-h-72\">\n <ng-container *ngFor=\"let group of groupedItems; let groupIndex = index\">\n <div *ngIf=\"group.name\" class=\"px-2 py-1.5 text-xs text-muted-foreground\">{{ group.name }}</div>\n <button\n *ngFor=\"let item of group.items\"\n type=\"button\"\n [disabled]=\"item.disabled\"\n class=\"flex w-full appearance-none items-center gap-2 rounded-sm border-0 bg-transparent px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground\"\n [ngClass]=\"[\n item.disabled ? 'opacity-50' : '',\n item.label === 'Calendar' ? 'bg-accent text-accent-foreground' : ''\n ]\"\n (click)=\"select(item.value)\"\n >\n <span class=\"inline-flex h-4 w-4 items-center justify-center text-foreground\">\n <pdm-icon *ngIf=\"item.icon\" [name]=\"item.icon\" [size]=\"16\" [decorative]=\"true\"></pdm-icon>\n </span>\n <span class=\"min-w-0 flex-1 text-foreground\">{{ item.label }}</span>\n <span *ngIf=\"item.shortcut\" class=\"text-xs text-muted-foreground\">{{ item.shortcut }}</span>\n </button>\n <div *ngIf=\"groupIndex === 0 && groupedItems.length > 1\" class=\"my-1 border-t border-border\"></div>\n </ng-container>\n\n <p *ngIf=\"filteredItems.length === 0\" class=\"m-0 py-6 text-center text-sm text-muted-foreground\">{{ emptyMessage }}</p>\n </div>\n </section>\n</div>\n" }]
1742
1776
  }], propDecorators: { open: [{
1743
1777
  type: Input
1744
1778
  }], hintLabel: [{
@@ -1785,6 +1819,15 @@ class PdmContextMenuComponent {
1785
1819
  this.x = 0;
1786
1820
  this.y = 0;
1787
1821
  }
1822
+ ngOnInit() {
1823
+ this.boundPointerDown = (event) => this.onDocumentPointerDown(event);
1824
+ document.addEventListener('pointerdown', this.boundPointerDown, { capture: true });
1825
+ }
1826
+ ngOnDestroy() {
1827
+ if (this.boundPointerDown) {
1828
+ document.removeEventListener('pointerdown', this.boundPointerDown, { capture: true });
1829
+ }
1830
+ }
1788
1831
  onContextMenu(event) {
1789
1832
  event.preventDefault();
1790
1833
  this.x = event.clientX;
@@ -1798,9 +1841,11 @@ class PdmContextMenuComponent {
1798
1841
  this.open = false;
1799
1842
  }
1800
1843
  onEsc() {
1801
- this.open = false;
1844
+ if (this.open) {
1845
+ this.open = false;
1846
+ }
1802
1847
  }
1803
- onDocumentClick(event) {
1848
+ onDocumentPointerDown(event) {
1804
1849
  if (!this.open)
1805
1850
  return;
1806
1851
  const target = event.target;
@@ -1810,10 +1855,10 @@ class PdmContextMenuComponent {
1810
1855
  }
1811
1856
  }
1812
1857
  PdmContextMenuComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmContextMenuComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
1813
- PdmContextMenuComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmContextMenuComponent, selector: "pdm-context-menu", inputs: { items: "items", className: "className", triggerLabel: "triggerLabel", width: "width", height: "height" }, outputs: { itemSelect: "itemSelect" }, host: { listeners: { "document:keydown.escape": "onEsc()", "document:click": "onDocumentClick($event)" } }, ngImport: i0, template: "<div class=\"relative\" [ngClass]=\"className\" (contextmenu)=\"onContextMenu($event)\">\n <div\n class=\"flex items-center justify-center rounded-md border border-dashed border-border\"\n [style.width.px]=\"width\"\n [style.height.px]=\"height\"\n >\n <span class=\"text-sm font-medium text-foreground\">{{ triggerLabel }}</span>\n </div>\n\n <div\n *ngIf=\"open\"\n class=\"fixed z-50 w-52 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md\"\n [style.left.px]=\"x + 4\"\n [style.top.px]=\"y + 2\"\n >\n <div>\n <ng-container *ngFor=\"let item of items\">\n <div *ngIf=\"item.type === 'separator'\" class=\"-mx-1 my-1 h-px bg-muted\"></div>\n\n <div *ngIf=\"item.type === 'label'\" class=\"px-2 py-1.5 text-sm font-semibold text-foreground\">\n {{ item.label }}\n </div>\n\n <button\n *ngIf=\"!item.type || item.type === 'item'\"\n type=\"button\"\n [disabled]=\"item.disabled\"\n class=\"relative flex w-full appearance-none cursor-default select-none items-center rounded-sm border-0 bg-transparent py-1.5 pr-2 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n [ngClass]=\"item.inset ? 'pl-8' : 'px-2'\"\n (click)=\"select(item)\"\n >\n <span class=\"mr-2 inline-flex w-4 shrink-0 items-center justify-center text-foreground\">\n <svg *ngIf=\"item.checked\" viewBox=\"0 0 24 24\" class=\"h-3.5 w-3.5\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5 12.5L9.2 16.7L19 7\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n <span *ngIf=\"item.selectedDot\" class=\"h-2 w-2 rounded-full bg-foreground\"></span>\n </span>\n <span class=\"min-w-0 flex-1 truncate text-foreground\">{{ item.label }}</span>\n <span *ngIf=\"item.shortcut\" class=\"text-xs text-muted-foreground\">{{ item.shortcut }}</span>\n <svg *ngIf=\"item.showChevron\" viewBox=\"0 0 24 24\" class=\"h-3.5 w-3.5 text-muted-foreground\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M9 6L15 12L9 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n </ng-container>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1858
+ PdmContextMenuComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmContextMenuComponent, selector: "pdm-context-menu", inputs: { items: "items", className: "className", triggerLabel: "triggerLabel", width: "width", height: "height" }, outputs: { itemSelect: "itemSelect" }, host: { listeners: { "document:keydown.escape": "onEsc()" } }, ngImport: i0, template: "<div class=\"relative\" [ngClass]=\"className\" (contextmenu)=\"onContextMenu($event)\">\n <div\n class=\"flex items-center justify-center rounded-md border border-dashed border-border\"\n [style.width.px]=\"width\"\n [style.height.px]=\"height\"\n >\n <span class=\"text-sm font-medium text-foreground\">{{ triggerLabel }}</span>\n </div>\n\n <div\n *ngIf=\"open\"\n class=\"fixed z-[70] min-w-48 max-w-xs rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md sm:min-w-52\"\n [style.left.px]=\"x + 4\"\n [style.top.px]=\"y + 2\"\n >\n <div>\n <ng-container *ngFor=\"let item of items\">\n <div *ngIf=\"item.type === 'separator'\" class=\"-mx-1 my-1 h-px bg-muted\"></div>\n\n <div *ngIf=\"item.type === 'label'\" class=\"px-2 py-1.5 text-sm font-semibold text-foreground\">\n {{ item.label }}\n </div>\n\n <button\n *ngIf=\"!item.type || item.type === 'item'\"\n type=\"button\"\n [disabled]=\"item.disabled\"\n class=\"relative flex w-full appearance-none cursor-default select-none items-center rounded-sm border-0 bg-transparent py-1.5 pr-2 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n [ngClass]=\"item.inset ? 'pl-8' : 'px-2'\"\n (click)=\"select(item)\"\n >\n <span class=\"mr-2 inline-flex w-4 shrink-0 items-center justify-center text-foreground\">\n <svg *ngIf=\"item.checked\" viewBox=\"0 0 24 24\" class=\"h-3.5 w-3.5\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5 12.5L9.2 16.7L19 7\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n <span *ngIf=\"item.selectedDot\" class=\"h-2 w-2 rounded-full bg-foreground\"></span>\n </span>\n <span class=\"min-w-0 flex-1 truncate text-foreground\">{{ item.label }}</span>\n <span *ngIf=\"item.shortcut\" class=\"text-xs text-muted-foreground\">{{ item.shortcut }}</span>\n <svg *ngIf=\"item.showChevron\" viewBox=\"0 0 24 24\" class=\"h-3.5 w-3.5 text-muted-foreground\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M9 6L15 12L9 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n </ng-container>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1814
1859
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmContextMenuComponent, decorators: [{
1815
1860
  type: Component,
1816
- args: [{ selector: 'pdm-context-menu', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"relative\" [ngClass]=\"className\" (contextmenu)=\"onContextMenu($event)\">\n <div\n class=\"flex items-center justify-center rounded-md border border-dashed border-border\"\n [style.width.px]=\"width\"\n [style.height.px]=\"height\"\n >\n <span class=\"text-sm font-medium text-foreground\">{{ triggerLabel }}</span>\n </div>\n\n <div\n *ngIf=\"open\"\n class=\"fixed z-50 w-52 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md\"\n [style.left.px]=\"x + 4\"\n [style.top.px]=\"y + 2\"\n >\n <div>\n <ng-container *ngFor=\"let item of items\">\n <div *ngIf=\"item.type === 'separator'\" class=\"-mx-1 my-1 h-px bg-muted\"></div>\n\n <div *ngIf=\"item.type === 'label'\" class=\"px-2 py-1.5 text-sm font-semibold text-foreground\">\n {{ item.label }}\n </div>\n\n <button\n *ngIf=\"!item.type || item.type === 'item'\"\n type=\"button\"\n [disabled]=\"item.disabled\"\n class=\"relative flex w-full appearance-none cursor-default select-none items-center rounded-sm border-0 bg-transparent py-1.5 pr-2 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n [ngClass]=\"item.inset ? 'pl-8' : 'px-2'\"\n (click)=\"select(item)\"\n >\n <span class=\"mr-2 inline-flex w-4 shrink-0 items-center justify-center text-foreground\">\n <svg *ngIf=\"item.checked\" viewBox=\"0 0 24 24\" class=\"h-3.5 w-3.5\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5 12.5L9.2 16.7L19 7\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n <span *ngIf=\"item.selectedDot\" class=\"h-2 w-2 rounded-full bg-foreground\"></span>\n </span>\n <span class=\"min-w-0 flex-1 truncate text-foreground\">{{ item.label }}</span>\n <span *ngIf=\"item.shortcut\" class=\"text-xs text-muted-foreground\">{{ item.shortcut }}</span>\n <svg *ngIf=\"item.showChevron\" viewBox=\"0 0 24 24\" class=\"h-3.5 w-3.5 text-muted-foreground\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M9 6L15 12L9 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n </ng-container>\n </div>\n </div>\n</div>\n" }]
1861
+ args: [{ selector: 'pdm-context-menu', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"relative\" [ngClass]=\"className\" (contextmenu)=\"onContextMenu($event)\">\n <div\n class=\"flex items-center justify-center rounded-md border border-dashed border-border\"\n [style.width.px]=\"width\"\n [style.height.px]=\"height\"\n >\n <span class=\"text-sm font-medium text-foreground\">{{ triggerLabel }}</span>\n </div>\n\n <div\n *ngIf=\"open\"\n class=\"fixed z-[70] min-w-48 max-w-xs rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md sm:min-w-52\"\n [style.left.px]=\"x + 4\"\n [style.top.px]=\"y + 2\"\n >\n <div>\n <ng-container *ngFor=\"let item of items\">\n <div *ngIf=\"item.type === 'separator'\" class=\"-mx-1 my-1 h-px bg-muted\"></div>\n\n <div *ngIf=\"item.type === 'label'\" class=\"px-2 py-1.5 text-sm font-semibold text-foreground\">\n {{ item.label }}\n </div>\n\n <button\n *ngIf=\"!item.type || item.type === 'item'\"\n type=\"button\"\n [disabled]=\"item.disabled\"\n class=\"relative flex w-full appearance-none cursor-default select-none items-center rounded-sm border-0 bg-transparent py-1.5 pr-2 text-left text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n [ngClass]=\"item.inset ? 'pl-8' : 'px-2'\"\n (click)=\"select(item)\"\n >\n <span class=\"mr-2 inline-flex w-4 shrink-0 items-center justify-center text-foreground\">\n <svg *ngIf=\"item.checked\" viewBox=\"0 0 24 24\" class=\"h-3.5 w-3.5\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5 12.5L9.2 16.7L19 7\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n <span *ngIf=\"item.selectedDot\" class=\"h-2 w-2 rounded-full bg-foreground\"></span>\n </span>\n <span class=\"min-w-0 flex-1 truncate text-foreground\">{{ item.label }}</span>\n <span *ngIf=\"item.shortcut\" class=\"text-xs text-muted-foreground\">{{ item.shortcut }}</span>\n <svg *ngIf=\"item.showChevron\" viewBox=\"0 0 24 24\" class=\"h-3.5 w-3.5 text-muted-foreground\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M9 6L15 12L9 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n </ng-container>\n </div>\n </div>\n</div>\n" }]
1817
1862
  }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { items: [{
1818
1863
  type: Input
1819
1864
  }], className: [{
@@ -1829,36 +1874,461 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
1829
1874
  }], onEsc: [{
1830
1875
  type: HostListener,
1831
1876
  args: ['document:keydown.escape']
1832
- }], onDocumentClick: [{
1833
- type: HostListener,
1834
- args: ['document:click', ['$event']]
1835
1877
  }] } });
1836
1878
 
1879
+ /**
1880
+ * Sistema de responsive utilities para PDM UI Kit
1881
+ *
1882
+ * Proporciona constantes, tipos y helpers para manejar responsive design
1883
+ * de forma consistente en todos los componentes.
1884
+ */
1885
+ /**
1886
+ * Breakpoints estándar de Tailwind CSS
1887
+ * Mobile-first approach: los estilos base son para mobile, los breakpoints son MIN-WIDTH
1888
+ */
1889
+ const BREAKPOINTS = {
1890
+ sm: '640px',
1891
+ md: '768px',
1892
+ lg: '1024px',
1893
+ xl: '1280px',
1894
+ '2xl': '1536px' // extra large desktop
1895
+ };
1896
+ /**
1897
+ * Helper para generar clases responsive de forma programática
1898
+ *
1899
+ * @example
1900
+ * responsive({ default: 'block', sm: 'flex', lg: 'grid' })
1901
+ * // Returns: 'block sm:flex lg:grid'
1902
+ */
1903
+ function responsive(config) {
1904
+ const classes = [];
1905
+ if (config.default) {
1906
+ classes.push(config.default);
1907
+ }
1908
+ ['sm', 'md', 'lg', 'xl', '2xl'].forEach(bp => {
1909
+ if (config[bp]) {
1910
+ classes.push(`${bp}:${config[bp]}`);
1911
+ }
1912
+ });
1913
+ return classes.join(' ');
1914
+ }
1915
+ /**
1916
+ * Helper para overflow responsive
1917
+ * Maneja el caso común de scroll en mobile, auto en desktop
1918
+ *
1919
+ * @example
1920
+ * overflowResponsive('x', 'scroll', 'auto')
1921
+ * // Returns: 'overflow-x-scroll sm:overflow-x-auto'
1922
+ */
1923
+ function overflowResponsive(axis, mobile, desktop) {
1924
+ const axisClass = axis === 'both' ? 'overflow' : `overflow-${axis}`;
1925
+ const mobileClass = `${axisClass}-${mobile}`;
1926
+ if (!desktop || desktop === mobile) {
1927
+ return mobileClass;
1928
+ }
1929
+ return `${mobileClass} sm:${axisClass}-${desktop}`;
1930
+ }
1931
+ /**
1932
+ * Helper para spacing responsive
1933
+ * Útil para padding/margin que necesita ajustarse por breakpoint
1934
+ *
1935
+ * @example
1936
+ * spacingResponsive('px', { default: '4', sm: '6', lg: '8' })
1937
+ * // Returns: 'px-4 sm:px-6 lg:px-8'
1938
+ */
1939
+ function spacingResponsive(property, values) {
1940
+ return responsive(Object.entries(values).reduce((acc, [key, value]) => {
1941
+ acc[key] = `${property}-${value}`;
1942
+ return acc;
1943
+ }, {}));
1944
+ }
1945
+ /**
1946
+ * Helper para width responsive
1947
+ *
1948
+ * @example
1949
+ * widthResponsive({ default: 'full', sm: 'auto', lg: '1/2' })
1950
+ * // Returns: 'w-full sm:w-auto lg:w-1/2'
1951
+ */
1952
+ function widthResponsive(values) {
1953
+ return responsive(Object.entries(values).reduce((acc, [key, value]) => {
1954
+ acc[key] = `w-${value}`;
1955
+ return acc;
1956
+ }, {}));
1957
+ }
1958
+ /**
1959
+ * Clases comunes para containers responsive
1960
+ * Pensadas para wrappers que contienen contenido que puede desbordar
1961
+ */
1962
+ const RESPONSIVE_CONTAINER = {
1963
+ // Container con scroll horizontal en mobile, contenido visible en desktop
1964
+ tableWrapper: 'relative w-full overflow-x-auto sm:overflow-x-visible',
1965
+ // Container con padding negativo en mobile para scroll edge-to-edge
1966
+ tableWrapperFullBleed: 'relative w-full -mx-4 px-4 overflow-x-auto sm:mx-0 sm:px-0 sm:overflow-x-visible',
1967
+ // Container con max-width responsive
1968
+ contentWrapper: 'w-full mx-auto px-4 sm:px-6 lg:px-8 max-w-screen-2xl',
1969
+ // Container para modals/dialogs
1970
+ modalWrapper: 'w-full max-w-lg mx-auto px-4 sm:px-0',
1971
+ // Container para forms
1972
+ formWrapper: 'w-full max-w-md mx-auto space-y-4'
1973
+ };
1974
+ /**
1975
+ * Clases comunes para display responsive
1976
+ */
1977
+ const RESPONSIVE_DISPLAY = {
1978
+ // Ocultar en mobile, mostrar en desktop
1979
+ hideOnMobile: 'hidden sm:block',
1980
+ // Mostrar solo en mobile
1981
+ showOnMobile: 'block sm:hidden',
1982
+ // Stack en mobile, flex en desktop
1983
+ stackToFlex: 'flex flex-col sm:flex-row',
1984
+ // Stack en mobile, grid en desktop
1985
+ stackToGrid: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3'
1986
+ };
1987
+ /**
1988
+ * Clases para table responsive strategies
1989
+ */
1990
+ const TABLE_RESPONSIVE = {
1991
+ // Scroll horizontal (default, más simple)
1992
+ scroll: {
1993
+ wrapper: 'relative w-full overflow-x-auto',
1994
+ table: 'w-full min-w-full',
1995
+ cell: 'whitespace-nowrap'
1996
+ },
1997
+ // Permitir wrap del contenido
1998
+ wrap: {
1999
+ wrapper: 'relative w-full overflow-x-auto',
2000
+ table: 'w-full',
2001
+ cell: 'whitespace-normal break-words'
2002
+ },
2003
+ // Stack en mobile (cada fila se convierte en card)
2004
+ // Requiere lógica adicional en el componente
2005
+ stack: {
2006
+ wrapper: 'relative w-full',
2007
+ table: 'w-full',
2008
+ row: 'block sm:table-row border-b sm:border-b-0',
2009
+ cell: 'block sm:table-cell py-2 sm:py-0 before:content-[attr(data-label)] before:font-medium before:inline-block before:w-32 sm:before:content-none'
2010
+ },
2011
+ // Collapse: ocultar columnas menos importantes en mobile
2012
+ // Se usa con clases de visibility en las columnas específicas
2013
+ collapse: {
2014
+ wrapper: 'relative w-full overflow-x-auto',
2015
+ table: 'w-full',
2016
+ cell: 'whitespace-nowrap',
2017
+ // Estas clases se aplican a columnas opcionales
2018
+ optionalColumn: 'hidden md:table-cell'
2019
+ }
2020
+ };
2021
+
2022
+ /**
2023
+ * Componente base de tabla con soporte responsive
2024
+ *
2025
+ * SIMPLIFICADO: Ya no incluye drag & drop (usar pdm-draggable-table para eso)
2026
+ *
2027
+ * @example
2028
+ * // Tabla simple con scroll horizontal
2029
+ * <pdm-table variant="default">
2030
+ * <thead><tr><th>Name</th><th>Email</th></tr></thead>
2031
+ * <tbody><tr><td>John</td><td>john@example.com</td></tr></tbody>
2032
+ * </pdm-table>
2033
+ *
2034
+ * @example
2035
+ * // Tabla interactiva con wrap en mobile
2036
+ * <pdm-table variant="interactive" responsiveStrategy="wrap">
2037
+ * ...
2038
+ * </pdm-table>
2039
+ */
2040
+ class PdmTableComponent {
2041
+ constructor() {
2042
+ /**
2043
+ * Variante visual de la tabla
2044
+ * - default: tabla básica sin estilos extra
2045
+ * - data: tabla con bordes y espaciado para data
2046
+ * - interactive: tabla con hover, sticky header y estilos interactivos
2047
+ */
2048
+ this.variant = 'default';
2049
+ /**
2050
+ * Estrategia responsive para la tabla
2051
+ * - scroll: scroll horizontal en mobile (default, más simple)
2052
+ * - wrap: permite que el contenido haga wrap
2053
+ * - stack: convierte filas en cards en mobile (requiere data-label en celdas)
2054
+ * - collapse: oculta columnas menos importantes en mobile
2055
+ */
2056
+ this.responsiveStrategy = 'scroll';
2057
+ /**
2058
+ * Clases CSS adicionales para el wrapper
2059
+ */
2060
+ this.className = '';
2061
+ /**
2062
+ * Si es true, aplica padding negativo en mobile para scroll edge-to-edge
2063
+ * Útil cuando la tabla está dentro de un container con padding
2064
+ */
2065
+ this.fullBleed = false;
2066
+ }
2067
+ get wrapperClasses() {
2068
+ const baseClasses = ['relative', 'w-full'];
2069
+ const strategyClasses = this.getResponsiveStrategyClasses();
2070
+ const variantClasses = this.getVariantWrapperClasses();
2071
+ // Full bleed: scroll edge-to-edge en mobile
2072
+ if (this.fullBleed && this.responsiveStrategy === 'scroll') {
2073
+ baseClasses.push('-mx-4', 'px-4', 'sm:mx-0', 'sm:px-0');
2074
+ }
2075
+ return [
2076
+ ...baseClasses,
2077
+ ...strategyClasses,
2078
+ ...variantClasses,
2079
+ this.className
2080
+ ].filter(Boolean);
2081
+ }
2082
+ get tableClasses() {
2083
+ const baseClasses = ['w-full', 'caption-bottom', 'text-sm'];
2084
+ const variantClasses = this.getVariantTableClasses();
2085
+ const cellClasses = this.getCellClasses();
2086
+ return [...baseClasses, ...variantClasses, ...cellClasses].filter(Boolean);
2087
+ }
2088
+ getResponsiveStrategyClasses() {
2089
+ const strategy = TABLE_RESPONSIVE[this.responsiveStrategy];
2090
+ if (this.responsiveStrategy === 'scroll') {
2091
+ return ['overflow-x-auto'];
2092
+ }
2093
+ if (this.responsiveStrategy === 'wrap') {
2094
+ return ['overflow-x-auto'];
2095
+ }
2096
+ if (this.responsiveStrategy === 'stack') {
2097
+ // Stack requiere lógica en el template, aquí solo el wrapper
2098
+ return [];
2099
+ }
2100
+ if (this.responsiveStrategy === 'collapse') {
2101
+ return ['overflow-x-auto'];
2102
+ }
2103
+ return ['overflow-auto'];
2104
+ }
2105
+ getVariantWrapperClasses() {
2106
+ if (this.variant === 'interactive') {
2107
+ return ['rounded-xl', 'border', 'border-border', 'bg-background'];
2108
+ }
2109
+ if (this.variant === 'data') {
2110
+ return ['rounded-md', 'border', 'border-border', 'bg-background'];
2111
+ }
2112
+ return [];
2113
+ }
2114
+ getVariantTableClasses() {
2115
+ if (this.variant === 'data') {
2116
+ return [
2117
+ 'border-collapse',
2118
+ 'text-foreground',
2119
+ '[&_thead_tr]:border-b',
2120
+ '[&_thead_tr]:border-border',
2121
+ '[&_tbody_tr]:border-b',
2122
+ '[&_tbody_tr]:border-border',
2123
+ '[&_tbody_tr:last-child]:border-b-0',
2124
+ '[&_th]:h-10',
2125
+ '[&_th]:px-2',
2126
+ '[&_th]:text-left',
2127
+ '[&_th]:align-middle',
2128
+ '[&_th]:font-medium',
2129
+ '[&_td]:p-2',
2130
+ '[&_td]:align-middle'
2131
+ ];
2132
+ }
2133
+ if (this.variant === 'interactive') {
2134
+ return [
2135
+ 'text-foreground',
2136
+ '[&_thead]:sticky',
2137
+ '[&_thead]:top-0',
2138
+ '[&_thead]:z-10',
2139
+ '[&_thead]:bg-muted/70',
2140
+ '[&_thead_tr]:border-b',
2141
+ '[&_thead_tr]:border-border',
2142
+ '[&_th]:h-12',
2143
+ '[&_th]:px-4',
2144
+ '[&_th]:text-left',
2145
+ '[&_th]:align-middle',
2146
+ '[&_th]:text-sm',
2147
+ '[&_th]:font-medium',
2148
+ '[&_tbody_tr]:border-b',
2149
+ '[&_tbody_tr]:border-border',
2150
+ '[&_tbody_tr]:transition-colors',
2151
+ '[&_tbody_tr:hover]:bg-muted/50',
2152
+ '[&_tbody_tr:last-child]:border-b-0',
2153
+ '[&_td]:h-14',
2154
+ '[&_td]:px-4',
2155
+ '[&_td]:align-middle',
2156
+ '[&_td]:text-sm',
2157
+ '[&_svg]:text-muted-foreground'
2158
+ ];
2159
+ }
2160
+ return [];
2161
+ }
2162
+ getCellClasses() {
2163
+ // Manejo responsive de whitespace
2164
+ if (this.responsiveStrategy === 'scroll') {
2165
+ // En scroll, permitir wrap en mobile, nowrap en desktop
2166
+ return ['[&_td]:whitespace-normal', '[&_th]:whitespace-normal', 'sm:[&_td]:whitespace-nowrap', 'sm:[&_th]:whitespace-nowrap'];
2167
+ }
2168
+ if (this.responsiveStrategy === 'wrap') {
2169
+ // En wrap, siempre permitir wrap
2170
+ return ['[&_td]:whitespace-normal', '[&_td]:break-words', '[&_th]:whitespace-normal'];
2171
+ }
2172
+ // Default: nowrap (comportamiento anterior para backward compatibility)
2173
+ return [];
2174
+ }
2175
+ }
2176
+ PdmTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2177
+ PdmTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmTableComponent, selector: "pdm-table", inputs: { variant: "variant", responsiveStrategy: "responsiveStrategy", className: "className", fullBleed: "fullBleed" }, ngImport: i0, template: "<div [ngClass]=\"wrapperClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table-container' : null\">\n <table #tableElement [ngClass]=\"tableClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table' : null\">\n <ng-content></ng-content>\n </table>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2178
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTableComponent, decorators: [{
2179
+ type: Component,
2180
+ args: [{ selector: 'pdm-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [ngClass]=\"wrapperClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table-container' : null\">\n <table #tableElement [ngClass]=\"tableClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table' : null\">\n <ng-content></ng-content>\n </table>\n</div>\n" }]
2181
+ }], propDecorators: { variant: [{
2182
+ type: Input
2183
+ }], responsiveStrategy: [{
2184
+ type: Input
2185
+ }], className: [{
2186
+ type: Input
2187
+ }], fullBleed: [{
2188
+ type: Input
2189
+ }] } });
2190
+
2191
+ /**
2192
+ * Data-table genérico con paginación, filtrado y selección
2193
+ *
2194
+ * NUEVO: Ahora es genérico y configurable via columnas
2195
+ *
2196
+ * @example
2197
+ * // Definir columnas
2198
+ * columns: PdmDataTableColumn<User>[] = [
2199
+ * { key: 'name', label: 'Name', sortable: true },
2200
+ * { key: 'email', label: 'Email', sortable: true },
2201
+ * { key: 'role', label: 'Role', hideOnMobile: true },
2202
+ * { key: 'createdAt', label: 'Created', render: (val) => formatDate(val) }
2203
+ * ];
2204
+ *
2205
+ * // En el template
2206
+ * <pdm-data-table
2207
+ * [columns]="columns"
2208
+ * [rows]="users"
2209
+ * [selectable]="true"
2210
+ * (selectionChange)="onSelect($event)">
2211
+ * </pdm-data-table>
2212
+ */
1837
2213
  class PdmDataTableComponent {
1838
2214
  constructor() {
1839
2215
  this.className = '';
2216
+ /**
2217
+ * Columnas a mostrar
2218
+ * Si no se provee, intenta inferir del primer row (legacy mode)
2219
+ */
2220
+ this.columns = [];
2221
+ /**
2222
+ * Estrategia responsive de la tabla
2223
+ */
2224
+ this.responsiveStrategy = 'scroll';
2225
+ /**
2226
+ * Si es true, muestra checkbox de selección en cada fila
2227
+ */
2228
+ this.selectable = false;
2229
+ /**
2230
+ * Si es true, muestra botón de acciones (tres puntos) en cada fila
2231
+ */
2232
+ this.showActions = false;
2233
+ /**
2234
+ * Si es true, muestra filtro de búsqueda
2235
+ */
2236
+ this.showFilter = true;
2237
+ /**
2238
+ * Si es true, muestra controles de paginación
2239
+ */
2240
+ this.showPagination = true;
2241
+ /**
2242
+ * Si es true, muestra selector de columnas
2243
+ */
2244
+ this.showColumnSelector = false;
2245
+ // Labels i18n
1840
2246
  this.filterPlaceholder = 'Filter...';
1841
2247
  this.columnsLabel = 'Columns';
1842
- this.statusLabel = 'Status';
1843
- this.emailLabel = 'Email';
1844
- this.amountLabel = 'Amount';
1845
2248
  this.previousLabel = 'Previous';
1846
2249
  this.nextLabel = 'Next';
1847
2250
  this.emptyLabel = 'No results.';
2251
+ this.rowsSelectedLabel = 'row(s) selected';
2252
+ // DEPRECATED: Labels hardcodeados para backward compatibility
2253
+ /**
2254
+ * @deprecated Use columns configuration instead
2255
+ */
2256
+ this.statusLabel = 'Status';
2257
+ /**
2258
+ * @deprecated Use columns configuration instead
2259
+ */
2260
+ this.emailLabel = 'Email';
2261
+ /**
2262
+ * @deprecated Use columns configuration instead
2263
+ */
2264
+ this.amountLabel = 'Amount';
2265
+ /**
2266
+ * Datos a mostrar
2267
+ */
1848
2268
  this.rows = [];
2269
+ /**
2270
+ * Página actual (1-indexed)
2271
+ */
1849
2272
  this.page = 1;
1850
- this.pageSize = 5;
2273
+ /**
2274
+ * Cantidad de filas por página
2275
+ */
2276
+ this.pageSize = 10;
2277
+ /**
2278
+ * Query de filtrado
2279
+ */
1851
2280
  this.query = '';
1852
2281
  this.queryChange = new EventEmitter();
1853
2282
  this.rowAction = new EventEmitter();
1854
2283
  this.pageChange = new EventEmitter();
1855
2284
  this.selectionChange = new EventEmitter();
2285
+ this.columnSort = new EventEmitter();
2286
+ // Estado interno
2287
+ this.selectedRows = new Set();
2288
+ this.sortDirection = 'asc';
2289
+ }
2290
+ /**
2291
+ * Backward compatibility: si no hay columnas definidas, inferir del primer row
2292
+ */
2293
+ get effectiveColumns() {
2294
+ if (this.columns.length > 0) {
2295
+ return this.columns;
2296
+ }
2297
+ // Legacy mode: inferir columnas del primer row (solo para PdmDataTableRow)
2298
+ if (this.rows.length > 0) {
2299
+ const firstRow = this.rows[0];
2300
+ return Object.keys(firstRow)
2301
+ .filter(key => key !== 'selected')
2302
+ .map(key => ({
2303
+ key: key,
2304
+ label: this.getLegacyLabel(key),
2305
+ align: key === 'amount' ? 'right' : 'left'
2306
+ }));
2307
+ }
2308
+ return [];
2309
+ }
2310
+ /**
2311
+ * LEGACY: mapeo de keys a labels hardcodeados
2312
+ */
2313
+ getLegacyLabel(key) {
2314
+ const map = {
2315
+ status: this.statusLabel,
2316
+ email: this.emailLabel,
2317
+ amount: this.amountLabel
2318
+ };
2319
+ return map[key] || key.charAt(0).toUpperCase() + key.slice(1);
1856
2320
  }
1857
2321
  get filteredRows() {
1858
2322
  const q = this.query.trim().toLowerCase();
1859
2323
  if (!q)
1860
2324
  return this.rows;
1861
- return this.rows.filter((r) => r.email.toLowerCase().includes(q));
2325
+ if (this.filterFn) {
2326
+ return this.rows.filter(row => this.filterFn(row, q));
2327
+ }
2328
+ // Filtrado default: buscar en todos los campos string
2329
+ return this.rows.filter(row => {
2330
+ return Object.values(row).some(val => typeof val === 'string' && val.toLowerCase().includes(q));
2331
+ });
1862
2332
  }
1863
2333
  get pagedRows() {
1864
2334
  const start = (this.page - 1) * this.pageSize;
@@ -1868,14 +2338,33 @@ class PdmDataTableComponent {
1868
2338
  return Math.max(1, Math.ceil(this.filteredRows.length / this.pageSize));
1869
2339
  }
1870
2340
  get selectedCount() {
1871
- return this.rows.filter((row) => row.selected).length;
2341
+ return this.selectedRows.size;
1872
2342
  }
1873
2343
  onQueryInput(event) {
1874
2344
  const value = event.target.value;
1875
2345
  this.queryChange.emit(value);
1876
2346
  }
1877
2347
  onToggleRow(row, event) {
1878
- this.selectionChange.emit({ id: row.id, selected: event.target.checked });
2348
+ const checked = event.target.checked;
2349
+ if (checked) {
2350
+ this.selectedRows.add(row);
2351
+ }
2352
+ else {
2353
+ this.selectedRows.delete(row);
2354
+ }
2355
+ this.selectionChange.emit({ row, selected: checked });
2356
+ }
2357
+ onToggleAll(event) {
2358
+ const checked = event.target.checked;
2359
+ if (checked) {
2360
+ this.pagedRows.forEach(row => this.selectedRows.add(row));
2361
+ }
2362
+ else {
2363
+ this.pagedRows.forEach(row => this.selectedRows.delete(row));
2364
+ }
2365
+ }
2366
+ isSelected(row) {
2367
+ return this.selectedRows.has(row);
1879
2368
  }
1880
2369
  previous() {
1881
2370
  if (this.page <= 1)
@@ -1888,25 +2377,75 @@ class PdmDataTableComponent {
1888
2377
  this.pageChange.emit(this.page + 1);
1889
2378
  }
1890
2379
  onAction(row) {
1891
- this.rowAction.emit(row.id);
2380
+ this.rowAction.emit(row);
2381
+ }
2382
+ onSort(column) {
2383
+ if (!column.sortable)
2384
+ return;
2385
+ if (this.sortColumn === column) {
2386
+ this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
2387
+ }
2388
+ else {
2389
+ this.sortColumn = column;
2390
+ this.sortDirection = 'asc';
2391
+ }
2392
+ this.columnSort.emit({ column, direction: this.sortDirection });
2393
+ }
2394
+ getCellValue(row, column) {
2395
+ const value = row[column.key];
2396
+ if (column.render) {
2397
+ return column.render(value, row);
2398
+ }
2399
+ return value != null ? String(value) : '';
2400
+ }
2401
+ getCellClass(column) {
2402
+ const classes = ['px-2', 'py-2'];
2403
+ if (column.align === 'center')
2404
+ classes.push('text-center');
2405
+ if (column.align === 'right')
2406
+ classes.push('text-right');
2407
+ if (column.hideOnMobile)
2408
+ classes.push('hidden', 'md:table-cell');
2409
+ if (column.cellClass)
2410
+ classes.push(column.cellClass);
2411
+ return classes.join(' ');
2412
+ }
2413
+ getHeaderClass(column) {
2414
+ const classes = ['px-2', 'py-2', 'text-left', 'font-medium'];
2415
+ if (column.hideOnMobile)
2416
+ classes.push('hidden', 'md:table-cell');
2417
+ if (column.headerClass)
2418
+ classes.push(column.headerClass);
2419
+ return classes.join(' ');
2420
+ }
2421
+ getColumnStyle(column) {
2422
+ return column.width ? { width: column.width } : {};
1892
2423
  }
1893
2424
  }
1894
2425
  PdmDataTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1895
- PdmDataTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmDataTableComponent, selector: "pdm-data-table", inputs: { className: "className", filterPlaceholder: "filterPlaceholder", columnsLabel: "columnsLabel", statusLabel: "statusLabel", emailLabel: "emailLabel", amountLabel: "amountLabel", previousLabel: "previousLabel", nextLabel: "nextLabel", emptyLabel: "emptyLabel", rows: "rows", page: "page", pageSize: "pageSize", query: "query" }, outputs: { queryChange: "queryChange", rowAction: "rowAction", pageChange: "pageChange", selectionChange: "selectionChange" }, ngImport: i0, template: "<section [ngClass]=\"['flex w-full max-w-3xl flex-col items-end', className]\">\n <div class=\"flex w-full items-center justify-between py-4\">\n <input\n type=\"text\"\n [placeholder]=\"filterPlaceholder\"\n [value]=\"query\"\n (input)=\"onQueryInput($event)\"\n class=\"h-9 flex-1 rounded-md border border-input bg-transparent px-3 py-1 text-sm text-foreground shadow-sm placeholder:text-muted-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n />\n\n <button type=\"button\" class=\"inline-flex h-9 appearance-none items-center gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm font-medium text-foreground shadow-sm\">\n <span>{{ columnsLabel }}</span>\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4 text-foreground\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M7 10L12 15L17 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"w-full overflow-hidden rounded-md border border-border bg-background\">\n <table class=\"w-full border-collapse text-sm text-foreground\">\n <thead>\n <tr class=\"border-b border-border\">\n <th class=\"w-10 px-2 text-left font-medium\"><input type=\"checkbox\" class=\"h-4 w-4 rounded-sm border border-input\" /></th>\n <th class=\"w-32 px-2 py-2 text-left font-medium\">{{ statusLabel }}</th>\n <th class=\"px-2 py-2 text-left font-medium\">\n <button type=\"button\" class=\"inline-flex appearance-none items-center gap-1 rounded-sm border-0 bg-transparent px-3 py-2 text-sm\">\n <span>{{ emailLabel }}</span>\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M8 6L4 10L8 14M16 18L20 14L16 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n </th>\n <th class=\"w-24 px-2 py-2 text-right font-medium\">{{ amountLabel }}</th>\n <th class=\"px-2 py-2\"></th>\n </tr>\n </thead>\n\n <tbody>\n <tr *ngFor=\"let row of pagedRows\" class=\"border-b border-border last:border-b-0\">\n <td class=\"px-2 py-2\"><input type=\"checkbox\" [checked]=\"row.selected\" (change)=\"onToggleRow(row, $event)\" class=\"h-4 w-4 rounded-sm border border-input\" /></td>\n <td class=\"px-2 py-2\">{{ row.status }}</td>\n <td class=\"px-2 py-2\">{{ row.email }}</td>\n <td class=\"px-2 py-2 text-right\">{{ row.amount }}</td>\n <td class=\"px-2 py-2\">\n <button type=\"button\" class=\"inline-flex h-8 w-8 appearance-none items-center justify-center border-0 bg-transparent p-0\" (click)=\"onAction(row)\">\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n <circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n <circle cx=\"18\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n </svg>\n </button>\n </td>\n </tr>\n <tr *ngIf=\"pagedRows.length === 0\">\n <td colspan=\"5\" class=\"px-3 py-6 text-center text-sm text-muted-foreground\">{{ emptyLabel }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <div class=\"flex w-full items-center gap-2 py-4\">\n <p class=\"m-0 flex-1 pr-2 text-sm text-muted-foreground\">{{ selectedCount }} of {{ rows.length }} row(s) selected.</p>\n <button type=\"button\" class=\"h-9 appearance-none rounded-md border border-input bg-background px-4 text-sm font-medium text-foreground shadow-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50\" [disabled]=\"page <= 1\" (click)=\"previous()\">{{ previousLabel }}</button>\n <button type=\"button\" class=\"h-9 appearance-none rounded-md border border-input bg-background px-4 text-sm font-medium text-foreground shadow-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50\" [disabled]=\"page >= totalPages\" (click)=\"next()\">{{ nextLabel }}</button>\n </div>\n</section>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2426
+ PdmDataTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmDataTableComponent, selector: "pdm-data-table", inputs: { className: "className", columns: "columns", responsiveStrategy: "responsiveStrategy", selectable: "selectable", showActions: "showActions", showFilter: "showFilter", showPagination: "showPagination", showColumnSelector: "showColumnSelector", filterPlaceholder: "filterPlaceholder", columnsLabel: "columnsLabel", previousLabel: "previousLabel", nextLabel: "nextLabel", emptyLabel: "emptyLabel", rowsSelectedLabel: "rowsSelectedLabel", statusLabel: "statusLabel", emailLabel: "emailLabel", amountLabel: "amountLabel", rows: "rows", page: "page", pageSize: "pageSize", query: "query", filterFn: "filterFn" }, outputs: { queryChange: "queryChange", rowAction: "rowAction", pageChange: "pageChange", selectionChange: "selectionChange", columnSort: "columnSort" }, ngImport: i0, template: "<section [ngClass]=\"['flex w-full flex-col', className]\">\n <!-- Toolbar: Filtro + Selector de columnas -->\n <div *ngIf=\"showFilter || showColumnSelector\" class=\"flex w-full items-center justify-between gap-2 py-4\">\n <input\n *ngIf=\"showFilter\"\n type=\"text\"\n [placeholder]=\"filterPlaceholder\"\n [value]=\"query\"\n (input)=\"onQueryInput($event)\"\n class=\"h-9 flex-1 rounded-md border border-input bg-transparent px-3 py-1 text-sm text-foreground shadow-sm placeholder:text-muted-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n />\n\n <button \n *ngIf=\"showColumnSelector\"\n type=\"button\" \n class=\"inline-flex h-9 appearance-none items-center gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm font-medium text-foreground shadow-sm whitespace-nowrap\">\n <span>{{ columnsLabel }}</span>\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4 text-foreground\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M7 10L12 15L17 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n </div>\n\n <!-- Tabla con responsive -->\n <pdm-table \n variant=\"data\"\n [responsiveStrategy]=\"responsiveStrategy\"\n [fullBleed]=\"false\">\n <thead>\n <tr>\n <!-- Columna de selecci\u00F3n -->\n <th *ngIf=\"selectable\" class=\"w-10 px-2 py-2 text-left font-medium\">\n <input \n type=\"checkbox\" \n (change)=\"onToggleAll($event)\"\n class=\"h-4 w-4 rounded-sm border border-input\" \n />\n </th>\n\n <!-- Columnas din\u00E1micas -->\n <th \n *ngFor=\"let column of effectiveColumns\"\n [ngClass]=\"getHeaderClass(column)\"\n [ngStyle]=\"getColumnStyle(column)\">\n \n <!-- Header sortable -->\n <button \n *ngIf=\"column.sortable\"\n type=\"button\" \n (click)=\"onSort(column)\"\n class=\"inline-flex appearance-none items-center gap-1 rounded-sm border-0 bg-transparent px-3 py-2 text-sm hover:underline\">\n <span>{{ column.label }}</span>\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M8 6L4 10L8 14M16 18L20 14L16 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n\n <!-- Header no sortable -->\n <span *ngIf=\"!column.sortable\">{{ column.label }}</span>\n </th>\n\n <!-- Columna de acciones -->\n <th *ngIf=\"showActions\" class=\"w-10 px-2 py-2\"></th>\n </tr>\n </thead>\n\n <tbody>\n <!-- Filas con datos -->\n <tr *ngFor=\"let row of pagedRows\">\n <!-- Celda de selecci\u00F3n -->\n <td *ngIf=\"selectable\" class=\"px-2 py-2\">\n <input \n type=\"checkbox\" \n [checked]=\"isSelected(row)\" \n (change)=\"onToggleRow(row, $event)\" \n class=\"h-4 w-4 rounded-sm border border-input\" \n />\n </td>\n\n <!-- Celdas din\u00E1micas -->\n <td \n *ngFor=\"let column of effectiveColumns\"\n [ngClass]=\"getCellClass(column)\">\n \n <!-- Template personalizado si existe -->\n <ng-container *ngIf=\"column.cellTemplate; else defaultCell\">\n <ng-container \n *ngTemplateOutlet=\"column.cellTemplate; context: { $implicit: row, value: row[column.key] }\">\n </ng-container>\n </ng-container>\n\n <!-- Renderizado default -->\n <ng-template #defaultCell>\n {{ getCellValue(row, column) }}\n </ng-template>\n </td>\n\n <!-- Celda de acciones -->\n <td *ngIf=\"showActions\" class=\"px-2 py-2\">\n <button \n type=\"button\" \n class=\"inline-flex h-8 w-8 appearance-none items-center justify-center border-0 bg-transparent p-0 hover:text-foreground\" \n (click)=\"onAction(row)\">\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n <circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n <circle cx=\"18\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n </svg>\n </button>\n </td>\n </tr>\n\n <!-- Fila vac\u00EDa -->\n <tr *ngIf=\"pagedRows.length === 0\">\n <td \n [attr.colspan]=\"effectiveColumns.length + (selectable ? 1 : 0) + (showActions ? 1 : 0)\" \n class=\"px-3 py-6 text-center text-sm text-muted-foreground\">\n {{ emptyLabel }}\n </td>\n </tr>\n </tbody>\n </pdm-table>\n\n <!-- Footer: Info + Paginaci\u00F3n -->\n <div *ngIf=\"showPagination || selectable\" class=\"flex w-full items-center gap-2 py-4 flex-wrap sm:flex-nowrap\">\n <p *ngIf=\"selectable\" class=\"m-0 flex-1 pr-2 text-sm text-muted-foreground whitespace-nowrap\">\n {{ selectedCount }} of {{ rows.length }} {{ rowsSelectedLabel }}\n </p>\n\n <div *ngIf=\"showPagination\" class=\"flex items-center gap-2 ml-auto\">\n <span class=\"text-sm text-muted-foreground whitespace-nowrap\">\n Page {{ page }} of {{ totalPages }}\n </span>\n <button \n type=\"button\" \n class=\"h-9 appearance-none rounded-md border border-input bg-background px-4 text-sm font-medium text-foreground shadow-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed\" \n [disabled]=\"page <= 1\" \n (click)=\"previous()\">\n {{ previousLabel }}\n </button>\n <button \n type=\"button\" \n class=\"h-9 appearance-none rounded-md border border-input bg-background px-4 text-sm font-medium text-foreground shadow-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed\" \n [disabled]=\"page >= totalPages\" \n (click)=\"next()\">\n {{ nextLabel }}\n </button>\n </div>\n </div>\n</section>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: PdmTableComponent, selector: "pdm-table", inputs: ["variant", "responsiveStrategy", "className", "fullBleed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1896
2427
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDataTableComponent, decorators: [{
1897
2428
  type: Component,
1898
- args: [{ selector: 'pdm-data-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<section [ngClass]=\"['flex w-full max-w-3xl flex-col items-end', className]\">\n <div class=\"flex w-full items-center justify-between py-4\">\n <input\n type=\"text\"\n [placeholder]=\"filterPlaceholder\"\n [value]=\"query\"\n (input)=\"onQueryInput($event)\"\n class=\"h-9 flex-1 rounded-md border border-input bg-transparent px-3 py-1 text-sm text-foreground shadow-sm placeholder:text-muted-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n />\n\n <button type=\"button\" class=\"inline-flex h-9 appearance-none items-center gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm font-medium text-foreground shadow-sm\">\n <span>{{ columnsLabel }}</span>\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4 text-foreground\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M7 10L12 15L17 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"w-full overflow-hidden rounded-md border border-border bg-background\">\n <table class=\"w-full border-collapse text-sm text-foreground\">\n <thead>\n <tr class=\"border-b border-border\">\n <th class=\"w-10 px-2 text-left font-medium\"><input type=\"checkbox\" class=\"h-4 w-4 rounded-sm border border-input\" /></th>\n <th class=\"w-32 px-2 py-2 text-left font-medium\">{{ statusLabel }}</th>\n <th class=\"px-2 py-2 text-left font-medium\">\n <button type=\"button\" class=\"inline-flex appearance-none items-center gap-1 rounded-sm border-0 bg-transparent px-3 py-2 text-sm\">\n <span>{{ emailLabel }}</span>\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M8 6L4 10L8 14M16 18L20 14L16 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n </th>\n <th class=\"w-24 px-2 py-2 text-right font-medium\">{{ amountLabel }}</th>\n <th class=\"px-2 py-2\"></th>\n </tr>\n </thead>\n\n <tbody>\n <tr *ngFor=\"let row of pagedRows\" class=\"border-b border-border last:border-b-0\">\n <td class=\"px-2 py-2\"><input type=\"checkbox\" [checked]=\"row.selected\" (change)=\"onToggleRow(row, $event)\" class=\"h-4 w-4 rounded-sm border border-input\" /></td>\n <td class=\"px-2 py-2\">{{ row.status }}</td>\n <td class=\"px-2 py-2\">{{ row.email }}</td>\n <td class=\"px-2 py-2 text-right\">{{ row.amount }}</td>\n <td class=\"px-2 py-2\">\n <button type=\"button\" class=\"inline-flex h-8 w-8 appearance-none items-center justify-center border-0 bg-transparent p-0\" (click)=\"onAction(row)\">\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n <circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n <circle cx=\"18\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n </svg>\n </button>\n </td>\n </tr>\n <tr *ngIf=\"pagedRows.length === 0\">\n <td colspan=\"5\" class=\"px-3 py-6 text-center text-sm text-muted-foreground\">{{ emptyLabel }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <div class=\"flex w-full items-center gap-2 py-4\">\n <p class=\"m-0 flex-1 pr-2 text-sm text-muted-foreground\">{{ selectedCount }} of {{ rows.length }} row(s) selected.</p>\n <button type=\"button\" class=\"h-9 appearance-none rounded-md border border-input bg-background px-4 text-sm font-medium text-foreground shadow-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50\" [disabled]=\"page <= 1\" (click)=\"previous()\">{{ previousLabel }}</button>\n <button type=\"button\" class=\"h-9 appearance-none rounded-md border border-input bg-background px-4 text-sm font-medium text-foreground shadow-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50\" [disabled]=\"page >= totalPages\" (click)=\"next()\">{{ nextLabel }}</button>\n </div>\n</section>\n" }]
2429
+ args: [{ selector: 'pdm-data-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<section [ngClass]=\"['flex w-full flex-col', className]\">\n <!-- Toolbar: Filtro + Selector de columnas -->\n <div *ngIf=\"showFilter || showColumnSelector\" class=\"flex w-full items-center justify-between gap-2 py-4\">\n <input\n *ngIf=\"showFilter\"\n type=\"text\"\n [placeholder]=\"filterPlaceholder\"\n [value]=\"query\"\n (input)=\"onQueryInput($event)\"\n class=\"h-9 flex-1 rounded-md border border-input bg-transparent px-3 py-1 text-sm text-foreground shadow-sm placeholder:text-muted-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n />\n\n <button \n *ngIf=\"showColumnSelector\"\n type=\"button\" \n class=\"inline-flex h-9 appearance-none items-center gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm font-medium text-foreground shadow-sm whitespace-nowrap\">\n <span>{{ columnsLabel }}</span>\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4 text-foreground\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M7 10L12 15L17 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n </div>\n\n <!-- Tabla con responsive -->\n <pdm-table \n variant=\"data\"\n [responsiveStrategy]=\"responsiveStrategy\"\n [fullBleed]=\"false\">\n <thead>\n <tr>\n <!-- Columna de selecci\u00F3n -->\n <th *ngIf=\"selectable\" class=\"w-10 px-2 py-2 text-left font-medium\">\n <input \n type=\"checkbox\" \n (change)=\"onToggleAll($event)\"\n class=\"h-4 w-4 rounded-sm border border-input\" \n />\n </th>\n\n <!-- Columnas din\u00E1micas -->\n <th \n *ngFor=\"let column of effectiveColumns\"\n [ngClass]=\"getHeaderClass(column)\"\n [ngStyle]=\"getColumnStyle(column)\">\n \n <!-- Header sortable -->\n <button \n *ngIf=\"column.sortable\"\n type=\"button\" \n (click)=\"onSort(column)\"\n class=\"inline-flex appearance-none items-center gap-1 rounded-sm border-0 bg-transparent px-3 py-2 text-sm hover:underline\">\n <span>{{ column.label }}</span>\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M8 6L4 10L8 14M16 18L20 14L16 10\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path>\n </svg>\n </button>\n\n <!-- Header no sortable -->\n <span *ngIf=\"!column.sortable\">{{ column.label }}</span>\n </th>\n\n <!-- Columna de acciones -->\n <th *ngIf=\"showActions\" class=\"w-10 px-2 py-2\"></th>\n </tr>\n </thead>\n\n <tbody>\n <!-- Filas con datos -->\n <tr *ngFor=\"let row of pagedRows\">\n <!-- Celda de selecci\u00F3n -->\n <td *ngIf=\"selectable\" class=\"px-2 py-2\">\n <input \n type=\"checkbox\" \n [checked]=\"isSelected(row)\" \n (change)=\"onToggleRow(row, $event)\" \n class=\"h-4 w-4 rounded-sm border border-input\" \n />\n </td>\n\n <!-- Celdas din\u00E1micas -->\n <td \n *ngFor=\"let column of effectiveColumns\"\n [ngClass]=\"getCellClass(column)\">\n \n <!-- Template personalizado si existe -->\n <ng-container *ngIf=\"column.cellTemplate; else defaultCell\">\n <ng-container \n *ngTemplateOutlet=\"column.cellTemplate; context: { $implicit: row, value: row[column.key] }\">\n </ng-container>\n </ng-container>\n\n <!-- Renderizado default -->\n <ng-template #defaultCell>\n {{ getCellValue(row, column) }}\n </ng-template>\n </td>\n\n <!-- Celda de acciones -->\n <td *ngIf=\"showActions\" class=\"px-2 py-2\">\n <button \n type=\"button\" \n class=\"inline-flex h-8 w-8 appearance-none items-center justify-center border-0 bg-transparent p-0 hover:text-foreground\" \n (click)=\"onAction(row)\">\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"6\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n <circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n <circle cx=\"18\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"></circle>\n </svg>\n </button>\n </td>\n </tr>\n\n <!-- Fila vac\u00EDa -->\n <tr *ngIf=\"pagedRows.length === 0\">\n <td \n [attr.colspan]=\"effectiveColumns.length + (selectable ? 1 : 0) + (showActions ? 1 : 0)\" \n class=\"px-3 py-6 text-center text-sm text-muted-foreground\">\n {{ emptyLabel }}\n </td>\n </tr>\n </tbody>\n </pdm-table>\n\n <!-- Footer: Info + Paginaci\u00F3n -->\n <div *ngIf=\"showPagination || selectable\" class=\"flex w-full items-center gap-2 py-4 flex-wrap sm:flex-nowrap\">\n <p *ngIf=\"selectable\" class=\"m-0 flex-1 pr-2 text-sm text-muted-foreground whitespace-nowrap\">\n {{ selectedCount }} of {{ rows.length }} {{ rowsSelectedLabel }}\n </p>\n\n <div *ngIf=\"showPagination\" class=\"flex items-center gap-2 ml-auto\">\n <span class=\"text-sm text-muted-foreground whitespace-nowrap\">\n Page {{ page }} of {{ totalPages }}\n </span>\n <button \n type=\"button\" \n class=\"h-9 appearance-none rounded-md border border-input bg-background px-4 text-sm font-medium text-foreground shadow-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed\" \n [disabled]=\"page <= 1\" \n (click)=\"previous()\">\n {{ previousLabel }}\n </button>\n <button \n type=\"button\" \n class=\"h-9 appearance-none rounded-md border border-input bg-background px-4 text-sm font-medium text-foreground shadow-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed\" \n [disabled]=\"page >= totalPages\" \n (click)=\"next()\">\n {{ nextLabel }}\n </button>\n </div>\n </div>\n</section>\n" }]
1899
2430
  }], propDecorators: { className: [{
1900
2431
  type: Input
1901
- }], filterPlaceholder: [{
2432
+ }], columns: [{
1902
2433
  type: Input
1903
- }], columnsLabel: [{
2434
+ }], responsiveStrategy: [{
1904
2435
  type: Input
1905
- }], statusLabel: [{
2436
+ }], selectable: [{
1906
2437
  type: Input
1907
- }], emailLabel: [{
2438
+ }], showActions: [{
1908
2439
  type: Input
1909
- }], amountLabel: [{
2440
+ }], showFilter: [{
2441
+ type: Input
2442
+ }], showPagination: [{
2443
+ type: Input
2444
+ }], showColumnSelector: [{
2445
+ type: Input
2446
+ }], filterPlaceholder: [{
2447
+ type: Input
2448
+ }], columnsLabel: [{
1910
2449
  type: Input
1911
2450
  }], previousLabel: [{
1912
2451
  type: Input
@@ -1914,6 +2453,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
1914
2453
  type: Input
1915
2454
  }], emptyLabel: [{
1916
2455
  type: Input
2456
+ }], rowsSelectedLabel: [{
2457
+ type: Input
2458
+ }], statusLabel: [{
2459
+ type: Input
2460
+ }], emailLabel: [{
2461
+ type: Input
2462
+ }], amountLabel: [{
2463
+ type: Input
1917
2464
  }], rows: [{
1918
2465
  type: Input
1919
2466
  }], page: [{
@@ -1922,6 +2469,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
1922
2469
  type: Input
1923
2470
  }], query: [{
1924
2471
  type: Input
2472
+ }], filterFn: [{
2473
+ type: Input
1925
2474
  }], queryChange: [{
1926
2475
  type: Output
1927
2476
  }], rowAction: [{
@@ -1930,6 +2479,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
1930
2479
  type: Output
1931
2480
  }], selectionChange: [{
1932
2481
  type: Output
2482
+ }], columnSort: [{
2483
+ type: Output
1933
2484
  }] } });
1934
2485
 
1935
2486
  /**
@@ -2347,13 +2898,135 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
2347
2898
  args: ['document:keydown.escape']
2348
2899
  }] } });
2349
2900
 
2350
- class PdmDialogComponent {
2351
- constructor() {
2352
- this.open = false;
2353
- this.variant = 'default';
2354
- this.size = 'desktop';
2355
- this.title = 'Edit profile';
2356
- this.description = '';
2901
+ /**
2902
+ * Z-Index Scale - Sistema centralizado de z-index
2903
+ *
2904
+ * JERARQUÍA (de menor a mayor):
2905
+ * 1. base (z-0) - Elementos normales del DOM
2906
+ * 2. dropdown (z-10) - Selects, combobox, date-pickers
2907
+ * 3. sticky (z-20) - Headers, navigation bars
2908
+ * 4. overlay (z-30) - Popovers, hover cards, context menus
2909
+ * 5. drawer (z-40) - Sidebar drawer, sheets laterales
2910
+ * 6. modal (z-50) - Dialogs, alert-dialogs
2911
+ * 7. modal-backdrop (z-40) - Backdrop de modals
2912
+ * 8. popover (z-60) - Tooltips, dropdowns DENTRO de modals
2913
+ * 9. toast (z-[100]) - Notificaciones que deben estar sobre TODO
2914
+ *
2915
+ * REGLA CRÍTICA:
2916
+ * - Componentes overlay (select options, dropdown menu, tooltip) SIEMPRE z-60 o mayor
2917
+ * - Esto permite que funcionen DENTRO de modals (z-50)
2918
+ * - Backdrop de modal debe ser z-40 para estar DEBAJO del contenido del modal (z-50)
2919
+ */
2920
+ const Z_INDEX = {
2921
+ /**
2922
+ * Base - contenido normal del DOM
2923
+ */
2924
+ base: 'z-0',
2925
+ /**
2926
+ * Dropdown - Selects, combobox, date-pickers
2927
+ * Debe estar SOBRE contenido normal pero BAJO modals
2928
+ */
2929
+ dropdown: 'z-10',
2930
+ /**
2931
+ * Sticky - Headers, navigation fija
2932
+ */
2933
+ sticky: 'z-20',
2934
+ /**
2935
+ * Overlay - Popovers, hover cards, context menus
2936
+ * Debe estar SOBRE sticky pero BAJO modals
2937
+ */
2938
+ overlay: 'z-30',
2939
+ /**
2940
+ * Drawer backdrop - Backdrop de sidebar drawer
2941
+ * Debe estar DEBAJO del drawer panel
2942
+ */
2943
+ drawerBackdrop: 'z-40',
2944
+ /**
2945
+ * Drawer - Sidebar drawer, sheets laterales
2946
+ * Debe estar SOBRE su backdrop pero BAJO modals
2947
+ */
2948
+ drawer: 'z-50',
2949
+ /**
2950
+ * Modal backdrop - Backdrop de dialogs
2951
+ * Debe estar SOBRE drawers pero DEBAJO del contenido del modal
2952
+ */
2953
+ modalBackdrop: 'z-50',
2954
+ /**
2955
+ * Modal - Dialogs, alert-dialogs, sheets
2956
+ * Debe estar SOBRE su backdrop
2957
+ */
2958
+ modal: 'z-[60]',
2959
+ /**
2960
+ * Popover - Tooltips, dropdowns, selects options DENTRO de modals
2961
+ * CRÍTICO: Debe ser MAYOR que modal (z-50) para aparecer sobre modals
2962
+ */
2963
+ popover: 'z-[70]',
2964
+ /**
2965
+ * Toast - Notificaciones globales
2966
+ * Debe estar sobre TODO
2967
+ */
2968
+ toast: 'z-[100]'
2969
+ };
2970
+ /**
2971
+ * Helper para debugging z-index issues
2972
+ */
2973
+ function logZIndexStack(element) {
2974
+ if (typeof window === 'undefined')
2975
+ return;
2976
+ let current = element;
2977
+ const stack = [];
2978
+ while (current && current !== document.body) {
2979
+ const computed = window.getComputedStyle(current);
2980
+ const zIndex = computed.zIndex;
2981
+ const position = computed.position;
2982
+ if (zIndex !== 'auto') {
2983
+ stack.push({
2984
+ element: current.tagName + (current.className ? `.${current.className.split(' ')[0]}` : ''),
2985
+ zIndex,
2986
+ position
2987
+ });
2988
+ }
2989
+ current = current.parentElement;
2990
+ }
2991
+ console.table(stack);
2992
+ }
2993
+
2994
+ /**
2995
+ * Modal/Dialog component con soporte responsive
2996
+ *
2997
+ * MEJORADO en v0.2.0:
2998
+ * - Modo 'responsive' (default): fullscreen en mobile, modal en desktop
2999
+ * - Tamaños predefinidos: sm, md, lg, xl
3000
+ * - Mejor manejo de scroll en mobile
3001
+ *
3002
+ * @example
3003
+ * // Responsive (recomendado)
3004
+ * <pdm-dialog [open]="isOpen" size="responsive">
3005
+ * <p>Content</p>
3006
+ * </pdm-dialog>
3007
+ *
3008
+ * @example
3009
+ * // Tamaño fijo
3010
+ * <pdm-dialog [open]="isOpen" size="lg">
3011
+ * <p>Content</p>
3012
+ * </pdm-dialog>
3013
+ */
3014
+ class PdmDialogComponent {
3015
+ constructor() {
3016
+ this.open = false;
3017
+ this.variant = 'default';
3018
+ /**
3019
+ * Tamaño del dialog
3020
+ * - responsive: fullscreen mobile, modal desktop (recomendado)
3021
+ * - sm: 400px max
3022
+ * - md: 500px max
3023
+ * - lg: 640px max (default)
3024
+ * - xl: 800px max
3025
+ * - desktop/mobile/mobile-fullscreen: legacy, deprecado
3026
+ */
3027
+ this.size = 'responsive';
3028
+ this.title = 'Edit profile';
3029
+ this.description = '';
2357
3030
  this.closeOnBackdrop = true;
2358
3031
  this.closeOnEsc = true;
2359
3032
  this.showCloseButton = true;
@@ -2390,47 +3063,132 @@ class PdmDialogComponent {
2390
3063
  }
2391
3064
  }
2392
3065
  get panelClassName() {
3066
+ // Legacy sizes (backward compatibility)
3067
+ if (this.size === 'desktop') {
3068
+ return this.buildClasses(['max-w-[640px]', 'max-h-[calc(100vh-2rem)]', 'rounded-[10px]']);
3069
+ }
3070
+ if (this.size === 'mobile') {
3071
+ return this.buildClasses(['max-w-[320px]', 'min-h-[240px]', 'rounded-[10px]']);
3072
+ }
3073
+ if (this.size === 'mobile-fullscreen') {
3074
+ return this.buildClasses(['max-w-[320px]', 'h-[min(100dvh,640px)]', 'rounded-none', 'sm:rounded-[10px]']);
3075
+ }
3076
+ // New responsive mode (recomendado)
3077
+ if (this.size === 'responsive') {
3078
+ return this.buildClasses([
3079
+ // Mobile: fullscreen con bordes redondeados solo arriba
3080
+ 'w-full',
3081
+ 'h-full',
3082
+ 'max-h-[100dvh]',
3083
+ 'rounded-t-[10px]',
3084
+ 'sm:rounded-[10px]',
3085
+ // Desktop: modal centrado
3086
+ 'sm:w-auto',
3087
+ 'sm:h-auto',
3088
+ 'sm:max-w-[640px]',
3089
+ 'sm:max-h-[calc(100vh-4rem)]'
3090
+ ]);
3091
+ }
3092
+ // New size options
3093
+ const sizeMap = {
3094
+ sm: 'sm:max-w-[400px]',
3095
+ md: 'sm:max-w-[500px]',
3096
+ lg: 'sm:max-w-[640px]',
3097
+ xl: 'sm:max-w-[800px]'
3098
+ };
3099
+ const maxWidth = sizeMap[this.size] || sizeMap.lg;
3100
+ return this.buildClasses([
3101
+ // Mobile: fullscreen
3102
+ 'w-full',
3103
+ 'h-full',
3104
+ 'max-h-[100dvh]',
3105
+ 'rounded-t-[10px]',
3106
+ // Desktop: modal
3107
+ 'sm:rounded-[10px]',
3108
+ 'sm:w-auto',
3109
+ 'sm:h-auto',
3110
+ maxWidth,
3111
+ 'sm:max-h-[calc(100vh-4rem)]'
3112
+ ]);
3113
+ }
3114
+ buildClasses(sizeClasses) {
2393
3115
  const base = [
2394
- 'relative z-10 w-full border border-border bg-background text-foreground shadow-lg',
2395
- this.size === 'desktop' ? 'max-w-[640px] max-h-[calc(100vh-2rem)] rounded-[10px] overflow-visible' : '',
2396
- this.size === 'mobile' ? 'max-w-[320px] min-h-[240px] rounded-[10px] overflow-visible' : '',
2397
- this.size === 'mobile-fullscreen'
2398
- ? 'max-w-[320px] h-[min(100dvh,640px)] rounded-none sm:rounded-[10px] overflow-visible'
2399
- : '',
3116
+ 'relative',
3117
+ Z_INDEX.modal,
3118
+ 'flex',
3119
+ 'flex-col',
3120
+ 'border',
3121
+ 'border-border',
3122
+ 'bg-background',
3123
+ 'text-foreground',
3124
+ 'shadow-lg',
3125
+ 'overflow-hidden',
3126
+ ...sizeClasses,
2400
3127
  this.className
2401
3128
  ];
2402
3129
  return base.filter(Boolean).join(' ');
2403
3130
  }
2404
3131
  get bodyWrapperClassName() {
2405
3132
  const base = [
2406
- 'min-h-0 flex-1',
2407
- this.size === 'mobile-fullscreen' ? 'overflow-y-auto px-4 py-6' : 'px-6 py-6',
3133
+ 'flex-1',
3134
+ 'overflow-y-auto',
3135
+ 'px-4',
3136
+ 'py-6',
3137
+ 'sm:px-6',
2408
3138
  this.bodyClassName
2409
3139
  ];
2410
3140
  return base.filter(Boolean).join(' ');
2411
3141
  }
2412
3142
  get headerWrapperClassName() {
2413
- return ['flex items-start justify-between gap-3 p-4', this.headerClassName].filter(Boolean).join(' ');
3143
+ const base = [
3144
+ 'flex',
3145
+ 'items-start',
3146
+ 'justify-between',
3147
+ 'gap-3',
3148
+ 'p-4',
3149
+ 'sm:p-6',
3150
+ 'border-b',
3151
+ 'border-border',
3152
+ this.headerClassName
3153
+ ];
3154
+ return base.filter(Boolean).join(' ');
2414
3155
  }
2415
3156
  get footerWrapperClassName() {
2416
3157
  const effectiveAlign = this.alignFooter === 'right' && this.variant === 'custom-close' ? 'left' : this.alignFooter;
2417
3158
  const base = [
2418
3159
  'p-4',
3160
+ 'sm:p-6',
3161
+ 'border-t',
3162
+ 'border-border',
3163
+ // Mobile: siempre full-width
3164
+ 'flex',
3165
+ 'flex-col',
3166
+ 'gap-2',
3167
+ // Desktop: según alignFooter
2419
3168
  effectiveAlign === 'full-width'
2420
- ? 'flex flex-col gap-2'
2421
- : effectiveAlign === 'left'
2422
- ? 'flex items-center gap-2 justify-start'
2423
- : 'flex items-center gap-2 justify-end',
3169
+ ? 'sm:flex-col'
3170
+ : 'sm:flex-row sm:items-center',
3171
+ effectiveAlign === 'left' ? 'sm:justify-start' : '',
3172
+ effectiveAlign === 'right' ? 'sm:justify-end' : '',
2424
3173
  this.footerClassName
2425
3174
  ];
2426
3175
  return base.filter(Boolean).join(' ');
2427
3176
  }
3177
+ get containerClassName() {
3178
+ // Container con backdrop z-50
3179
+ // Mobile: fullscreen desde el bottom
3180
+ // Desktop: centrado
3181
+ return responsive({
3182
+ default: `fixed inset-x-0 bottom-0 ${Z_INDEX.modalBackdrop} flex items-end justify-center`,
3183
+ sm: `fixed inset-0 ${Z_INDEX.modalBackdrop} flex items-center justify-center p-4`
3184
+ });
3185
+ }
2428
3186
  }
2429
3187
  PdmDialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2430
- PdmDialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmDialogComponent, selector: "pdm-dialog", inputs: { open: "open", variant: "variant", size: "size", title: "title", description: "description", closeOnBackdrop: "closeOnBackdrop", closeOnEsc: "closeOnEsc", showCloseButton: "showCloseButton", showHeader: "showHeader", showFooter: "showFooter", primaryActionText: "primaryActionText", secondaryActionText: "secondaryActionText", alignFooter: "alignFooter", headerClassName: "headerClassName", bodyClassName: "bodyClassName", footerClassName: "footerClassName", className: "className" }, outputs: { openChange: "openChange", primaryAction: "primaryAction", secondaryAction: "secondaryAction" }, host: { listeners: { "document:keydown.escape": "onEsc()" } }, ngImport: i0, template: "<div *ngIf=\"open\" class=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n <div class=\"absolute inset-0 bg-foreground/30\" (click)=\"onBackdropClick()\"></div>\n <section role=\"dialog\" aria-modal=\"true\" [ngClass]=\"panelClassName\">\n <div *ngIf=\"showHeader\" [ngClass]=\"headerWrapperClassName\">\n <div class=\"min-w-0\">\n <h2 class=\"m-0 text-lg font-semibold leading-none tracking-tight text-foreground\">{{ title }}</h2>\n <p *ngIf=\"description\" class=\"m-0 mt-2 text-sm text-muted-foreground\">{{ description }}</p>\n </div>\n <button\n *ngIf=\"showCloseButton\"\n type=\"button\"\n class=\"inline-flex h-6 w-6 appearance-none items-center justify-center rounded-sm border-0 bg-transparent p-0 text-foreground opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\"\n (click)=\"close()\"\n aria-label=\"Close dialog\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 6L18 18M18 6L6 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"></path>\n </svg>\n </button>\n </div>\n\n <div [ngClass]=\"bodyWrapperClassName\">\n <ng-content></ng-content>\n </div>\n\n <div *ngIf=\"showFooter\" [ngClass]=\"footerWrapperClassName\">\n <ng-container *ngIf=\"variant === 'custom-close'; else defaultActions\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onSecondaryAction()\"\n >\n {{ secondaryActionText }}\n </button>\n </ng-container>\n\n <ng-template #defaultActions>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onSecondaryAction()\"\n >\n {{ secondaryActionText }}\n </button>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm\"\n (click)=\"onPrimaryAction()\"\n >\n {{ primaryActionText }}\n </button>\n </ng-template>\n </div>\n </section>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3188
+ PdmDialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmDialogComponent, selector: "pdm-dialog", inputs: { open: "open", variant: "variant", size: "size", title: "title", description: "description", closeOnBackdrop: "closeOnBackdrop", closeOnEsc: "closeOnEsc", showCloseButton: "showCloseButton", showHeader: "showHeader", showFooter: "showFooter", primaryActionText: "primaryActionText", secondaryActionText: "secondaryActionText", alignFooter: "alignFooter", headerClassName: "headerClassName", bodyClassName: "bodyClassName", footerClassName: "footerClassName", className: "className" }, outputs: { openChange: "openChange", primaryAction: "primaryAction", secondaryAction: "secondaryAction" }, host: { listeners: { "document:keydown.escape": "onEsc()" } }, ngImport: i0, template: "<div *ngIf=\"open\" [ngClass]=\"containerClassName\">\n <!-- Backdrop -->\n <div class=\"absolute inset-0 bg-foreground/30 backdrop-blur-sm\" (click)=\"onBackdropClick()\"></div>\n \n <!-- Dialog Panel -->\n <section role=\"dialog\" aria-modal=\"true\" [ngClass]=\"panelClassName\">\n <!-- Header -->\n <div *ngIf=\"showHeader\" [ngClass]=\"headerWrapperClassName\">\n <div class=\"min-w-0 flex-1\">\n <h2 class=\"m-0 text-lg font-semibold leading-none tracking-tight text-foreground\">{{ title }}</h2>\n <p *ngIf=\"description\" class=\"m-0 mt-2 text-sm text-muted-foreground\">{{ description }}</p>\n </div>\n <button\n *ngIf=\"showCloseButton\"\n type=\"button\"\n class=\"inline-flex h-6 w-6 flex-shrink-0 appearance-none items-center justify-center rounded-sm border-0 bg-transparent p-0 text-foreground opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\"\n (click)=\"close()\"\n aria-label=\"Close dialog\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 6L18 18M18 6L6 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"></path>\n </svg>\n </button>\n </div>\n\n <!-- Body -->\n <div [ngClass]=\"bodyWrapperClassName\">\n <ng-content></ng-content>\n </div>\n\n <!-- Footer -->\n <div *ngIf=\"showFooter\" [ngClass]=\"footerWrapperClassName\">\n <ng-container *ngIf=\"variant === 'custom-close'; else defaultActions\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-full appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm sm:w-auto\"\n (click)=\"onSecondaryAction()\"\n >\n {{ secondaryActionText }}\n </button>\n </ng-container>\n\n <ng-template #defaultActions>\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-full appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm sm:w-auto\"\n (click)=\"onSecondaryAction()\"\n >\n {{ secondaryActionText }}\n </button>\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-full appearance-none items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm sm:w-auto\"\n (click)=\"onPrimaryAction()\"\n >\n {{ primaryActionText }}\n </button>\n </ng-template>\n </div>\n </section>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2431
3189
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDialogComponent, decorators: [{
2432
3190
  type: Component,
2433
- args: [{ selector: 'pdm-dialog', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div *ngIf=\"open\" class=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n <div class=\"absolute inset-0 bg-foreground/30\" (click)=\"onBackdropClick()\"></div>\n <section role=\"dialog\" aria-modal=\"true\" [ngClass]=\"panelClassName\">\n <div *ngIf=\"showHeader\" [ngClass]=\"headerWrapperClassName\">\n <div class=\"min-w-0\">\n <h2 class=\"m-0 text-lg font-semibold leading-none tracking-tight text-foreground\">{{ title }}</h2>\n <p *ngIf=\"description\" class=\"m-0 mt-2 text-sm text-muted-foreground\">{{ description }}</p>\n </div>\n <button\n *ngIf=\"showCloseButton\"\n type=\"button\"\n class=\"inline-flex h-6 w-6 appearance-none items-center justify-center rounded-sm border-0 bg-transparent p-0 text-foreground opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\"\n (click)=\"close()\"\n aria-label=\"Close dialog\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 6L18 18M18 6L6 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"></path>\n </svg>\n </button>\n </div>\n\n <div [ngClass]=\"bodyWrapperClassName\">\n <ng-content></ng-content>\n </div>\n\n <div *ngIf=\"showFooter\" [ngClass]=\"footerWrapperClassName\">\n <ng-container *ngIf=\"variant === 'custom-close'; else defaultActions\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onSecondaryAction()\"\n >\n {{ secondaryActionText }}\n </button>\n </ng-container>\n\n <ng-template #defaultActions>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm\"\n (click)=\"onSecondaryAction()\"\n >\n {{ secondaryActionText }}\n </button>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm\"\n (click)=\"onPrimaryAction()\"\n >\n {{ primaryActionText }}\n </button>\n </ng-template>\n </div>\n </section>\n</div>\n" }]
3191
+ args: [{ selector: 'pdm-dialog', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div *ngIf=\"open\" [ngClass]=\"containerClassName\">\n <!-- Backdrop -->\n <div class=\"absolute inset-0 bg-foreground/30 backdrop-blur-sm\" (click)=\"onBackdropClick()\"></div>\n \n <!-- Dialog Panel -->\n <section role=\"dialog\" aria-modal=\"true\" [ngClass]=\"panelClassName\">\n <!-- Header -->\n <div *ngIf=\"showHeader\" [ngClass]=\"headerWrapperClassName\">\n <div class=\"min-w-0 flex-1\">\n <h2 class=\"m-0 text-lg font-semibold leading-none tracking-tight text-foreground\">{{ title }}</h2>\n <p *ngIf=\"description\" class=\"m-0 mt-2 text-sm text-muted-foreground\">{{ description }}</p>\n </div>\n <button\n *ngIf=\"showCloseButton\"\n type=\"button\"\n class=\"inline-flex h-6 w-6 flex-shrink-0 appearance-none items-center justify-center rounded-sm border-0 bg-transparent p-0 text-foreground opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\"\n (click)=\"close()\"\n aria-label=\"Close dialog\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 6L18 18M18 6L6 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"></path>\n </svg>\n </button>\n </div>\n\n <!-- Body -->\n <div [ngClass]=\"bodyWrapperClassName\">\n <ng-content></ng-content>\n </div>\n\n <!-- Footer -->\n <div *ngIf=\"showFooter\" [ngClass]=\"footerWrapperClassName\">\n <ng-container *ngIf=\"variant === 'custom-close'; else defaultActions\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-full appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm sm:w-auto\"\n (click)=\"onSecondaryAction()\"\n >\n {{ secondaryActionText }}\n </button>\n </ng-container>\n\n <ng-template #defaultActions>\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-full appearance-none items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground shadow-sm sm:w-auto\"\n (click)=\"onSecondaryAction()\"\n >\n {{ secondaryActionText }}\n </button>\n <button\n type=\"button\"\n class=\"inline-flex h-9 w-full appearance-none items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm sm:w-auto\"\n (click)=\"onPrimaryAction()\"\n >\n {{ primaryActionText }}\n </button>\n </ng-template>\n </div>\n </section>\n</div>\n" }]
2434
3192
  }], propDecorators: { open: [{
2435
3193
  type: Input
2436
3194
  }], variant: [{
@@ -2476,6 +3234,303 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
2476
3234
  args: ['document:keydown.escape']
2477
3235
  }] } });
2478
3236
 
3237
+ /**
3238
+ * Tabla con funcionalidad de reordenamiento de filas mediante drag & drop
3239
+ *
3240
+ * Extiende pdm-table agregando la capacidad de reordenar filas.
3241
+ * Si no necesitás drag & drop, usá pdm-table directamente (más simple y liviano).
3242
+ *
3243
+ * @example
3244
+ * <pdm-draggable-table
3245
+ * variant="interactive"
3246
+ * [reorderRows]="true"
3247
+ * (rowOrderChange)="onOrderChange($event)">
3248
+ * <tbody>
3249
+ * <tr data-row-id="1"><td>Row 1</td></tr>
3250
+ * <tr data-row-id="2"><td>Row 2</td></tr>
3251
+ * </tbody>
3252
+ * </pdm-draggable-table>
3253
+ *
3254
+ * IMPORTANTE: Cada <tr> debe tener un atributo data-row-id único
3255
+ */
3256
+ class PdmDraggableTableComponent {
3257
+ constructor(renderer) {
3258
+ this.renderer = renderer;
3259
+ this.variant = 'default';
3260
+ this.responsiveStrategy = 'scroll';
3261
+ this.className = '';
3262
+ this.fullBleed = false;
3263
+ /**
3264
+ * Habilita el reordenamiento de filas mediante drag & drop
3265
+ */
3266
+ this.reorderRows = false;
3267
+ /**
3268
+ * Selector CSS para identificar los handles de drag
3269
+ * Por defecto busca: [data-drag-handle], [data-slot=row-drag-handle], .row-drag-handle
3270
+ * Si no encuentra ninguno, inserta un handle automático
3271
+ */
3272
+ this.dragHandleSelector = '[data-drag-handle],[data-slot=row-drag-handle],.row-drag-handle,[data-auto-drag-handle]';
3273
+ /**
3274
+ * Emite el nuevo orden de las filas cuando el usuario termina de arrastrar
3275
+ * Array de data-row-id en el nuevo orden
3276
+ */
3277
+ this.rowOrderChange = new EventEmitter();
3278
+ this.cleanupListeners = [];
3279
+ this.draggedRow = null;
3280
+ }
3281
+ ngAfterViewInit() {
3282
+ this.syncReorderBehavior();
3283
+ }
3284
+ // Getters para clases CSS (mismo comportamiento que pdm-table)
3285
+ get wrapperClasses() {
3286
+ const baseClasses = ['relative', 'w-full'];
3287
+ const strategyClasses = this.getResponsiveStrategyClasses();
3288
+ const variantClasses = this.getVariantWrapperClasses();
3289
+ if (this.fullBleed && this.responsiveStrategy === 'scroll') {
3290
+ baseClasses.push('-mx-4', 'px-4', 'sm:mx-0', 'sm:px-0');
3291
+ }
3292
+ return [
3293
+ ...baseClasses,
3294
+ ...strategyClasses,
3295
+ ...variantClasses,
3296
+ this.className
3297
+ ].filter(Boolean);
3298
+ }
3299
+ get tableClasses() {
3300
+ const baseClasses = ['w-full', 'caption-bottom', 'text-sm'];
3301
+ const variantClasses = this.getVariantTableClasses();
3302
+ const cellClasses = this.getCellClasses();
3303
+ return [...baseClasses, ...variantClasses, ...cellClasses].filter(Boolean);
3304
+ }
3305
+ getResponsiveStrategyClasses() {
3306
+ if (this.responsiveStrategy === 'scroll' || this.responsiveStrategy === 'wrap' || this.responsiveStrategy === 'collapse') {
3307
+ return ['overflow-x-auto'];
3308
+ }
3309
+ return [];
3310
+ }
3311
+ getVariantWrapperClasses() {
3312
+ if (this.variant === 'interactive') {
3313
+ return ['rounded-xl', 'border', 'border-border', 'bg-background'];
3314
+ }
3315
+ if (this.variant === 'data') {
3316
+ return ['rounded-md', 'border', 'border-border', 'bg-background'];
3317
+ }
3318
+ return [];
3319
+ }
3320
+ getVariantTableClasses() {
3321
+ if (this.variant === 'data') {
3322
+ return [
3323
+ 'border-collapse', 'text-foreground',
3324
+ '[&_thead_tr]:border-b', '[&_thead_tr]:border-border',
3325
+ '[&_tbody_tr]:border-b', '[&_tbody_tr]:border-border',
3326
+ '[&_tbody_tr:last-child]:border-b-0',
3327
+ '[&_th]:h-10', '[&_th]:px-2', '[&_th]:text-left', '[&_th]:align-middle', '[&_th]:font-medium',
3328
+ '[&_td]:p-2', '[&_td]:align-middle'
3329
+ ];
3330
+ }
3331
+ if (this.variant === 'interactive') {
3332
+ return [
3333
+ 'text-foreground',
3334
+ '[&_thead]:sticky', '[&_thead]:top-0', '[&_thead]:z-10', '[&_thead]:bg-muted/70',
3335
+ '[&_thead_tr]:border-b', '[&_thead_tr]:border-border',
3336
+ '[&_th]:h-12', '[&_th]:px-4', '[&_th]:text-left', '[&_th]:align-middle', '[&_th]:text-sm', '[&_th]:font-medium',
3337
+ '[&_tbody_tr]:border-b', '[&_tbody_tr]:border-border',
3338
+ '[&_tbody_tr]:transition-colors', '[&_tbody_tr:hover]:bg-muted/50',
3339
+ '[&_tbody_tr:last-child]:border-b-0',
3340
+ '[&_td]:h-14', '[&_td]:px-4', '[&_td]:align-middle', '[&_td]:text-sm',
3341
+ '[&_svg]:text-muted-foreground'
3342
+ ];
3343
+ }
3344
+ return [];
3345
+ }
3346
+ getCellClasses() {
3347
+ if (this.responsiveStrategy === 'scroll') {
3348
+ return ['[&_td]:whitespace-normal', '[&_th]:whitespace-normal', 'sm:[&_td]:whitespace-nowrap', 'sm:[&_th]:whitespace-nowrap'];
3349
+ }
3350
+ if (this.responsiveStrategy === 'wrap') {
3351
+ return ['[&_td]:whitespace-normal', '[&_td]:break-words', '[&_th]:whitespace-normal'];
3352
+ }
3353
+ return [];
3354
+ }
3355
+ ngOnChanges(changes) {
3356
+ if (changes['reorderRows'] || changes['variant']) {
3357
+ this.syncReorderBehavior();
3358
+ }
3359
+ }
3360
+ ngOnDestroy() {
3361
+ this.cleanupReorderBehavior();
3362
+ }
3363
+ syncReorderBehavior() {
3364
+ this.cleanupReorderBehavior();
3365
+ if (!this.reorderRows) {
3366
+ return;
3367
+ }
3368
+ const tbody = this.getTbody();
3369
+ if (!tbody) {
3370
+ return;
3371
+ }
3372
+ this.setRowsDraggable(tbody, true);
3373
+ this.cleanupListeners.push(this.renderer.listen(tbody, 'mousedown', (event) => this.armDragFromHandle(event)), this.renderer.listen(tbody, 'dragstart', (event) => this.onDragStart(event)), this.renderer.listen(tbody, 'dragover', (event) => this.onDragOver(event, tbody)), this.renderer.listen(tbody, 'drop', (event) => this.onDrop(event)), this.renderer.listen(tbody, 'dragend', () => this.onDragEnd()));
3374
+ // Observer para detectar cambios en el DOM (filas agregadas/removidas)
3375
+ this.observer = new MutationObserver(() => this.setRowsDraggable(tbody, true));
3376
+ this.observer.observe(tbody, { childList: true });
3377
+ }
3378
+ cleanupReorderBehavior() {
3379
+ this.cleanupListeners.forEach((dispose) => dispose());
3380
+ this.cleanupListeners = [];
3381
+ if (this.observer) {
3382
+ this.observer.disconnect();
3383
+ this.observer = undefined;
3384
+ }
3385
+ const tbody = this.getTbody();
3386
+ if (tbody) {
3387
+ this.setRowsDraggable(tbody, false);
3388
+ }
3389
+ this.draggedRow = null;
3390
+ }
3391
+ getTbody() {
3392
+ return this.tableElement?.nativeElement.tBodies.item(0) ?? null;
3393
+ }
3394
+ setRowsDraggable(tbody, enabled) {
3395
+ const rows = Array.from(tbody.rows);
3396
+ rows.forEach((row) => {
3397
+ this.syncAutoDragHandle(row, enabled);
3398
+ row.draggable = false;
3399
+ if (!enabled) {
3400
+ delete row.dataset['dragging'];
3401
+ delete row.dataset['dragArmed'];
3402
+ }
3403
+ });
3404
+ }
3405
+ /**
3406
+ * Inserta un handle de drag automático si no existe uno custom
3407
+ */
3408
+ syncAutoDragHandle(row, enabled) {
3409
+ const firstCell = row.cells.item(0);
3410
+ if (!firstCell) {
3411
+ return;
3412
+ }
3413
+ const existingAutoHandle = firstCell.querySelector('[data-auto-drag-handle]');
3414
+ if (!enabled) {
3415
+ existingAutoHandle?.remove();
3416
+ return;
3417
+ }
3418
+ const hasCustomHandle = !!firstCell.querySelector('[data-drag-handle],[data-slot=row-drag-handle],.row-drag-handle');
3419
+ if (hasCustomHandle || existingAutoHandle) {
3420
+ return;
3421
+ }
3422
+ // Crear handle automático
3423
+ const button = this.renderer.createElement('button');
3424
+ this.renderer.setAttribute(button, 'type', 'button');
3425
+ this.renderer.setAttribute(button, 'aria-label', 'Drag row');
3426
+ this.renderer.setAttribute(button, 'data-auto-drag-handle', 'true');
3427
+ this.renderer.addClass(button, 'inline-flex');
3428
+ this.renderer.addClass(button, 'h-7');
3429
+ this.renderer.addClass(button, 'w-7');
3430
+ this.renderer.addClass(button, 'items-center');
3431
+ this.renderer.addClass(button, 'justify-center');
3432
+ this.renderer.addClass(button, 'cursor-grab');
3433
+ this.renderer.addClass(button, 'active:cursor-grabbing');
3434
+ this.renderer.addClass(button, 'text-muted-foreground');
3435
+ const dots = this.renderer.createElement('span');
3436
+ this.renderer.addClass(dots, 'text-sm');
3437
+ this.renderer.addClass(dots, 'leading-none');
3438
+ this.renderer.setProperty(dots, 'textContent', '⋮⋮');
3439
+ this.renderer.appendChild(button, dots);
3440
+ this.renderer.insertBefore(firstCell, button, firstCell.firstChild);
3441
+ }
3442
+ onDragStart(event) {
3443
+ const target = event.target;
3444
+ const row = target?.closest('tr');
3445
+ if (!row) {
3446
+ return;
3447
+ }
3448
+ const handle = target?.closest(this.dragHandleSelector);
3449
+ const isArmed = row.dataset['dragArmed'] === 'true';
3450
+ if ((!handle || !row.contains(handle)) && !isArmed) {
3451
+ event.preventDefault();
3452
+ return;
3453
+ }
3454
+ this.draggedRow = row;
3455
+ this.draggedRow.dataset['dragging'] = 'true';
3456
+ if (event.dataTransfer) {
3457
+ event.dataTransfer.effectAllowed = 'move';
3458
+ event.dataTransfer.setData('text/plain', '');
3459
+ }
3460
+ }
3461
+ onDragOver(event, tbody) {
3462
+ if (!this.draggedRow) {
3463
+ return;
3464
+ }
3465
+ event.preventDefault();
3466
+ const target = event.target;
3467
+ const targetRow = target?.closest('tr');
3468
+ if (!targetRow || targetRow === this.draggedRow) {
3469
+ return;
3470
+ }
3471
+ const rect = targetRow.getBoundingClientRect();
3472
+ const shouldInsertBefore = event.clientY < rect.top + rect.height / 2;
3473
+ tbody.insertBefore(this.draggedRow, shouldInsertBefore ? targetRow : targetRow.nextSibling);
3474
+ }
3475
+ onDrop(event) {
3476
+ event.preventDefault();
3477
+ }
3478
+ onDragEnd() {
3479
+ const tbody = this.getTbody();
3480
+ if (tbody) {
3481
+ Array.from(tbody.rows).forEach((row) => {
3482
+ row.draggable = false;
3483
+ delete row.dataset['dragArmed'];
3484
+ });
3485
+ }
3486
+ if (this.draggedRow) {
3487
+ delete this.draggedRow.dataset['dragging'];
3488
+ this.draggedRow = null;
3489
+ }
3490
+ if (!tbody) {
3491
+ return;
3492
+ }
3493
+ const order = Array.from(tbody.rows).map((row, index) => row.getAttribute('data-row-id') || String(index));
3494
+ this.rowOrderChange.emit(order);
3495
+ }
3496
+ armDragFromHandle(event) {
3497
+ const target = event.target;
3498
+ const handle = target?.closest(this.dragHandleSelector);
3499
+ if (!handle) {
3500
+ return;
3501
+ }
3502
+ const row = handle.closest('tr');
3503
+ if (!row) {
3504
+ return;
3505
+ }
3506
+ row.draggable = true;
3507
+ row.dataset['dragArmed'] = 'true';
3508
+ }
3509
+ }
3510
+ PdmDraggableTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDraggableTableComponent, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
3511
+ PdmDraggableTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmDraggableTableComponent, selector: "pdm-draggable-table", inputs: { variant: "variant", responsiveStrategy: "responsiveStrategy", className: "className", fullBleed: "fullBleed", reorderRows: "reorderRows", dragHandleSelector: "dragHandleSelector" }, outputs: { rowOrderChange: "rowOrderChange" }, viewQueries: [{ propertyName: "tableElement", first: true, predicate: ["tableElement"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div [ngClass]=\"wrapperClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table-container' : null\">\n <table #tableElement [ngClass]=\"tableClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table' : null\">\n <ng-content></ng-content>\n </table>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3512
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDraggableTableComponent, decorators: [{
3513
+ type: Component,
3514
+ args: [{ selector: 'pdm-draggable-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [ngClass]=\"wrapperClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table-container' : null\">\n <table #tableElement [ngClass]=\"tableClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table' : null\">\n <ng-content></ng-content>\n </table>\n</div>\n" }]
3515
+ }], ctorParameters: function () { return [{ type: i0.Renderer2 }]; }, propDecorators: { variant: [{
3516
+ type: Input
3517
+ }], responsiveStrategy: [{
3518
+ type: Input
3519
+ }], className: [{
3520
+ type: Input
3521
+ }], fullBleed: [{
3522
+ type: Input
3523
+ }], reorderRows: [{
3524
+ type: Input
3525
+ }], dragHandleSelector: [{
3526
+ type: Input
3527
+ }], rowOrderChange: [{
3528
+ type: Output
3529
+ }], tableElement: [{
3530
+ type: ViewChild,
3531
+ args: ['tableElement']
3532
+ }] } });
3533
+
2479
3534
  class PdmDropdownMenuComponent {
2480
3535
  constructor(elementRef, cdr, overlay, viewContainerRef) {
2481
3536
  this.elementRef = elementRef;
@@ -2575,7 +3630,9 @@ class PdmDropdownMenuComponent {
2575
3630
  }
2576
3631
  }
2577
3632
  onEsc() {
2578
- this.closePanel();
3633
+ if (this.open) {
3634
+ this.closePanel();
3635
+ }
2579
3636
  }
2580
3637
  openPanel() {
2581
3638
  if (this.overlayRef)
@@ -2588,7 +3645,7 @@ class PdmDropdownMenuComponent {
2588
3645
  const positionStrategy = createFlexiblePositionStrategy(this.overlay, triggerEl, 8);
2589
3646
  // Resolve panelClass: overlayOptions.panelClass wins; otherwise map panelClassName.
2590
3647
  const resolvedPanelClass = this.overlayOptions?.panelClass
2591
- ?? (this.panelClassName ? ['block', this.panelClassName] : ['block']);
3648
+ ?? (this.panelClassName ? [Z_INDEX.popover, this.panelClassName] : [Z_INDEX.popover]);
2592
3649
  this.overlayRef = this.overlay.create({
2593
3650
  positionStrategy,
2594
3651
  scrollStrategy: this.overlay.scrollStrategies.reposition(),
@@ -2659,13 +3716,67 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
2659
3716
  args: ['document:keydown.escape']
2660
3717
  }] } });
2661
3718
 
3719
+ /**
3720
+ * Drawer/Sheet component con soporte responsive
3721
+ *
3722
+ * MEJORADO en v0.2.0:
3723
+ * - Posicionamiento configurable (bottom, left, right, top)
3724
+ * - Tamaños predefinidos
3725
+ * - Responsive: bottom sheet en mobile, side panel en desktop
3726
+ * - Contenido genérico via ng-content
3727
+ *
3728
+ * @example
3729
+ * // Drawer simple desde el bottom
3730
+ * <pdm-drawer [open]="isOpen" position="bottom">
3731
+ * <h3>Title</h3>
3732
+ * <p>Content</p>
3733
+ * </pdm-drawer>
3734
+ *
3735
+ * @example
3736
+ * // Side panel desde la right
3737
+ * <pdm-drawer [open]="isOpen" position="right" size="md">
3738
+ * <p>Content</p>
3739
+ * </pdm-drawer>
3740
+ */
2662
3741
  class PdmDrawerComponent {
2663
3742
  constructor() {
2664
3743
  this.open = false;
3744
+ /**
3745
+ * Posición del drawer
3746
+ * - bottom: desde abajo (default, mejor para mobile)
3747
+ * - left: side panel desde izquierda
3748
+ * - right: side panel desde derecha
3749
+ * - top: desde arriba (poco común)
3750
+ */
3751
+ this.position = 'bottom';
3752
+ /**
3753
+ * Tamaño del drawer
3754
+ * - sm: 400px (side) / 50vh (bottom/top)
3755
+ * - md: 500px (side) / 66vh (bottom/top) (default)
3756
+ * - lg: 640px (side) / 80vh (bottom/top)
3757
+ * - full: 100% ancho/alto
3758
+ */
3759
+ this.size = 'md';
3760
+ /**
3761
+ * @deprecated Use position="bottom" instead
3762
+ */
2665
3763
  this.variant = 'drawer';
2666
3764
  this.className = '';
2667
3765
  this.title = '';
2668
3766
  this.description = '';
3767
+ /**
3768
+ * Mostrar handle visual (línea para arrastrar)
3769
+ * Solo tiene sentido en position="bottom"
3770
+ */
3771
+ this.showHandle = true;
3772
+ /**
3773
+ * Mostrar botón de cerrar
3774
+ */
3775
+ this.showCloseButton = true;
3776
+ this.closeOnEsc = true;
3777
+ this.closeOnBackdropClick = true;
3778
+ this.openChange = new EventEmitter();
3779
+ // DEPRECATED: contenido específico que se movió a ng-content
2669
3780
  this.value = '';
2670
3781
  this.unit = '';
2671
3782
  this.decrementLabel = '-';
@@ -2679,10 +3790,19 @@ class PdmDrawerComponent {
2679
3790
  this.usernameLabel = 'Username';
2680
3791
  this.usernameValue = '';
2681
3792
  this.responsivePrimaryLabel = '';
2682
- this.openChange = new EventEmitter();
3793
+ this.bars = [];
2683
3794
  this.primaryAction = new EventEmitter();
2684
3795
  this.secondaryAction = new EventEmitter();
2685
- this.bars = [];
3796
+ }
3797
+ onEsc() {
3798
+ if (this.open && this.closeOnEsc) {
3799
+ this.close();
3800
+ }
3801
+ }
3802
+ onBackdropClick() {
3803
+ if (this.closeOnBackdropClick) {
3804
+ this.close();
3805
+ }
2686
3806
  }
2687
3807
  close() {
2688
3808
  this.openChange.emit(false);
@@ -2693,14 +3813,67 @@ class PdmDrawerComponent {
2693
3813
  onSecondaryAction() {
2694
3814
  this.secondaryAction.emit();
2695
3815
  }
3816
+ get containerClassName() {
3817
+ return `fixed inset-0 ${Z_INDEX.drawer} ${this.className}`;
3818
+ }
3819
+ get panelClassName() {
3820
+ const base = [
3821
+ 'absolute',
3822
+ 'bg-background',
3823
+ 'border',
3824
+ 'border-border',
3825
+ 'shadow-lg',
3826
+ 'overflow-auto'
3827
+ ];
3828
+ // Posicionamiento
3829
+ const positionClasses = this.getPositionClasses();
3830
+ // Tamaño
3831
+ const sizeClasses = this.getSizeClasses();
3832
+ return [...base, ...positionClasses, ...sizeClasses].filter(Boolean).join(' ');
3833
+ }
3834
+ getPositionClasses() {
3835
+ const map = {
3836
+ bottom: ['inset-x-0', 'bottom-0', 'rounded-t-xl'],
3837
+ top: ['inset-x-0', 'top-0', 'rounded-b-xl'],
3838
+ left: ['inset-y-0', 'left-0', 'rounded-r-xl'],
3839
+ right: ['inset-y-0', 'right-0', 'rounded-l-xl']
3840
+ };
3841
+ return map[this.position] || map.bottom;
3842
+ }
3843
+ getSizeClasses() {
3844
+ const isVertical = this.position === 'bottom' || this.position === 'top';
3845
+ if (this.size === 'full') {
3846
+ return ['w-full', 'h-full'];
3847
+ }
3848
+ const sizeMap = {
3849
+ sm: isVertical ? 'max-h-[50vh]' : 'max-w-[400px]',
3850
+ md: isVertical ? 'max-h-[66vh]' : 'max-w-[500px]',
3851
+ lg: isVertical ? 'max-h-[80vh]' : 'max-w-[640px]'
3852
+ };
3853
+ const maxDimension = sizeMap[this.size] || sizeMap.md;
3854
+ if (isVertical) {
3855
+ return ['w-full', maxDimension];
3856
+ }
3857
+ else {
3858
+ return ['h-full', maxDimension];
3859
+ }
3860
+ }
3861
+ get showLegacyContent() {
3862
+ // Mostrar contenido legacy si variant está siendo usado
3863
+ return this.variant === 'drawer' || this.variant === 'responsive-dialog';
3864
+ }
2696
3865
  }
2697
3866
  PdmDrawerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2698
- PdmDrawerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmDrawerComponent, selector: "pdm-drawer", inputs: { open: "open", variant: "variant", className: "className", title: "title", description: "description", value: "value", unit: "unit", decrementLabel: "decrementLabel", incrementLabel: "incrementLabel", primaryLabel: "primaryLabel", secondaryLabel: "secondaryLabel", profileTitle: "profileTitle", profileDescription: "profileDescription", nameLabel: "nameLabel", nameValue: "nameValue", usernameLabel: "usernameLabel", usernameValue: "usernameValue", responsivePrimaryLabel: "responsivePrimaryLabel", bars: "bars" }, outputs: { openChange: "openChange", primaryAction: "primaryAction", secondaryAction: "secondaryAction" }, ngImport: i0, template: "<div *ngIf=\"open\" class=\"fixed inset-0 z-50\" [ngClass]=\"className\">\n <div class=\"absolute inset-0 bg-foreground/30\" (click)=\"close()\"></div>\n\n <section\n *ngIf=\"variant === 'drawer'; else responsiveDialog\"\n class=\"absolute inset-x-0 bottom-0 mx-auto w-full max-w-6xl rounded-t-lg border border-border bg-background p-6 shadow-lg\"\n >\n <div class=\"mx-auto mb-4 h-1 w-10 rounded-full bg-border\"></div>\n\n <div class=\"mx-auto flex max-w-sm flex-col items-center\">\n <h3 class=\"m-0 text-sm font-semibold text-foreground\">{{ title }}</h3>\n <p class=\"m-0 mt-1 text-xs text-muted-foreground\">{{ description }}</p>\n\n <div class=\"mt-3 flex w-full items-center justify-center gap-4\">\n <button type=\"button\" class=\"inline-flex h-6 w-6 appearance-none items-center justify-center rounded-full border border-border bg-transparent p-0 text-muted-foreground\">{{ decrementLabel }}</button>\n <div class=\"text-center\">\n <div class=\"text-5xl font-semibold leading-none text-foreground\">{{ value }}</div>\n <div class=\"mt-1 text-xs tracking-wide text-muted-foreground\">{{ unit }}</div>\n </div>\n <button type=\"button\" class=\"inline-flex h-6 w-6 appearance-none items-center justify-center rounded-full border border-border bg-transparent p-0 text-muted-foreground\">{{ incrementLabel }}</button>\n </div>\n\n <div *ngIf=\"bars.length\" class=\"mt-3 flex h-14 w-full items-end gap-1\">\n <div *ngFor=\"let bar of bars\" class=\"flex-1 bg-foreground\" [style.height.px]=\"bar\"></div>\n </div>\n\n <button *ngIf=\"primaryLabel\" type=\"button\" class=\"mt-3 h-9 w-full appearance-none rounded-md bg-primary text-sm font-medium text-primary-foreground\" (click)=\"onPrimaryAction()\">{{ primaryLabel }}</button>\n <button *ngIf=\"secondaryLabel\" type=\"button\" class=\"mt-2 h-9 w-full appearance-none rounded-md border border-input bg-background text-sm font-medium text-foreground\" (click)=\"onSecondaryAction()\">{{ secondaryLabel }}</button>\n </div>\n </section>\n\n <ng-template #responsiveDialog>\n <section class=\"absolute left-1/2 top-1/2 w-full max-w-lg -translate-x-1/2 -translate-y-1/2 rounded-lg border border-border bg-background p-6 shadow-lg\">\n <div class=\"flex items-start justify-between\">\n <div>\n <h3 class=\"m-0 text-lg font-semibold leading-none tracking-tight text-foreground\">{{ profileTitle }}</h3>\n <p class=\"m-0 mt-1 text-sm text-muted-foreground\">{{ profileDescription }}</p>\n </div>\n <button type=\"button\" class=\"h-5 w-5 appearance-none border-0 bg-transparent p-0 text-muted-foreground\" (click)=\"close()\">\u00D7</button>\n </div>\n\n <div class=\"mt-3 flex flex-col gap-3\">\n <div>\n <label class=\"mb-1 block text-xs font-medium text-foreground\">{{ nameLabel }}</label>\n <div class=\"h-8 rounded-md border border-border bg-background px-2 py-1 text-xs text-foreground\">{{ nameValue }}</div>\n </div>\n <div>\n <label class=\"mb-1 block text-xs font-medium text-foreground\">{{ usernameLabel }}</label>\n <div class=\"h-8 rounded-md border border-border bg-background px-2 py-1 text-xs text-foreground\">{{ usernameValue }}</div>\n </div>\n </div>\n\n <button *ngIf=\"responsivePrimaryLabel\" type=\"button\" class=\"mt-3 h-8 w-full appearance-none rounded-md bg-primary text-xs font-medium text-primary-foreground\" (click)=\"onPrimaryAction()\">{{ responsivePrimaryLabel }}</button>\n </section>\n </ng-template>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3867
+ PdmDrawerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmDrawerComponent, selector: "pdm-drawer", inputs: { open: "open", position: "position", size: "size", variant: "variant", className: "className", title: "title", description: "description", showHandle: "showHandle", showCloseButton: "showCloseButton", closeOnEsc: "closeOnEsc", closeOnBackdropClick: "closeOnBackdropClick", value: "value", unit: "unit", decrementLabel: "decrementLabel", incrementLabel: "incrementLabel", primaryLabel: "primaryLabel", secondaryLabel: "secondaryLabel", profileTitle: "profileTitle", profileDescription: "profileDescription", nameLabel: "nameLabel", nameValue: "nameValue", usernameLabel: "usernameLabel", usernameValue: "usernameValue", responsivePrimaryLabel: "responsivePrimaryLabel", bars: "bars" }, outputs: { openChange: "openChange", primaryAction: "primaryAction", secondaryAction: "secondaryAction" }, host: { listeners: { "document:keydown.escape": "onEsc()" } }, ngImport: i0, template: "<div *ngIf=\"open\" [ngClass]=\"containerClassName\">\n <!-- Backdrop -->\n <div class=\"absolute inset-0 bg-foreground/30 backdrop-blur-sm\" (click)=\"onBackdropClick()\"></div>\n\n <!-- Panel -->\n <section [ngClass]=\"panelClassName\">\n <!-- Handle visual (solo para bottom) -->\n <div \n *ngIf=\"showHandle && position === 'bottom'\" \n class=\"mx-auto mb-4 mt-2 h-1 w-10 rounded-full bg-border\">\n </div>\n\n <!-- Header (opcional) -->\n <div *ngIf=\"title || description || showCloseButton\" class=\"flex items-start justify-between gap-4 p-6 pb-4\">\n <div *ngIf=\"title || description\" class=\"flex-1 min-w-0\">\n <h3 *ngIf=\"title\" class=\"m-0 text-lg font-semibold leading-none tracking-tight text-foreground\">\n {{ title }}\n </h3>\n <p *ngIf=\"description\" class=\"m-0 mt-1 text-sm text-muted-foreground\">\n {{ description }}\n </p>\n </div>\n \n <button\n *ngIf=\"showCloseButton\"\n type=\"button\"\n class=\"inline-flex h-6 w-6 flex-shrink-0 appearance-none items-center justify-center rounded-sm border-0 bg-transparent p-0 text-foreground opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\"\n (click)=\"close()\"\n aria-label=\"Close drawer\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 6L18 18M18 6L6 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"></path>\n </svg>\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"px-6 pb-6\">\n <!-- Contenido gen\u00E9rico -->\n <ng-container *ngIf=\"!showLegacyContent\">\n <ng-content></ng-content>\n </ng-container>\n\n <!-- LEGACY: contenido espec\u00EDfico hardcodeado (backward compatibility) -->\n <ng-container *ngIf=\"showLegacyContent\">\n <!-- Variant: drawer -->\n <div *ngIf=\"variant === 'drawer'\" class=\"mx-auto flex max-w-sm flex-col items-center\">\n <div *ngIf=\"value !== ''\" class=\"mt-3 flex w-full items-center justify-center gap-4\">\n <button type=\"button\" class=\"inline-flex h-6 w-6 appearance-none items-center justify-center rounded-full border border-border bg-transparent p-0 text-muted-foreground\">{{ decrementLabel }}</button>\n <div class=\"text-center\">\n <div class=\"text-5xl font-semibold leading-none text-foreground\">{{ value }}</div>\n <div *ngIf=\"unit\" class=\"mt-1 text-xs tracking-wide text-muted-foreground\">{{ unit }}</div>\n </div>\n <button type=\"button\" class=\"inline-flex h-6 w-6 appearance-none items-center justify-center rounded-full border border-border bg-transparent p-0 text-muted-foreground\">{{ incrementLabel }}</button>\n </div>\n\n <div *ngIf=\"bars.length\" class=\"mt-3 flex h-14 w-full items-end gap-1\">\n <div *ngFor=\"let bar of bars\" class=\"flex-1 bg-foreground\" [style.height.px]=\"bar\"></div>\n </div>\n\n <button *ngIf=\"primaryLabel\" type=\"button\" class=\"mt-3 h-9 w-full appearance-none rounded-md bg-primary text-sm font-medium text-primary-foreground\" (click)=\"onPrimaryAction()\">{{ primaryLabel }}</button>\n <button *ngIf=\"secondaryLabel\" type=\"button\" class=\"mt-2 h-9 w-full appearance-none rounded-md border border-input bg-background text-sm font-medium text-foreground\" (click)=\"onSecondaryAction()\">{{ secondaryLabel }}</button>\n </div>\n\n <!-- Variant: responsive-dialog -->\n <div *ngIf=\"variant === 'responsive-dialog'\" class=\"flex flex-col gap-3\">\n <div *ngIf=\"nameLabel && nameValue\">\n <label class=\"mb-1 block text-xs font-medium text-foreground\">{{ nameLabel }}</label>\n <div class=\"h-8 rounded-md border border-border bg-background px-2 py-1 text-xs text-foreground\">{{ nameValue }}</div>\n </div>\n <div *ngIf=\"usernameLabel && usernameValue\">\n <label class=\"mb-1 block text-xs font-medium text-foreground\">{{ usernameLabel }}</label>\n <div class=\"h-8 rounded-md border border-border bg-background px-2 py-1 text-xs text-foreground\">{{ usernameValue }}</div>\n </div>\n\n <button *ngIf=\"responsivePrimaryLabel\" type=\"button\" class=\"mt-3 h-8 w-full appearance-none rounded-md bg-primary text-xs font-medium text-primary-foreground\" (click)=\"onPrimaryAction()\">{{ responsivePrimaryLabel }}</button>\n </div>\n </ng-container>\n </div>\n </section>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2699
3868
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDrawerComponent, decorators: [{
2700
3869
  type: Component,
2701
- args: [{ selector: 'pdm-drawer', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div *ngIf=\"open\" class=\"fixed inset-0 z-50\" [ngClass]=\"className\">\n <div class=\"absolute inset-0 bg-foreground/30\" (click)=\"close()\"></div>\n\n <section\n *ngIf=\"variant === 'drawer'; else responsiveDialog\"\n class=\"absolute inset-x-0 bottom-0 mx-auto w-full max-w-6xl rounded-t-lg border border-border bg-background p-6 shadow-lg\"\n >\n <div class=\"mx-auto mb-4 h-1 w-10 rounded-full bg-border\"></div>\n\n <div class=\"mx-auto flex max-w-sm flex-col items-center\">\n <h3 class=\"m-0 text-sm font-semibold text-foreground\">{{ title }}</h3>\n <p class=\"m-0 mt-1 text-xs text-muted-foreground\">{{ description }}</p>\n\n <div class=\"mt-3 flex w-full items-center justify-center gap-4\">\n <button type=\"button\" class=\"inline-flex h-6 w-6 appearance-none items-center justify-center rounded-full border border-border bg-transparent p-0 text-muted-foreground\">{{ decrementLabel }}</button>\n <div class=\"text-center\">\n <div class=\"text-5xl font-semibold leading-none text-foreground\">{{ value }}</div>\n <div class=\"mt-1 text-xs tracking-wide text-muted-foreground\">{{ unit }}</div>\n </div>\n <button type=\"button\" class=\"inline-flex h-6 w-6 appearance-none items-center justify-center rounded-full border border-border bg-transparent p-0 text-muted-foreground\">{{ incrementLabel }}</button>\n </div>\n\n <div *ngIf=\"bars.length\" class=\"mt-3 flex h-14 w-full items-end gap-1\">\n <div *ngFor=\"let bar of bars\" class=\"flex-1 bg-foreground\" [style.height.px]=\"bar\"></div>\n </div>\n\n <button *ngIf=\"primaryLabel\" type=\"button\" class=\"mt-3 h-9 w-full appearance-none rounded-md bg-primary text-sm font-medium text-primary-foreground\" (click)=\"onPrimaryAction()\">{{ primaryLabel }}</button>\n <button *ngIf=\"secondaryLabel\" type=\"button\" class=\"mt-2 h-9 w-full appearance-none rounded-md border border-input bg-background text-sm font-medium text-foreground\" (click)=\"onSecondaryAction()\">{{ secondaryLabel }}</button>\n </div>\n </section>\n\n <ng-template #responsiveDialog>\n <section class=\"absolute left-1/2 top-1/2 w-full max-w-lg -translate-x-1/2 -translate-y-1/2 rounded-lg border border-border bg-background p-6 shadow-lg\">\n <div class=\"flex items-start justify-between\">\n <div>\n <h3 class=\"m-0 text-lg font-semibold leading-none tracking-tight text-foreground\">{{ profileTitle }}</h3>\n <p class=\"m-0 mt-1 text-sm text-muted-foreground\">{{ profileDescription }}</p>\n </div>\n <button type=\"button\" class=\"h-5 w-5 appearance-none border-0 bg-transparent p-0 text-muted-foreground\" (click)=\"close()\">\u00D7</button>\n </div>\n\n <div class=\"mt-3 flex flex-col gap-3\">\n <div>\n <label class=\"mb-1 block text-xs font-medium text-foreground\">{{ nameLabel }}</label>\n <div class=\"h-8 rounded-md border border-border bg-background px-2 py-1 text-xs text-foreground\">{{ nameValue }}</div>\n </div>\n <div>\n <label class=\"mb-1 block text-xs font-medium text-foreground\">{{ usernameLabel }}</label>\n <div class=\"h-8 rounded-md border border-border bg-background px-2 py-1 text-xs text-foreground\">{{ usernameValue }}</div>\n </div>\n </div>\n\n <button *ngIf=\"responsivePrimaryLabel\" type=\"button\" class=\"mt-3 h-8 w-full appearance-none rounded-md bg-primary text-xs font-medium text-primary-foreground\" (click)=\"onPrimaryAction()\">{{ responsivePrimaryLabel }}</button>\n </section>\n </ng-template>\n</div>\n" }]
3870
+ args: [{ selector: 'pdm-drawer', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div *ngIf=\"open\" [ngClass]=\"containerClassName\">\n <!-- Backdrop -->\n <div class=\"absolute inset-0 bg-foreground/30 backdrop-blur-sm\" (click)=\"onBackdropClick()\"></div>\n\n <!-- Panel -->\n <section [ngClass]=\"panelClassName\">\n <!-- Handle visual (solo para bottom) -->\n <div \n *ngIf=\"showHandle && position === 'bottom'\" \n class=\"mx-auto mb-4 mt-2 h-1 w-10 rounded-full bg-border\">\n </div>\n\n <!-- Header (opcional) -->\n <div *ngIf=\"title || description || showCloseButton\" class=\"flex items-start justify-between gap-4 p-6 pb-4\">\n <div *ngIf=\"title || description\" class=\"flex-1 min-w-0\">\n <h3 *ngIf=\"title\" class=\"m-0 text-lg font-semibold leading-none tracking-tight text-foreground\">\n {{ title }}\n </h3>\n <p *ngIf=\"description\" class=\"m-0 mt-1 text-sm text-muted-foreground\">\n {{ description }}\n </p>\n </div>\n \n <button\n *ngIf=\"showCloseButton\"\n type=\"button\"\n class=\"inline-flex h-6 w-6 flex-shrink-0 appearance-none items-center justify-center rounded-sm border-0 bg-transparent p-0 text-foreground opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\"\n (click)=\"close()\"\n aria-label=\"Close drawer\"\n >\n <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 6L18 18M18 6L6 18\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"></path>\n </svg>\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"px-6 pb-6\">\n <!-- Contenido gen\u00E9rico -->\n <ng-container *ngIf=\"!showLegacyContent\">\n <ng-content></ng-content>\n </ng-container>\n\n <!-- LEGACY: contenido espec\u00EDfico hardcodeado (backward compatibility) -->\n <ng-container *ngIf=\"showLegacyContent\">\n <!-- Variant: drawer -->\n <div *ngIf=\"variant === 'drawer'\" class=\"mx-auto flex max-w-sm flex-col items-center\">\n <div *ngIf=\"value !== ''\" class=\"mt-3 flex w-full items-center justify-center gap-4\">\n <button type=\"button\" class=\"inline-flex h-6 w-6 appearance-none items-center justify-center rounded-full border border-border bg-transparent p-0 text-muted-foreground\">{{ decrementLabel }}</button>\n <div class=\"text-center\">\n <div class=\"text-5xl font-semibold leading-none text-foreground\">{{ value }}</div>\n <div *ngIf=\"unit\" class=\"mt-1 text-xs tracking-wide text-muted-foreground\">{{ unit }}</div>\n </div>\n <button type=\"button\" class=\"inline-flex h-6 w-6 appearance-none items-center justify-center rounded-full border border-border bg-transparent p-0 text-muted-foreground\">{{ incrementLabel }}</button>\n </div>\n\n <div *ngIf=\"bars.length\" class=\"mt-3 flex h-14 w-full items-end gap-1\">\n <div *ngFor=\"let bar of bars\" class=\"flex-1 bg-foreground\" [style.height.px]=\"bar\"></div>\n </div>\n\n <button *ngIf=\"primaryLabel\" type=\"button\" class=\"mt-3 h-9 w-full appearance-none rounded-md bg-primary text-sm font-medium text-primary-foreground\" (click)=\"onPrimaryAction()\">{{ primaryLabel }}</button>\n <button *ngIf=\"secondaryLabel\" type=\"button\" class=\"mt-2 h-9 w-full appearance-none rounded-md border border-input bg-background text-sm font-medium text-foreground\" (click)=\"onSecondaryAction()\">{{ secondaryLabel }}</button>\n </div>\n\n <!-- Variant: responsive-dialog -->\n <div *ngIf=\"variant === 'responsive-dialog'\" class=\"flex flex-col gap-3\">\n <div *ngIf=\"nameLabel && nameValue\">\n <label class=\"mb-1 block text-xs font-medium text-foreground\">{{ nameLabel }}</label>\n <div class=\"h-8 rounded-md border border-border bg-background px-2 py-1 text-xs text-foreground\">{{ nameValue }}</div>\n </div>\n <div *ngIf=\"usernameLabel && usernameValue\">\n <label class=\"mb-1 block text-xs font-medium text-foreground\">{{ usernameLabel }}</label>\n <div class=\"h-8 rounded-md border border-border bg-background px-2 py-1 text-xs text-foreground\">{{ usernameValue }}</div>\n </div>\n\n <button *ngIf=\"responsivePrimaryLabel\" type=\"button\" class=\"mt-3 h-8 w-full appearance-none rounded-md bg-primary text-xs font-medium text-primary-foreground\" (click)=\"onPrimaryAction()\">{{ responsivePrimaryLabel }}</button>\n </div>\n </ng-container>\n </div>\n </section>\n</div>\n" }]
2702
3871
  }], propDecorators: { open: [{
2703
3872
  type: Input
3873
+ }], position: [{
3874
+ type: Input
3875
+ }], size: [{
3876
+ type: Input
2704
3877
  }], variant: [{
2705
3878
  type: Input
2706
3879
  }], className: [{
@@ -2709,6 +3882,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
2709
3882
  type: Input
2710
3883
  }], description: [{
2711
3884
  type: Input
3885
+ }], showHandle: [{
3886
+ type: Input
3887
+ }], showCloseButton: [{
3888
+ type: Input
3889
+ }], closeOnEsc: [{
3890
+ type: Input
3891
+ }], closeOnBackdropClick: [{
3892
+ type: Input
3893
+ }], openChange: [{
3894
+ type: Output
2712
3895
  }], value: [{
2713
3896
  type: Input
2714
3897
  }], unit: [{
@@ -2735,14 +3918,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
2735
3918
  type: Input
2736
3919
  }], responsivePrimaryLabel: [{
2737
3920
  type: Input
2738
- }], openChange: [{
2739
- type: Output
3921
+ }], bars: [{
3922
+ type: Input
2740
3923
  }], primaryAction: [{
2741
3924
  type: Output
2742
3925
  }], secondaryAction: [{
2743
3926
  type: Output
2744
- }], bars: [{
2745
- type: Input
3927
+ }], onEsc: [{
3928
+ type: HostListener,
3929
+ args: ['document:keydown.escape']
2746
3930
  }] } });
2747
3931
 
2748
3932
  class PdmEmptyComponent {
@@ -2884,10 +4068,10 @@ class PdmHoverCardComponent {
2884
4068
  }
2885
4069
  }
2886
4070
  PdmHoverCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmHoverCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2887
- PdmHoverCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmHoverCardComponent, selector: "pdm-hover-card", inputs: { className: "className", panelClassName: "panelClassName", side: "side", align: "align", panelWidth: "panelWidth" }, ngImport: i0, template: "<div\n class=\"relative inline-flex\"\n [ngClass]=\"className\"\n (mouseenter)=\"open = true\"\n (mouseleave)=\"open = false\"\n (focusin)=\"open = true\"\n (focusout)=\"open = false\"\n>\n <div>\n <ng-content select=\"[pdmHoverTrigger]\"></ng-content>\n </div>\n\n <section\n *ngIf=\"open\"\n [style.width.px]=\"panelWidth\"\n [ngClass]=\"[\n 'absolute z-30 rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md',\n positionClass,\n panelClassName\n ]\"\n >\n <ng-content select=\"[pdmHoverContent]\"></ng-content>\n </section>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4071
+ PdmHoverCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmHoverCardComponent, selector: "pdm-hover-card", inputs: { className: "className", panelClassName: "panelClassName", side: "side", align: "align", panelWidth: "panelWidth" }, ngImport: i0, template: "<div\n class=\"relative inline-flex\"\n [ngClass]=\"className\"\n (mouseenter)=\"open = true\"\n (mouseleave)=\"open = false\"\n (focusin)=\"open = true\"\n (focusout)=\"open = false\"\n>\n <div>\n <ng-content select=\"[pdmHoverTrigger]\"></ng-content>\n </div>\n\n <section\n *ngIf=\"open\"\n [style.width.px]=\"panelWidth\"\n [ngClass]=\"[\n 'absolute z-[70] rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md',\n positionClass,\n panelClassName\n ]\"\n >\n <ng-content select=\"[pdmHoverContent]\"></ng-content>\n </section>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2888
4072
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmHoverCardComponent, decorators: [{
2889
4073
  type: Component,
2890
- args: [{ selector: 'pdm-hover-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"relative inline-flex\"\n [ngClass]=\"className\"\n (mouseenter)=\"open = true\"\n (mouseleave)=\"open = false\"\n (focusin)=\"open = true\"\n (focusout)=\"open = false\"\n>\n <div>\n <ng-content select=\"[pdmHoverTrigger]\"></ng-content>\n </div>\n\n <section\n *ngIf=\"open\"\n [style.width.px]=\"panelWidth\"\n [ngClass]=\"[\n 'absolute z-30 rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md',\n positionClass,\n panelClassName\n ]\"\n >\n <ng-content select=\"[pdmHoverContent]\"></ng-content>\n </section>\n</div>\n" }]
4074
+ args: [{ selector: 'pdm-hover-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"relative inline-flex\"\n [ngClass]=\"className\"\n (mouseenter)=\"open = true\"\n (mouseleave)=\"open = false\"\n (focusin)=\"open = true\"\n (focusout)=\"open = false\"\n>\n <div>\n <ng-content select=\"[pdmHoverTrigger]\"></ng-content>\n </div>\n\n <section\n *ngIf=\"open\"\n [style.width.px]=\"panelWidth\"\n [ngClass]=\"[\n 'absolute z-[70] rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md',\n positionClass,\n panelClassName\n ]\"\n >\n <ng-content select=\"[pdmHoverContent]\"></ng-content>\n </section>\n</div>\n" }]
2891
4075
  }], propDecorators: { className: [{
2892
4076
  type: Input
2893
4077
  }], panelClassName: [{
@@ -3274,12 +4458,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
3274
4458
  }] } });
3275
4459
 
3276
4460
  class PdmMenubarComponent {
3277
- constructor() {
4461
+ constructor(elementRef, cdr) {
4462
+ this.elementRef = elementRef;
4463
+ this.cdr = cdr;
3278
4464
  this.menus = [];
3279
4465
  this.className = '';
3280
4466
  this.itemSelect = new EventEmitter();
3281
4467
  this.openIndex = -1;
3282
4468
  }
4469
+ ngOnInit() {
4470
+ this.boundPointerDown = (event) => this.onDocumentPointerDown(event);
4471
+ document.addEventListener('pointerdown', this.boundPointerDown, { capture: true });
4472
+ }
4473
+ ngOnDestroy() {
4474
+ if (this.boundPointerDown) {
4475
+ document.removeEventListener('pointerdown', this.boundPointerDown, { capture: true });
4476
+ }
4477
+ }
4478
+ onEsc() {
4479
+ if (this.openIndex >= 0) {
4480
+ this.openIndex = -1;
4481
+ this.cdr.markForCheck();
4482
+ }
4483
+ }
3283
4484
  toggle(index) {
3284
4485
  this.openIndex = this.openIndex === index ? -1 : index;
3285
4486
  }
@@ -3293,18 +4494,32 @@ class PdmMenubarComponent {
3293
4494
  }
3294
4495
  this.select(item.value);
3295
4496
  }
4497
+ onDocumentPointerDown(event) {
4498
+ if (this.openIndex < 0)
4499
+ return;
4500
+ const target = event.target;
4501
+ if (!target)
4502
+ return;
4503
+ if (!this.elementRef.nativeElement.contains(target)) {
4504
+ this.openIndex = -1;
4505
+ this.cdr.markForCheck();
4506
+ }
4507
+ }
3296
4508
  }
3297
- PdmMenubarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmMenubarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3298
- PdmMenubarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmMenubarComponent, selector: "pdm-menubar", inputs: { menus: "menus", className: "className" }, outputs: { itemSelect: "itemSelect" }, ngImport: i0, template: "<nav role=\"menubar\" [ngClass]=\"['inline-flex h-9 items-center gap-0.5 rounded-md border border-border bg-background p-1 shadow-sm', className]\">\n <div *ngFor=\"let menu of menus; let i = index\" class=\"relative\">\n <button type=\"button\" class=\"inline-flex h-7 appearance-none items-center rounded-sm border-0 bg-transparent px-3 text-sm text-foreground hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\" (click)=\"toggle(i)\">{{ menu.label }}</button>\n <div *ngIf=\"openIndex === i\" class=\"absolute left-0 top-full z-50 mt-1 min-w-48 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md\">\n <button\n *ngFor=\"let item of menu.items\"\n type=\"button\"\n [disabled]=\"item.disabled || !item.value\"\n class=\"relative flex w-full appearance-none cursor-default select-none items-center rounded-sm border-0 bg-transparent px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"selectItem(item)\"\n >\n {{ item.label }}\n </button>\n </div>\n </div>\n</nav>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4509
+ PdmMenubarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmMenubarComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
4510
+ PdmMenubarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmMenubarComponent, selector: "pdm-menubar", inputs: { menus: "menus", className: "className" }, outputs: { itemSelect: "itemSelect" }, host: { listeners: { "document:keydown.escape": "onEsc()" } }, ngImport: i0, template: "<nav role=\"menubar\" [ngClass]=\"['inline-flex h-9 items-center gap-0.5 rounded-md border border-border bg-background p-1 shadow-sm', className]\">\n <div *ngFor=\"let menu of menus; let i = index\" class=\"relative\">\n <button type=\"button\" class=\"inline-flex h-7 appearance-none items-center rounded-sm border-0 bg-transparent px-3 text-sm text-foreground hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\" (click)=\"toggle(i)\">{{ menu.label }}</button>\n <div *ngIf=\"openIndex === i\" class=\"absolute left-0 top-full z-[70] mt-1 min-w-40 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md sm:min-w-48\">\n <button\n *ngFor=\"let item of menu.items\"\n type=\"button\"\n [disabled]=\"item.disabled || !item.value\"\n class=\"relative flex w-full appearance-none cursor-default select-none items-center rounded-sm border-0 bg-transparent px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"selectItem(item)\"\n >\n {{ item.label }}\n </button>\n </div>\n </div>\n</nav>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3299
4511
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmMenubarComponent, decorators: [{
3300
4512
  type: Component,
3301
- args: [{ selector: 'pdm-menubar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav role=\"menubar\" [ngClass]=\"['inline-flex h-9 items-center gap-0.5 rounded-md border border-border bg-background p-1 shadow-sm', className]\">\n <div *ngFor=\"let menu of menus; let i = index\" class=\"relative\">\n <button type=\"button\" class=\"inline-flex h-7 appearance-none items-center rounded-sm border-0 bg-transparent px-3 text-sm text-foreground hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\" (click)=\"toggle(i)\">{{ menu.label }}</button>\n <div *ngIf=\"openIndex === i\" class=\"absolute left-0 top-full z-50 mt-1 min-w-48 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md\">\n <button\n *ngFor=\"let item of menu.items\"\n type=\"button\"\n [disabled]=\"item.disabled || !item.value\"\n class=\"relative flex w-full appearance-none cursor-default select-none items-center rounded-sm border-0 bg-transparent px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"selectItem(item)\"\n >\n {{ item.label }}\n </button>\n </div>\n </div>\n</nav>\n" }]
3302
- }], propDecorators: { menus: [{
4513
+ args: [{ selector: 'pdm-menubar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav role=\"menubar\" [ngClass]=\"['inline-flex h-9 items-center gap-0.5 rounded-md border border-border bg-background p-1 shadow-sm', className]\">\n <div *ngFor=\"let menu of menus; let i = index\" class=\"relative\">\n <button type=\"button\" class=\"inline-flex h-7 appearance-none items-center rounded-sm border-0 bg-transparent px-3 text-sm text-foreground hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\" (click)=\"toggle(i)\">{{ menu.label }}</button>\n <div *ngIf=\"openIndex === i\" class=\"absolute left-0 top-full z-[70] mt-1 min-w-40 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md sm:min-w-48\">\n <button\n *ngFor=\"let item of menu.items\"\n type=\"button\"\n [disabled]=\"item.disabled || !item.value\"\n class=\"relative flex w-full appearance-none cursor-default select-none items-center rounded-sm border-0 bg-transparent px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"selectItem(item)\"\n >\n {{ item.label }}\n </button>\n </div>\n </div>\n</nav>\n" }]
4514
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { menus: [{
3303
4515
  type: Input
3304
4516
  }], className: [{
3305
4517
  type: Input
3306
4518
  }], itemSelect: [{
3307
4519
  type: Output
4520
+ }], onEsc: [{
4521
+ type: HostListener,
4522
+ args: ['document:keydown.escape']
3308
4523
  }] } });
3309
4524
 
3310
4525
  class PdmNativeSelectComponent {
@@ -3345,21 +4560,125 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
3345
4560
  type: Output
3346
4561
  }] } });
3347
4562
 
4563
+ /**
4564
+ * Navigation Menu component - Navegación horizontal responsive
4565
+ *
4566
+ * MEJORAS en v0.2.0:
4567
+ * - Modo scroll: overflow-x-auto con scroll indicators en mobile
4568
+ * - Modo compact: items abreviados en mobile, completos en desktop
4569
+ * - Scroll smooth automático al item activo
4570
+ *
4571
+ * @example
4572
+ * <!-- Scroll horizontal (default) -->
4573
+ * <pdm-navigation-menu [items]="navItems"></pdm-navigation-menu>
4574
+ *
4575
+ * <!-- Compact mode -->
4576
+ * <pdm-navigation-menu [items]="navItems" mobileMode="compact"></pdm-navigation-menu>
4577
+ */
3348
4578
  class PdmNavigationMenuComponent {
3349
4579
  constructor() {
3350
4580
  this.items = [];
3351
4581
  this.className = '';
4582
+ /**
4583
+ * Mobile behavior: 'scroll' (horizontal scroll) o 'compact' (items reducidos)
4584
+ * @default 'scroll'
4585
+ */
4586
+ this.mobileMode = 'scroll';
3352
4587
  }
3353
4588
  }
3354
4589
  PdmNavigationMenuComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmNavigationMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3355
- PdmNavigationMenuComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmNavigationMenuComponent, selector: "pdm-navigation-menu", inputs: { items: "items", className: "className" }, ngImport: i0, template: "<nav [ngClass]=\"['relative z-10 flex max-w-max flex-1 items-center justify-center', className]\">\n <ul class=\"group flex flex-1 list-none items-center justify-center space-x-1\">\n <li *ngFor=\"let item of items\">\n <a\n [href]=\"item.href || '#'\"\n [ngClass]=\"[\n 'group inline-flex h-9 w-max items-center justify-center rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n item.active ? 'bg-accent text-accent-foreground' : 'text-foreground'\n ]\"\n >\n {{ item.label }}\n </a>\n </li>\n </ul>\n</nav>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4590
+ PdmNavigationMenuComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmNavigationMenuComponent, selector: "pdm-navigation-menu", inputs: { items: "items", className: "className", mobileMode: "mobileMode" }, ngImport: i0, template: "<nav\n [ngClass]=\"[\n 'relative z-10 flex w-full items-center',\n mobileMode === 'scroll' ? 'overflow-x-auto scrollbar-thin' : '',\n className\n ]\"\n>\n <ul\n [ngClass]=\"[\n 'group flex list-none items-center gap-1',\n mobileMode === 'scroll' ? 'flex-nowrap' : 'flex-wrap justify-center'\n ]\"\n >\n <li *ngFor=\"let item of items\">\n <a\n [href]=\"item.href || '#'\"\n [ngClass]=\"[\n 'group inline-flex h-9 items-center justify-center rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 whitespace-nowrap',\n item.active ? 'bg-accent text-accent-foreground' : 'text-foreground'\n ]\"\n >\n {{ item.label }}\n </a>\n </li>\n </ul>\n</nav>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3356
4591
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmNavigationMenuComponent, decorators: [{
3357
4592
  type: Component,
3358
- args: [{ selector: 'pdm-navigation-menu', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav [ngClass]=\"['relative z-10 flex max-w-max flex-1 items-center justify-center', className]\">\n <ul class=\"group flex flex-1 list-none items-center justify-center space-x-1\">\n <li *ngFor=\"let item of items\">\n <a\n [href]=\"item.href || '#'\"\n [ngClass]=\"[\n 'group inline-flex h-9 w-max items-center justify-center rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n item.active ? 'bg-accent text-accent-foreground' : 'text-foreground'\n ]\"\n >\n {{ item.label }}\n </a>\n </li>\n </ul>\n</nav>\n" }]
4593
+ args: [{ selector: 'pdm-navigation-menu', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav\n [ngClass]=\"[\n 'relative z-10 flex w-full items-center',\n mobileMode === 'scroll' ? 'overflow-x-auto scrollbar-thin' : '',\n className\n ]\"\n>\n <ul\n [ngClass]=\"[\n 'group flex list-none items-center gap-1',\n mobileMode === 'scroll' ? 'flex-nowrap' : 'flex-wrap justify-center'\n ]\"\n >\n <li *ngFor=\"let item of items\">\n <a\n [href]=\"item.href || '#'\"\n [ngClass]=\"[\n 'group inline-flex h-9 items-center justify-center rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 whitespace-nowrap',\n item.active ? 'bg-accent text-accent-foreground' : 'text-foreground'\n ]\"\n >\n {{ item.label }}\n </a>\n </li>\n </ul>\n</nav>\n" }]
3359
4594
  }], propDecorators: { items: [{
3360
4595
  type: Input
3361
4596
  }], className: [{
3362
4597
  type: Input
4598
+ }], mobileMode: [{
4599
+ type: Input
4600
+ }] } });
4601
+
4602
+ /**
4603
+ * Emits `(pdmOutsideClick)` whenever a `pointerdown` event fires outside the host element.
4604
+ *
4605
+ * Uses the CAPTURE phase so it intercepts clicks even when inner elements call
4606
+ * `stopPropagation()` (e.g. CDK overlay panels).
4607
+ *
4608
+ * SSR-safe: no listener is registered when running on the server.
4609
+ *
4610
+ * @example
4611
+ * ```html
4612
+ * <div [pdmOutsideClick]
4613
+ * (pdmOutsideClick)="close()"
4614
+ * [pdmOutsideClickDisabled]="!open">
4615
+ * </div>
4616
+ * ```
4617
+ */
4618
+ class PdmOutsideClickDirective {
4619
+ constructor(elementRef, document, platformId) {
4620
+ this.elementRef = elementRef;
4621
+ this.document = document;
4622
+ /** When `true`, the outside-click listener is inactive. */
4623
+ this.pdmOutsideClickDisabled = false;
4624
+ /**
4625
+ * Additional elements to exclude from the "outside" check.
4626
+ * Useful when the trigger lives outside the host (e.g. a menubar button
4627
+ * that opens a floating panel bound to a different root element).
4628
+ */
4629
+ this.pdmOutsideClickExclude = [];
4630
+ /** Fires when a `pointerdown` lands outside the host (and excluded elements). */
4631
+ this.pdmOutsideClick = new EventEmitter();
4632
+ this.isBrowser = isPlatformBrowser(platformId);
4633
+ }
4634
+ ngOnInit() {
4635
+ if (!this.isBrowser)
4636
+ return;
4637
+ this.boundHandler = (event) => this.onPointerDown(event);
4638
+ this.document.addEventListener('pointerdown', this.boundHandler, { capture: true });
4639
+ }
4640
+ ngOnDestroy() {
4641
+ if (!this.isBrowser || !this.boundHandler)
4642
+ return;
4643
+ this.document.removeEventListener('pointerdown', this.boundHandler, { capture: true });
4644
+ }
4645
+ onPointerDown(event) {
4646
+ if (this.pdmOutsideClickDisabled)
4647
+ return;
4648
+ const target = event.target;
4649
+ if (!target)
4650
+ return;
4651
+ // Check if click is inside the host element.
4652
+ if (this.elementRef.nativeElement.contains(target))
4653
+ return;
4654
+ // Check if click is inside any excluded element.
4655
+ for (const excluded of this.pdmOutsideClickExclude) {
4656
+ const el = excluded instanceof ElementRef ? excluded.nativeElement : excluded;
4657
+ if (el && el.contains(target))
4658
+ return;
4659
+ }
4660
+ this.pdmOutsideClick.emit(event);
4661
+ }
4662
+ }
4663
+ PdmOutsideClickDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmOutsideClickDirective, deps: [{ token: i0.ElementRef }, { token: DOCUMENT }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Directive });
4664
+ PdmOutsideClickDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.3.0", type: PdmOutsideClickDirective, selector: "[pdmOutsideClick]", inputs: { pdmOutsideClickDisabled: "pdmOutsideClickDisabled", pdmOutsideClickExclude: "pdmOutsideClickExclude" }, outputs: { pdmOutsideClick: "pdmOutsideClick" }, ngImport: i0 });
4665
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmOutsideClickDirective, decorators: [{
4666
+ type: Directive,
4667
+ args: [{
4668
+ selector: '[pdmOutsideClick]'
4669
+ }]
4670
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: Document, decorators: [{
4671
+ type: Inject,
4672
+ args: [DOCUMENT]
4673
+ }] }, { type: undefined, decorators: [{
4674
+ type: Inject,
4675
+ args: [PLATFORM_ID]
4676
+ }] }]; }, propDecorators: { pdmOutsideClickDisabled: [{
4677
+ type: Input
4678
+ }], pdmOutsideClickExclude: [{
4679
+ type: Input
4680
+ }], pdmOutsideClick: [{
4681
+ type: Output
3363
4682
  }] } });
3364
4683
 
3365
4684
  /**
@@ -3481,7 +4800,9 @@ class PdmSelectComponent {
3481
4800
  this.closePanel();
3482
4801
  }
3483
4802
  onEscape() {
3484
- this.closePanel();
4803
+ if (this.open) {
4804
+ this.closePanel();
4805
+ }
3485
4806
  }
3486
4807
  openPanel() {
3487
4808
  if (this.overlayRef)
@@ -3493,8 +4814,9 @@ class PdmSelectComponent {
3493
4814
  this.cdr.markForCheck();
3494
4815
  const positionStrategy = createFlexiblePositionStrategy(this.overlay, triggerEl, 4);
3495
4816
  this.overlayRef = this.overlay.create({
3496
- // Fix: use a token array DOMTokenList.add() rejects space-containing strings.
3497
- panelClass: ['block'],
4817
+ // CRÍTICO: z-[70] para aparecer SOBRE modals (z-[60])
4818
+ // panelClass se aplica al cdk-overlay-pane wrapper
4819
+ panelClass: [Z_INDEX.popover],
3498
4820
  positionStrategy,
3499
4821
  scrollStrategy: this.overlay.scrollStrategies.reposition(),
3500
4822
  width: triggerEl.offsetWidth,
@@ -3609,10 +4931,10 @@ class PdmPaginationComponent {
3609
4931
  }
3610
4932
  }
3611
4933
  PdmPaginationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmPaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3612
- PdmPaginationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmPaginationComponent, selector: "pdm-pagination", inputs: { page: "page", pageCount: "pageCount", maxVisible: "maxVisible", className: "className", rowsPerPageLabel: "rowsPerPageLabel", rowsPerPage: "rowsPerPage", rowsPerPageOptions: "rowsPerPageOptions" }, outputs: { pageChange: "pageChange", rowsPerPageChange: "rowsPerPageChange" }, ngImport: i0, template: "<nav\n aria-label=\"Pagination\"\n [ngClass]=\"[\n 'mx-auto flex w-full flex-wrap items-center justify-center gap-4',\n className,\n ]\"\n>\n <div class=\"flex items-center gap-3\" *ngIf=\"rowsPerPageOptions.length > 0\">\n <span class=\"text-sm font-medium text-foreground\">{{\n rowsPerPageLabel\n }}</span>\n <pdm-select\n [value]=\"rowsPerPageValue\"\n [options]=\"rowsPerPageSelectOptions\"\n [placeholder]=\"rowsPerPageValue\"\n className=\"w-[120px]\"\n (valueChange)=\"onRowsPerPageChangeValue($event)\"\n ></pdm-select>\n </div>\n\n <ul class=\"m-0 flex list-none items-center gap-1 p-0\">\n <li>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center gap-1 rounded-md border-0 bg-transparent px-2 text-sm text-foreground hover:bg-accent disabled:opacity-50 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n [disabled]=\"page <= 1\"\n (click)=\"setPage(page - 1)\"\n >\n <pdm-icon name=\"chevron-left\" [size]=\"14\"></pdm-icon>\n Previous\n </button>\n </li>\n <li *ngFor=\"let pageNumber of visiblePages\">\n <ng-container *ngIf=\"pageNumber === 'ellipsis'; else pageButton\">\n <span\n class=\"inline-flex h-9 min-w-9 items-center justify-center px-2 text-sm text-muted-foreground\"\n >...</span\n >\n </ng-container>\n <ng-template #pageButton>\n <button\n type=\"button\"\n [ngClass]=\"[\n 'inline-flex h-9 min-w-9 items-center justify-center rounded-md px-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n pageNumber === page\n ? 'appearance-none border border-border bg-muted text-foreground shadow-sm'\n : 'appearance-none border-0 bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground',\n ]\"\n (click)=\"setPage(+pageNumber)\"\n >\n {{ pageNumber }}\n </button>\n </ng-template>\n </li>\n <li>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center gap-1 rounded-md border-0 bg-transparent px-2 text-sm text-foreground hover:bg-accent disabled:opacity-50 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n [disabled]=\"page >= pageCount\"\n (click)=\"setPage(page + 1)\"\n >\n Next\n <pdm-icon name=\"chevron-right\" [size]=\"14\"></pdm-icon>\n </button>\n </li>\n </ul>\n</nav>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PdmIconComponent, selector: "pdm-icon", inputs: ["name", "library", "assetUrl", "size", "strokeWidth", "className", "ariaLabel", "decorative"] }, { kind: "component", type: PdmSelectComponent, selector: "pdm-select", inputs: ["id", "value", "options", "disabled", "invalid", "className", "placeholder", "overlayOptions"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4934
+ PdmPaginationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmPaginationComponent, selector: "pdm-pagination", inputs: { page: "page", pageCount: "pageCount", maxVisible: "maxVisible", className: "className", rowsPerPageLabel: "rowsPerPageLabel", rowsPerPage: "rowsPerPage", rowsPerPageOptions: "rowsPerPageOptions" }, outputs: { pageChange: "pageChange", rowsPerPageChange: "rowsPerPageChange" }, ngImport: i0, template: "<nav\n aria-label=\"Pagination\"\n [ngClass]=\"[\n 'mx-auto flex w-full flex-wrap items-center justify-center gap-4',\n className,\n ]\"\n>\n <div class=\"flex items-center gap-3\" *ngIf=\"rowsPerPageOptions.length > 0\">\n <span class=\"text-sm font-medium text-foreground\">{{\n rowsPerPageLabel\n }}</span>\n <pdm-select\n [value]=\"rowsPerPageValue\"\n [options]=\"rowsPerPageSelectOptions\"\n [placeholder]=\"rowsPerPageValue\"\n className=\"w-[100px] sm:w-[120px]\"\n (valueChange)=\"onRowsPerPageChangeValue($event)\"\n ></pdm-select>\n </div>\n\n <ul class=\"m-0 flex list-none items-center gap-1 p-0\">\n <li>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center gap-1 rounded-md border-0 bg-transparent px-2 text-sm text-foreground hover:bg-accent disabled:opacity-50 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n [disabled]=\"page <= 1\"\n (click)=\"setPage(page - 1)\"\n >\n <pdm-icon name=\"chevron-left\" [size]=\"14\"></pdm-icon>\n Previous\n </button>\n </li>\n <li *ngFor=\"let pageNumber of visiblePages\">\n <ng-container *ngIf=\"pageNumber === 'ellipsis'; else pageButton\">\n <span\n class=\"inline-flex h-9 min-w-9 items-center justify-center px-2 text-sm text-muted-foreground\"\n >...</span\n >\n </ng-container>\n <ng-template #pageButton>\n <button\n type=\"button\"\n [ngClass]=\"[\n 'inline-flex h-9 min-w-9 items-center justify-center rounded-md px-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n pageNumber === page\n ? 'appearance-none border border-border bg-muted text-foreground shadow-sm'\n : 'appearance-none border-0 bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground',\n ]\"\n (click)=\"setPage(+pageNumber)\"\n >\n {{ pageNumber }}\n </button>\n </ng-template>\n </li>\n <li>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center gap-1 rounded-md border-0 bg-transparent px-2 text-sm text-foreground hover:bg-accent disabled:opacity-50 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n [disabled]=\"page >= pageCount\"\n (click)=\"setPage(page + 1)\"\n >\n Next\n <pdm-icon name=\"chevron-right\" [size]=\"14\"></pdm-icon>\n </button>\n </li>\n </ul>\n</nav>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PdmIconComponent, selector: "pdm-icon", inputs: ["name", "library", "assetUrl", "size", "strokeWidth", "className", "ariaLabel", "decorative"] }, { kind: "component", type: PdmSelectComponent, selector: "pdm-select", inputs: ["id", "value", "options", "disabled", "invalid", "className", "placeholder", "overlayOptions"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3613
4935
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmPaginationComponent, decorators: [{
3614
4936
  type: Component,
3615
- args: [{ selector: 'pdm-pagination', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav\n aria-label=\"Pagination\"\n [ngClass]=\"[\n 'mx-auto flex w-full flex-wrap items-center justify-center gap-4',\n className,\n ]\"\n>\n <div class=\"flex items-center gap-3\" *ngIf=\"rowsPerPageOptions.length > 0\">\n <span class=\"text-sm font-medium text-foreground\">{{\n rowsPerPageLabel\n }}</span>\n <pdm-select\n [value]=\"rowsPerPageValue\"\n [options]=\"rowsPerPageSelectOptions\"\n [placeholder]=\"rowsPerPageValue\"\n className=\"w-[120px]\"\n (valueChange)=\"onRowsPerPageChangeValue($event)\"\n ></pdm-select>\n </div>\n\n <ul class=\"m-0 flex list-none items-center gap-1 p-0\">\n <li>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center gap-1 rounded-md border-0 bg-transparent px-2 text-sm text-foreground hover:bg-accent disabled:opacity-50 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n [disabled]=\"page <= 1\"\n (click)=\"setPage(page - 1)\"\n >\n <pdm-icon name=\"chevron-left\" [size]=\"14\"></pdm-icon>\n Previous\n </button>\n </li>\n <li *ngFor=\"let pageNumber of visiblePages\">\n <ng-container *ngIf=\"pageNumber === 'ellipsis'; else pageButton\">\n <span\n class=\"inline-flex h-9 min-w-9 items-center justify-center px-2 text-sm text-muted-foreground\"\n >...</span\n >\n </ng-container>\n <ng-template #pageButton>\n <button\n type=\"button\"\n [ngClass]=\"[\n 'inline-flex h-9 min-w-9 items-center justify-center rounded-md px-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n pageNumber === page\n ? 'appearance-none border border-border bg-muted text-foreground shadow-sm'\n : 'appearance-none border-0 bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground',\n ]\"\n (click)=\"setPage(+pageNumber)\"\n >\n {{ pageNumber }}\n </button>\n </ng-template>\n </li>\n <li>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center gap-1 rounded-md border-0 bg-transparent px-2 text-sm text-foreground hover:bg-accent disabled:opacity-50 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n [disabled]=\"page >= pageCount\"\n (click)=\"setPage(page + 1)\"\n >\n Next\n <pdm-icon name=\"chevron-right\" [size]=\"14\"></pdm-icon>\n </button>\n </li>\n </ul>\n</nav>\n" }]
4937
+ args: [{ selector: 'pdm-pagination', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav\n aria-label=\"Pagination\"\n [ngClass]=\"[\n 'mx-auto flex w-full flex-wrap items-center justify-center gap-4',\n className,\n ]\"\n>\n <div class=\"flex items-center gap-3\" *ngIf=\"rowsPerPageOptions.length > 0\">\n <span class=\"text-sm font-medium text-foreground\">{{\n rowsPerPageLabel\n }}</span>\n <pdm-select\n [value]=\"rowsPerPageValue\"\n [options]=\"rowsPerPageSelectOptions\"\n [placeholder]=\"rowsPerPageValue\"\n className=\"w-[100px] sm:w-[120px]\"\n (valueChange)=\"onRowsPerPageChangeValue($event)\"\n ></pdm-select>\n </div>\n\n <ul class=\"m-0 flex list-none items-center gap-1 p-0\">\n <li>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center gap-1 rounded-md border-0 bg-transparent px-2 text-sm text-foreground hover:bg-accent disabled:opacity-50 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n [disabled]=\"page <= 1\"\n (click)=\"setPage(page - 1)\"\n >\n <pdm-icon name=\"chevron-left\" [size]=\"14\"></pdm-icon>\n Previous\n </button>\n </li>\n <li *ngFor=\"let pageNumber of visiblePages\">\n <ng-container *ngIf=\"pageNumber === 'ellipsis'; else pageButton\">\n <span\n class=\"inline-flex h-9 min-w-9 items-center justify-center px-2 text-sm text-muted-foreground\"\n >...</span\n >\n </ng-container>\n <ng-template #pageButton>\n <button\n type=\"button\"\n [ngClass]=\"[\n 'inline-flex h-9 min-w-9 items-center justify-center rounded-md px-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n pageNumber === page\n ? 'appearance-none border border-border bg-muted text-foreground shadow-sm'\n : 'appearance-none border-0 bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground',\n ]\"\n (click)=\"setPage(+pageNumber)\"\n >\n {{ pageNumber }}\n </button>\n </ng-template>\n </li>\n <li>\n <button\n type=\"button\"\n class=\"inline-flex h-9 appearance-none items-center justify-center gap-1 rounded-md border-0 bg-transparent px-2 text-sm text-foreground hover:bg-accent disabled:opacity-50 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n [disabled]=\"page >= pageCount\"\n (click)=\"setPage(page + 1)\"\n >\n Next\n <pdm-icon name=\"chevron-right\" [size]=\"14\"></pdm-icon>\n </button>\n </li>\n </ul>\n</nav>\n" }]
3616
4938
  }], propDecorators: { page: [{
3617
4939
  type: Input
3618
4940
  }], pageCount: [{
@@ -3645,6 +4967,15 @@ class PdmPopoverComponent {
3645
4967
  this.openChange = new EventEmitter();
3646
4968
  this.panelPlacement = 'bottom';
3647
4969
  }
4970
+ ngOnInit() {
4971
+ this.boundPointerDown = (event) => this.onDocumentPointerDown(event);
4972
+ document.addEventListener('pointerdown', this.boundPointerDown, { capture: true });
4973
+ }
4974
+ ngOnDestroy() {
4975
+ if (this.boundPointerDown) {
4976
+ document.removeEventListener('pointerdown', this.boundPointerDown, { capture: true });
4977
+ }
4978
+ }
3648
4979
  set open(value) {
3649
4980
  this._open = !!value;
3650
4981
  if (this._open) {
@@ -3659,10 +4990,11 @@ class PdmPopoverComponent {
3659
4990
  return this._open;
3660
4991
  }
3661
4992
  get panelClasses() {
4993
+ const baseClasses = 'min-w-80 rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md';
3662
4994
  return [
3663
4995
  this.panelPlacement === 'top'
3664
- ? 'absolute bottom-full left-0 z-30 mb-2 min-w-80 rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md'
3665
- : 'absolute left-0 top-full z-30 mt-2 min-w-80 rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md',
4996
+ ? `absolute bottom-full left-0 ${Z_INDEX.popover} mb-2 ${baseClasses}`
4997
+ : `absolute left-0 top-full ${Z_INDEX.popover} mt-2 ${baseClasses}`,
3666
4998
  this.panelClassName
3667
4999
  ];
3668
5000
  }
@@ -3676,7 +5008,10 @@ class PdmPopoverComponent {
3676
5008
  this.openChange.emit(false);
3677
5009
  }
3678
5010
  }
3679
- onDocumentClick(event) {
5011
+ onViewportChange() {
5012
+ this.updatePanelPlacement();
5013
+ }
5014
+ onDocumentPointerDown(event) {
3680
5015
  if (!this.open)
3681
5016
  return;
3682
5017
  const target = event.target;
@@ -3685,9 +5020,6 @@ class PdmPopoverComponent {
3685
5020
  this.openChange.emit(false);
3686
5021
  }
3687
5022
  }
3688
- onViewportChange() {
3689
- this.updatePanelPlacement();
3690
- }
3691
5023
  schedulePanelPlacementUpdate() {
3692
5024
  setTimeout(() => this.updatePanelPlacement());
3693
5025
  }
@@ -3713,7 +5045,7 @@ class PdmPopoverComponent {
3713
5045
  }
3714
5046
  }
3715
5047
  PdmPopoverComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmPopoverComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
3716
- PdmPopoverComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmPopoverComponent, selector: "pdm-popover", inputs: { triggerText: "triggerText", className: "className", panelClassName: "panelClassName", showTrigger: "showTrigger", open: "open" }, outputs: { openChange: "openChange" }, host: { listeners: { "document:keydown.escape": "onEsc()", "document:click": "onDocumentClick($event)", "window:resize": "onViewportChange()", "window:scroll": "onViewportChange()" } }, viewQueries: [{ propertyName: "anchorRef", first: true, predicate: ["anchorEl"], descendants: true }, { propertyName: "triggerRef", first: true, predicate: ["triggerEl"], descendants: true }, { propertyName: "panelRef", first: true, predicate: ["panelEl"], descendants: true }], ngImport: i0, template: "<div #anchorEl class=\"relative inline-block\" [ngClass]=\"className\">\n <button #triggerEl *ngIf=\"showTrigger\" type=\"button\" class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium text-foreground shadow-sm\" [attr.aria-expanded]=\"open\" (click)=\"toggle()\">{{ triggerText }}</button>\n <div #panelEl *ngIf=\"open || !showTrigger\" [ngClass]=\"panelClasses\">\n <ng-content></ng-content>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5048
+ PdmPopoverComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmPopoverComponent, selector: "pdm-popover", inputs: { triggerText: "triggerText", className: "className", panelClassName: "panelClassName", showTrigger: "showTrigger", open: "open" }, outputs: { openChange: "openChange" }, host: { listeners: { "document:keydown.escape": "onEsc()", "window:resize": "onViewportChange()", "window:scroll": "onViewportChange()" } }, viewQueries: [{ propertyName: "anchorRef", first: true, predicate: ["anchorEl"], descendants: true }, { propertyName: "triggerRef", first: true, predicate: ["triggerEl"], descendants: true }, { propertyName: "panelRef", first: true, predicate: ["panelEl"], descendants: true }], ngImport: i0, template: "<div #anchorEl class=\"relative inline-block\" [ngClass]=\"className\">\n <button #triggerEl *ngIf=\"showTrigger\" type=\"button\" class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium text-foreground shadow-sm\" [attr.aria-expanded]=\"open\" (click)=\"toggle()\">{{ triggerText }}</button>\n <div #panelEl *ngIf=\"open || !showTrigger\" [ngClass]=\"panelClasses\">\n <ng-content></ng-content>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3717
5049
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmPopoverComponent, decorators: [{
3718
5050
  type: Component,
3719
5051
  args: [{ selector: 'pdm-popover', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div #anchorEl class=\"relative inline-block\" [ngClass]=\"className\">\n <button #triggerEl *ngIf=\"showTrigger\" type=\"button\" class=\"inline-flex h-9 appearance-none items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium text-foreground shadow-sm\" [attr.aria-expanded]=\"open\" (click)=\"toggle()\">{{ triggerText }}</button>\n <div #panelEl *ngIf=\"open || !showTrigger\" [ngClass]=\"panelClasses\">\n <ng-content></ng-content>\n </div>\n</div>\n" }]
@@ -3741,9 +5073,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
3741
5073
  }], onEsc: [{
3742
5074
  type: HostListener,
3743
5075
  args: ['document:keydown.escape']
3744
- }], onDocumentClick: [{
3745
- type: HostListener,
3746
- args: ['document:click', ['$event']]
3747
5076
  }], onViewportChange: [{
3748
5077
  type: HostListener,
3749
5078
  args: ['window:resize']
@@ -3857,56 +5186,180 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
3857
5186
  type: Input
3858
5187
  }] } });
3859
5188
 
5189
+ /**
5190
+ * Sheet/Side panel component con soporte responsive
5191
+ *
5192
+ * MEJORADO en v0.2.0:
5193
+ * - Tamaños configurables
5194
+ * - Mejor manejo de overflow
5195
+ * - Responsive sizes
5196
+ *
5197
+ * @example
5198
+ * <pdm-sheet [open]="isOpen" side="right" size="md">
5199
+ * <h3>Settings</h3>
5200
+ * <p>Content here</p>
5201
+ * </pdm-sheet>
5202
+ */
3860
5203
  class PdmSheetComponent {
3861
5204
  constructor() {
3862
5205
  this.open = false;
5206
+ /**
5207
+ * Lado desde donde aparece el sheet
5208
+ */
3863
5209
  this.side = 'right';
5210
+ /**
5211
+ * Tamaño del sheet
5212
+ * - sm: 320px (side) / 40vh (top/bottom)
5213
+ * - md: 400px (side) / 50vh (top/bottom) (default)
5214
+ * - lg: 500px (side) / 66vh (top/bottom)
5215
+ * - xl: 640px (side) / 80vh (top/bottom)
5216
+ * - full: 100%
5217
+ */
5218
+ this.size = 'md';
3864
5219
  this.className = '';
5220
+ this.closeOnEsc = true;
5221
+ this.closeOnBackdropClick = true;
3865
5222
  this.openChange = new EventEmitter();
3866
5223
  }
5224
+ onEsc() {
5225
+ if (this.open && this.closeOnEsc) {
5226
+ this.close();
5227
+ }
5228
+ }
5229
+ onBackdropClick() {
5230
+ if (this.closeOnBackdropClick) {
5231
+ this.close();
5232
+ }
5233
+ }
3867
5234
  close() {
3868
5235
  this.openChange.emit(false);
3869
5236
  }
3870
5237
  get panelClass() {
3871
- if (this.side === 'left')
3872
- return 'left-0 top-0 h-full w-full max-w-[360px] border-r';
3873
- if (this.side === 'top')
3874
- return 'top-0 left-0 w-full border-b';
3875
- if (this.side === 'bottom')
3876
- return 'bottom-0 left-0 w-full border-t';
3877
- return 'right-0 top-0 h-full w-full max-w-[360px] border-l';
5238
+ const base = 'absolute bg-background border-border shadow-lg overflow-auto';
5239
+ const position = this.getPositionClass();
5240
+ const sizing = this.getSizingClass();
5241
+ return `${base} ${position} ${sizing} ${this.className}`.trim();
5242
+ }
5243
+ getPositionClass() {
5244
+ const map = {
5245
+ left: 'left-0 top-0 h-full border-r',
5246
+ right: 'right-0 top-0 h-full border-l',
5247
+ top: 'top-0 left-0 w-full border-b',
5248
+ bottom: 'bottom-0 left-0 w-full border-t'
5249
+ };
5250
+ return map[this.side];
5251
+ }
5252
+ getSizingClass() {
5253
+ if (this.size === 'full') {
5254
+ return 'w-full h-full';
5255
+ }
5256
+ const isHorizontal = this.side === 'left' || this.side === 'right';
5257
+ if (isHorizontal) {
5258
+ const widthMap = {
5259
+ sm: 'w-full max-w-[320px] sm:max-w-[320px]',
5260
+ md: 'w-full max-w-[360px] sm:max-w-[400px]',
5261
+ lg: 'w-full max-w-[400px] sm:max-w-[500px]',
5262
+ xl: 'w-full max-w-[500px] sm:max-w-[640px]'
5263
+ };
5264
+ return widthMap[this.size] || widthMap.md;
5265
+ }
5266
+ else {
5267
+ const heightMap = {
5268
+ sm: 'max-h-[40vh]',
5269
+ md: 'max-h-[50vh]',
5270
+ lg: 'max-h-[66vh]',
5271
+ xl: 'max-h-[80vh]'
5272
+ };
5273
+ return heightMap[this.size] || heightMap.md;
5274
+ }
3878
5275
  }
3879
5276
  }
3880
5277
  PdmSheetComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3881
- PdmSheetComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmSheetComponent, selector: "pdm-sheet", inputs: { open: "open", side: "side", className: "className" }, outputs: { openChange: "openChange" }, ngImport: i0, template: "<div *ngIf=\"open\" class=\"fixed inset-0 z-50\">\n <button type=\"button\" class=\"absolute inset-0 appearance-none border-0 bg-foreground/80 p-0\" aria-label=\"Close sheet\" (click)=\"close()\"></button>\n\n <section [ngClass]=\"['absolute border border-border bg-background p-6 shadow-lg', panelClass, className]\" role=\"dialog\" aria-modal=\"true\">\n <button type=\"button\" class=\"absolute right-3 top-3 appearance-none rounded-sm border-0 bg-transparent p-0 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\" (click)=\"close()\">\n <pdm-icon name=\"x\" [size]=\"16\"></pdm-icon>\n </button>\n <ng-content></ng-content>\n </section>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PdmIconComponent, selector: "pdm-icon", inputs: ["name", "library", "assetUrl", "size", "strokeWidth", "className", "ariaLabel", "decorative"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5278
+ PdmSheetComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmSheetComponent, selector: "pdm-sheet", inputs: { open: "open", side: "side", size: "size", className: "className", closeOnEsc: "closeOnEsc", closeOnBackdropClick: "closeOnBackdropClick" }, outputs: { openChange: "openChange" }, host: { listeners: { "document:keydown.escape": "onEsc()" } }, ngImport: i0, template: "<div *ngIf=\"open\" class=\"fixed inset-0 z-50\">\n <button type=\"button\" class=\"absolute inset-0 appearance-none border-0 bg-foreground/80 p-0\" aria-label=\"Close sheet\" (click)=\"onBackdropClick()\"></button>\n\n <section [ngClass]=\"['absolute z-[60] border border-border bg-background p-6 shadow-lg', panelClass, className]\" role=\"dialog\" aria-modal=\"true\">\n <button type=\"button\" class=\"absolute right-3 top-3 appearance-none rounded-sm border-0 bg-transparent p-0 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\" (click)=\"close()\">\n <pdm-icon name=\"x\" [size]=\"16\"></pdm-icon>\n </button>\n <ng-content></ng-content>\n </section>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PdmIconComponent, selector: "pdm-icon", inputs: ["name", "library", "assetUrl", "size", "strokeWidth", "className", "ariaLabel", "decorative"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3882
5279
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmSheetComponent, decorators: [{
3883
5280
  type: Component,
3884
- args: [{ selector: 'pdm-sheet', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div *ngIf=\"open\" class=\"fixed inset-0 z-50\">\n <button type=\"button\" class=\"absolute inset-0 appearance-none border-0 bg-foreground/80 p-0\" aria-label=\"Close sheet\" (click)=\"close()\"></button>\n\n <section [ngClass]=\"['absolute border border-border bg-background p-6 shadow-lg', panelClass, className]\" role=\"dialog\" aria-modal=\"true\">\n <button type=\"button\" class=\"absolute right-3 top-3 appearance-none rounded-sm border-0 bg-transparent p-0 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\" (click)=\"close()\">\n <pdm-icon name=\"x\" [size]=\"16\"></pdm-icon>\n </button>\n <ng-content></ng-content>\n </section>\n</div>\n" }]
5281
+ args: [{ selector: 'pdm-sheet', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div *ngIf=\"open\" class=\"fixed inset-0 z-50\">\n <button type=\"button\" class=\"absolute inset-0 appearance-none border-0 bg-foreground/80 p-0\" aria-label=\"Close sheet\" (click)=\"onBackdropClick()\"></button>\n\n <section [ngClass]=\"['absolute z-[60] border border-border bg-background p-6 shadow-lg', panelClass, className]\" role=\"dialog\" aria-modal=\"true\">\n <button type=\"button\" class=\"absolute right-3 top-3 appearance-none rounded-sm border-0 bg-transparent p-0 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\" (click)=\"close()\">\n <pdm-icon name=\"x\" [size]=\"16\"></pdm-icon>\n </button>\n <ng-content></ng-content>\n </section>\n</div>\n" }]
3885
5282
  }], propDecorators: { open: [{
3886
5283
  type: Input
3887
5284
  }], side: [{
3888
5285
  type: Input
5286
+ }], size: [{
5287
+ type: Input
3889
5288
  }], className: [{
3890
5289
  type: Input
5290
+ }], closeOnEsc: [{
5291
+ type: Input
5292
+ }], closeOnBackdropClick: [{
5293
+ type: Input
3891
5294
  }], openChange: [{
3892
5295
  type: Output
5296
+ }], onEsc: [{
5297
+ type: HostListener,
5298
+ args: ['document:keydown.escape']
3893
5299
  }] } });
3894
5300
 
5301
+ /**
5302
+ * Sidebar component - Navegación lateral responsive
5303
+ *
5304
+ * MEJORAS en v0.2.0:
5305
+ * - Mobile drawer mode: overlay fullscreen en mobile, sidebar fijo en desktop
5306
+ * - Sidebar mode: sidebar persistente con widths responsive
5307
+ * - Backdrop automático en mobile drawer mode
5308
+ *
5309
+ * @example
5310
+ * <!-- Mobile drawer (default) -->
5311
+ * <pdm-sidebar [open]="sidebarOpen" (openChange)="sidebarOpen = $event">
5312
+ * <nav>Menu items...</nav>
5313
+ * </pdm-sidebar>
5314
+ *
5315
+ * <!-- Sidebar persistente -->
5316
+ * <pdm-sidebar mobileMode="sidebar" [collapsed]="collapsed">
5317
+ * <nav>Menu items...</nav>
5318
+ * </pdm-sidebar>
5319
+ */
3895
5320
  class PdmSidebarComponent {
3896
5321
  constructor() {
5322
+ /**
5323
+ * Mobile behavior: 'drawer' (overlay) o 'sidebar' (persistente)
5324
+ * @default 'drawer'
5325
+ */
5326
+ this.mobileMode = 'drawer';
5327
+ /**
5328
+ * Collapsed state (solo aplica en mobileMode="sidebar")
5329
+ */
3897
5330
  this.collapsed = false;
5331
+ /**
5332
+ * Open state (solo aplica en mobileMode="drawer")
5333
+ */
5334
+ this.open = false;
3898
5335
  this.className = '';
5336
+ /**
5337
+ * Emite cuando el drawer se cierra (solo en mobileMode="drawer")
5338
+ */
5339
+ this.openChange = new EventEmitter();
5340
+ }
5341
+ onBackdropClick() {
5342
+ if (this.mobileMode === 'drawer') {
5343
+ this.open = false;
5344
+ this.openChange.emit(false);
5345
+ }
3899
5346
  }
3900
5347
  }
3901
5348
  PdmSidebarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3902
- PdmSidebarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmSidebarComponent, selector: "pdm-sidebar", inputs: { collapsed: "collapsed", className: "className" }, ngImport: i0, template: "<aside [ngClass]=\"['h-full border-r border-border bg-background transition-all', collapsed ? 'w-14' : 'w-64', className]\">\n <ng-content></ng-content>\n</aside>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5349
+ PdmSidebarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmSidebarComponent, selector: "pdm-sidebar", inputs: { mobileMode: "mobileMode", collapsed: "collapsed", open: "open", className: "className" }, outputs: { openChange: "openChange" }, ngImport: i0, template: "<!-- Mobile drawer mode -->\n<ng-container *ngIf=\"mobileMode === 'drawer'\">\n <!-- Backdrop -->\n <div\n *ngIf=\"open\"\n class=\"fixed inset-0 z-40 bg-black/50 lg:hidden\"\n (click)=\"onBackdropClick()\"\n ></div>\n \n <!-- Drawer -->\n <aside\n [ngClass]=\"[\n 'fixed inset-y-0 left-0 z-50 h-full w-64 transform border-r border-border bg-background transition-transform duration-300 lg:relative lg:z-auto lg:translate-x-0',\n open ? 'translate-x-0' : '-translate-x-full',\n className\n ]\"\n >\n <ng-content></ng-content>\n </aside>\n</ng-container>\n\n<!-- Sidebar persistente mode -->\n<aside\n *ngIf=\"mobileMode === 'sidebar'\"\n [ngClass]=\"[\n 'h-full border-r border-border bg-background transition-all',\n collapsed ? 'w-14 sm:w-14' : 'w-48 sm:w-56 lg:w-64',\n className\n ]\"\n>\n <ng-content></ng-content>\n</aside>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3903
5350
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmSidebarComponent, decorators: [{
3904
5351
  type: Component,
3905
- args: [{ selector: 'pdm-sidebar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<aside [ngClass]=\"['h-full border-r border-border bg-background transition-all', collapsed ? 'w-14' : 'w-64', className]\">\n <ng-content></ng-content>\n</aside>\n" }]
3906
- }], propDecorators: { collapsed: [{
5352
+ args: [{ selector: 'pdm-sidebar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Mobile drawer mode -->\n<ng-container *ngIf=\"mobileMode === 'drawer'\">\n <!-- Backdrop -->\n <div\n *ngIf=\"open\"\n class=\"fixed inset-0 z-40 bg-black/50 lg:hidden\"\n (click)=\"onBackdropClick()\"\n ></div>\n \n <!-- Drawer -->\n <aside\n [ngClass]=\"[\n 'fixed inset-y-0 left-0 z-50 h-full w-64 transform border-r border-border bg-background transition-transform duration-300 lg:relative lg:z-auto lg:translate-x-0',\n open ? 'translate-x-0' : '-translate-x-full',\n className\n ]\"\n >\n <ng-content></ng-content>\n </aside>\n</ng-container>\n\n<!-- Sidebar persistente mode -->\n<aside\n *ngIf=\"mobileMode === 'sidebar'\"\n [ngClass]=\"[\n 'h-full border-r border-border bg-background transition-all',\n collapsed ? 'w-14 sm:w-14' : 'w-48 sm:w-56 lg:w-64',\n className\n ]\"\n>\n <ng-content></ng-content>\n</aside>\n" }]
5353
+ }], propDecorators: { mobileMode: [{
5354
+ type: Input
5355
+ }], collapsed: [{
5356
+ type: Input
5357
+ }], open: [{
3907
5358
  type: Input
3908
5359
  }], className: [{
3909
5360
  type: Input
5361
+ }], openChange: [{
5362
+ type: Output
3910
5363
  }] } });
3911
5364
 
3912
5365
  class PdmSkeletonComponent {
@@ -4073,212 +5526,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
4073
5526
  type: Output
4074
5527
  }] } });
4075
5528
 
4076
- class PdmTableComponent {
4077
- constructor(renderer) {
4078
- this.renderer = renderer;
4079
- this.variant = 'default';
4080
- this.reorderRows = false;
4081
- this.dragHandleSelector = '[data-drag-handle],[data-slot=row-drag-handle],.row-drag-handle,[data-auto-drag-handle]';
4082
- this.className = '';
4083
- this.rowOrderChange = new EventEmitter();
4084
- this.cleanupListeners = [];
4085
- this.draggedRow = null;
4086
- }
4087
- ngAfterViewInit() {
4088
- this.syncReorderBehavior();
4089
- }
4090
- ngOnChanges(changes) {
4091
- if (changes['reorderRows'] || changes['variant']) {
4092
- this.syncReorderBehavior();
4093
- }
4094
- }
4095
- ngOnDestroy() {
4096
- this.cleanupReorderBehavior();
4097
- }
4098
- get wrapperClasses() {
4099
- return [
4100
- 'relative w-full overflow-auto',
4101
- this.variant === 'interactive' ? 'overflow-x-auto overflow-y-hidden rounded-xl border border-border bg-background' : '',
4102
- this.variant === 'data' ? 'overflow-hidden rounded-md border border-border bg-background' : '',
4103
- this.className
4104
- ];
4105
- }
4106
- get tableClasses() {
4107
- return [
4108
- 'w-full caption-bottom text-sm',
4109
- this.variant === 'data'
4110
- ? 'border-collapse text-foreground [&_thead_tr]:border-b [&_thead_tr]:border-border [&_tbody_tr]:border-b [&_tbody_tr]:border-border [&_tbody_tr:last-child]:border-b-0 [&_th]:h-10 [&_th]:px-2 [&_th]:text-left [&_th]:align-middle [&_th]:font-medium [&_td]:p-2 [&_td]:align-middle'
4111
- : '',
4112
- this.variant === 'interactive'
4113
- ? 'text-foreground [&_thead]:sticky [&_thead]:top-0 [&_thead]:z-10 [&_thead]:bg-muted/70 [&_thead_tr]:border-b [&_thead_tr]:border-border [&_th]:h-12 [&_th]:px-4 [&_th]:text-left [&_th]:align-middle [&_th]:text-sm [&_th]:font-medium [&_th]:whitespace-nowrap [&_tbody_tr]:border-b [&_tbody_tr]:border-border [&_tbody_tr]:transition-colors [&_tbody_tr:hover]:bg-muted/50 [&_tbody_tr:last-child]:border-b-0 [&_td]:h-14 [&_td]:px-4 [&_td]:align-middle [&_td]:text-sm [&_td]:whitespace-nowrap [&_td:first-child]:w-10 [&_td:last-child]:w-10 [&_svg]:text-muted-foreground'
4114
- : ''
4115
- ];
4116
- }
4117
- syncReorderBehavior() {
4118
- this.cleanupReorderBehavior();
4119
- if (!this.isReorderEnabled) {
4120
- return;
4121
- }
4122
- const tbody = this.getTbody();
4123
- if (!tbody) {
4124
- return;
4125
- }
4126
- this.setRowsDraggable(tbody, true);
4127
- this.cleanupListeners.push(this.renderer.listen(tbody, 'mousedown', (event) => this.armDragFromHandle(event)), this.renderer.listen(tbody, 'dragstart', (event) => this.onDragStart(event)), this.renderer.listen(tbody, 'dragover', (event) => this.onDragOver(event, tbody)), this.renderer.listen(tbody, 'drop', (event) => this.onDrop(event)), this.renderer.listen(tbody, 'dragend', () => this.onDragEnd()));
4128
- this.observer = new MutationObserver(() => this.setRowsDraggable(tbody, true));
4129
- this.observer.observe(tbody, { childList: true });
4130
- }
4131
- cleanupReorderBehavior() {
4132
- this.cleanupListeners.forEach((dispose) => dispose());
4133
- this.cleanupListeners = [];
4134
- if (this.observer) {
4135
- this.observer.disconnect();
4136
- this.observer = undefined;
4137
- }
4138
- const tbody = this.getTbody();
4139
- if (tbody) {
4140
- this.setRowsDraggable(tbody, false);
4141
- }
4142
- this.draggedRow = null;
4143
- }
4144
- get isReorderEnabled() {
4145
- return this.reorderRows;
4146
- }
4147
- getTbody() {
4148
- return this.tableElement?.nativeElement.tBodies.item(0) ?? null;
4149
- }
4150
- setRowsDraggable(tbody, enabled) {
4151
- const rows = Array.from(tbody.rows);
4152
- rows.forEach((row) => {
4153
- this.syncAutoDragHandle(row, enabled);
4154
- row.draggable = false;
4155
- if (!enabled) {
4156
- delete row.dataset['dragging'];
4157
- delete row.dataset['dragArmed'];
4158
- }
4159
- });
4160
- }
4161
- syncAutoDragHandle(row, enabled) {
4162
- const firstCell = row.cells.item(0);
4163
- if (!firstCell) {
4164
- return;
4165
- }
4166
- const existingAutoHandle = firstCell.querySelector('[data-auto-drag-handle]');
4167
- if (!enabled) {
4168
- existingAutoHandle?.remove();
4169
- return;
4170
- }
4171
- const hasCustomHandle = !!firstCell.querySelector('[data-drag-handle],[data-slot=row-drag-handle],.row-drag-handle');
4172
- if (hasCustomHandle || existingAutoHandle) {
4173
- return;
4174
- }
4175
- const button = this.renderer.createElement('button');
4176
- this.renderer.setAttribute(button, 'type', 'button');
4177
- this.renderer.setAttribute(button, 'aria-label', 'Drag row');
4178
- this.renderer.setAttribute(button, 'data-auto-drag-handle', 'true');
4179
- this.renderer.addClass(button, 'inline-flex');
4180
- this.renderer.addClass(button, 'h-7');
4181
- this.renderer.addClass(button, 'w-7');
4182
- this.renderer.addClass(button, 'items-center');
4183
- this.renderer.addClass(button, 'justify-center');
4184
- this.renderer.addClass(button, 'cursor-grab');
4185
- this.renderer.addClass(button, 'active:cursor-grabbing');
4186
- this.renderer.addClass(button, 'text-muted-foreground');
4187
- const dots = this.renderer.createElement('span');
4188
- this.renderer.addClass(dots, 'text-sm');
4189
- this.renderer.addClass(dots, 'leading-none');
4190
- this.renderer.setProperty(dots, 'textContent', '⋮⋮');
4191
- this.renderer.appendChild(button, dots);
4192
- this.renderer.insertBefore(firstCell, button, firstCell.firstChild);
4193
- }
4194
- onDragStart(event) {
4195
- const target = event.target;
4196
- const row = target?.closest('tr');
4197
- if (!row) {
4198
- return;
4199
- }
4200
- const handle = target?.closest(this.dragHandleSelector);
4201
- const isArmed = row.dataset['dragArmed'] === 'true';
4202
- if ((!handle || !row.contains(handle)) && !isArmed) {
4203
- event.preventDefault();
4204
- return;
4205
- }
4206
- this.draggedRow = row;
4207
- this.draggedRow.dataset['dragging'] = 'true';
4208
- if (event.dataTransfer) {
4209
- event.dataTransfer.effectAllowed = 'move';
4210
- event.dataTransfer.setData('text/plain', '');
4211
- }
4212
- }
4213
- onDragOver(event, tbody) {
4214
- if (!this.draggedRow) {
4215
- return;
4216
- }
4217
- event.preventDefault();
4218
- const target = event.target;
4219
- const targetRow = target?.closest('tr');
4220
- if (!targetRow || targetRow === this.draggedRow) {
4221
- return;
4222
- }
4223
- const rect = targetRow.getBoundingClientRect();
4224
- const shouldInsertBefore = event.clientY < rect.top + rect.height / 2;
4225
- tbody.insertBefore(this.draggedRow, shouldInsertBefore ? targetRow : targetRow.nextSibling);
4226
- }
4227
- onDrop(event) {
4228
- event.preventDefault();
4229
- }
4230
- onDragEnd() {
4231
- const tbody = this.getTbody();
4232
- if (tbody) {
4233
- Array.from(tbody.rows).forEach((row) => {
4234
- row.draggable = false;
4235
- delete row.dataset['dragArmed'];
4236
- });
4237
- }
4238
- if (this.draggedRow) {
4239
- delete this.draggedRow.dataset['dragging'];
4240
- this.draggedRow = null;
4241
- }
4242
- if (!tbody) {
4243
- return;
4244
- }
4245
- const order = Array.from(tbody.rows).map((row, index) => row.getAttribute('data-row-id') || String(index));
4246
- this.rowOrderChange.emit(order);
4247
- }
4248
- armDragFromHandle(event) {
4249
- const target = event.target;
4250
- const handle = target?.closest(this.dragHandleSelector);
4251
- if (!handle) {
4252
- return;
4253
- }
4254
- const row = handle.closest('tr');
4255
- if (!row) {
4256
- return;
4257
- }
4258
- row.draggable = true;
4259
- row.dataset['dragArmed'] = 'true';
4260
- }
4261
- }
4262
- PdmTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTableComponent, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
4263
- PdmTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmTableComponent, selector: "pdm-table", inputs: { variant: "variant", reorderRows: "reorderRows", dragHandleSelector: "dragHandleSelector", className: "className" }, outputs: { rowOrderChange: "rowOrderChange" }, viewQueries: [{ propertyName: "tableElement", first: true, predicate: ["tableElement"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div [ngClass]=\"wrapperClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table-container' : null\">\n <table #tableElement [ngClass]=\"tableClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table' : null\">\n <ng-content></ng-content>\n </table>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4264
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTableComponent, decorators: [{
4265
- type: Component,
4266
- args: [{ selector: 'pdm-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [ngClass]=\"wrapperClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table-container' : null\">\n <table #tableElement [ngClass]=\"tableClasses\" [attr.data-slot]=\"variant === 'interactive' ? 'table' : null\">\n <ng-content></ng-content>\n </table>\n</div>\n" }]
4267
- }], ctorParameters: function () { return [{ type: i0.Renderer2 }]; }, propDecorators: { variant: [{
4268
- type: Input
4269
- }], reorderRows: [{
4270
- type: Input
4271
- }], dragHandleSelector: [{
4272
- type: Input
4273
- }], className: [{
4274
- type: Input
4275
- }], rowOrderChange: [{
4276
- type: Output
4277
- }], tableElement: [{
4278
- type: ViewChild,
4279
- args: ['tableElement']
4280
- }] } });
4281
-
4282
5529
  class PdmTabsComponent {
4283
5530
  constructor(cdr) {
4284
5531
  this.cdr = cdr;
@@ -4296,10 +5543,10 @@ class PdmTabsComponent {
4296
5543
  }
4297
5544
  }
4298
5545
  PdmTabsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTabsComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
4299
- PdmTabsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmTabsComponent, selector: "pdm-tabs", inputs: { items: "items", value: "value", className: "className" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div [ngClass]=\"['w-full', className]\">\n <div role=\"tablist\" class=\"inline-flex h-8 w-fit items-center justify-center rounded-lg bg-muted p-[3px] text-muted-foreground\">\n <button\n *ngFor=\"let item of items\"\n role=\"tab\"\n [attr.aria-selected]=\"value === item.value\"\n [disabled]=\"item.disabled\"\n [ngClass]=\"[\n 'relative inline-flex h-[calc(100%-1px)] appearance-none flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium transition-all focus-visible:border-ring focus-visible:outline-none focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50',\n value === item.value ? 'bg-background text-foreground shadow-sm' : 'bg-transparent text-muted-foreground'\n ]\"\n (click)=\"select(item)\"\n type=\"button\"\n >\n {{ item.label }}\n </button>\n </div>\n <div class=\"mt-4\">\n <ng-content></ng-content>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5546
+ PdmTabsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmTabsComponent, selector: "pdm-tabs", inputs: { items: "items", value: "value", className: "className" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div [ngClass]=\"['w-full', className]\">\n <div\n role=\"tablist\"\n class=\"inline-flex h-8 w-full items-center overflow-x-auto scrollbar-thin rounded-lg bg-muted p-[3px] text-muted-foreground md:w-fit\"\n >\n <button\n *ngFor=\"let item of items\"\n role=\"tab\"\n [attr.aria-selected]=\"value === item.value\"\n [disabled]=\"item.disabled\"\n [ngClass]=\"[\n 'relative inline-flex h-[calc(100%-1px)] appearance-none flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-3 py-0.5 text-sm font-medium transition-all focus-visible:border-ring focus-visible:outline-none focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 md:flex-initial md:px-4',\n value === item.value ? 'bg-background text-foreground shadow-sm' : 'bg-transparent text-muted-foreground'\n ]\"\n (click)=\"select(item)\"\n type=\"button\"\n >\n {{ item.label }}\n </button>\n </div>\n <div class=\"mt-4\">\n <ng-content></ng-content>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4300
5547
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTabsComponent, decorators: [{
4301
5548
  type: Component,
4302
- args: [{ selector: 'pdm-tabs', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [ngClass]=\"['w-full', className]\">\n <div role=\"tablist\" class=\"inline-flex h-8 w-fit items-center justify-center rounded-lg bg-muted p-[3px] text-muted-foreground\">\n <button\n *ngFor=\"let item of items\"\n role=\"tab\"\n [attr.aria-selected]=\"value === item.value\"\n [disabled]=\"item.disabled\"\n [ngClass]=\"[\n 'relative inline-flex h-[calc(100%-1px)] appearance-none flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium transition-all focus-visible:border-ring focus-visible:outline-none focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50',\n value === item.value ? 'bg-background text-foreground shadow-sm' : 'bg-transparent text-muted-foreground'\n ]\"\n (click)=\"select(item)\"\n type=\"button\"\n >\n {{ item.label }}\n </button>\n </div>\n <div class=\"mt-4\">\n <ng-content></ng-content>\n </div>\n</div>\n" }]
5549
+ args: [{ selector: 'pdm-tabs', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [ngClass]=\"['w-full', className]\">\n <div\n role=\"tablist\"\n class=\"inline-flex h-8 w-full items-center overflow-x-auto scrollbar-thin rounded-lg bg-muted p-[3px] text-muted-foreground md:w-fit\"\n >\n <button\n *ngFor=\"let item of items\"\n role=\"tab\"\n [attr.aria-selected]=\"value === item.value\"\n [disabled]=\"item.disabled\"\n [ngClass]=\"[\n 'relative inline-flex h-[calc(100%-1px)] appearance-none flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-3 py-0.5 text-sm font-medium transition-all focus-visible:border-ring focus-visible:outline-none focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 md:flex-initial md:px-4',\n value === item.value ? 'bg-background text-foreground shadow-sm' : 'bg-transparent text-muted-foreground'\n ]\"\n (click)=\"select(item)\"\n type=\"button\"\n >\n {{ item.label }}\n </button>\n </div>\n <div class=\"mt-4\">\n <ng-content></ng-content>\n </div>\n</div>\n" }]
4303
5550
  }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { items: [{
4304
5551
  type: Input
4305
5552
  }], value: [{
@@ -4450,10 +5697,10 @@ class PdmTooltipComponent {
4450
5697
  }
4451
5698
  }
4452
5699
  PdmTooltipComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4453
- PdmTooltipComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmTooltipComponent, selector: "pdm-tooltip", inputs: { text: "text", side: "side", className: "className" }, ngImport: i0, template: "<span class=\"relative inline-flex\" [ngClass]=\"className\" (mouseenter)=\"open = true\" (mouseleave)=\"open = false\" (focusin)=\"open = true\" (focusout)=\"open = false\">\n <ng-content></ng-content>\n <span *ngIf=\"open\" [ngClass]=\"['pointer-events-none absolute z-50 overflow-hidden rounded-md bg-foreground px-3 py-1.5 text-xs text-background animate-in fade-in-0 zoom-in-95', positionClass]\">\n {{ text }}\n </span>\n</span>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5700
+ PdmTooltipComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: PdmTooltipComponent, selector: "pdm-tooltip", inputs: { text: "text", side: "side", className: "className" }, ngImport: i0, template: "<span class=\"relative inline-flex\" [ngClass]=\"className\" (mouseenter)=\"open = true\" (mouseleave)=\"open = false\" (focusin)=\"open = true\" (focusout)=\"open = false\">\n <ng-content></ng-content>\n <span *ngIf=\"open\" [ngClass]=\"['pointer-events-none absolute z-[70] overflow-hidden rounded-md bg-foreground px-3 py-1.5 text-xs text-background animate-in fade-in-0 zoom-in-95', positionClass]\">\n {{ text }}\n </span>\n</span>\n", dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4454
5701
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTooltipComponent, decorators: [{
4455
5702
  type: Component,
4456
- args: [{ selector: 'pdm-tooltip', changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"relative inline-flex\" [ngClass]=\"className\" (mouseenter)=\"open = true\" (mouseleave)=\"open = false\" (focusin)=\"open = true\" (focusout)=\"open = false\">\n <ng-content></ng-content>\n <span *ngIf=\"open\" [ngClass]=\"['pointer-events-none absolute z-50 overflow-hidden rounded-md bg-foreground px-3 py-1.5 text-xs text-background animate-in fade-in-0 zoom-in-95', positionClass]\">\n {{ text }}\n </span>\n</span>\n" }]
5703
+ args: [{ selector: 'pdm-tooltip', changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"relative inline-flex\" [ngClass]=\"className\" (mouseenter)=\"open = true\" (mouseleave)=\"open = false\" (focusin)=\"open = true\" (focusout)=\"open = false\">\n <ng-content></ng-content>\n <span *ngIf=\"open\" [ngClass]=\"['pointer-events-none absolute z-[70] overflow-hidden rounded-md bg-foreground px-3 py-1.5 text-xs text-background animate-in fade-in-0 zoom-in-95', positionClass]\">\n {{ text }}\n </span>\n</span>\n" }]
4457
5704
  }], propDecorators: { text: [{
4458
5705
  type: Input
4459
5706
  }], side: [{
@@ -4484,6 +5731,7 @@ const COMPONENTS = [
4484
5731
  PdmDataTableComponent,
4485
5732
  PdmDatePickerComponent,
4486
5733
  PdmDialogComponent,
5734
+ PdmDraggableTableComponent,
4487
5735
  PdmDropdownMenuComponent,
4488
5736
  PdmDrawerComponent,
4489
5737
  PdmEmptyComponent,
@@ -4500,6 +5748,7 @@ const COMPONENTS = [
4500
5748
  PdmMenubarComponent,
4501
5749
  PdmNativeSelectComponent,
4502
5750
  PdmNavigationMenuComponent,
5751
+ PdmOutsideClickDirective,
4503
5752
  PdmPaginationComponent,
4504
5753
  PdmPopoverComponent,
4505
5754
  PdmProgressComponent,
@@ -4546,6 +5795,7 @@ PdmUiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
4546
5795
  PdmDataTableComponent,
4547
5796
  PdmDatePickerComponent,
4548
5797
  PdmDialogComponent,
5798
+ PdmDraggableTableComponent,
4549
5799
  PdmDropdownMenuComponent,
4550
5800
  PdmDrawerComponent,
4551
5801
  PdmEmptyComponent,
@@ -4562,6 +5812,7 @@ PdmUiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
4562
5812
  PdmMenubarComponent,
4563
5813
  PdmNativeSelectComponent,
4564
5814
  PdmNavigationMenuComponent,
5815
+ PdmOutsideClickDirective,
4565
5816
  PdmPaginationComponent,
4566
5817
  PdmPopoverComponent,
4567
5818
  PdmProgressComponent,
@@ -4603,6 +5854,7 @@ PdmUiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
4603
5854
  PdmDataTableComponent,
4604
5855
  PdmDatePickerComponent,
4605
5856
  PdmDialogComponent,
5857
+ PdmDraggableTableComponent,
4606
5858
  PdmDropdownMenuComponent,
4607
5859
  PdmDrawerComponent,
4608
5860
  PdmEmptyComponent,
@@ -4619,6 +5871,7 @@ PdmUiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
4619
5871
  PdmMenubarComponent,
4620
5872
  PdmNativeSelectComponent,
4621
5873
  PdmNavigationMenuComponent,
5874
+ PdmOutsideClickDirective,
4622
5875
  PdmPaginationComponent,
4623
5876
  PdmPopoverComponent,
4624
5877
  PdmProgressComponent,
@@ -4654,5 +5907,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
4654
5907
  * Generated bundle index. Do not edit.
4655
5908
  */
4656
5909
 
4657
- export { PdmAccordionComponent, PdmAlertComponent, PdmAlertDialogComponent, PdmAspectRatioComponent, PdmAvatarComponent, PdmBadgeComponent, PdmBreadcrumbComponent, PdmButtonComponent, PdmButtonGroupComponent, PdmCalendarComponent, PdmCardComponent, PdmCarouselComponent, PdmChartComponent, PdmCheckboxComponent, PdmCollapsibleComponent, PdmComboboxComponent, PdmCommandComponent, PdmContextMenuComponent, PdmDataTableComponent, PdmDatePickerComponent, PdmDialogComponent, PdmDrawerComponent, PdmDropdownMenuComponent, PdmEmptyComponent, PdmFieldComponent, PdmHoverCardComponent, PdmIconComponent, PdmInputComponent, PdmInputGroupComponent, PdmInputOtpComponent, PdmInputPasswordComponent, PdmItemComponent, PdmKbdComponent, PdmLabelComponent, PdmMenubarComponent, PdmNativeSelectComponent, PdmNavigationMenuComponent, PdmPaginationComponent, PdmPopoverComponent, PdmProgressComponent, PdmRadioGroupComponent, PdmScrollAreaComponent, PdmSelectComponent, PdmSelectOptionDirective, PdmSeparatorComponent, PdmSheetComponent, PdmSidebarComponent, PdmSkeletonComponent, PdmSliderComponent, PdmSonnerComponent, PdmSpinnerComponent, PdmSwitchComponent, PdmTableComponent, PdmTabsComponent, PdmTextareaComponent, PdmToggleComponent, PdmToggleGroupComponent, PdmTooltipComponent, PdmUiKitModule, createFlexiblePositionStrategy };
5910
+ export { BREAKPOINTS, PdmAccordionComponent, PdmAlertComponent, PdmAlertDialogComponent, PdmAspectRatioComponent, PdmAvatarComponent, PdmBadgeComponent, PdmBreadcrumbComponent, PdmButtonComponent, PdmButtonGroupComponent, PdmCalendarComponent, PdmCardComponent, PdmCarouselComponent, PdmChartComponent, PdmCheckboxComponent, PdmCollapsibleComponent, PdmComboboxComponent, PdmCommandComponent, PdmContextMenuComponent, PdmDataTableComponent, PdmDatePickerComponent, PdmDialogComponent, PdmDraggableTableComponent, PdmDrawerComponent, PdmDropdownMenuComponent, PdmEmptyComponent, PdmFieldComponent, PdmHoverCardComponent, PdmIconComponent, PdmInputComponent, PdmInputGroupComponent, PdmInputOtpComponent, PdmInputPasswordComponent, PdmItemComponent, PdmKbdComponent, PdmLabelComponent, PdmMenubarComponent, PdmNativeSelectComponent, PdmNavigationMenuComponent, PdmOutsideClickDirective, PdmPaginationComponent, PdmPopoverComponent, PdmProgressComponent, PdmRadioGroupComponent, PdmScrollAreaComponent, PdmSelectComponent, PdmSelectOptionDirective, PdmSeparatorComponent, PdmSheetComponent, PdmSidebarComponent, PdmSkeletonComponent, PdmSliderComponent, PdmSonnerComponent, PdmSpinnerComponent, PdmSwitchComponent, PdmTableComponent, PdmTabsComponent, PdmTextareaComponent, PdmToggleComponent, PdmToggleGroupComponent, PdmTooltipComponent, PdmUiKitModule, RESPONSIVE_CONTAINER, RESPONSIVE_DISPLAY, TABLE_RESPONSIVE, Z_INDEX, createFlexiblePositionStrategy, logZIndexStack, overflowResponsive, responsive, spacingResponsive, widthResponsive };
4658
5911
  //# sourceMappingURL=pdm-ui-kit.mjs.map