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';
@@ -91,35 +91,51 @@ class PdmAlertDialogComponent {
91
91
  this.confirmText = 'Continue';
92
92
  this.cancelText = 'Cancel';
93
93
  this.className = '';
94
+ /** Close when the ESC key is pressed. Default: `true`. */
95
+ this.closeOnEsc = true;
94
96
  this.openChange = new EventEmitter();
95
97
  this.confirm = new EventEmitter();
96
98
  this.cancel = new EventEmitter();
97
99
  }
100
+ /**
101
+ * Returns `true` when at least one consumer listens to `openChange`.
102
+ * - **Controlled** (has observers): parent manages `open` via two-way binding → only emit.
103
+ * - **Uncontrolled** (no observers): we own the `open` state → mutate it locally.
104
+ */
105
+ get isControlled() {
106
+ return this.openChange.observed;
107
+ }
98
108
  onTriggerClick() {
99
- this.open = true;
109
+ if (!this.isControlled) {
110
+ this.open = true;
111
+ }
100
112
  this.openChange.emit(true);
101
113
  }
102
114
  onCancel() {
103
115
  this.cancel.emit();
104
- this.open = false;
116
+ if (!this.isControlled) {
117
+ this.open = false;
118
+ }
105
119
  this.openChange.emit(false);
106
120
  }
107
121
  onConfirm() {
108
122
  this.confirm.emit();
109
- this.open = false;
123
+ if (!this.isControlled) {
124
+ this.open = false;
125
+ }
110
126
  this.openChange.emit(false);
111
127
  }
112
128
  onEsc() {
113
- if (this.open) {
129
+ if (this.open && this.closeOnEsc) {
114
130
  this.onCancel();
115
131
  }
116
132
  }
117
133
  }
118
134
  PdmAlertDialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmAlertDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
119
- 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 });
135
+ 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 });
120
136
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmAlertDialogComponent, decorators: [{
121
137
  type: Component,
122
- 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" }]
138
+ 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" }]
123
139
  }], propDecorators: { open: [{
124
140
  type: Input
125
141
  }], showTrigger: [{
@@ -136,6 +152,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
136
152
  type: Input
137
153
  }], className: [{
138
154
  type: Input
155
+ }], closeOnEsc: [{
156
+ type: Input
139
157
  }], openChange: [{
140
158
  type: Output
141
159
  }], confirm: [{
@@ -238,30 +256,63 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
238
256
  type: Input
239
257
  }] } });
240
258
 
259
+ /**
260
+ * Breadcrumb component con soporte responsive
261
+ *
262
+ * MEJORADO en v0.2.0:
263
+ * - Modo responsive real con overflow-x-auto
264
+ * - Collapse inteligente en mobile
265
+ *
266
+ * @example
267
+ * <pdm-breadcrumb
268
+ * mode="responsive"
269
+ * [items]="['Home', 'Products', 'Electronics', 'Laptops']">
270
+ * </pdm-breadcrumb>
271
+ */
241
272
  class PdmBreadcrumbComponent {
242
273
  constructor() {
243
274
  this.mode = 'link-component';
244
275
  this.items = ['Home', 'Components', 'Breadcrumb'];
245
276
  this.className = '';
277
+ /**
278
+ * Cantidad mínima de items para mostrar en mobile cuando mode="responsive"
279
+ * Default: 2 (primer y último item)
280
+ */
281
+ this.minItemsMobile = 2;
246
282
  }
247
283
  get renderedItems() {
248
- if ((this.mode === 'collapsed' || this.mode === 'responsive') && this.items.length > 3) {
284
+ if (this.mode === 'collapsed' && this.items.length > 3) {
249
285
  return [this.items[0], '...', this.items[this.items.length - 2], this.items[this.items.length - 1]];
250
286
  }
287
+ // Responsive mode: no collapse en el TS, se maneja en el template con CSS
251
288
  return this.items;
252
289
  }
290
+ /**
291
+ * Determina si un item debe estar visible en mobile (modo responsive)
292
+ */
293
+ shouldShowInMobile(index) {
294
+ if (this.mode !== 'responsive')
295
+ return true;
296
+ const totalItems = this.items.length;
297
+ if (totalItems <= this.minItemsMobile)
298
+ return true;
299
+ // Siempre mostrar primero y último
300
+ return index === 0 || index === totalItems - 1;
301
+ }
253
302
  }
254
303
  PdmBreadcrumbComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
255
- 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 });
304
+ 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 });
256
305
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmBreadcrumbComponent, decorators: [{
257
306
  type: Component,
258
- 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" }]
307
+ 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" }]
259
308
  }], propDecorators: { mode: [{
260
309
  type: Input
261
310
  }], items: [{
262
311
  type: Input
263
312
  }], className: [{
264
313
  type: Input
314
+ }], minItemsMobile: [{
315
+ type: Input
265
316
  }] } });
266
317
 
267
318
  class PdmButtonGroupComponent {
@@ -1046,10 +1097,10 @@ class PdmCalendarComponent {
1046
1097
  }
1047
1098
  }
1048
1099
  PdmCalendarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCalendarComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1049
- 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 });
1100
+ 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 });
1050
1101
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCalendarComponent, decorators: [{
1051
1102
  type: Component,
1052
- 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" }]
1103
+ 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" }]
1053
1104
  }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { variant: [{
1054
1105
  type: Input
1055
1106
  }], className: [{
@@ -1187,66 +1238,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
1187
1238
  type: Output
1188
1239
  }] } });
1189
1240
 
1241
+ /**
1242
+ * Card component - Visual container primitivo
1243
+ *
1244
+ * BREAKING CHANGE en v0.2.0: variant="login" eliminado.
1245
+ * Card es ahora un componente UI puro sin lógica de negocio.
1246
+ *
1247
+ * Para crear un login form, componer con primitivos:
1248
+ *
1249
+ * @example
1250
+ * <pdm-card>
1251
+ * <div pdmCardHeader>
1252
+ * <h3 class="text-lg font-semibold">Login</h3>
1253
+ * <p class="text-sm text-muted-foreground">Enter your credentials</p>
1254
+ * </div>
1255
+ * <div pdmCardContent>
1256
+ * <form [formGroup]="form">
1257
+ * <pdm-field>
1258
+ * <pdm-label>Email</pdm-label>
1259
+ * <pdm-input type="email" formControlName="email" />
1260
+ * </pdm-field>
1261
+ * <pdm-field>
1262
+ * <pdm-label>Password</pdm-label>
1263
+ * <pdm-input-password formControlName="password" />
1264
+ * </pdm-field>
1265
+ * </form>
1266
+ * </div>
1267
+ * <div pdmCardFooter>
1268
+ * <pdm-button (click)="onLogin()">Login</pdm-button>
1269
+ * </div>
1270
+ * </pdm-card>
1271
+ */
1190
1272
  class PdmCardComponent {
1191
1273
  constructor() {
1192
- this.variant = 'default';
1193
1274
  this.className = '';
1194
- this.title = 'Login to your account';
1195
- this.description = 'Enter your email below to login to your account';
1196
- this.actionText = 'Sign up';
1197
- this.emailLabel = 'Email';
1198
- this.emailPlaceholder = 'm@example.com';
1199
- this.passwordLabel = 'Password';
1200
- this.passwordHint = 'Forgot password?';
1201
- this.primaryActionText = 'Login';
1202
- this.secondaryActionText = 'Login with Google';
1203
- this.primaryAction = new EventEmitter();
1204
- this.secondaryAction = new EventEmitter();
1205
- this.actionPressed = new EventEmitter();
1206
- }
1207
- onPrimaryAction() {
1208
- this.primaryAction.emit();
1209
- }
1210
- onSecondaryAction() {
1211
- this.secondaryAction.emit();
1212
- }
1213
- onActionPressed() {
1214
- this.actionPressed.emit();
1215
1275
  }
1216
1276
  }
1217
1277
  PdmCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1218
- 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 });
1278
+ 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 });
1219
1279
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCardComponent, decorators: [{
1220
1280
  type: Component,
1221
- 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" }]
1222
- }], propDecorators: { variant: [{
1223
- type: Input
1224
- }], className: [{
1225
- type: Input
1226
- }], title: [{
1227
- type: Input
1228
- }], description: [{
1229
- type: Input
1230
- }], actionText: [{
1231
- type: Input
1232
- }], emailLabel: [{
1233
- type: Input
1234
- }], emailPlaceholder: [{
1235
- type: Input
1236
- }], passwordLabel: [{
1237
- type: Input
1238
- }], passwordHint: [{
1239
- type: Input
1240
- }], primaryActionText: [{
1241
- type: Input
1242
- }], secondaryActionText: [{
1281
+ 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" }]
1282
+ }], propDecorators: { className: [{
1243
1283
  type: Input
1244
- }], primaryAction: [{
1245
- type: Output
1246
- }], secondaryAction: [{
1247
- type: Output
1248
- }], actionPressed: [{
1249
- type: Output
1250
1284
  }] } });
1251
1285
 
1252
1286
  class PdmChartComponent {
@@ -1746,10 +1780,10 @@ class PdmCommandComponent {
1746
1780
  }
1747
1781
  }
1748
1782
  PdmCommandComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCommandComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1749
- 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 });
1783
+ 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 });
1750
1784
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmCommandComponent, decorators: [{
1751
1785
  type: Component,
1752
- 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" }]
1786
+ 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" }]
1753
1787
  }], propDecorators: { open: [{
1754
1788
  type: Input
1755
1789
  }], hintLabel: [{
@@ -1796,6 +1830,15 @@ class PdmContextMenuComponent {
1796
1830
  this.x = 0;
1797
1831
  this.y = 0;
1798
1832
  }
1833
+ ngOnInit() {
1834
+ this.boundPointerDown = (event) => this.onDocumentPointerDown(event);
1835
+ document.addEventListener('pointerdown', this.boundPointerDown, { capture: true });
1836
+ }
1837
+ ngOnDestroy() {
1838
+ if (this.boundPointerDown) {
1839
+ document.removeEventListener('pointerdown', this.boundPointerDown, { capture: true });
1840
+ }
1841
+ }
1799
1842
  onContextMenu(event) {
1800
1843
  event.preventDefault();
1801
1844
  this.x = event.clientX;
@@ -1809,9 +1852,11 @@ class PdmContextMenuComponent {
1809
1852
  this.open = false;
1810
1853
  }
1811
1854
  onEsc() {
1812
- this.open = false;
1855
+ if (this.open) {
1856
+ this.open = false;
1857
+ }
1813
1858
  }
1814
- onDocumentClick(event) {
1859
+ onDocumentPointerDown(event) {
1815
1860
  if (!this.open)
1816
1861
  return;
1817
1862
  const target = event.target;
@@ -1821,10 +1866,10 @@ class PdmContextMenuComponent {
1821
1866
  }
1822
1867
  }
1823
1868
  PdmContextMenuComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmContextMenuComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
1824
- 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 });
1869
+ 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 });
1825
1870
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmContextMenuComponent, decorators: [{
1826
1871
  type: Component,
1827
- 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" }]
1872
+ 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" }]
1828
1873
  }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { items: [{
1829
1874
  type: Input
1830
1875
  }], className: [{
@@ -1840,36 +1885,461 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
1840
1885
  }], onEsc: [{
1841
1886
  type: HostListener,
1842
1887
  args: ['document:keydown.escape']
1843
- }], onDocumentClick: [{
1844
- type: HostListener,
1845
- args: ['document:click', ['$event']]
1846
1888
  }] } });
1847
1889
 
