ps-helix 4.0.2 → 4.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A comprehensive Angular component library built with Angular 21+ featuring modern design patterns, accessibility-first development, and optimal developer experience.
4
4
 
5
- [![npm version](https://img.shields.io/badge/npm-4.0.2-blue.svg)](https://www.npmjs.com/package/ps-helix)
5
+ [![npm version](https://img.shields.io/badge/npm-4.0.3-blue.svg)](https://www.npmjs.com/package/ps-helix)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
  [![Angular](https://img.shields.io/badge/Angular-21.0.3-red.svg)](https://angular.dev/)
8
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.9.0-blue.svg)](https://www.typescriptlang.org/)
@@ -106,7 +106,7 @@ After installation, verify that ps-helix is in your `package.json`:
106
106
  ```json
107
107
  {
108
108
  "dependencies": {
109
- "ps-helix": "^4.0.2"
109
+ "ps-helix": "^4.0.3"
110
110
  }
111
111
  }
112
112
  ```
@@ -1182,7 +1182,7 @@ Copyright (c) 2025 PACK Solutions
1182
1182
 
1183
1183
  ---
1184
1184
 
1185
- **Version**: 4.0.2
1185
+ **Version**: 4.0.3
1186
1186
  **Built with**: Angular 21.0.3, TypeScript 5.9.0, Phosphor Icons 2.0.3
1187
1187
  **Author**: Fabrice PEREZ | Product Designer at PACK Solutions
1188
1188
  **Last Updated**: January 2026
@@ -3784,9 +3784,7 @@ class PshTableComponent {
3784
3784
  this.currentSortSignal.set(sort);
3785
3785
  this.sortChange.emit(sort);
3786
3786
  }
3787
- handleGlobalSearch(event) {
3788
- const value = event.target.value;
3789
- this.searchTermSignal.set(value);
3787
+ onSearchValueChange(value) {
3790
3788
  this.globalSearchChange.emit(value);
3791
3789
  }
3792
3790
  handleRowClick(row) {
@@ -3799,14 +3797,14 @@ class PshTableComponent {
3799
3797
  return this.getNestedValue(row, column.path || column.key);
3800
3798
  }
3801
3799
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PshTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3802
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: PshTableComponent, isStandalone: true, selector: "psh-table", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, globalSearch: { classPropertyName: "globalSearch", publicName: "globalSearch", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, noResultsMessage: { classPropertyName: "noResultsMessage", publicName: "noResultsMessage", isSignal: true, isRequired: false, transformFunction: null }, globalSearchPlaceholder: { classPropertyName: "globalSearchPlaceholder", publicName: "globalSearchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, tableLayout: { classPropertyName: "tableLayout", publicName: "tableLayout", isSignal: true, isRequired: false, transformFunction: null }, truncateText: { classPropertyName: "truncateText", publicName: "truncateText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChange: "sortChange", globalSearchChange: "globalSearchChange", rowClick: "rowClick" }, host: { properties: { "class.full-width": "fullWidth()" }, classAttribute: "psh-table" }, ngImport: i0, template: "<div\n class=\"table-wrapper\"\n [class.striped]=\"striped()\"\n [class.hoverable]=\"hoverable()\"\n [class.bordered]=\"bordered()\"\n [class.small]=\"size() === 'small'\"\n [class.large]=\"size() === 'large'\"\n [class.outline]=\"variant() === 'outline'\"\n [class.table-fixed]=\"tableLayout() === 'fixed'\"\n [class.truncate]=\"truncateText()\"\n [attr.data-state]=\"state()\"\n>\n @if (globalSearch()) {\n <div class=\"global-search\">\n <input\n type=\"text\"\n [attr.placeholder]=\"globalSearchPlaceholder()\"\n [value]=\"searchTerm()\"\n (input)=\"handleGlobalSearch($event)\"\n [attr.aria-label]=\"globalSearchPlaceholder()\"\n />\n <i class=\"ph ph-magnifying-glass\" aria-hidden=\"true\"></i>\n </div>\n }\n\n <table role=\"table\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th \n [style.width]=\"column.width\"\n [attr.aria-sort]=\"column.sortable ? (currentSort()?.key === column.key ? (currentSort()?.direction === 'asc' ? 'ascending' : 'descending') : 'none') : null\"\n [class.sortable]=\"column.sortable\" \n (click)=\"handleSort(column)\"\n >\n <div class=\"th-content\">\n {{ column.label }}\n @if (column.sortable) {\n @if (currentSort()?.key === column.key) {\n <i\n [class]=\"'ph ' + (currentSort()?.direction === 'desc' ? 'ph-sort-descending' : 'ph-sort-ascending')\"\n [attr.aria-label]=\"'Sort ' + currentSort()?.direction\"\n aria-hidden=\"true\">\n </i>\n } @else {\n <i\n class=\"ph ph-arrows-down-up sort-icon-neutral\"\n aria-label=\"Sortable\"\n aria-hidden=\"true\">\n </i>\n }\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n \n <tbody>\n @if (loading()) {\n <tr class=\"loading-row\">\n <td [attr.colspan]=\"columns().length\">\n <div class=\"loading-indicator\">\n <div class=\"spinner\"></div>\n </div>\n </td>\n </tr>\n } @else if (filteredData().length === 0) {\n <tr class=\"empty-row\">\n <td [attr.colspan]=\"columns().length\">\n {{ computedEmptyMessage() }}\n </td>\n </tr>\n } @else {\n @for (row of filteredData(); track row.id) {\n <tr (click)=\"handleRowClick(row)\">\n @for (column of columns(); track column.key) {\n <td>\n @if (column.template) {\n <ng-container\n [ngTemplateOutlet]=\"column.template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"\n ></ng-container>\n } @else {\n {{ getCellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>", styles: [":host{display:block}:host(.full-width){max-width:none;width:100%}.table-wrapper{width:100%;overflow-x:auto;background:var(--surface-card);border-radius:var(--border-radius)}.table-wrapper.outline{background:transparent;border:var(--border-width-1) solid var(--surface-border)}table{width:100%;border-collapse:collapse;text-align:left}th,td{padding:var(--spacing-md);color:var(--text-color);border-bottom:var(--border-width-1) solid var(--surface-border)}th{font-weight:600;color:var(--text-color);background:var(--surface-ground);white-space:nowrap;-webkit-user-select:none;user-select:none}th.sortable{cursor:pointer}th.sortable:hover{background:var(--surface-hover)}.th-content{display:flex;align-items:center;gap:var(--spacing-xs)}.th-content i{font-size:var(--font-size-base);color:var(--primary-color);display:inline-block;transition:color var(--animation-duration-fast) var(--animation-easing-default)}.th-content i.sort-icon-neutral{color:var(--text-color-secondary);opacity:.5}th.sortable:hover .sort-icon-neutral{opacity:1;color:var(--primary-color)}.table-wrapper.striped tr:nth-child(2n){background:var(--surface-ground)}.table-wrapper.hoverable tr:hover{background:var(--surface-hover)}.table-wrapper.bordered th,.table-wrapper.bordered td{border:var(--border-width-1) solid var(--surface-border)}.table-wrapper.small th,.table-wrapper.small td{padding:var(--spacing-sm);font-size:var(--font-size-sm)}.table-wrapper.large th,.table-wrapper.large td{padding:var(--spacing-lg);font-size:var(--font-size-lg)}.loading-row td,.empty-row td{text-align:center;padding:var(--spacing-xl);color:var(--text-color-secondary)}.loading-indicator{display:flex;justify-content:center;align-items:center}.spinner{width:var(--size-6);height:var(--size-6);border:var(--border-width-2) solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.global-search{position:relative;margin-bottom:var(--spacing-md)}.global-search input{width:100%;padding:var(--spacing-sm) var(--spacing-lg);padding-left:calc(var(--spacing-xl) + var(--spacing-xs));border:var(--border-width-1) solid var(--surface-border);border-radius:var(--border-radius);background:var(--surface-card);color:var(--text-color);font-size:var(--font-size-base);transition:all var(--animation-duration-normal) var(--animation-easing-default)}.global-search input:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 var(--focus-outline-width) var(--focus-ring-color)}.global-search i{position:absolute;left:var(--spacing-md);top:50%;transform:translateY(-50%);color:var(--text-color-secondary);font-size:var(--icon-size-md)}.sort-button{background:transparent;border:none;padding:var(--spacing-xs);cursor:pointer;color:var(--primary-color);border-radius:var(--border-radius);transition:all var(--animation-duration-normal) var(--animation-easing-default)}.sort-button:hover{background:rgba(var(--primary-color-rgb),.1)}.sort-button:focus{outline:none;box-shadow:0 0 0 var(--focus-outline-width) var(--focus-ring-color)}.table-wrapper.table-fixed table{table-layout:fixed}.table-wrapper.truncate td{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:0}.table-wrapper.truncate th{overflow:hidden;text-overflow:ellipsis}@media(max-width:48em){.table-wrapper{font-size:var(--font-size-sm)}th,td{padding:var(--spacing-sm)}.global-search{margin-bottom:var(--spacing-sm)}.global-search input{padding:var(--spacing-xs) var(--spacing-md);padding-left:calc(var(--spacing-lg) + 4px);font-size:var(--font-size-sm)}}@media(max-width:30em){.table-wrapper{border-radius:0;margin:0 calc(-1 * var(--spacing-sm))}th,td{padding:var(--spacing-xs) var(--spacing-sm);font-size:var(--font-size-xs)}.th-content{gap:var(--spacing-xxs)}.th-content i{font-size:var(--font-size-sm)}.global-search input{padding:var(--spacing-xs);padding-left:var(--spacing-md);font-size:var(--font-size-xs)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3800
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: PshTableComponent, isStandalone: true, selector: "psh-table", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, globalSearch: { classPropertyName: "globalSearch", publicName: "globalSearch", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, noResultsMessage: { classPropertyName: "noResultsMessage", publicName: "noResultsMessage", isSignal: true, isRequired: false, transformFunction: null }, globalSearchPlaceholder: { classPropertyName: "globalSearchPlaceholder", publicName: "globalSearchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, tableLayout: { classPropertyName: "tableLayout", publicName: "tableLayout", isSignal: true, isRequired: false, transformFunction: null }, truncateText: { classPropertyName: "truncateText", publicName: "truncateText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChange: "sortChange", globalSearchChange: "globalSearchChange", rowClick: "rowClick" }, host: { properties: { "class.full-width": "fullWidth()" }, classAttribute: "psh-table" }, ngImport: i0, template: "<div\n class=\"table-wrapper\"\n [class.striped]=\"striped()\"\n [class.hoverable]=\"hoverable()\"\n [class.bordered]=\"bordered()\"\n [class.small]=\"size() === 'small'\"\n [class.large]=\"size() === 'large'\"\n [class.outline]=\"variant() === 'outline'\"\n [class.table-fixed]=\"tableLayout() === 'fixed'\"\n [class.truncate]=\"truncateText()\"\n [attr.data-state]=\"state()\"\n>\n @if (globalSearch()) {\n <div class=\"global-search\" role=\"search\">\n <psh-input\n [placeholder]=\"globalSearchPlaceholder()\"\n [ariaLabel]=\"globalSearchPlaceholder()\"\n [size]=\"size()\"\n [fullWidth]=\"true\"\n [showLabel]=\"false\"\n iconStart=\"magnifying-glass\"\n type=\"search\"\n [(value)]=\"searchTermSignal\"\n (valueChange)=\"onSearchValueChange($event)\"\n />\n </div>\n }\n\n <table role=\"table\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th \n [style.width]=\"column.width\"\n [attr.aria-sort]=\"column.sortable ? (currentSort()?.key === column.key ? (currentSort()?.direction === 'asc' ? 'ascending' : 'descending') : 'none') : null\"\n [class.sortable]=\"column.sortable\" \n (click)=\"handleSort(column)\"\n >\n <div class=\"th-content\">\n {{ column.label }}\n @if (column.sortable) {\n @if (currentSort()?.key === column.key) {\n <i\n [class]=\"'ph ' + (currentSort()?.direction === 'desc' ? 'ph-sort-descending' : 'ph-sort-ascending')\"\n [attr.aria-label]=\"'Sort ' + currentSort()?.direction\"\n aria-hidden=\"true\">\n </i>\n } @else {\n <i\n class=\"ph ph-arrows-down-up sort-icon-neutral\"\n aria-label=\"Sortable\"\n aria-hidden=\"true\">\n </i>\n }\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n \n <tbody>\n @if (loading()) {\n <tr class=\"loading-row\">\n <td [attr.colspan]=\"columns().length\">\n <div class=\"loading-indicator\">\n <div class=\"spinner\"></div>\n </div>\n </td>\n </tr>\n } @else if (filteredData().length === 0) {\n <tr class=\"empty-row\">\n <td [attr.colspan]=\"columns().length\">\n {{ computedEmptyMessage() }}\n </td>\n </tr>\n } @else {\n @for (row of filteredData(); track row.id) {\n <tr (click)=\"handleRowClick(row)\">\n @for (column of columns(); track column.key) {\n <td>\n @if (column.template) {\n <ng-container\n [ngTemplateOutlet]=\"column.template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"\n ></ng-container>\n } @else {\n {{ getCellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>", styles: [":host{display:block}:host(.full-width){max-width:none;width:100%}.table-wrapper{width:100%;overflow-x:auto;background:var(--surface-card);border-radius:var(--border-radius)}.table-wrapper.outline{background:transparent;border:var(--border-width-1) solid var(--surface-border)}table{width:100%;border-collapse:collapse;text-align:left}th,td{padding:var(--spacing-md);color:var(--text-color);border-bottom:var(--border-width-1) solid var(--surface-border)}th{font-weight:600;color:var(--text-color);background:var(--surface-ground);white-space:nowrap;-webkit-user-select:none;user-select:none}th.sortable{cursor:pointer}th.sortable:hover{background:var(--surface-hover)}.th-content{display:flex;align-items:center;gap:var(--spacing-xs)}.th-content i{font-size:var(--font-size-base);color:var(--primary-color);display:inline-block;transition:color var(--animation-duration-fast) var(--animation-easing-default)}.th-content i.sort-icon-neutral{color:var(--text-color-secondary);opacity:.5}th.sortable:hover .sort-icon-neutral{opacity:1;color:var(--primary-color)}.table-wrapper.striped tr:nth-child(2n){background:var(--surface-ground)}.table-wrapper.hoverable tr:hover{background:var(--surface-hover)}.table-wrapper.bordered th,.table-wrapper.bordered td{border:var(--border-width-1) solid var(--surface-border)}.table-wrapper.small th,.table-wrapper.small td{padding:var(--spacing-sm);font-size:var(--font-size-sm)}.table-wrapper.large th,.table-wrapper.large td{padding:var(--spacing-lg);font-size:var(--font-size-lg)}.loading-row td,.empty-row td{text-align:center;padding:var(--spacing-xl);color:var(--text-color-secondary)}.loading-indicator{display:flex;justify-content:center;align-items:center}.spinner{width:var(--size-6);height:var(--size-6);border:var(--border-width-2) solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.global-search{margin-bottom:var(--spacing-md)}.sort-button{background:transparent;border:none;padding:var(--spacing-xs);cursor:pointer;color:var(--primary-color);border-radius:var(--border-radius);transition:all var(--animation-duration-normal) var(--animation-easing-default)}.sort-button:hover{background:rgba(var(--primary-color-rgb),.1)}.sort-button:focus{outline:none;box-shadow:0 0 0 var(--focus-outline-width) var(--focus-ring-color)}.table-wrapper.table-fixed table{table-layout:fixed}.table-wrapper.truncate td{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:0}.table-wrapper.truncate th{overflow:hidden;text-overflow:ellipsis}@media(max-width:48em){.table-wrapper{font-size:var(--font-size-sm)}th,td{padding:var(--spacing-sm)}.global-search{margin-bottom:var(--spacing-sm)}}@media(max-width:30em){.table-wrapper{border-radius:0;margin:0 calc(-1 * var(--spacing-sm))}th,td{padding:var(--spacing-xs) var(--spacing-sm);font-size:var(--font-size-xs)}.th-content{gap:var(--spacing-xxs)}.th-content i{font-size:var(--font-size-sm)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: PshInputComponent, selector: "psh-input", inputs: ["value", "disabled", "readonly", "loading", "touched", "variant", "size", "fullWidth", "required", "showLabel", "type", "placeholder", "label", "ariaLabel", "iconStart", "iconEnd", "error", "success", "hint", "suggestions", "autocompleteConfig"], outputs: ["valueChange", "disabledChange", "readonlyChange", "loadingChange", "touchedChange", "inputFocus", "inputBlur", "suggestionSelect"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3803
3801
  }
3804
3802
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PshTableComponent, decorators: [{
3805
3803
  type: Component,
3806
- args: [{ selector: 'psh-table', imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, host: {
3804
+ args: [{ selector: 'psh-table', imports: [NgTemplateOutlet, PshInputComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
3807
3805
  'class': 'psh-table',
3808
3806
  '[class.full-width]': 'fullWidth()'
3809
- }, template: "<div\n class=\"table-wrapper\"\n [class.striped]=\"striped()\"\n [class.hoverable]=\"hoverable()\"\n [class.bordered]=\"bordered()\"\n [class.small]=\"size() === 'small'\"\n [class.large]=\"size() === 'large'\"\n [class.outline]=\"variant() === 'outline'\"\n [class.table-fixed]=\"tableLayout() === 'fixed'\"\n [class.truncate]=\"truncateText()\"\n [attr.data-state]=\"state()\"\n>\n @if (globalSearch()) {\n <div class=\"global-search\">\n <input\n type=\"text\"\n [attr.placeholder]=\"globalSearchPlaceholder()\"\n [value]=\"searchTerm()\"\n (input)=\"handleGlobalSearch($event)\"\n [attr.aria-label]=\"globalSearchPlaceholder()\"\n />\n <i class=\"ph ph-magnifying-glass\" aria-hidden=\"true\"></i>\n </div>\n }\n\n <table role=\"table\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th \n [style.width]=\"column.width\"\n [attr.aria-sort]=\"column.sortable ? (currentSort()?.key === column.key ? (currentSort()?.direction === 'asc' ? 'ascending' : 'descending') : 'none') : null\"\n [class.sortable]=\"column.sortable\" \n (click)=\"handleSort(column)\"\n >\n <div class=\"th-content\">\n {{ column.label }}\n @if (column.sortable) {\n @if (currentSort()?.key === column.key) {\n <i\n [class]=\"'ph ' + (currentSort()?.direction === 'desc' ? 'ph-sort-descending' : 'ph-sort-ascending')\"\n [attr.aria-label]=\"'Sort ' + currentSort()?.direction\"\n aria-hidden=\"true\">\n </i>\n } @else {\n <i\n class=\"ph ph-arrows-down-up sort-icon-neutral\"\n aria-label=\"Sortable\"\n aria-hidden=\"true\">\n </i>\n }\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n \n <tbody>\n @if (loading()) {\n <tr class=\"loading-row\">\n <td [attr.colspan]=\"columns().length\">\n <div class=\"loading-indicator\">\n <div class=\"spinner\"></div>\n </div>\n </td>\n </tr>\n } @else if (filteredData().length === 0) {\n <tr class=\"empty-row\">\n <td [attr.colspan]=\"columns().length\">\n {{ computedEmptyMessage() }}\n </td>\n </tr>\n } @else {\n @for (row of filteredData(); track row.id) {\n <tr (click)=\"handleRowClick(row)\">\n @for (column of columns(); track column.key) {\n <td>\n @if (column.template) {\n <ng-container\n [ngTemplateOutlet]=\"column.template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"\n ></ng-container>\n } @else {\n {{ getCellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>", styles: [":host{display:block}:host(.full-width){max-width:none;width:100%}.table-wrapper{width:100%;overflow-x:auto;background:var(--surface-card);border-radius:var(--border-radius)}.table-wrapper.outline{background:transparent;border:var(--border-width-1) solid var(--surface-border)}table{width:100%;border-collapse:collapse;text-align:left}th,td{padding:var(--spacing-md);color:var(--text-color);border-bottom:var(--border-width-1) solid var(--surface-border)}th{font-weight:600;color:var(--text-color);background:var(--surface-ground);white-space:nowrap;-webkit-user-select:none;user-select:none}th.sortable{cursor:pointer}th.sortable:hover{background:var(--surface-hover)}.th-content{display:flex;align-items:center;gap:var(--spacing-xs)}.th-content i{font-size:var(--font-size-base);color:var(--primary-color);display:inline-block;transition:color var(--animation-duration-fast) var(--animation-easing-default)}.th-content i.sort-icon-neutral{color:var(--text-color-secondary);opacity:.5}th.sortable:hover .sort-icon-neutral{opacity:1;color:var(--primary-color)}.table-wrapper.striped tr:nth-child(2n){background:var(--surface-ground)}.table-wrapper.hoverable tr:hover{background:var(--surface-hover)}.table-wrapper.bordered th,.table-wrapper.bordered td{border:var(--border-width-1) solid var(--surface-border)}.table-wrapper.small th,.table-wrapper.small td{padding:var(--spacing-sm);font-size:var(--font-size-sm)}.table-wrapper.large th,.table-wrapper.large td{padding:var(--spacing-lg);font-size:var(--font-size-lg)}.loading-row td,.empty-row td{text-align:center;padding:var(--spacing-xl);color:var(--text-color-secondary)}.loading-indicator{display:flex;justify-content:center;align-items:center}.spinner{width:var(--size-6);height:var(--size-6);border:var(--border-width-2) solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.global-search{position:relative;margin-bottom:var(--spacing-md)}.global-search input{width:100%;padding:var(--spacing-sm) var(--spacing-lg);padding-left:calc(var(--spacing-xl) + var(--spacing-xs));border:var(--border-width-1) solid var(--surface-border);border-radius:var(--border-radius);background:var(--surface-card);color:var(--text-color);font-size:var(--font-size-base);transition:all var(--animation-duration-normal) var(--animation-easing-default)}.global-search input:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 var(--focus-outline-width) var(--focus-ring-color)}.global-search i{position:absolute;left:var(--spacing-md);top:50%;transform:translateY(-50%);color:var(--text-color-secondary);font-size:var(--icon-size-md)}.sort-button{background:transparent;border:none;padding:var(--spacing-xs);cursor:pointer;color:var(--primary-color);border-radius:var(--border-radius);transition:all var(--animation-duration-normal) var(--animation-easing-default)}.sort-button:hover{background:rgba(var(--primary-color-rgb),.1)}.sort-button:focus{outline:none;box-shadow:0 0 0 var(--focus-outline-width) var(--focus-ring-color)}.table-wrapper.table-fixed table{table-layout:fixed}.table-wrapper.truncate td{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:0}.table-wrapper.truncate th{overflow:hidden;text-overflow:ellipsis}@media(max-width:48em){.table-wrapper{font-size:var(--font-size-sm)}th,td{padding:var(--spacing-sm)}.global-search{margin-bottom:var(--spacing-sm)}.global-search input{padding:var(--spacing-xs) var(--spacing-md);padding-left:calc(var(--spacing-lg) + 4px);font-size:var(--font-size-sm)}}@media(max-width:30em){.table-wrapper{border-radius:0;margin:0 calc(-1 * var(--spacing-sm))}th,td{padding:var(--spacing-xs) var(--spacing-sm);font-size:var(--font-size-xs)}.th-content{gap:var(--spacing-xxs)}.th-content i{font-size:var(--font-size-sm)}.global-search input{padding:var(--spacing-xs);padding-left:var(--spacing-md);font-size:var(--font-size-xs)}}\n"] }]
3807
+ }, template: "<div\n class=\"table-wrapper\"\n [class.striped]=\"striped()\"\n [class.hoverable]=\"hoverable()\"\n [class.bordered]=\"bordered()\"\n [class.small]=\"size() === 'small'\"\n [class.large]=\"size() === 'large'\"\n [class.outline]=\"variant() === 'outline'\"\n [class.table-fixed]=\"tableLayout() === 'fixed'\"\n [class.truncate]=\"truncateText()\"\n [attr.data-state]=\"state()\"\n>\n @if (globalSearch()) {\n <div class=\"global-search\" role=\"search\">\n <psh-input\n [placeholder]=\"globalSearchPlaceholder()\"\n [ariaLabel]=\"globalSearchPlaceholder()\"\n [size]=\"size()\"\n [fullWidth]=\"true\"\n [showLabel]=\"false\"\n iconStart=\"magnifying-glass\"\n type=\"search\"\n [(value)]=\"searchTermSignal\"\n (valueChange)=\"onSearchValueChange($event)\"\n />\n </div>\n }\n\n <table role=\"table\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th \n [style.width]=\"column.width\"\n [attr.aria-sort]=\"column.sortable ? (currentSort()?.key === column.key ? (currentSort()?.direction === 'asc' ? 'ascending' : 'descending') : 'none') : null\"\n [class.sortable]=\"column.sortable\" \n (click)=\"handleSort(column)\"\n >\n <div class=\"th-content\">\n {{ column.label }}\n @if (column.sortable) {\n @if (currentSort()?.key === column.key) {\n <i\n [class]=\"'ph ' + (currentSort()?.direction === 'desc' ? 'ph-sort-descending' : 'ph-sort-ascending')\"\n [attr.aria-label]=\"'Sort ' + currentSort()?.direction\"\n aria-hidden=\"true\">\n </i>\n } @else {\n <i\n class=\"ph ph-arrows-down-up sort-icon-neutral\"\n aria-label=\"Sortable\"\n aria-hidden=\"true\">\n </i>\n }\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n \n <tbody>\n @if (loading()) {\n <tr class=\"loading-row\">\n <td [attr.colspan]=\"columns().length\">\n <div class=\"loading-indicator\">\n <div class=\"spinner\"></div>\n </div>\n </td>\n </tr>\n } @else if (filteredData().length === 0) {\n <tr class=\"empty-row\">\n <td [attr.colspan]=\"columns().length\">\n {{ computedEmptyMessage() }}\n </td>\n </tr>\n } @else {\n @for (row of filteredData(); track row.id) {\n <tr (click)=\"handleRowClick(row)\">\n @for (column of columns(); track column.key) {\n <td>\n @if (column.template) {\n <ng-container\n [ngTemplateOutlet]=\"column.template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"\n ></ng-container>\n } @else {\n {{ getCellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>", styles: [":host{display:block}:host(.full-width){max-width:none;width:100%}.table-wrapper{width:100%;overflow-x:auto;background:var(--surface-card);border-radius:var(--border-radius)}.table-wrapper.outline{background:transparent;border:var(--border-width-1) solid var(--surface-border)}table{width:100%;border-collapse:collapse;text-align:left}th,td{padding:var(--spacing-md);color:var(--text-color);border-bottom:var(--border-width-1) solid var(--surface-border)}th{font-weight:600;color:var(--text-color);background:var(--surface-ground);white-space:nowrap;-webkit-user-select:none;user-select:none}th.sortable{cursor:pointer}th.sortable:hover{background:var(--surface-hover)}.th-content{display:flex;align-items:center;gap:var(--spacing-xs)}.th-content i{font-size:var(--font-size-base);color:var(--primary-color);display:inline-block;transition:color var(--animation-duration-fast) var(--animation-easing-default)}.th-content i.sort-icon-neutral{color:var(--text-color-secondary);opacity:.5}th.sortable:hover .sort-icon-neutral{opacity:1;color:var(--primary-color)}.table-wrapper.striped tr:nth-child(2n){background:var(--surface-ground)}.table-wrapper.hoverable tr:hover{background:var(--surface-hover)}.table-wrapper.bordered th,.table-wrapper.bordered td{border:var(--border-width-1) solid var(--surface-border)}.table-wrapper.small th,.table-wrapper.small td{padding:var(--spacing-sm);font-size:var(--font-size-sm)}.table-wrapper.large th,.table-wrapper.large td{padding:var(--spacing-lg);font-size:var(--font-size-lg)}.loading-row td,.empty-row td{text-align:center;padding:var(--spacing-xl);color:var(--text-color-secondary)}.loading-indicator{display:flex;justify-content:center;align-items:center}.spinner{width:var(--size-6);height:var(--size-6);border:var(--border-width-2) solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.global-search{margin-bottom:var(--spacing-md)}.sort-button{background:transparent;border:none;padding:var(--spacing-xs);cursor:pointer;color:var(--primary-color);border-radius:var(--border-radius);transition:all var(--animation-duration-normal) var(--animation-easing-default)}.sort-button:hover{background:rgba(var(--primary-color-rgb),.1)}.sort-button:focus{outline:none;box-shadow:0 0 0 var(--focus-outline-width) var(--focus-ring-color)}.table-wrapper.table-fixed table{table-layout:fixed}.table-wrapper.truncate td{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:0}.table-wrapper.truncate th{overflow:hidden;text-overflow:ellipsis}@media(max-width:48em){.table-wrapper{font-size:var(--font-size-sm)}th,td{padding:var(--spacing-sm)}.global-search{margin-bottom:var(--spacing-sm)}}@media(max-width:30em){.table-wrapper{border-radius:0;margin:0 calc(-1 * var(--spacing-sm))}th,td{padding:var(--spacing-xs) var(--spacing-sm);font-size:var(--font-size-xs)}.th-content{gap:var(--spacing-xxs)}.th-content i{font-size:var(--font-size-sm)}}\n"] }]
3810
3808
  }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], bordered: [{ type: i0.Input, args: [{ isSignal: true, alias: "bordered", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], globalSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalSearch", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], noResultsMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "noResultsMessage", required: false }] }], globalSearchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalSearchPlaceholder", required: false }] }], tableLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableLayout", required: false }] }], truncateText: [{ type: i0.Input, args: [{ isSignal: true, alias: "truncateText", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], globalSearchChange: [{ type: i0.Output, args: ["globalSearchChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }] } });
3811
3809
 
3812
3810
  class PshTabComponent {