1890
+ /**
1891
+ * Sistema de responsive utilities para PDM UI Kit
1892
+ *
1893
+ * Proporciona constantes, tipos y helpers para manejar responsive design
1894
+ * de forma consistente en todos los componentes.
1895
+ */
1896
+ /**
1897
+ * Breakpoints estándar de Tailwind CSS
1898
+ * Mobile-first approach: los estilos base son para mobile, los breakpoints son MIN-WIDTH
1899
+ */
1900
+ const BREAKPOINTS = {
1901
+ sm: '640px',
1902
+ md: '768px',
1903
+ lg: '1024px',
1904
+ xl: '1280px',
1905
+ '2xl': '1536px' // extra large desktop
1906
+ };
1907
+ /**
1908
+ * Helper para generar clases responsive de forma programática
1909
+ *
1910
+ * @example
1911
+ * responsive({ default: 'block', sm: 'flex', lg: 'grid' })
1912
+ * // Returns: 'block sm:flex lg:grid'
1913
+ */
1914
+ function responsive(config) {
1915
+ const classes = [];
1916
+ if (config.default) {
1917
+ classes.push(config.default);
1918
+ }
1919
+ ['sm', 'md', 'lg', 'xl', '2xl'].forEach(bp => {
1920
+ if (config[bp]) {
1921
+ classes.push(`${bp}:${config[bp]}`);
1922
+ }
1923
+ });
1924
+ return classes.join(' ');
1925
+ }
1926
+ /**
1927
+ * Helper para overflow responsive
1928
+ * Maneja el caso común de scroll en mobile, auto en desktop
1929
+ *
1930
+ * @example
1931
+ * overflowResponsive('x', 'scroll', 'auto')
1932
+ * // Returns: 'overflow-x-scroll sm:overflow-x-auto'
1933
+ */
1934
+ function overflowResponsive(axis, mobile, desktop) {
1935
+ const axisClass = axis === 'both' ? 'overflow' : `overflow-${axis}`;
1936
+ const mobileClass = `${axisClass}-${mobile}`;
1937
+ if (!desktop || desktop === mobile) {
1938
+ return mobileClass;
1939
+ }
1940
+ return `${mobileClass} sm:${axisClass}-${desktop}`;
1941
+ }
1942
+ /**
1943
+ * Helper para spacing responsive
1944
+ * Útil para padding/margin que necesita ajustarse por breakpoint
1945
+ *
1946
+ * @example
1947
+ * spacingResponsive('px', { default: '4', sm: '6', lg: '8' })
1948
+ * // Returns: 'px-4 sm:px-6 lg:px-8'
1949
+ */
1950
+ function spacingResponsive(property, values) {
1951
+ return responsive(Object.entries(values).reduce((acc, [key, value]) => {
1952
+ acc[key] = `${property}-${value}`;
1953
+ return acc;
1954
+ }, {}));
1955
+ }
1956
+ /**
1957
+ * Helper para width responsive
1958
+ *
1959
+ * @example
1960
+ * widthResponsive({ default: 'full', sm: 'auto', lg: '1/2' })
1961
+ * // Returns: 'w-full sm:w-auto lg:w-1/2'
1962
+ */
1963
+ function widthResponsive(values) {
1964
+ return responsive(Object.entries(values).reduce((acc, [key, value]) => {
1965
+ acc[key] = `w-${value}`;
1966
+ return acc;
1967
+ }, {}));
1968
+ }
1969
+ /**
1970
+ * Clases comunes para containers responsive
1971
+ * Pensadas para wrappers que contienen contenido que puede desbordar
1972
+ */
1973
+ const RESPONSIVE_CONTAINER = {
1974
+ // Container con scroll horizontal en mobile, contenido visible en desktop
1975
+ tableWrapper: 'relative w-full overflow-x-auto sm:overflow-x-visible',
1976
+ // Container con padding negativo en mobile para scroll edge-to-edge
1977
+ tableWrapperFullBleed: 'relative w-full -mx-4 px-4 overflow-x-auto sm:mx-0 sm:px-0 sm:overflow-x-visible',
1978
+ // Container con max-width responsive
1979
+ contentWrapper: 'w-full mx-auto px-4 sm:px-6 lg:px-8 max-w-screen-2xl',
1980
+ // Container para modals/dialogs
1981
+ modalWrapper: 'w-full max-w-lg mx-auto px-4 sm:px-0',
1982
+ // Container para forms
1983
+ formWrapper: 'w-full max-w-md mx-auto space-y-4'
1984
+ };
1985
+ /**
1986
+ * Clases comunes para display responsive
1987
+ */
1988
+ const RESPONSIVE_DISPLAY = {
1989
+ // Ocultar en mobile, mostrar en desktop
1990
+ hideOnMobile: 'hidden sm:block',
1991
+ // Mostrar solo en mobile
1992
+ showOnMobile: 'block sm:hidden',
1993
+ // Stack en mobile, flex en desktop
1994
+ stackToFlex: 'flex flex-col sm:flex-row',
1995
+ // Stack en mobile, grid en desktop
1996
+ stackToGrid: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3'
1997
+ };
1998
+ /**
1999
+ * Clases para table responsive strategies
2000
+ */
2001
+ const TABLE_RESPONSIVE = {
2002
+ // Scroll horizontal (default, más simple)
2003
+ scroll: {
2004
+ wrapper: 'relative w-full overflow-x-auto',
2005
+ table: 'w-full min-w-full',
2006
+ cell: 'whitespace-nowrap'
2007
+ },
2008
+ // Permitir wrap del contenido
2009
+ wrap: {
2010
+ wrapper: 'relative w-full overflow-x-auto',
2011
+ table: 'w-full',
2012
+ cell: 'whitespace-normal break-words'
2013
+ },
2014
+ // Stack en mobile (cada fila se convierte en card)
2015
+ // Requiere lógica adicional en el componente
2016
+ stack: {
2017
+ wrapper: 'relative w-full',
2018
+ table: 'w-full',
2019
+ row: 'block sm:table-row border-b sm:border-b-0',
2020
+ 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'
2021
+ },
2022
+ // Collapse: ocultar columnas menos importantes en mobile
2023
+ // Se usa con clases de visibility en las columnas específicas
2024
+ collapse: {
2025
+ wrapper: 'relative w-full overflow-x-auto',
2026
+ table: 'w-full',
2027
+ cell: 'whitespace-nowrap',
2028
+ // Estas clases se aplican a columnas opcionales
2029
+ optionalColumn: 'hidden md:table-cell'
2030
+ }
2031
+ };
2032
+
2033
+ /**
2034
+ * Componente base de tabla con soporte responsive
2035
+ *
2036
+ * SIMPLIFICADO: Ya no incluye drag & drop (usar pdm-draggable-table para eso)
2037
+ *
2038
+ * @example
2039
+ * // Tabla simple con scroll horizontal
2040
+ * <pdm-table variant="default">
2041
+ * <thead><tr><th>Name</th><th>Email</th></tr></thead>
2042
+ * <tbody><tr><td>John</td><td>john@example.com</td></tr></tbody>
2043
+ * </pdm-table>
2044
+ *
2045
+ * @example
2046
+ * // Tabla interactiva con wrap en mobile
2047
+ * <pdm-table variant="interactive" responsiveStrategy="wrap">
2048
+ * ...
2049
+ * </pdm-table>
2050
+ */
2051
+ class PdmTableComponent {
2052
+ constructor() {
2053
+ /**
2054
+ * Variante visual de la tabla
2055
+ * - default: tabla básica sin estilos extra
2056
+ * - data: tabla con bordes y espaciado para data
2057
+ * - interactive: tabla con hover, sticky header y estilos interactivos
2058
+ */
2059
+ this.variant = 'default';
2060
+ /**
2061
+ * Estrategia responsive para la tabla
2062
+ * - scroll: scroll horizontal en mobile (default, más simple)
2063
+ * - wrap: permite que el contenido haga wrap
2064
+ * - stack: convierte filas en cards en mobile (requiere data-label en celdas)
2065
+ * - collapse: oculta columnas menos importantes en mobile
2066
+ */
2067
+ this.responsiveStrategy = 'scroll';
2068
+ /**
2069
+ * Clases CSS adicionales para el wrapper
2070
+ */
2071
+ this.className = '';
2072
+ /**
2073
+ * Si es true, aplica padding negativo en mobile para scroll edge-to-edge
2074
+ * Útil cuando la tabla está dentro de un container con padding
2075
+ */
2076
+ this.fullBleed = false;
2077
+ }
2078
+ get wrapperClasses() {
2079
+ const baseClasses = ['relative', 'w-full'];
2080
+ const strategyClasses = this.getResponsiveStrategyClasses();
2081
+ const variantClasses = this.getVariantWrapperClasses();
2082
+ // Full bleed: scroll edge-to-edge en mobile
2083
+ if (this.fullBleed && this.responsiveStrategy === 'scroll') {
2084
+ baseClasses.push('-mx-4', 'px-4', 'sm:mx-0', 'sm:px-0');
2085
+ }
2086
+ return [
2087
+ ...baseClasses,
2088
+ ...strategyClasses,
2089
+ ...variantClasses,
2090
+ this.className
2091
+ ].filter(Boolean);
2092
+ }
2093
+ get tableClasses() {
2094
+ const baseClasses = ['w-full', 'caption-bottom', 'text-sm'];
2095
+ const variantClasses = this.getVariantTableClasses();
2096
+ const cellClasses = this.getCellClasses();
2097
+ return [...baseClasses, ...variantClasses, ...cellClasses].filter(Boolean);
2098
+ }
2099
+ getResponsiveStrategyClasses() {
2100
+ const strategy = TABLE_RESPONSIVE[this.responsiveStrategy];
2101
+ if (this.responsiveStrategy === 'scroll') {
2102
+ return ['overflow-x-auto'];
2103
+ }
2104
+ if (this.responsiveStrategy === 'wrap') {
2105
+ return ['overflow-x-auto'];
2106
+ }
2107
+ if (this.responsiveStrategy === 'stack') {
2108
+ // Stack requiere lógica en el template, aquí solo el wrapper
2109
+ return [];
2110
+ }
2111
+ if (this.responsiveStrategy === 'collapse') {
2112
+ return ['overflow-x-auto'];
2113
+ }
2114
+ return ['overflow-auto'];
2115
+ }
2116
+ getVariantWrapperClasses() {
2117
+ if (this.variant === 'interactive') {
2118
+ return ['rounded-xl', 'border', 'border-border', 'bg-background'];
2119
+ }
2120
+ if (this.variant === 'data') {
2121
+ return ['rounded-md', 'border', 'border-border', 'bg-background'];
2122
+ }
2123
+ return [];
2124
+ }
2125
+ getVariantTableClasses() {
2126
+ if (this.variant === 'data') {
2127
+ return [
2128
+ 'border-collapse',
2129
+ 'text-foreground',
2130
+ '[&_thead_tr]:border-b',
2131
+ '[&_thead_tr]:border-border',
2132
+ '[&_tbody_tr]:border-b',
2133
+ '[&_tbody_tr]:border-border',
2134
+ '[&_tbody_tr:last-child]:border-b-0',
2135
+ '[&_th]:h-10',
2136
+ '[&_th]:px-2',
2137
+ '[&_th]:text-left',
2138
+ '[&_th]:align-middle',
2139
+ '[&_th]:font-medium',
2140
+ '[&_td]:p-2',
2141
+ '[&_td]:align-middle'
2142
+ ];
2143
+ }
2144
+ if (this.variant === 'interactive') {
2145
+ return [
2146
+ 'text-foreground',
2147
+ '[&_thead]:sticky',
2148
+ '[&_thead]:top-0',
2149
+ '[&_thead]:z-10',
2150
+ '[&_thead]:bg-muted/70',
2151
+ '[&_thead_tr]:border-b',
2152
+ '[&_thead_tr]:border-border',
2153
+ '[&_th]:h-12',
2154
+ '[&_th]:px-4',
2155
+ '[&_th]:text-left',
2156
+ '[&_th]:align-middle',
2157
+ '[&_th]:text-sm',
2158
+ '[&_th]:font-medium',
2159
+ '[&_tbody_tr]:border-b',
2160
+ '[&_tbody_tr]:border-border',
2161
+ '[&_tbody_tr]:transition-colors',
2162
+ '[&_tbody_tr:hover]:bg-muted/50',
2163
+ '[&_tbody_tr:last-child]:border-b-0',
2164
+ '[&_td]:h-14',
2165
+ '[&_td]:px-4',
2166
+ '[&_td]:align-middle',
2167
+ '[&_td]:text-sm',
2168
+ '[&_svg]:text-muted-foreground'
2169
+ ];
2170
+ }
2171
+ return [];
2172
+ }
2173
+ getCellClasses() {
2174
+ // Manejo responsive de whitespace
2175
+ if (this.responsiveStrategy === 'scroll') {
2176
+ // En scroll, permitir wrap en mobile, nowrap en desktop
2177
+ return ['[&_td]:whitespace-normal', '[&_th]:whitespace-normal', 'sm:[&_td]:whitespace-nowrap', 'sm:[&_th]:whitespace-nowrap'];
2178
+ }
2179
+ if (this.responsiveStrategy === 'wrap') {
2180
+ // En wrap, siempre permitir wrap
2181
+ return ['[&_td]:whitespace-normal', '[&_td]:break-words', '[&_th]:whitespace-normal'];
2182
+ }
2183
+ // Default: nowrap (comportamiento anterior para backward compatibility)
2184
+ return [];
2185
+ }
2186
+ }
2187
+ PdmTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2188
+ 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 });
2189
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTableComponent, decorators: [{
2190
+ type: Component,
2191
+ 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" }]
2192
+ }], propDecorators: { variant: [{
2193
+ type: Input
2194
+ }], responsiveStrategy: [{
2195
+ type: Input
2196
+ }], className: [{
2197
+ type: Input
2198
+ }], fullBleed: [{
2199
+ type: Input
2200
+ }] } });
2201
+
2202
+ /**
2203
+ * Data-table genérico con paginación, filtrado y selección
2204
+ *
2205
+ * NUEVO: Ahora es genérico y configurable via columnas
2206
+ *
2207
+ * @example
2208
+ * // Definir columnas
2209
+ * columns: PdmDataTableColumn<User>[] = [
2210
+ * { key: 'name', label: 'Name', sortable: true },
2211
+ * { key: 'email', label: 'Email', sortable: true },
2212
+ * { key: 'role', label: 'Role', hideOnMobile: true },
2213
+ * { key: 'createdAt', label: 'Created', render: (val) => formatDate(val) }
2214
+ * ];
2215
+ *
2216
+ * // En el template
2217
+ * <pdm-data-table
2218
+ * [columns]="columns"
2219
+ * [rows]="users"
2220
+ * [selectable]="true"
2221
+ * (selectionChange)="onSelect($event)">
2222
+ * </pdm-data-table>
2223
+ */
1848
2224
  class PdmDataTableComponent {
1849
2225
  constructor() {
1850
2226
  this.className = '';
2227
+ /**
2228
+ * Columnas a mostrar
2229
+ * Si no se provee, intenta inferir del primer row (legacy mode)
2230
+ */
2231
+ this.columns = [];
2232
+ /**
2233
+ * Estrategia responsive de la tabla
2234
+ */
2235
+ this.responsiveStrategy = 'scroll';
2236
+ /**
2237
+ * Si es true, muestra checkbox de selección en cada fila
2238
+ */
2239
+ this.selectable = false;
2240
+ /**
2241
+ * Si es true, muestra botón de acciones (tres puntos) en cada fila
2242
+ */
2243
+ this.showActions = false;
2244
+ /**
2245
+ * Si es true, muestra filtro de búsqueda
2246
+ */
2247
+ this.showFilter = true;
2248
+ /**
2249
+ * Si es true, muestra controles de paginación
2250
+ */
2251
+ this.showPagination = true;
2252
+ /**
2253
+ * Si es true, muestra selector de columnas
2254
+ */
2255
+ this.showColumnSelector = false;
2256
+ // Labels i18n
1851
2257
  this.filterPlaceholder = 'Filter...';
1852
2258
  this.columnsLabel = 'Columns';
1853
- this.statusLabel = 'Status';
1854
- this.emailLabel = 'Email';
1855
- this.amountLabel = 'Amount';
1856
2259
  this.previousLabel = 'Previous';
1857
2260
  this.nextLabel = 'Next';
1858
2261
  this.emptyLabel = 'No results.';
2262
+ this.rowsSelectedLabel = 'row(s) selected';
2263
+ // DEPRECATED: Labels hardcodeados para backward compatibility
2264
+ /**
2265
+ * @deprecated Use columns configuration instead
2266
+ */
2267
+ this.statusLabel = 'Status';
2268
+ /**
2269
+ * @deprecated Use columns configuration instead
2270
+ */
2271
+ this.emailLabel = 'Email';
2272
+ /**
2273
+ * @deprecated Use columns configuration instead
2274
+ */
2275
+ this.amountLabel = 'Amount';
2276
+ /**
2277
+ * Datos a mostrar
2278
+ */
1859
2279
  this.rows = [];
2280
+ /**
2281
+ * Página actual (1-indexed)
2282
+ */
1860
2283
  this.page = 1;
1861
- this.pageSize = 5;
2284
+ /**
2285
+ * Cantidad de filas por página
2286
+ */
2287
+ this.pageSize = 10;
2288
+ /**
2289
+ * Query de filtrado
2290
+ */
1862
2291
  this.query = '';
1863
2292
  this.queryChange = new EventEmitter();
1864
2293
  this.rowAction = new EventEmitter();
1865
2294
  this.pageChange = new EventEmitter();
1866
2295
  this.selectionChange = new EventEmitter();
2296
+ this.columnSort = new EventEmitter();
2297
+ // Estado interno
2298
+ this.selectedRows = new Set();
2299
+ this.sortDirection = 'asc';
2300
+ }
2301
+ /**
2302
+ * Backward compatibility: si no hay columnas definidas, inferir del primer row
2303
+ */
2304
+ get effectiveColumns() {
2305
+ if (this.columns.length > 0) {
2306
+ return this.columns;
2307
+ }
2308
+ // Legacy mode: inferir columnas del primer row (solo para PdmDataTableRow)
2309
+ if (this.rows.length > 0) {
2310
+ const firstRow = this.rows[0];
2311
+ return Object.keys(firstRow)
2312
+ .filter(key => key !== 'selected')
2313
+ .map(key => ({
2314
+ key: key,
2315
+ label: this.getLegacyLabel(key),
2316
+ align: key === 'amount' ? 'right' : 'left'
2317
+ }));
2318
+ }
2319
+ return [];
2320
+ }
2321
+ /**
2322
+ * LEGACY: mapeo de keys a labels hardcodeados
2323
+ */
2324
+ getLegacyLabel(key) {
2325
+ const map = {
2326
+ status: this.statusLabel,
2327
+ email: this.emailLabel,
2328
+ amount: this.amountLabel
2329
+ };
2330
+ return map[key] || key.charAt(0).toUpperCase() + key.slice(1);
1867
2331
  }
1868
2332
  get filteredRows() {
1869
2333
  const q = this.query.trim().toLowerCase();
1870
2334
  if (!q)
1871
2335
  return this.rows;
1872
- return this.rows.filter((r) => r.email.toLowerCase().includes(q));
2336
+ if (this.filterFn) {
2337
+ return this.rows.filter(row => this.filterFn(row, q));
2338
+ }
2339
+ // Filtrado default: buscar en todos los campos string
2340
+ return this.rows.filter(row => {
2341
+ return Object.values(row).some(val => typeof val === 'string' && val.toLowerCase().includes(q));
2342
+ });
1873
2343
  }
1874
2344
  get pagedRows() {
1875
2345
  const start = (this.page - 1) * this.pageSize;
@@ -1879,14 +2349,33 @@ class PdmDataTableComponent {
1879
2349
  return Math.max(1, Math.ceil(this.filteredRows.length / this.pageSize));
1880
2350
  }
1881
2351
  get selectedCount() {
1882
- return this.rows.filter((row) => row.selected).length;
2352
+ return this.selectedRows.size;
1883
2353
  }
1884
2354
  onQueryInput(event) {
1885
2355
  const value = event.target.value;
1886
2356
  this.queryChange.emit(value);
1887
2357
  }
1888
2358
  onToggleRow(row, event) {
1889
- this.selectionChange.emit({ id: row.id, selected: event.target.checked });
2359
+ const checked = event.target.checked;
2360
+ if (checked) {
2361
+ this.selectedRows.add(row);
2362
+ }
2363
+ else {
2364
+ this.selectedRows.delete(row);
2365
+ }
2366
+ this.selectionChange.emit({ row, selected: checked });
2367
+ }
2368
+ onToggleAll(event) {
2369
+ const checked = event.target.checked;
2370
+ if (checked) {
2371
+ this.pagedRows.forEach(row => this.selectedRows.add(row));
2372
+ }
2373
+ else {
2374
+ this.pagedRows.forEach(row => this.selectedRows.delete(row));
2375
+ }
2376
+ }
2377
+ isSelected(row) {
2378
+ return this.selectedRows.has(row);
1890
2379
  }
1891
2380
  previous() {
1892
2381
  if (this.page <= 1)
@@ -1899,25 +2388,75 @@ class PdmDataTableComponent {
1899
2388
  this.pageChange.emit(this.page + 1);
1900
2389
  }
1901
2390
  onAction(row) {
1902
- this.rowAction.emit(row.id);
2391
+ this.rowAction.emit(row);
2392
+ }
2393
+ onSort(column) {
2394
+ if (!column.sortable)
2395
+ return;
2396
+ if (this.sortColumn === column) {
2397
+ this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
2398
+ }
2399
+ else {
2400
+ this.sortColumn = column;
2401
+ this.sortDirection = 'asc';
2402
+ }
2403
+ this.columnSort.emit({ column, direction: this.sortDirection });
2404
+ }
2405
+ getCellValue(row, column) {
2406
+ const value = row[column.key];
2407
+ if (column.render) {
2408
+ return column.render(value, row);
2409
+ }
2410
+ return value != null ? String(value) : '';
2411
+ }
2412
+ getCellClass(column) {
2413
+ const classes = ['px-2', 'py-2'];
2414
+ if (column.align === 'center')
2415
+ classes.push('text-center');
2416
+ if (column.align === 'right')
2417
+ classes.push('text-right');
2418
+ if (column.hideOnMobile)
2419
+ classes.push('hidden', 'md:table-cell');
2420
+ if (column.cellClass)
2421
+ classes.push(column.cellClass);
2422
+ return classes.join(' ');
2423
+ }
2424
+ getHeaderClass(column) {
2425
+ const classes = ['px-2', 'py-2', 'text-left', 'font-medium'];
2426
+ if (column.hideOnMobile)
2427
+ classes.push('hidden', 'md:table-cell');
2428
+ if (column.headerClass)
2429
+ classes.push(column.headerClass);
2430
+ return classes.join(' ');
2431
+ }
2432
+ getColumnStyle(column) {
2433
+ return column.width ? { width: column.width } : {};
1903
2434
  }
1904
2435
  }
1905
2436
  PdmDataTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1906
- 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 });
2437
+ 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 });
1907
2438
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDataTableComponent, decorators: [{
1908
2439
  type: Component,
1909
- 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" }]
2440
+ 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" }]
1910
2441
  }], propDecorators: { className: [{
1911
2442
  type: Input
1912
- }], filterPlaceholder: [{
2443
+ }], columns: [{
1913
2444
  type: Input
1914
- }], columnsLabel: [{
2445
+ }], responsiveStrategy: [{
1915
2446
  type: Input
1916
- }], statusLabel: [{
2447
+ }], selectable: [{
1917
2448
  type: Input
1918
- }], emailLabel: [{
2449
+ }], showActions: [{
1919
2450
  type: Input
1920
- }], amountLabel: [{
2451
+ }], showFilter: [{
2452
+ type: Input
2453
+ }], showPagination: [{
2454
+ type: Input
2455
+ }], showColumnSelector: [{
2456
+ type: Input
2457
+ }], filterPlaceholder: [{
2458
+ type: Input
2459
+ }], columnsLabel: [{
1921
2460
  type: Input
1922
2461
  }], previousLabel: [{
1923
2462
  type: Input
@@ -1925,6 +2464,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
1925
2464
  type: Input
1926
2465
  }], emptyLabel: [{
1927
2466
  type: Input
2467
+ }], rowsSelectedLabel: [{
2468
+ type: Input
2469
+ }], statusLabel: [{
2470
+ type: Input
2471
+ }], emailLabel: [{
2472
+ type: Input
2473
+ }], amountLabel: [{
2474
+ type: Input
1928
2475
  }], rows: [{
1929
2476
  type: Input
1930
2477
  }], page: [{
@@ -1933,6 +2480,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
1933
2480
  type: Input
1934
2481
  }], query: [{
1935
2482
  type: Input
2483
+ }], filterFn: [{
2484
+ type: Input
1936
2485
  }], queryChange: [{
1937
2486
  type: Output
1938
2487
  }], rowAction: [{
@@ -1941,6 +2490,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
1941
2490
  type: Output
1942
2491
  }], selectionChange: [{
1943
2492
  type: Output
2493
+ }], columnSort: [{
2494
+ type: Output
1944
2495
  }] } });
1945
2496
 
1946
2497
  /**
@@ -2356,13 +2907,135 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
2356
2907
  args: ['document:keydown.escape']
2357
2908
  }] } });
2358
2909
 
2359
- class PdmDialogComponent {
2360
- constructor() {
2361
- this.open = false;
2362
- this.variant = 'default';
2363
- this.size = 'desktop';
2364
- this.title = 'Edit profile';
2365
- this.description = '';
2910
+ /**
2911
+ * Z-Index Scale - Sistema centralizado de z-index
2912
+ *
2913
+ * JERARQUÍA (de menor a mayor):
2914
+ * 1. base (z-0) - Elementos normales del DOM
2915
+ * 2. dropdown (z-10) - Selects, combobox, date-pickers
2916
+ * 3. sticky (z-20) - Headers, navigation bars
2917
+ * 4. overlay (z-30) - Popovers, hover cards, context menus
2918
+ * 5. drawer (z-40) - Sidebar drawer, sheets laterales
2919
+ * 6. modal (z-50) - Dialogs, alert-dialogs
2920
+ * 7. modal-backdrop (z-40) - Backdrop de modals
2921
+ * 8. popover (z-60) - Tooltips, dropdowns DENTRO de modals
2922
+ * 9. toast (z-[100]) - Notificaciones que deben estar sobre TODO
2923
+ *
2924
+ * REGLA CRÍTICA:
2925
+ * - Componentes overlay (select options, dropdown menu, tooltip) SIEMPRE z-60 o mayor
2926
+ * - Esto permite que funcionen DENTRO de modals (z-50)
2927
+ * - Backdrop de modal debe ser z-40 para estar DEBAJO del contenido del modal (z-50)
2928
+ */
2929
+ const Z_INDEX = {
2930
+ /**
2931
+ * Base - contenido normal del DOM
2932
+ */
2933
+ base: 'z-0',
2934
+ /**
2935
+ * Dropdown - Selects, combobox, date-pickers
2936
+ * Debe estar SOBRE contenido normal pero BAJO modals
2937
+ */
2938
+ dropdown: 'z-10',
2939
+ /**
2940
+ * Sticky - Headers, navigation fija
2941
+ */
2942
+ sticky: 'z-20',
2943
+ /**
2944
+ * Overlay - Popovers, hover cards, context menus
2945
+ * Debe estar SOBRE sticky pero BAJO modals
2946
+ */
2947
+ overlay: 'z-30',
2948
+ /**
2949
+ * Drawer backdrop - Backdrop de sidebar drawer
2950
+ * Debe estar DEBAJO del drawer panel
2951
+ */
2952
+ drawerBackdrop: 'z-40',
2953
+ /**
2954
+ * Drawer - Sidebar drawer, sheets laterales
2955
+ * Debe estar SOBRE su backdrop pero BAJO modals
2956
+ */
2957
+ drawer: 'z-50',
2958
+ /**
2959
+ * Modal backdrop - Backdrop de dialogs
2960
+ * Debe estar SOBRE drawers pero DEBAJO del contenido del modal
2961
+ */
2962
+ modalBackdrop: 'z-50',
2963
+ /**
2964
+ * Modal - Dialogs, alert-dialogs, sheets
2965
+ * Debe estar SOBRE su backdrop
2966
+ */
2967
+ modal: 'z-[60]',
2968
+ /**
2969
+ * Popover - Tooltips, dropdowns, selects options DENTRO de modals
2970
+ * CRÍTICO: Debe ser MAYOR que modal (z-50) para aparecer sobre modals
2971
+ */
2972
+ popover: 'z-[70]',
2973
+ /**
2974
+ * Toast - Notificaciones globales
2975
+ * Debe estar sobre TODO
2976
+ */
2977
+ toast: 'z-[100]'
2978
+ };
2979
+ /**
2980
+ * Helper para debugging z-index issues
2981
+ */
2982
+ function logZIndexStack(element) {
2983
+ if (typeof window === 'undefined')
2984
+ return;
2985
+ let current = element;
2986
+ const stack = [];
2987
+ while (current && current !== document.body) {
2988
+ const computed = window.getComputedStyle(current);
2989
+ const zIndex = computed.zIndex;
2990
+ const position = computed.position;
2991
+ if (zIndex !== 'auto') {
2992
+ stack.push({
2993
+ element: current.tagName + (current.className ? `.${current.className.split(' ')[0]}` : ''),
2994
+ zIndex,
2995
+ position
2996
+ });
2997
+ }
2998
+ current = current.parentElement;
2999
+ }
3000
+ console.table(stack);
3001
+ }
3002
+
3003
+ /**
3004
+ * Modal/Dialog component con soporte responsive
3005
+ *
3006
+ * MEJORADO en v0.2.0:
3007
+ * - Modo 'responsive' (default): fullscreen en mobile, modal en desktop
3008
+ * - Tamaños predefinidos: sm, md, lg, xl
3009
+ * - Mejor manejo de scroll en mobile
3010
+ *
3011
+ * @example
3012
+ * // Responsive (recomendado)
3013
+ * <pdm-dialog [open]="isOpen" size="responsive">
3014
+ * <p>Content</p>
3015
+ * </pdm-dialog>
3016
+ *
3017
+ * @example
3018
+ * // Tamaño fijo
3019
+ * <pdm-dialog [open]="isOpen" size="lg">
3020
+ * <p>Content</p>
3021
+ * </pdm-dialog>
3022
+ */
3023
+ class PdmDialogComponent {
3024
+ constructor() {
3025
+ this.open = false;
3026
+ this.variant = 'default';
3027
+ /**
3028
+ * Tamaño del dialog
3029
+ * - responsive: fullscreen mobile, modal desktop (recomendado)
3030
+ * - sm: 400px max
3031
+ * - md: 500px max
3032
+ * - lg: 640px max (default)
3033
+ * - xl: 800px max
3034
+ * - desktop/mobile/mobile-fullscreen: legacy, deprecado
3035
+ */
3036
+ this.size = 'responsive';
3037
+ this.title = 'Edit profile';
3038
+ this.description = '';
2366
3039
  this.closeOnBackdrop = true;
2367
3040
  this.closeOnEsc = true;
2368
3041
  this.showCloseButton = true;
@@ -2399,47 +3072,132 @@ class PdmDialogComponent {
2399
3072
  }
2400
3073
  }
2401
3074
  get panelClassName() {
3075
+ // Legacy sizes (backward compatibility)
3076
+ if (this.size === 'desktop') {
3077
+ return this.buildClasses(['max-w-[640px]', 'max-h-[calc(100vh-2rem)]', 'rounded-[10px]']);
3078
+ }
3079
+ if (this.size === 'mobile') {
3080
+ return this.buildClasses(['max-w-[320px]', 'min-h-[240px]', 'rounded-[10px]']);
3081
+ }
3082
+ if (this.size === 'mobile-fullscreen') {
3083
+ return this.buildClasses(['max-w-[320px]', 'h-[min(100dvh,640px)]', 'rounded-none', 'sm:rounded-[10px]']);
3084
+ }
3085
+ // New responsive mode (recomendado)
3086
+ if (this.size === 'responsive') {
3087
+ return this.buildClasses([
3088
+ // Mobile: fullscreen con bordes redondeados solo arriba
3089
+ 'w-full',
3090
+ 'h-full',
3091
+ 'max-h-[100dvh]',
3092
+ 'rounded-t-[10px]',
3093
+ 'sm:rounded-[10px]',
3094
+ // Desktop: modal centrado
3095
+ 'sm:w-auto',
3096
+ 'sm:h-auto',
3097
+ 'sm:max-w-[640px]',
3098
+ 'sm:max-h-[calc(100vh-4rem)]'
3099
+ ]);
3100
+ }
3101
+ // New size options
3102
+ const sizeMap = {
3103
+ sm: 'sm:max-w-[400px]',
3104
+ md: 'sm:max-w-[500px]',
3105
+ lg: 'sm:max-w-[640px]',
3106
+ xl: 'sm:max-w-[800px]'
3107
+ };
3108
+ const maxWidth = sizeMap[this.size] || sizeMap.lg;
3109
+ return this.buildClasses([
3110
+ // Mobile: fullscreen
3111
+ 'w-full',
3112
+ 'h-full',
3113
+ 'max-h-[100dvh]',
3114
+ 'rounded-t-[10px]',
3115
+ // Desktop: modal
3116
+ 'sm:rounded-[10px]',
3117
+ 'sm:w-auto',
3118
+ 'sm:h-auto',
3119
+ maxWidth,
3120
+ 'sm:max-h-[calc(100vh-4rem)]'
3121
+ ]);
3122
+ }
3123
+ buildClasses(sizeClasses) {
2402
3124
  const base = [
2403
- 'relative z-10 w-full border border-border bg-background text-foreground shadow-lg',
2404
- this.size === 'desktop' ? 'max-w-[640px] max-h-[calc(100vh-2rem)] rounded-[10px] overflow-visible' : '',
2405
- this.size === 'mobile' ? 'max-w-[320px] min-h-[240px] rounded-[10px] overflow-visible' : '',
2406
- this.size === 'mobile-fullscreen'
2407
- ? 'max-w-[320px] h-[min(100dvh,640px)] rounded-none sm:rounded-[10px] overflow-visible'
2408
- : '',
3125
+ 'relative',
3126
+ Z_INDEX.modal,
3127
+ 'flex',
3128
+ 'flex-col',
3129
+ 'border',
3130
+ 'border-border',
3131
+ 'bg-background',
3132
+ 'text-foreground',
3133
+ 'shadow-lg',
3134
+ 'overflow-hidden',
3135
+ ...sizeClasses,
2409
3136
  this.className
2410
3137
  ];
2411
3138
  return base.filter(Boolean).join(' ');
2412
3139
  }
2413
3140
  get bodyWrapperClassName() {
2414
3141
  const base = [
2415
- 'min-h-0 flex-1',
2416
- this.size === 'mobile-fullscreen' ? 'overflow-y-auto px-4 py-6' : 'px-6 py-6',
3142
+ 'flex-1',
3143
+ 'overflow-y-auto',
3144
+ 'px-4',
3145
+ 'py-6',
3146
+ 'sm:px-6',
2417
3147
  this.bodyClassName
2418
3148
  ];
2419
3149
  return base.filter(Boolean).join(' ');
2420
3150
  }
2421
3151
  get headerWrapperClassName() {
2422
- return ['flex items-start justify-between gap-3 p-4', this.headerClassName].filter(Boolean).join(' ');
3152
+ const base = [
3153
+ 'flex',
3154
+ 'items-start',
3155
+ 'justify-between',
3156
+ 'gap-3',
3157
+ 'p-4',
3158
+ 'sm:p-6',
3159
+ 'border-b',
3160
+ 'border-border',
3161
+ this.headerClassName
3162
+ ];
3163
+ return base.filter(Boolean).join(' ');
2423
3164
  }
2424
3165
  get footerWrapperClassName() {
2425
3166
  const effectiveAlign = this.alignFooter === 'right' && this.variant === 'custom-close' ? 'left' : this.alignFooter;
2426
3167
  const base = [
2427
3168
  'p-4',
3169
+ 'sm:p-6',
3170
+ 'border-t',
3171
+ 'border-border',
3172
+ // Mobile: siempre full-width
3173
+ 'flex',
3174
+ 'flex-col',
3175
+ 'gap-2',
3176
+ // Desktop: según alignFooter
2428
3177
  effectiveAlign === 'full-width'
2429
- ? 'flex flex-col gap-2'
2430
- : effectiveAlign === 'left'
2431
- ? 'flex items-center gap-2 justify-start'
2432
- : 'flex items-center gap-2 justify-end',
3178
+ ? 'sm:flex-col'
3179
+ : 'sm:flex-row sm:items-center',
3180
+ effectiveAlign === 'left' ? 'sm:justify-start' : '',
3181
+ effectiveAlign === 'right' ? 'sm:justify-end' : '',
2433
3182
  this.footerClassName
2434
3183
  ];
2435
3184
  return base.filter(Boolean).join(' ');
2436
3185
  }
3186
+ get containerClassName() {
3187
+ // Container con backdrop z-50
3188
+ // Mobile: fullscreen desde el bottom
3189
+ // Desktop: centrado
3190
+ return responsive({
3191
+ default: `fixed inset-x-0 bottom-0 ${Z_INDEX.modalBackdrop} flex items-end justify-center`,
3192
+ sm: `fixed inset-0 ${Z_INDEX.modalBackdrop} flex items-center justify-center p-4`
3193
+ });
3194
+ }
2437
3195
  }
2438
3196
  PdmDialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2439
- 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 });
3197
+ 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 });
2440
3198
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDialogComponent, decorators: [{
2441
3199
  type: Component,
2442
- 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" }]
3200
+ 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" }]
2443
3201
  }], propDecorators: { open: [{
2444
3202
  type: Input
2445
3203
  }], variant: [{
@@ -2485,6 +3243,304 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
2485
3243
  args: ['document:keydown.escape']
2486
3244
  }] } });
2487
3245
 
3246
+ /**
3247
+ * Tabla con funcionalidad de reordenamiento de filas mediante drag & drop
3248
+ *
3249
+ * Extiende pdm-table agregando la capacidad de reordenar filas.
3250
+ * Si no necesitás drag & drop, usá pdm-table directamente (más simple y liviano).
3251
+ *
3252
+ * @example
3253
+ * <pdm-draggable-table
3254
+ * variant="interactive"
3255
+ * [reorderRows]="true"
3256
+ * (rowOrderChange)="onOrderChange($event)">
3257
+ * <tbody>
3258
+ * <tr data-row-id="1"><td>Row 1</td></tr>
3259
+ * <tr data-row-id="2"><td>Row 2</td></tr>
3260
+ * </tbody>
3261
+ * </pdm-draggable-table>
3262
+ *
3263
+ * IMPORTANTE: Cada <tr> debe tener un atributo data-row-id único
3264
+ */
3265
+ class PdmDraggableTableComponent {
3266
+ constructor(renderer) {
3267
+ this.renderer = renderer;
3268
+ this.variant = 'default';
3269
+ this.responsiveStrategy = 'scroll';
3270
+ this.className = '';
3271
+ this.fullBleed = false;
3272
+ /**
3273
+ * Habilita el reordenamiento de filas mediante drag & drop
3274
+ */
3275
+ this.reorderRows = false;
3276
+ /**
3277
+ * Selector CSS para identificar los handles de drag
3278
+ * Por defecto busca: [data-drag-handle], [data-slot=row-drag-handle], .row-drag-handle
3279
+ * Si no encuentra ninguno, inserta un handle automático
3280
+ */
3281
+ this.dragHandleSelector = '[data-drag-handle],[data-slot=row-drag-handle],.row-drag-handle,[data-auto-drag-handle]';
3282
+ /**
3283
+ * Emite el nuevo orden de las filas cuando el usuario termina de arrastrar
3284
+ * Array de data-row-id en el nuevo orden
3285
+ */
3286
+ this.rowOrderChange = new EventEmitter();
3287
+ this.cleanupListeners = [];
3288
+ this.draggedRow = null;
3289
+ }
3290
+ ngAfterViewInit() {
3291
+ this.syncReorderBehavior();
3292
+ }
3293
+ // Getters para clases CSS (mismo comportamiento que pdm-table)
3294
+ get wrapperClasses() {
3295
+ const baseClasses = ['relative', 'w-full'];
3296
+ const strategyClasses = this.getResponsiveStrategyClasses();
3297
+ const variantClasses = this.getVariantWrapperClasses();
3298
+ if (this.fullBleed && this.responsiveStrategy === 'scroll') {
3299
+ baseClasses.push('-mx-4', 'px-4', 'sm:mx-0', 'sm:px-0');
3300
+ }
3301
+ return [
3302
+ ...baseClasses,
3303
+ ...strategyClasses,
3304
+ ...variantClasses,
3305
+ this.className
3306
+ ].filter(Boolean);
3307
+ }
3308
+ get tableClasses() {
3309
+ const baseClasses = ['w-full', 'caption-bottom', 'text-sm'];
3310
+ const variantClasses = this.getVariantTableClasses();
3311
+ const cellClasses = this.getCellClasses();
3312
+ return [...baseClasses, ...variantClasses, ...cellClasses].filter(Boolean);
3313
+ }
3314
+ getResponsiveStrategyClasses() {
3315
+ if (this.responsiveStrategy === 'scroll' || this.responsiveStrategy === 'wrap' || this.responsiveStrategy === 'collapse') {
3316
+ return ['overflow-x-auto'];
3317
+ }
3318
+ return [];
3319
+ }
3320
+ getVariantWrapperClasses() {
3321
+ if (this.variant === 'interactive') {
3322
+ return ['rounded-xl', 'border', 'border-border', 'bg-background'];
3323
+ }
3324
+ if (this.variant === 'data') {
3325
+ return ['rounded-md', 'border', 'border-border', 'bg-background'];
3326
+ }
3327
+ return [];
3328
+ }
3329
+ getVariantTableClasses() {
3330
+ if (this.variant === 'data') {
3331
+ return [
3332
+ 'border-collapse', 'text-foreground',
3333
+ '[&_thead_tr]:border-b', '[&_thead_tr]:border-border',
3334
+ '[&_tbody_tr]:border-b', '[&_tbody_tr]:border-border',
3335
+ '[&_tbody_tr:last-child]:border-b-0',
3336
+ '[&_th]:h-10', '[&_th]:px-2', '[&_th]:text-left', '[&_th]:align-middle', '[&_th]:font-medium',
3337
+ '[&_td]:p-2', '[&_td]:align-middle'
3338
+ ];
3339
+ }
3340
+ if (this.variant === 'interactive') {
3341
+ return [
3342
+ 'text-foreground',
3343
+ '[&_thead]:sticky', '[&_thead]:top-0', '[&_thead]:z-10', '[&_thead]:bg-muted/70',
3344
+ '[&_thead_tr]:border-b', '[&_thead_tr]:border-border',
3345
+ '[&_th]:h-12', '[&_th]:px-4', '[&_th]:text-left', '[&_th]:align-middle', '[&_th]:text-sm', '[&_th]:font-medium',
3346
+ '[&_tbody_tr]:border-b', '[&_tbody_tr]:border-border',
3347
+ '[&_tbody_tr]:transition-colors', '[&_tbody_tr:hover]:bg-muted/50',
3348
+ '[&_tbody_tr:last-child]:border-b-0',
3349
+ '[&_td]:h-14', '[&_td]:px-4', '[&_td]:align-middle', '[&_td]:text-sm',
3350
+ '[&_svg]:text-muted-foreground'
3351
+ ];
3352
+ }
3353
+ return [];
3354
+ }
3355
+ getCellClasses() {
3356
+ if (this.responsiveStrategy === 'scroll') {
3357
+ return ['[&_td]:whitespace-normal', '[&_th]:whitespace-normal', 'sm:[&_td]:whitespace-nowrap', 'sm:[&_th]:whitespace-nowrap'];
3358
+ }
3359
+ if (this.responsiveStrategy === 'wrap') {
3360
+ return ['[&_td]:whitespace-normal', '[&_td]:break-words', '[&_th]:whitespace-normal'];
3361
+ }
3362
+ return [];
3363
+ }
3364
+ ngOnChanges(changes) {
3365
+ if (changes['reorderRows'] || changes['variant']) {
3366
+ this.syncReorderBehavior();
3367
+ }
3368
+ }
3369
+ ngOnDestroy() {
3370
+ this.cleanupReorderBehavior();
3371
+ }
3372
+ syncReorderBehavior() {
3373
+ this.cleanupReorderBehavior();
3374
+ if (!this.reorderRows) {
3375
+ return;
3376
+ }
3377
+ const tbody = this.getTbody();
3378
+ if (!tbody) {
3379
+ return;
3380
+ }
3381
+ this.setRowsDraggable(tbody, true);
3382
+ 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()));
3383
+ // Observer para detectar cambios en el DOM (filas agregadas/removidas)
3384
+ this.observer = new MutationObserver(() => this.setRowsDraggable(tbody, true));
3385
+ this.observer.observe(tbody, { childList: true });
3386
+ }
3387
+ cleanupReorderBehavior() {
3388
+ this.cleanupListeners.forEach((dispose) => dispose());
3389
+ this.cleanupListeners = [];
3390
+ if (this.observer) {
3391
+ this.observer.disconnect();
3392
+ this.observer = undefined;
3393
+ }
3394
+ const tbody = this.getTbody();
3395
+ if (tbody) {
3396
+ this.setRowsDraggable(tbody, false);
3397
+ }
3398
+ this.draggedRow = null;
3399
+ }
3400
+ getTbody() {
3401
+ var _a, _b;
3402
+ return (_b = (_a = this.tableElement) === null || _a === void 0 ? void 0 : _a.nativeElement.tBodies.item(0)) !== null && _b !== void 0 ? _b : null;
3403
+ }
3404
+ setRowsDraggable(tbody, enabled) {
3405
+ const rows = Array.from(tbody.rows);
3406
+ rows.forEach((row) => {
3407
+ this.syncAutoDragHandle(row, enabled);
3408
+ row.draggable = false;
3409
+ if (!enabled) {
3410
+ delete row.dataset['dragging'];
3411
+ delete row.dataset['dragArmed'];
3412
+ }
3413
+ });
3414
+ }
3415
+ /**
3416
+ * Inserta un handle de drag automático si no existe uno custom
3417
+ */
3418
+ syncAutoDragHandle(row, enabled) {
3419
+ const firstCell = row.cells.item(0);
3420
+ if (!firstCell) {
3421
+ return;
3422
+ }
3423
+ const existingAutoHandle = firstCell.querySelector('[data-auto-drag-handle]');
3424
+ if (!enabled) {
3425
+ existingAutoHandle === null || existingAutoHandle === void 0 ? void 0 : existingAutoHandle.remove();
3426
+ return;
3427
+ }
3428
+ const hasCustomHandle = !!firstCell.querySelector('[data-drag-handle],[data-slot=row-drag-handle],.row-drag-handle');
3429
+ if (hasCustomHandle || existingAutoHandle) {
3430
+ return;
3431
+ }
3432
+ // Crear handle automático
3433
+ const button = this.renderer.createElement('button');
3434
+ this.renderer.setAttribute(button, 'type', 'button');
3435
+ this.renderer.setAttribute(button, 'aria-label', 'Drag row');
3436
+ this.renderer.setAttribute(button, 'data-auto-drag-handle', 'true');
3437
+ this.renderer.addClass(button, 'inline-flex');
3438
+ this.renderer.addClass(button, 'h-7');
3439
+ this.renderer.addClass(button, 'w-7');
3440
+ this.renderer.addClass(button, 'items-center');
3441
+ this.renderer.addClass(button, 'justify-center');
3442
+ this.renderer.addClass(button, 'cursor-grab');
3443
+ this.renderer.addClass(button, 'active:cursor-grabbing');
3444
+ this.renderer.addClass(button, 'text-muted-foreground');
3445
+ const dots = this.renderer.createElement('span');
3446
+ this.renderer.addClass(dots, 'text-sm');
3447
+ this.renderer.addClass(dots, 'leading-none');
3448
+ this.renderer.setProperty(dots, 'textContent', '⋮⋮');
3449
+ this.renderer.appendChild(button, dots);
3450
+ this.renderer.insertBefore(firstCell, button, firstCell.firstChild);
3451
+ }
3452
+ onDragStart(event) {
3453
+ const target = event.target;
3454
+ const row = target === null || target === void 0 ? void 0 : target.closest('tr');
3455
+ if (!row) {
3456
+ return;
3457
+ }
3458
+ const handle = target === null || target === void 0 ? void 0 : target.closest(this.dragHandleSelector);
3459
+ const isArmed = row.dataset['dragArmed'] === 'true';
3460
+ if ((!handle || !row.contains(handle)) && !isArmed) {
3461
+ event.preventDefault();
3462
+ return;
3463
+ }
3464
+ this.draggedRow = row;
3465
+ this.draggedRow.dataset['dragging'] = 'true';
3466
+ if (event.dataTransfer) {
3467
+ event.dataTransfer.effectAllowed = 'move';
3468
+ event.dataTransfer.setData('text/plain', '');
3469
+ }
3470
+ }
3471
+ onDragOver(event, tbody) {
3472
+ if (!this.draggedRow) {
3473
+ return;
3474
+ }
3475
+ event.preventDefault();
3476
+ const target = event.target;
3477
+ const targetRow = target === null || target === void 0 ? void 0 : target.closest('tr');
3478
+ if (!targetRow || targetRow === this.draggedRow) {
3479
+ return;
3480
+ }
3481
+ const rect = targetRow.getBoundingClientRect();
3482
+ const shouldInsertBefore = event.clientY < rect.top + rect.height / 2;
3483
+ tbody.insertBefore(this.draggedRow, shouldInsertBefore ? targetRow : targetRow.nextSibling);
3484
+ }
3485
+ onDrop(event) {
3486
+ event.preventDefault();
3487
+ }
3488
+ onDragEnd() {
3489
+ const tbody = this.getTbody();
3490
+ if (tbody) {
3491
+ Array.from(tbody.rows).forEach((row) => {
3492
+ row.draggable = false;
3493
+ delete row.dataset['dragArmed'];
3494
+ });
3495
+ }
3496
+ if (this.draggedRow) {
3497
+ delete this.draggedRow.dataset['dragging'];
3498
+ this.draggedRow = null;
3499
+ }
3500
+ if (!tbody) {
3501
+ return;
3502
+ }
3503
+ const order = Array.from(tbody.rows).map((row, index) => row.getAttribute('data-row-id') || String(index));
3504
+ this.rowOrderChange.emit(order);
3505
+ }
3506
+ armDragFromHandle(event) {
3507
+ const target = event.target;
3508
+ const handle = target === null || target === void 0 ? void 0 : target.closest(this.dragHandleSelector);
3509
+ if (!handle) {
3510
+ return;
3511
+ }
3512
+ const row = handle.closest('tr');
3513
+ if (!row) {
3514
+ return;
3515
+ }
3516
+ row.draggable = true;
3517
+ row.dataset['dragArmed'] = 'true';
3518
+ }
3519
+ }
3520
+ PdmDraggableTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDraggableTableComponent, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
3521
+ 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 });
3522
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDraggableTableComponent, decorators: [{
3523
+ type: Component,
3524
+ 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" }]
3525
+ }], ctorParameters: function () { return [{ type: i0.Renderer2 }]; }, propDecorators: { variant: [{
3526
+ type: Input
3527
+ }], responsiveStrategy: [{
3528
+ type: Input
3529
+ }], className: [{
3530
+ type: Input
3531
+ }], fullBleed: [{
3532
+ type: Input
3533
+ }], reorderRows: [{
3534
+ type: Input
3535
+ }], dragHandleSelector: [{
3536
+ type: Input
3537
+ }], rowOrderChange: [{
3538
+ type: Output
3539
+ }], tableElement: [{
3540
+ type: ViewChild,
3541
+ args: ['tableElement']
3542
+ }] } });
3543
+
2488
3544
  class PdmDropdownMenuComponent {
2489
3545
  constructor(elementRef, cdr, overlay, viewContainerRef) {
2490
3546
  this.elementRef = elementRef;
@@ -2583,7 +3639,9 @@ class PdmDropdownMenuComponent {
2583
3639
  }
2584
3640
  }
2585
3641
  onEsc() {
2586
- this.closePanel();
3642
+ if (this.open) {
3643
+ this.closePanel();
3644
+ }
2587
3645
  }
2588
3646
  openPanel() {
2589
3647
  var _a, _b, _c;
@@ -2596,7 +3654,7 @@ class PdmDropdownMenuComponent {
2596
3654
  this.cdr.markForCheck();
2597
3655
  const positionStrategy = createFlexiblePositionStrategy(this.overlay, triggerEl, 8);
2598
3656
  // Resolve panelClass: overlayOptions.panelClass wins; otherwise map panelClassName.
2599
- const resolvedPanelClass = (_c = (_b = this.overlayOptions) === null || _b === void 0 ? void 0 : _b.panelClass) !== null && _c !== void 0 ? _c : (this.panelClassName ? ['block', this.panelClassName] : ['block']);
3657
+ const resolvedPanelClass = (_c = (_b = this.overlayOptions) === null || _b === void 0 ? void 0 : _b.panelClass) !== null && _c !== void 0 ? _c : (this.panelClassName ? [Z_INDEX.popover, this.panelClassName] : [Z_INDEX.popover]);
2600
3658
  this.overlayRef = this.overlay.create(Object.assign(Object.assign({ positionStrategy, scrollStrategy: this.overlay.scrollStrategies.reposition() }, this.overlayOptions), {
2601
3659
  // panelClass always overrides last: it already merges panelClassName + overlayOptions.
2602
3660
  panelClass: resolvedPanelClass }));
@@ -2662,13 +3720,67 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
2662
3720
  args: ['document:keydown.escape']
2663
3721
  }] } });
2664
3722
 
3723
+ /**
3724
+ * Drawer/Sheet component con soporte responsive
3725
+ *
3726
+ * MEJORADO en v0.2.0:
3727
+ * - Posicionamiento configurable (bottom, left, right, top)
3728
+ * - Tamaños predefinidos
3729
+ * - Responsive: bottom sheet en mobile, side panel en desktop
3730
+ * - Contenido genérico via ng-content
3731
+ *
3732
+ * @example
3733
+ * // Drawer simple desde el bottom
3734
+ * <pdm-drawer [open]="isOpen" position="bottom">
3735
+ * <h3>Title</h3>
3736
+ * <p>Content</p>
3737
+ * </pdm-drawer>
3738
+ *
3739
+ * @example
3740
+ * // Side panel desde la right
3741
+ * <pdm-drawer [open]="isOpen" position="right" size="md">
3742
+ * <p>Content</p>
3743
+ * </pdm-drawer>
3744
+ */
2665
3745
  class PdmDrawerComponent {
2666
3746
  constructor() {
2667
3747
  this.open = false;
3748
+ /**
3749
+ * Posición del drawer
3750
+ * - bottom: desde abajo (default, mejor para mobile)
3751
+ * - left: side panel desde izquierda
3752
+ * - right: side panel desde derecha
3753
+ * - top: desde arriba (poco común)
3754
+ */
3755
+ this.position = 'bottom';
3756
+ /**
3757
+ * Tamaño del drawer
3758
+ * - sm: 400px (side) / 50vh (bottom/top)
3759
+ * - md: 500px (side) / 66vh (bottom/top) (default)
3760
+ * - lg: 640px (side) / 80vh (bottom/top)
3761
+ * - full: 100% ancho/alto
3762
+ */
3763
+ this.size = 'md';
3764
+ /**
3765
+ * @deprecated Use position="bottom" instead
3766
+ */
2668
3767
  this.variant = 'drawer';
2669
3768
  this.className = '';
2670
3769
  this.title = '';
2671
3770
  this.description = '';
3771
+ /**
3772
+ * Mostrar handle visual (línea para arrastrar)
3773
+ * Solo tiene sentido en position="bottom"
3774
+ */
3775
+ this.showHandle = true;
3776
+ /**
3777
+ * Mostrar botón de cerrar
3778
+ */
3779
+ this.showCloseButton = true;
3780
+ this.closeOnEsc = true;
3781
+ this.closeOnBackdropClick = true;
3782
+ this.openChange = new EventEmitter();
3783
+ // DEPRECATED: contenido específico que se movió a ng-content
2672
3784
  this.value = '';
2673
3785
  this.unit = '';
2674
3786
  this.decrementLabel = '-';
@@ -2682,10 +3794,19 @@ class PdmDrawerComponent {
2682
3794
  this.usernameLabel = 'Username';
2683
3795
  this.usernameValue = '';
2684
3796
  this.responsivePrimaryLabel = '';
2685
- this.openChange = new EventEmitter();
3797
+ this.bars = [];
2686
3798
  this.primaryAction = new EventEmitter();
2687
3799
  this.secondaryAction = new EventEmitter();
2688
- this.bars = [];
3800
+ }
3801
+ onEsc() {
3802
+ if (this.open && this.closeOnEsc) {
3803
+ this.close();
3804
+ }
3805
+ }
3806
+ onBackdropClick() {
3807
+ if (this.closeOnBackdropClick) {
3808
+ this.close();
3809
+ }
2689
3810
  }
2690
3811
  close() {
2691
3812
  this.openChange.emit(false);
@@ -2696,14 +3817,67 @@ class PdmDrawerComponent {
2696
3817
  onSecondaryAction() {
2697
3818
  this.secondaryAction.emit();
2698
3819
  }
3820
+ get containerClassName() {
3821
+ return `fixed inset-0 ${Z_INDEX.drawer} ${this.className}`;
3822
+ }
3823
+ get panelClassName() {
3824
+ const base = [
3825
+ 'absolute',
3826
+ 'bg-background',
3827
+ 'border',
3828
+ 'border-border',
3829
+ 'shadow-lg',
3830
+ 'overflow-auto'
3831
+ ];
3832
+ // Posicionamiento
3833
+ const positionClasses = this.getPositionClasses();
3834
+ // Tamaño
3835
+ const sizeClasses = this.getSizeClasses();
3836
+ return [...base, ...positionClasses, ...sizeClasses].filter(Boolean).join(' ');
3837
+ }
3838
+ getPositionClasses() {
3839
+ const map = {
3840
+ bottom: ['inset-x-0', 'bottom-0', 'rounded-t-xl'],
3841
+ top: ['inset-x-0', 'top-0', 'rounded-b-xl'],
3842
+ left: ['inset-y-0', 'left-0', 'rounded-r-xl'],
3843
+ right: ['inset-y-0', 'right-0', 'rounded-l-xl']
3844
+ };
3845
+ return map[this.position] || map.bottom;
3846
+ }
3847
+ getSizeClasses() {
3848
+ const isVertical = this.position === 'bottom' || this.position === 'top';
3849
+ if (this.size === 'full') {
3850
+ return ['w-full', 'h-full'];
3851
+ }
3852
+ const sizeMap = {
3853
+ sm: isVertical ? 'max-h-[50vh]' : 'max-w-[400px]',
3854
+ md: isVertical ? 'max-h-[66vh]' : 'max-w-[500px]',
3855
+ lg: isVertical ? 'max-h-[80vh]' : 'max-w-[640px]'
3856
+ };
3857
+ const maxDimension = sizeMap[this.size] || sizeMap.md;
3858
+ if (isVertical) {
3859
+ return ['w-full', maxDimension];
3860
+ }
3861
+ else {
3862
+ return ['h-full', maxDimension];
3863
+ }
3864
+ }
3865
+ get showLegacyContent() {
3866
+ // Mostrar contenido legacy si variant está siendo usado
3867
+ return this.variant === 'drawer' || this.variant === 'responsive-dialog';
3868
+ }
2699
3869
  }
2700
3870
  PdmDrawerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2701
- 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 });
3871
+ 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 });
2702
3872
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmDrawerComponent, decorators: [{
2703
3873
  type: Component,
2704
- 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" }]
3874
+ 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" }]
2705
3875
  }], propDecorators: { open: [{
2706
3876
  type: Input
3877
+ }], position: [{
3878
+ type: Input
3879
+ }], size: [{
3880
+ type: Input
2707
3881
  }], variant: [{
2708
3882
  type: Input
2709
3883
  }], className: [{
@@ -2712,6 +3886,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
2712
3886
  type: Input
2713
3887
  }], description: [{
2714
3888
  type: Input
3889
+ }], showHandle: [{
3890
+ type: Input
3891
+ }], showCloseButton: [{
3892
+ type: Input
3893
+ }], closeOnEsc: [{
3894
+ type: Input
3895
+ }], closeOnBackdropClick: [{
3896
+ type: Input
3897
+ }], openChange: [{
3898
+ type: Output
2715
3899
  }], value: [{
2716
3900
  type: Input
2717
3901
  }], unit: [{
@@ -2738,14 +3922,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
2738
3922
  type: Input
2739
3923
  }], responsivePrimaryLabel: [{
2740
3924
  type: Input
2741
- }], openChange: [{
2742
- type: Output
3925
+ }], bars: [{
3926
+ type: Input
2743
3927
  }], primaryAction: [{
2744
3928
  type: Output
2745
3929
  }], secondaryAction: [{
2746
3930
  type: Output
2747
- }], bars: [{
2748
- type: Input
3931
+ }], onEsc: [{
3932
+ type: HostListener,
3933
+ args: ['document:keydown.escape']
2749
3934
  }] } });
2750
3935
 
2751
3936
  class PdmEmptyComponent {
@@ -2887,10 +4072,10 @@ class PdmHoverCardComponent {
2887
4072
  }
2888
4073
  }
2889
4074
  PdmHoverCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmHoverCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2890
- 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 });
4075
+ 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 });
2891
4076
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmHoverCardComponent, decorators: [{
2892
4077
  type: Component,
2893
- 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" }]
4078
+ 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" }]
2894
4079
  }], propDecorators: { className: [{
2895
4080
  type: Input
2896
4081
  }], panelClassName: [{
@@ -3279,12 +4464,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
3279
4464
  }] } });
3280
4465
 
3281
4466
  class PdmMenubarComponent {
3282
- constructor() {
4467
+ constructor(elementRef, cdr) {
4468
+ this.elementRef = elementRef;
4469
+ this.cdr = cdr;
3283
4470
  this.menus = [];
3284
4471
  this.className = '';
3285
4472
  this.itemSelect = new EventEmitter();
3286
4473
  this.openIndex = -1;
3287
4474
  }
4475
+ ngOnInit() {
4476
+ this.boundPointerDown = (event) => this.onDocumentPointerDown(event);
4477
+ document.addEventListener('pointerdown', this.boundPointerDown, { capture: true });
4478
+ }
4479
+ ngOnDestroy() {
4480
+ if (this.boundPointerDown) {
4481
+ document.removeEventListener('pointerdown', this.boundPointerDown, { capture: true });
4482
+ }
4483
+ }
4484
+ onEsc() {
4485
+ if (this.openIndex >= 0) {
4486
+ this.openIndex = -1;
4487
+ this.cdr.markForCheck();
4488
+ }
4489
+ }
3288
4490
  toggle(index) {
3289
4491
  this.openIndex = this.openIndex === index ? -1 : index;
3290
4492
  }
@@ -3298,18 +4500,32 @@ class PdmMenubarComponent {
3298
4500
  }
3299
4501
  this.select(item.value);
3300
4502
  }
4503
+ onDocumentPointerDown(event) {
4504
+ if (this.openIndex < 0)
4505
+ return;
4506
+ const target = event.target;
4507
+ if (!target)
4508
+ return;
4509
+ if (!this.elementRef.nativeElement.contains(target)) {
4510
+ this.openIndex = -1;
4511
+ this.cdr.markForCheck();
4512
+ }
4513
+ }
3301
4514
  }
3302
- PdmMenubarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmMenubarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3303
- 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 });
4515
+ 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 });
4516
+ 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 });
3304
4517
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmMenubarComponent, decorators: [{
3305
4518
  type: Component,
3306
- 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" }]
3307
- }], propDecorators: { menus: [{
4519
+ 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" }]
4520
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { menus: [{
3308
4521
  type: Input
3309
4522
  }], className: [{
3310
4523
  type: Input
3311
4524
  }], itemSelect: [{
3312
4525
  type: Output
4526
+ }], onEsc: [{
4527
+ type: HostListener,
4528
+ args: ['document:keydown.escape']
3313
4529
  }] } });
3314
4530
 
3315
4531
  class PdmNativeSelectComponent {
@@ -3350,21 +4566,127 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
3350
4566
  type: Output
3351
4567
  }] } });
3352
4568
 
4569
+ /**
4570
+ * Navigation Menu component - Navegación horizontal responsive
4571
+ *
4572
+ * MEJORAS en v0.2.0:
4573
+ * - Modo scroll: overflow-x-auto con scroll indicators en mobile
4574
+ * - Modo compact: items abreviados en mobile, completos en desktop
4575
+ * - Scroll smooth automático al item activo
4576
+ *
4577
+ * @example
4578
+ * <!-- Scroll horizontal (default) -->
4579
+ * <pdm-navigation-menu [items]="navItems"></pdm-navigation-menu>
4580
+ *
4581
+ * <!-- Compact mode -->
4582
+ * <pdm-navigation-menu [items]="navItems" mobileMode="compact"></pdm-navigation-menu>
4583
+ */
3353
4584
  class PdmNavigationMenuComponent {
3354
4585
  constructor() {
3355
4586
  this.items = [];
3356
4587
  this.className = '';
4588
+ /**
4589
+ * Mobile behavior: 'scroll' (horizontal scroll) o 'compact' (items reducidos)
4590
+ * @default 'scroll'
4591
+ */
4592
+ this.mobileMode = 'scroll';
3357
4593
  }
3358
4594
  }
3359
4595
  PdmNavigationMenuComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmNavigationMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3360
- 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 });
4596
+ 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 });
3361
4597
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmNavigationMenuComponent, decorators: [{
3362
4598
  type: Component,
3363
- 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" }]
4599
+ 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" }]
3364
4600
  }], propDecorators: { items: [{
3365
4601
  type: Input
3366
4602
  }], className: [{
3367
4603
  type: Input
4604
+ }], mobileMode: [{
4605
+ type: Input
4606
+ }] } });
4607
+
4608
+ /**
4609
+ * Emits `(pdmOutsideClick)` whenever a `pointerdown` event fires outside the host element.
4610
+ *
4611
+ * Uses the CAPTURE phase so it intercepts clicks even when inner elements call
4612
+ * `stopPropagation()` (e.g. CDK overlay panels).
4613
+ *
4614
+ * SSR-safe: no listener is registered when running on the server.
4615
+ *
4616
+ * @example
4617
+ * ```html
4618
+ * <div [pdmOutsideClick]
4619
+ * (pdmOutsideClick)="close()"
4620
+ * [pdmOutsideClickDisabled]="!open">
4621
+ * </div>
4622
+ * ```
4623
+ */
4624
+ class PdmOutsideClickDirective {
4625
+ constructor(elementRef, document, platformId) {
4626
+ this.elementRef = elementRef;
4627
+ this.document = document;
4628
+ /** When `true`, the outside-click listener is inactive. */
4629
+ this.pdmOutsideClickDisabled = false;
4630
+ /**
4631
+ * Additional elements to exclude from the "outside" check.
4632
+ * Useful when the trigger lives outside the host (e.g. a menubar button
4633
+ * that opens a floating panel bound to a different root element).
4634
+ */
4635
+ this.pdmOutsideClickExclude = [];
4636
+ /** Fires when a `pointerdown` lands outside the host (and excluded elements). */
4637
+ this.pdmOutsideClick = new EventEmitter();
4638
+ this.isBrowser = isPlatformBrowser(platformId);
4639
+ }
4640
+ ngOnInit() {
4641
+ if (!this.isBrowser)
4642
+ return;
4643
+ this.boundHandler = (event) => this.onPointerDown(event);
4644
+ this.document.addEventListener('pointerdown', this.boundHandler, { capture: true });
4645
+ }
4646
+ ngOnDestroy() {
4647
+ if (!this.isBrowser || !this.boundHandler)
4648
+ return;
4649
+ this.document.removeEventListener('pointerdown', this.boundHandler, { capture: true });
4650
+ }
4651
+ onPointerDown(event) {
4652
+ if (this.pdmOutsideClickDisabled)
4653
+ return;
4654
+ const target = event.target;
4655
+ if (!target)
4656
+ return;
4657
+ // Check if click is inside the host element.
4658
+ if (this.elementRef.nativeElement.contains(target))
4659
+ return;
4660
+ // Check if click is inside any excluded element.
4661
+ for (const excluded of this.pdmOutsideClickExclude) {
4662
+ const el = excluded instanceof ElementRef ? excluded.nativeElement : excluded;
4663
+ if (el && el.contains(target))
4664
+ return;
4665
+ }
4666
+ this.pdmOutsideClick.emit(event);
4667
+ }
4668
+ }
4669
+ 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 });
4670
+ 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 });
4671
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmOutsideClickDirective, decorators: [{
4672
+ type: Directive,
4673
+ args: [{
4674
+ selector: '[pdmOutsideClick]'
4675
+ }]
4676
+ }], ctorParameters: function () {
4677
+ return [{ type: i0.ElementRef }, { type: Document, decorators: [{
4678
+ type: Inject,
4679
+ args: [DOCUMENT]
4680
+ }] }, { type: undefined, decorators: [{
4681
+ type: Inject,
4682
+ args: [PLATFORM_ID]
4683
+ }] }];
4684
+ }, propDecorators: { pdmOutsideClickDisabled: [{
4685
+ type: Input
4686
+ }], pdmOutsideClickExclude: [{
4687
+ type: Input
4688
+ }], pdmOutsideClick: [{
4689
+ type: Output
3368
4690
  }] } });
3369
4691
 
3370
4692
  /**
@@ -3488,7 +4810,9 @@ class PdmSelectComponent {
3488
4810
  this.closePanel();
3489
4811
  }
3490
4812
  onEscape() {
3491
- this.closePanel();
4813
+ if (this.open) {
4814
+ this.closePanel();
4815
+ }
3492
4816
  }
3493
4817
  openPanel() {
3494
4818
  var _a;
@@ -3501,8 +4825,9 @@ class PdmSelectComponent {
3501
4825
  this.cdr.markForCheck();
3502
4826
  const positionStrategy = createFlexiblePositionStrategy(this.overlay, triggerEl, 4);
3503
4827
  this.overlayRef = this.overlay.create(Object.assign({
3504
- // Fix: use a token array DOMTokenList.add() rejects space-containing strings.
3505
- panelClass: ['block'], positionStrategy, scrollStrategy: this.overlay.scrollStrategies.reposition(), width: triggerEl.offsetWidth }, this.overlayOptions));
4828
+ // CRÍTICO: z-[70] para aparecer SOBRE modals (z-[60])
4829
+ // panelClass se aplica al cdk-overlay-pane wrapper
4830
+ panelClass: [Z_INDEX.popover], positionStrategy, scrollStrategy: this.overlay.scrollStrategies.reposition(), width: triggerEl.offsetWidth }, this.overlayOptions));
3506
4831
  const portal = new TemplatePortal(this.panelTemplateRef, this.viewContainerRef);
3507
4832
  this.overlayRef.attach(portal);
3508
4833
  this.backdropSub = this.overlayRef.outsidePointerEvents().subscribe((event) => {
@@ -3611,10 +4936,10 @@ class PdmPaginationComponent {
3611
4936
  }
3612
4937
  }
3613
4938
  PdmPaginationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmPaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3614
- 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 });
4939
+ 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 });
3615
4940
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmPaginationComponent, decorators: [{
3616
4941
  type: Component,
3617
- 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" }]
4942
+ 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" }]
3618
4943
  }], propDecorators: { page: [{
3619
4944
  type: Input
3620
4945
  }], pageCount: [{
@@ -3647,6 +4972,15 @@ class PdmPopoverComponent {
3647
4972
  this.openChange = new EventEmitter();
3648
4973
  this.panelPlacement = 'bottom';
3649
4974
  }
4975
+ ngOnInit() {
4976
+ this.boundPointerDown = (event) => this.onDocumentPointerDown(event);
4977
+ document.addEventListener('pointerdown', this.boundPointerDown, { capture: true });
4978
+ }
4979
+ ngOnDestroy() {
4980
+ if (this.boundPointerDown) {
4981
+ document.removeEventListener('pointerdown', this.boundPointerDown, { capture: true });
4982
+ }
4983
+ }
3650
4984
  set open(value) {
3651
4985
  this._open = !!value;
3652
4986
  if (this._open) {
@@ -3661,10 +4995,11 @@ class PdmPopoverComponent {
3661
4995
  return this._open;
3662
4996
  }
3663
4997
  get panelClasses() {
4998
+ const baseClasses = 'min-w-80 rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md';
3664
4999
  return [
3665
5000
  this.panelPlacement === 'top'
3666
- ? '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'
3667
- : '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',
5001
+ ? `absolute bottom-full left-0 ${Z_INDEX.popover} mb-2 ${baseClasses}`
5002
+ : `absolute left-0 top-full ${Z_INDEX.popover} mt-2 ${baseClasses}`,
3668
5003
  this.panelClassName
3669
5004
  ];
3670
5005
  }
@@ -3678,17 +5013,17 @@ class PdmPopoverComponent {
3678
5013
  this.openChange.emit(false);
3679
5014
  }
3680
5015
  }
3681
- onDocumentClick(event) {
5016
+ onViewportChange() {
5017
+ this.updatePanelPlacement();
5018
+ }
5019
+ onDocumentPointerDown(event) {
3682
5020
  if (!this.open)
3683
5021
  return;
3684
5022
  const target = event.target;
3685
5023
  if (target && !this.elementRef.nativeElement.contains(target)) {
3686
5024
  this.open = false;
3687
- this.openChange.emit(false);
3688
- }
3689
- }
3690
- onViewportChange() {
3691
- this.updatePanelPlacement();
5025
+ this.openChange.emit(false);
5026
+ }
3692
5027
  }
3693
5028
  schedulePanelPlacementUpdate() {
3694
5029
  setTimeout(() => this.updatePanelPlacement());
@@ -3716,7 +5051,7 @@ class PdmPopoverComponent {
3716
5051
  }
3717
5052
  }
3718
5053
  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 });
3719
- 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 });
5054
+ 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 });
3720
5055
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmPopoverComponent, decorators: [{
3721
5056
  type: Component,
3722
5057
  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" }]
@@ -3744,9 +5079,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
3744
5079
  }], onEsc: [{
3745
5080
  type: HostListener,
3746
5081
  args: ['document:keydown.escape']
3747
- }], onDocumentClick: [{
3748
- type: HostListener,
3749
- args: ['document:click', ['$event']]
3750
5082
  }], onViewportChange: [{
3751
5083
  type: HostListener,
3752
5084
  args: ['window:resize']
@@ -3860,56 +5192,180 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
3860
5192
  type: Input
3861
5193
  }] } });
3862
5194
 
5195
+ /**
5196
+ * Sheet/Side panel component con soporte responsive
5197
+ *
5198
+ * MEJORADO en v0.2.0:
5199
+ * - Tamaños configurables
5200
+ * - Mejor manejo de overflow
5201
+ * - Responsive sizes
5202
+ *
5203
+ * @example
5204
+ * <pdm-sheet [open]="isOpen" side="right" size="md">
5205
+ * <h3>Settings</h3>
5206
+ * <p>Content here</p>
5207
+ * </pdm-sheet>
5208
+ */
3863
5209
  class PdmSheetComponent {
3864
5210
  constructor() {
3865
5211
  this.open = false;
5212
+ /**
5213
+ * Lado desde donde aparece el sheet
5214
+ */
3866
5215
  this.side = 'right';
5216
+ /**
5217
+ * Tamaño del sheet
5218
+ * - sm: 320px (side) / 40vh (top/bottom)
5219
+ * - md: 400px (side) / 50vh (top/bottom) (default)
5220
+ * - lg: 500px (side) / 66vh (top/bottom)
5221
+ * - xl: 640px (side) / 80vh (top/bottom)
5222
+ * - full: 100%
5223
+ */
5224
+ this.size = 'md';
3867
5225
  this.className = '';
5226
+ this.closeOnEsc = true;
5227
+ this.closeOnBackdropClick = true;
3868
5228
  this.openChange = new EventEmitter();
3869
5229
  }
5230
+ onEsc() {
5231
+ if (this.open && this.closeOnEsc) {
5232
+ this.close();
5233
+ }
5234
+ }
5235
+ onBackdropClick() {
5236
+ if (this.closeOnBackdropClick) {
5237
+ this.close();
5238
+ }
5239
+ }
3870
5240
  close() {
3871
5241
  this.openChange.emit(false);
3872
5242
  }
3873
5243
  get panelClass() {
3874
- if (this.side === 'left')
3875
- return 'left-0 top-0 h-full w-full max-w-[360px] border-r';
3876
- if (this.side === 'top')
3877
- return 'top-0 left-0 w-full border-b';
3878
- if (this.side === 'bottom')
3879
- return 'bottom-0 left-0 w-full border-t';
3880
- return 'right-0 top-0 h-full w-full max-w-[360px] border-l';
5244
+ const base = 'absolute bg-background border-border shadow-lg overflow-auto';
5245
+ const position = this.getPositionClass();
5246
+ const sizing = this.getSizingClass();
5247
+ return `${base} ${position} ${sizing} ${this.className}`.trim();
5248
+ }
5249
+ getPositionClass() {
5250
+ const map = {
5251
+ left: 'left-0 top-0 h-full border-r',
5252
+ right: 'right-0 top-0 h-full border-l',
5253
+ top: 'top-0 left-0 w-full border-b',
5254
+ bottom: 'bottom-0 left-0 w-full border-t'
5255
+ };
5256
+ return map[this.side];
5257
+ }
5258
+ getSizingClass() {
5259
+ if (this.size === 'full') {
5260
+ return 'w-full h-full';
5261
+ }
5262
+ const isHorizontal = this.side === 'left' || this.side === 'right';
5263
+ if (isHorizontal) {
5264
+ const widthMap = {
5265
+ sm: 'w-full max-w-[320px] sm:max-w-[320px]',
5266
+ md: 'w-full max-w-[360px] sm:max-w-[400px]',
5267
+ lg: 'w-full max-w-[400px] sm:max-w-[500px]',
5268
+ xl: 'w-full max-w-[500px] sm:max-w-[640px]'
5269
+ };
5270
+ return widthMap[this.size] || widthMap.md;
5271
+ }
5272
+ else {
5273
+ const heightMap = {
5274
+ sm: 'max-h-[40vh]',
5275
+ md: 'max-h-[50vh]',
5276
+ lg: 'max-h-[66vh]',
5277
+ xl: 'max-h-[80vh]'
5278
+ };
5279
+ return heightMap[this.size] || heightMap.md;
5280
+ }
3881
5281
  }
3882
5282
  }
3883
5283
  PdmSheetComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3884
- 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 });
5284
+ 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 });
3885
5285
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmSheetComponent, decorators: [{
3886
5286
  type: Component,
3887
- 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" }]
5287
+ 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" }]
3888
5288
  }], propDecorators: { open: [{
3889
5289
  type: Input
3890
5290
  }], side: [{
3891
5291
  type: Input
5292
+ }], size: [{
5293
+ type: Input
3892
5294
  }], className: [{
3893
5295
  type: Input
5296
+ }], closeOnEsc: [{
5297
+ type: Input
5298
+ }], closeOnBackdropClick: [{
5299
+ type: Input
3894
5300
  }], openChange: [{
3895
5301
  type: Output
5302
+ }], onEsc: [{
5303
+ type: HostListener,
5304
+ args: ['document:keydown.escape']
3896
5305
  }] } });
3897
5306
 
5307
+ /**
5308
+ * Sidebar component - Navegación lateral responsive
5309
+ *
5310
+ * MEJORAS en v0.2.0:
5311
+ * - Mobile drawer mode: overlay fullscreen en mobile, sidebar fijo en desktop
5312
+ * - Sidebar mode: sidebar persistente con widths responsive
5313
+ * - Backdrop automático en mobile drawer mode
5314
+ *
5315
+ * @example
5316
+ * <!-- Mobile drawer (default) -->
5317
+ * <pdm-sidebar [open]="sidebarOpen" (openChange)="sidebarOpen = $event">
5318
+ * <nav>Menu items...</nav>
5319
+ * </pdm-sidebar>
5320
+ *
5321
+ * <!-- Sidebar persistente -->
5322
+ * <pdm-sidebar mobileMode="sidebar" [collapsed]="collapsed">
5323
+ * <nav>Menu items...</nav>
5324
+ * </pdm-sidebar>
5325
+ */
3898
5326
  class PdmSidebarComponent {
3899
5327
  constructor() {
5328
+ /**
5329
+ * Mobile behavior: 'drawer' (overlay) o 'sidebar' (persistente)
5330
+ * @default 'drawer'
5331
+ */
5332
+ this.mobileMode = 'drawer';
5333
+ /**
5334
+ * Collapsed state (solo aplica en mobileMode="sidebar")
5335
+ */
3900
5336
  this.collapsed = false;
5337
+ /**
5338
+ * Open state (solo aplica en mobileMode="drawer")
5339
+ */
5340
+ this.open = false;
3901
5341
  this.className = '';
5342
+ /**
5343
+ * Emite cuando el drawer se cierra (solo en mobileMode="drawer")
5344
+ */
5345
+ this.openChange = new EventEmitter();
5346
+ }
5347
+ onBackdropClick() {
5348
+ if (this.mobileMode === 'drawer') {
5349
+ this.open = false;
5350
+ this.openChange.emit(false);
5351
+ }
3902
5352
  }
3903
5353
  }
3904
5354
  PdmSidebarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3905
- 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 });
5355
+ 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 });
3906
5356
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmSidebarComponent, decorators: [{
3907
5357
  type: Component,
3908
- 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" }]
3909
- }], propDecorators: { collapsed: [{
5358
+ 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" }]
5359
+ }], propDecorators: { mobileMode: [{
5360
+ type: Input
5361
+ }], collapsed: [{
5362
+ type: Input
5363
+ }], open: [{
3910
5364
  type: Input
3911
5365
  }], className: [{
3912
5366
  type: Input
5367
+ }], openChange: [{
5368
+ type: Output
3913
5369
  }] } });
3914
5370
 
3915
5371
  class PdmSkeletonComponent {
@@ -4076,213 +5532,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
4076
5532
  type: Output
4077
5533
  }] } });
4078
5534
 
4079
- class PdmTableComponent {
4080
- constructor(renderer) {
4081
- this.renderer = renderer;
4082
- this.variant = 'default';
4083
- this.reorderRows = false;
4084
- this.dragHandleSelector = '[data-drag-handle],[data-slot=row-drag-handle],.row-drag-handle,[data-auto-drag-handle]';
4085
- this.className = '';
4086
- this.rowOrderChange = new EventEmitter();
4087
- this.cleanupListeners = [];
4088
- this.draggedRow = null;
4089
- }
4090
- ngAfterViewInit() {
4091
- this.syncReorderBehavior();
4092
- }
4093
- ngOnChanges(changes) {
4094
- if (changes['reorderRows'] || changes['variant']) {
4095
- this.syncReorderBehavior();
4096
- }
4097
- }
4098
- ngOnDestroy() {
4099
- this.cleanupReorderBehavior();
4100
- }
4101
- get wrapperClasses() {
4102
- return [
4103
- 'relative w-full overflow-auto',
4104
- this.variant === 'interactive' ? 'overflow-x-auto overflow-y-hidden rounded-xl border border-border bg-background' : '',
4105
- this.variant === 'data' ? 'overflow-hidden rounded-md border border-border bg-background' : '',
4106
- this.className
4107
- ];
4108
- }
4109
- get tableClasses() {
4110
- return [
4111
- 'w-full caption-bottom text-sm',
4112
- this.variant === 'data'
4113
- ? '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'
4114
- : '',
4115
- this.variant === 'interactive'
4116
- ? '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'
4117
- : ''
4118
- ];
4119
- }
4120
- syncReorderBehavior() {
4121
- this.cleanupReorderBehavior();
4122
- if (!this.isReorderEnabled) {
4123
- return;
4124
- }
4125
- const tbody = this.getTbody();
4126
- if (!tbody) {
4127
- return;
4128
- }
4129
- this.setRowsDraggable(tbody, true);
4130
- 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()));
4131
- this.observer = new MutationObserver(() => this.setRowsDraggable(tbody, true));
4132
- this.observer.observe(tbody, { childList: true });
4133
- }
4134
- cleanupReorderBehavior() {
4135
- this.cleanupListeners.forEach((dispose) => dispose());
4136
- this.cleanupListeners = [];
4137
- if (this.observer) {
4138
- this.observer.disconnect();
4139
- this.observer = undefined;
4140
- }
4141
- const tbody = this.getTbody();
4142
- if (tbody) {
4143
- this.setRowsDraggable(tbody, false);
4144
- }
4145
- this.draggedRow = null;
4146
- }
4147
- get isReorderEnabled() {
4148
- return this.reorderRows;
4149
- }
4150
- getTbody() {
4151
- var _a, _b;
4152
- return (_b = (_a = this.tableElement) === null || _a === void 0 ? void 0 : _a.nativeElement.tBodies.item(0)) !== null && _b !== void 0 ? _b : null;
4153
- }
4154
- setRowsDraggable(tbody, enabled) {
4155
- const rows = Array.from(tbody.rows);
4156
- rows.forEach((row) => {
4157
- this.syncAutoDragHandle(row, enabled);
4158
- row.draggable = false;
4159
- if (!enabled) {
4160
- delete row.dataset['dragging'];
4161
- delete row.dataset['dragArmed'];
4162
- }
4163
- });
4164
- }
4165
- syncAutoDragHandle(row, enabled) {
4166
- const firstCell = row.cells.item(0);
4167
- if (!firstCell) {
4168
- return;
4169
- }
4170
- const existingAutoHandle = firstCell.querySelector('[data-auto-drag-handle]');
4171
- if (!enabled) {
4172
- existingAutoHandle === null || existingAutoHandle === void 0 ? void 0 : existingAutoHandle.remove();
4173
- return;
4174
- }
4175
- const hasCustomHandle = !!firstCell.querySelector('[data-drag-handle],[data-slot=row-drag-handle],.row-drag-handle');
4176
- if (hasCustomHandle || existingAutoHandle) {
4177
- return;
4178
- }
4179
- const button = this.renderer.createElement('button');
4180
- this.renderer.setAttribute(button, 'type', 'button');
4181
- this.renderer.setAttribute(button, 'aria-label', 'Drag row');
4182
- this.renderer.setAttribute(button, 'data-auto-drag-handle', 'true');
4183
- this.renderer.addClass(button, 'inline-flex');
4184
- this.renderer.addClass(button, 'h-7');
4185
- this.renderer.addClass(button, 'w-7');
4186
- this.renderer.addClass(button, 'items-center');
4187
- this.renderer.addClass(button, 'justify-center');
4188
- this.renderer.addClass(button, 'cursor-grab');
4189
- this.renderer.addClass(button, 'active:cursor-grabbing');
4190
- this.renderer.addClass(button, 'text-muted-foreground');
4191
- const dots = this.renderer.createElement('span');
4192
- this.renderer.addClass(dots, 'text-sm');
4193
- this.renderer.addClass(dots, 'leading-none');
4194
- this.renderer.setProperty(dots, 'textContent', '⋮⋮');
4195
- this.renderer.appendChild(button, dots);
4196
- this.renderer.insertBefore(firstCell, button, firstCell.firstChild);
4197
- }
4198
- onDragStart(event) {
4199
- const target = event.target;
4200
- const row = target === null || target === void 0 ? void 0 : target.closest('tr');
4201
- if (!row) {
4202
- return;
4203
- }
4204
- const handle = target === null || target === void 0 ? void 0 : target.closest(this.dragHandleSelector);
4205
- const isArmed = row.dataset['dragArmed'] === 'true';
4206
- if ((!handle || !row.contains(handle)) && !isArmed) {
4207
- event.preventDefault();
4208
- return;
4209
- }
4210
- this.draggedRow = row;
4211
- this.draggedRow.dataset['dragging'] = 'true';
4212
- if (event.dataTransfer) {
4213
- event.dataTransfer.effectAllowed = 'move';
4214
- event.dataTransfer.setData('text/plain', '');
4215
- }
4216
- }
4217
- onDragOver(event, tbody) {
4218
- if (!this.draggedRow) {
4219
- return;
4220
- }
4221
- event.preventDefault();
4222
- const target = event.target;
4223
- const targetRow = target === null || target === void 0 ? void 0 : target.closest('tr');
4224
- if (!targetRow || targetRow === this.draggedRow) {
4225
- return;
4226
- }
4227
- const rect = targetRow.getBoundingClientRect();
4228
- const shouldInsertBefore = event.clientY < rect.top + rect.height / 2;
4229
- tbody.insertBefore(this.draggedRow, shouldInsertBefore ? targetRow : targetRow.nextSibling);
4230
- }
4231
- onDrop(event) {
4232
- event.preventDefault();
4233
- }
4234
- onDragEnd() {
4235
- const tbody = this.getTbody();
4236
- if (tbody) {
4237
- Array.from(tbody.rows).forEach((row) => {
4238
- row.draggable = false;
4239
- delete row.dataset['dragArmed'];
4240
- });
4241
- }
4242
- if (this.draggedRow) {
4243
- delete this.draggedRow.dataset['dragging'];
4244
- this.draggedRow = null;
4245
- }
4246
- if (!tbody) {
4247
- return;
4248
- }
4249
- const order = Array.from(tbody.rows).map((row, index) => row.getAttribute('data-row-id') || String(index));
4250
- this.rowOrderChange.emit(order);
4251
- }
4252
- armDragFromHandle(event) {
4253
- const target = event.target;
4254
- const handle = target === null || target === void 0 ? void 0 : target.closest(this.dragHandleSelector);
4255
- if (!handle) {
4256
- return;
4257
- }
4258
- const row = handle.closest('tr');
4259
- if (!row) {
4260
- return;
4261
- }
4262
- row.draggable = true;
4263
- row.dataset['dragArmed'] = 'true';
4264
- }
4265
- }
4266
- PdmTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTableComponent, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
4267
- 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 });
4268
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTableComponent, decorators: [{
4269
- type: Component,
4270
- 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" }]
4271
- }], ctorParameters: function () { return [{ type: i0.Renderer2 }]; }, propDecorators: { variant: [{
4272
- type: Input
4273
- }], reorderRows: [{
4274
- type: Input
4275
- }], dragHandleSelector: [{
4276
- type: Input
4277
- }], className: [{
4278
- type: Input
4279
- }], rowOrderChange: [{
4280
- type: Output
4281
- }], tableElement: [{
4282
- type: ViewChild,
4283
- args: ['tableElement']
4284
- }] } });
4285
-
4286
5535
  class PdmTabsComponent {
4287
5536
  constructor(cdr) {
4288
5537
  this.cdr = cdr;
@@ -4300,10 +5549,10 @@ class PdmTabsComponent {
4300
5549
  }
4301
5550
  }
4302
5551
  PdmTabsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTabsComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
4303
- 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 });
5552
+ 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 });
4304
5553
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTabsComponent, decorators: [{
4305
5554
  type: Component,
4306
- 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" }]
5555
+ 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" }]
4307
5556
  }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { items: [{
4308
5557
  type: Input
4309
5558
  }], value: [{
@@ -4454,10 +5703,10 @@ class PdmTooltipComponent {
4454
5703
  }
4455
5704
  }
4456
5705
  PdmTooltipComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4457
- 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 });
5706
+ 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 });
4458
5707
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: PdmTooltipComponent, decorators: [{
4459
5708
  type: Component,
4460
- 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" }]
5709
+ 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" }]
4461
5710
  }], propDecorators: { text: [{
4462
5711
  type: Input
4463
5712
  }], side: [{
@@ -4488,6 +5737,7 @@ const COMPONENTS = [
4488
5737
  PdmDataTableComponent,
4489
5738
  PdmDatePickerComponent,
4490
5739
  PdmDialogComponent,
5740
+ PdmDraggableTableComponent,
4491
5741
  PdmDropdownMenuComponent,
4492
5742
  PdmDrawerComponent,
4493
5743
  PdmEmptyComponent,
@@ -4504,6 +5754,7 @@ const COMPONENTS = [
4504
5754
  PdmMenubarComponent,
4505
5755
  PdmNativeSelectComponent,
4506
5756
  PdmNavigationMenuComponent,
5757
+ PdmOutsideClickDirective,
4507
5758
  PdmPaginationComponent,
4508
5759
  PdmPopoverComponent,
4509
5760
  PdmProgressComponent,
@@ -4550,6 +5801,7 @@ PdmUiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
4550
5801
  PdmDataTableComponent,
4551
5802
  PdmDatePickerComponent,
4552
5803
  PdmDialogComponent,
5804
+ PdmDraggableTableComponent,
4553
5805
  PdmDropdownMenuComponent,
4554
5806
  PdmDrawerComponent,
4555
5807
  PdmEmptyComponent,
@@ -4566,6 +5818,7 @@ PdmUiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
4566
5818
  PdmMenubarComponent,
4567
5819
  PdmNativeSelectComponent,
4568
5820
  PdmNavigationMenuComponent,
5821
+ PdmOutsideClickDirective,
4569
5822
  PdmPaginationComponent,
4570
5823
  PdmPopoverComponent,
4571
5824
  PdmProgressComponent,
@@ -4607,6 +5860,7 @@ PdmUiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
4607
5860
  PdmDataTableComponent,
4608
5861
  PdmDatePickerComponent,
4609
5862
  PdmDialogComponent,
5863
+ PdmDraggableTableComponent,
4610
5864
  PdmDropdownMenuComponent,
4611
5865
  PdmDrawerComponent,
4612
5866
  PdmEmptyComponent,
@@ -4623,6 +5877,7 @@ PdmUiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
4623
5877
  PdmMenubarComponent,
4624
5878
  PdmNativeSelectComponent,
4625
5879
  PdmNavigationMenuComponent,
5880
+ PdmOutsideClickDirective,
4626
5881
  PdmPaginationComponent,
4627
5882
  PdmPopoverComponent,
4628
5883
  PdmProgressComponent,
@@ -4658,5 +5913,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
4658
5913
  * Generated bundle index. Do not edit.
4659
5914
  */
4660
5915
 
4661
- 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 };
5916
+ 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 };
4662
5917
  //# sourceMappingURL=pdm-ui-kit.mjs.map