ps-helix 3.0.4 → 3.0.6

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-3.0.4-blue.svg)](https://www.npmjs.com/package/ps-helix)
5
+ [![npm version](https://img.shields.io/badge/npm-3.0.6-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/)
@@ -118,7 +118,7 @@ After installation, verify that ps-helix is in your `package.json`:
118
118
  ```json
119
119
  {
120
120
  "dependencies": {
121
- "ps-helix": "^3.0.4"
121
+ "ps-helix": "^3.0.6"
122
122
  }
123
123
  }
124
124
  ```
@@ -1173,7 +1173,7 @@ Copyright (c) 2025 PACK Solutions
1173
1173
 
1174
1174
  ---
1175
1175
 
1176
- **Version**: 3.0.4
1176
+ **Version**: 3.0.6
1177
1177
  **Built with**: Angular 21.0.3, TypeScript 5.9.0, Phosphor Icons 2.0.3
1178
1178
  **Author**: Fabrice PEREZ | Product Designer at PACK Solutions
1179
1179
  **Last Updated**: January 2026
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, output, computed, ChangeDetectionStrategy, Component, model, inject, ElementRef, signal, PLATFORM_ID, ViewEncapsulation, InjectionToken, effect, ChangeDetectorRef, DestroyRef, forwardRef, Injectable, Renderer2, contentChild, viewChild, contentChildren, afterNextRender } from '@angular/core';
2
+ import { input, output, computed, ChangeDetectionStrategy, Component, model, inject, ElementRef, signal, PLATFORM_ID, ViewEncapsulation, InjectionToken, viewChild, effect, ChangeDetectorRef, DestroyRef, forwardRef, Injectable, Renderer2, contentChild, contentChildren, afterNextRender } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule, isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
5
5
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
@@ -948,6 +948,9 @@ class PshCheckboxComponent {
948
948
  constructor() {
949
949
  this.config = inject(CHECKBOX_CONFIG);
950
950
  this.uniqueId = `checkbox-${++checkboxIdCounter}`;
951
+ this.checkboxInput = viewChild('checkboxInput', ...(ngDevMode ? [{ debugName: "checkboxInput" }] : []));
952
+ this.onChange = (value) => { };
953
+ this.onTouched = () => { };
951
954
  this.checked = model(this.config.checked ?? false, ...(ngDevMode ? [{ debugName: "checked" }] : []));
952
955
  this.disabled = model(this.config.disabled ?? false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
953
956
  this.required = model(this.config.required ?? false, ...(ngDevMode ? [{ debugName: "required" }] : []));
@@ -992,6 +995,8 @@ class PshCheckboxComponent {
992
995
  if (!this.disabled()) {
993
996
  this.checked.update(v => !v);
994
997
  this.indeterminate.set(false);
998
+ this.onChange(this.checked());
999
+ this.onTouched();
995
1000
  }
996
1001
  }
997
1002
  handleKeydown(event) {
@@ -1006,20 +1011,52 @@ class PshCheckboxComponent {
1006
1011
  this.toggle();
1007
1012
  }
1008
1013
  }
1014
+ writeValue(value) {
1015
+ this.checked.set(value ?? false);
1016
+ }
1017
+ registerOnChange(fn) {
1018
+ this.onChange = fn;
1019
+ }
1020
+ registerOnTouched(fn) {
1021
+ this.onTouched = fn;
1022
+ }
1023
+ setDisabledState(isDisabled) {
1024
+ this.disabled.set(isDisabled);
1025
+ }
1026
+ focus() {
1027
+ const input = this.checkboxInput();
1028
+ if (input?.nativeElement) {
1029
+ input.nativeElement.focus();
1030
+ }
1031
+ }
1032
+ blur() {
1033
+ const input = this.checkboxInput();
1034
+ if (input?.nativeElement) {
1035
+ input.nativeElement.blur();
1036
+ }
1037
+ }
1009
1038
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PshCheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1010
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: PshCheckboxComponent, isStandalone: true, selector: "psh-checkbox", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, success: { classPropertyName: "success", publicName: "success", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, labelPosition: { classPropertyName: "labelPosition", publicName: "labelPosition", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", disabled: "disabledChange", required: "requiredChange", indeterminate: "indeterminateChange" }, host: { properties: { "class.checkbox-disabled": "disabled()", "class.checkbox-error": "!!error()", "class.checkbox-success": "!!success()", "class.checkbox-small": "size() === \"small\"", "class.checkbox-large": "size() === \"large\"", "attr.data-state": "state()" } }, ngImport: i0, template: "<div class=\"checkbox-container\">\n <label class=\"checkbox-label\" [class.label-left]=\"labelPosition() === 'left'\">\n <input\n type=\"checkbox\"\n class=\"checkbox-input\"\n [checked]=\"checked()\"\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [attr.aria-label]=\"computedAriaLabel()\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-checked]=\"ariaChecked()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n (change)=\"toggle()\"\n (keydown)=\"handleKeydown($event)\"\n />\n <span \n class=\"checkbox-control\"\n [class.error]=\"!!error()\"\n [class.success]=\"!!success()\"\n [class.indeterminate]=\"indeterminate()\"\n >\n @if (checked() && !indeterminate()) {\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\n }\n @if (indeterminate()) {\n <i class=\"ph ph-minus\" aria-hidden=\"true\"></i>\n }\n </span>\n <span class=\"checkbox-text\">\n @if (label()) {\n {{ label() }}\n } @else {\n <ng-content></ng-content>\n }\n </span>\n </label>\n\n @if (error()) {\n <div [id]=\"errorMessageId()\" class=\"checkbox-error\" role=\"alert\">\n {{ error() }}\n </div>\n }\n\n @if (success()) {\n <div [id]=\"successMessageId()\" class=\"checkbox-success\" role=\"status\">\n {{ success() }}\n </div>\n }\n</div>", styles: [".checkbox-container{display:inline-flex;flex-direction:column;gap:var(--spacing-xs)}.checkbox-label{display:inline-flex;align-items:center;gap:var(--spacing-sm);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--text-color)}.checkbox-label.label-left{flex-direction:row-reverse;justify-content:space-between}.checkbox-input{position:absolute;opacity:0;width:0;height:0}.checkbox-control{display:flex;align-items:center;justify-content:center;width:var(--size-4-5);height:var(--size-4-5);min-width:var(--size-4-5);min-height:var(--size-4-5);border:var(--border-width-2) solid var(--surface-border);border-radius:var(--border-radius);background:var(--surface-card);transition:background-color var(--animation-duration-fast) var(--animation-easing-default),border-color var(--animation-duration-fast) var(--animation-easing-default);color:var(--primary-color-text);position:relative;flex-shrink:0}.checkbox-input:checked+.checkbox-control{background:var(--primary-color);border-color:var(--primary-color)}.checkbox-input:focus-visible+.checkbox-control{outline:var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);outline-offset:var(--focus-outline-offset)}.checkbox-control i{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) scale(0);font-size:var(--font-size-xs);opacity:0;transition:opacity var(--animation-duration-fast) var(--animation-easing-default),transform var(--animation-duration-fast) var(--animation-easing-default)}.checkbox-input:checked+.checkbox-control i,.checkbox-control.indeterminate i{opacity:1;transform:translate(-50%,-50%) scale(1.1)}.checkbox-control.indeterminate{background:var(--primary-color);border-color:var(--primary-color)}:host(.checkbox-disabled) .checkbox-label{opacity:var(--opacity-disabled);cursor:not-allowed}:host(.checkbox-error) .checkbox-control{border-color:var(--danger-color)}:host(.checkbox-error) .checkbox-input:checked+.checkbox-control{background:var(--danger-color);border-color:var(--danger-color)}:host(.checkbox-success) .checkbox-control{border-color:var(--success-color)}:host(.checkbox-success) .checkbox-input:checked+.checkbox-control{background:var(--success-color);border-color:var(--success-color)}.checkbox-error{font-size:var(--font-size-sm);color:var(--danger-color)}.checkbox-success{font-size:var(--font-size-sm);color:var(--success-color)}:host(.checkbox-small) .checkbox-control{width:var(--size-4);height:var(--size-4);min-width:var(--size-4);min-height:var(--size-4)}:host(.checkbox-small) .checkbox-text{font-size:var(--font-size-sm)}:host(.checkbox-large) .checkbox-control{width:var(--size-5-5);height:var(--size-5-5);min-width:var(--size-5-5);min-height:var(--size-5-5)}:host(.checkbox-large) .checkbox-text{font-size:var(--font-size-lg)}.checkbox-text{color:var(--text-color)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1039
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: PshCheckboxComponent, isStandalone: true, selector: "psh-checkbox", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, success: { classPropertyName: "success", publicName: "success", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, labelPosition: { classPropertyName: "labelPosition", publicName: "labelPosition", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", disabled: "disabledChange", required: "requiredChange", indeterminate: "indeterminateChange" }, host: { properties: { "class.checkbox-disabled": "disabled()", "class.checkbox-error": "!!error()", "class.checkbox-success": "!!success()", "class.checkbox-small": "size() === \"small\"", "class.checkbox-large": "size() === \"large\"", "attr.data-state": "state()" } }, providers: [{
1040
+ provide: NG_VALUE_ACCESSOR,
1041
+ useExisting: PshCheckboxComponent,
1042
+ multi: true
1043
+ }], viewQueries: [{ propertyName: "checkboxInput", first: true, predicate: ["checkboxInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"checkbox-container\">\n <label class=\"checkbox-label\" [class.label-left]=\"labelPosition() === 'left'\">\n <input\n #checkboxInput\n type=\"checkbox\"\n class=\"checkbox-input\"\n [checked]=\"checked()\"\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [attr.aria-label]=\"computedAriaLabel()\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-checked]=\"ariaChecked()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n (change)=\"toggle()\"\n (keydown)=\"handleKeydown($event)\"\n />\n <span \n class=\"checkbox-control\"\n [class.error]=\"!!error()\"\n [class.success]=\"!!success()\"\n [class.indeterminate]=\"indeterminate()\"\n >\n @if (checked() && !indeterminate()) {\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\n }\n @if (indeterminate()) {\n <i class=\"ph ph-minus\" aria-hidden=\"true\"></i>\n }\n </span>\n <span class=\"checkbox-text\">\n @if (label()) {\n {{ label() }}\n } @else {\n <ng-content></ng-content>\n }\n </span>\n </label>\n\n @if (error()) {\n <div [id]=\"errorMessageId()\" class=\"checkbox-error\" role=\"alert\">\n {{ error() }}\n </div>\n }\n\n @if (success()) {\n <div [id]=\"successMessageId()\" class=\"checkbox-success\" role=\"status\">\n {{ success() }}\n </div>\n }\n</div>", styles: [".checkbox-container{display:inline-flex;flex-direction:column;gap:var(--spacing-xs)}.checkbox-label{display:inline-flex;align-items:center;gap:var(--spacing-sm);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--text-color)}.checkbox-label.label-left{flex-direction:row-reverse;justify-content:space-between}.checkbox-input{position:absolute;opacity:0;width:0;height:0}.checkbox-control{display:flex;align-items:center;justify-content:center;width:var(--size-4-5);height:var(--size-4-5);min-width:var(--size-4-5);min-height:var(--size-4-5);border:var(--border-width-2) solid var(--surface-border);border-radius:var(--border-radius);background:var(--surface-card);transition:background-color var(--animation-duration-fast) var(--animation-easing-default),border-color var(--animation-duration-fast) var(--animation-easing-default);color:var(--primary-color-text);position:relative;flex-shrink:0}.checkbox-input:checked+.checkbox-control{background:var(--primary-color);border-color:var(--primary-color)}.checkbox-input:focus-visible+.checkbox-control{outline:var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);outline-offset:var(--focus-outline-offset)}.checkbox-control i{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) scale(0);font-size:var(--font-size-xs);opacity:0;transition:opacity var(--animation-duration-fast) var(--animation-easing-default),transform var(--animation-duration-fast) var(--animation-easing-default)}.checkbox-input:checked+.checkbox-control i,.checkbox-control.indeterminate i{opacity:1;transform:translate(-50%,-50%) scale(1.1)}.checkbox-control.indeterminate{background:var(--primary-color);border-color:var(--primary-color)}:host(.checkbox-disabled) .checkbox-label{opacity:var(--opacity-disabled);cursor:not-allowed}:host(.checkbox-error) .checkbox-control{border-color:var(--danger-color)}:host(.checkbox-error) .checkbox-input:checked+.checkbox-control{background:var(--danger-color);border-color:var(--danger-color)}:host(.checkbox-success) .checkbox-control{border-color:var(--success-color)}:host(.checkbox-success) .checkbox-input:checked+.checkbox-control{background:var(--success-color);border-color:var(--success-color)}.checkbox-error{font-size:var(--font-size-sm);color:var(--danger-color)}.checkbox-success{font-size:var(--font-size-sm);color:var(--success-color)}:host(.checkbox-small) .checkbox-control{width:var(--size-4);height:var(--size-4);min-width:var(--size-4);min-height:var(--size-4)}:host(.checkbox-small) .checkbox-text{font-size:var(--font-size-sm)}:host(.checkbox-large) .checkbox-control{width:var(--size-5-5);height:var(--size-5-5);min-width:var(--size-5-5);min-height:var(--size-5-5)}:host(.checkbox-large) .checkbox-text{font-size:var(--font-size-lg)}.checkbox-text{color:var(--text-color)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1011
1044
  }
1012
1045
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PshCheckboxComponent, decorators: [{
1013
1046
  type: Component,
1014
- args: [{ selector: 'psh-checkbox', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, host: {
1047
+ args: [{ selector: 'psh-checkbox', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, providers: [{
1048
+ provide: NG_VALUE_ACCESSOR,
1049
+ useExisting: PshCheckboxComponent,
1050
+ multi: true
1051
+ }], host: {
1015
1052
  '[class.checkbox-disabled]': 'disabled()',
1016
1053
  '[class.checkbox-error]': '!!error()',
1017
1054
  '[class.checkbox-success]': '!!success()',
1018
1055
  '[class.checkbox-small]': 'size() === "small"',
1019
1056
  '[class.checkbox-large]': 'size() === "large"',
1020
1057
  '[attr.data-state]': 'state()'
1021
- }, template: "<div class=\"checkbox-container\">\n <label class=\"checkbox-label\" [class.label-left]=\"labelPosition() === 'left'\">\n <input\n type=\"checkbox\"\n class=\"checkbox-input\"\n [checked]=\"checked()\"\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [attr.aria-label]=\"computedAriaLabel()\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-checked]=\"ariaChecked()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n (change)=\"toggle()\"\n (keydown)=\"handleKeydown($event)\"\n />\n <span \n class=\"checkbox-control\"\n [class.error]=\"!!error()\"\n [class.success]=\"!!success()\"\n [class.indeterminate]=\"indeterminate()\"\n >\n @if (checked() && !indeterminate()) {\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\n }\n @if (indeterminate()) {\n <i class=\"ph ph-minus\" aria-hidden=\"true\"></i>\n }\n </span>\n <span class=\"checkbox-text\">\n @if (label()) {\n {{ label() }}\n } @else {\n <ng-content></ng-content>\n }\n </span>\n </label>\n\n @if (error()) {\n <div [id]=\"errorMessageId()\" class=\"checkbox-error\" role=\"alert\">\n {{ error() }}\n </div>\n }\n\n @if (success()) {\n <div [id]=\"successMessageId()\" class=\"checkbox-success\" role=\"status\">\n {{ success() }}\n </div>\n }\n</div>", styles: [".checkbox-container{display:inline-flex;flex-direction:column;gap:var(--spacing-xs)}.checkbox-label{display:inline-flex;align-items:center;gap:var(--spacing-sm);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--text-color)}.checkbox-label.label-left{flex-direction:row-reverse;justify-content:space-between}.checkbox-input{position:absolute;opacity:0;width:0;height:0}.checkbox-control{display:flex;align-items:center;justify-content:center;width:var(--size-4-5);height:var(--size-4-5);min-width:var(--size-4-5);min-height:var(--size-4-5);border:var(--border-width-2) solid var(--surface-border);border-radius:var(--border-radius);background:var(--surface-card);transition:background-color var(--animation-duration-fast) var(--animation-easing-default),border-color var(--animation-duration-fast) var(--animation-easing-default);color:var(--primary-color-text);position:relative;flex-shrink:0}.checkbox-input:checked+.checkbox-control{background:var(--primary-color);border-color:var(--primary-color)}.checkbox-input:focus-visible+.checkbox-control{outline:var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);outline-offset:var(--focus-outline-offset)}.checkbox-control i{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) scale(0);font-size:var(--font-size-xs);opacity:0;transition:opacity var(--animation-duration-fast) var(--animation-easing-default),transform var(--animation-duration-fast) var(--animation-easing-default)}.checkbox-input:checked+.checkbox-control i,.checkbox-control.indeterminate i{opacity:1;transform:translate(-50%,-50%) scale(1.1)}.checkbox-control.indeterminate{background:var(--primary-color);border-color:var(--primary-color)}:host(.checkbox-disabled) .checkbox-label{opacity:var(--opacity-disabled);cursor:not-allowed}:host(.checkbox-error) .checkbox-control{border-color:var(--danger-color)}:host(.checkbox-error) .checkbox-input:checked+.checkbox-control{background:var(--danger-color);border-color:var(--danger-color)}:host(.checkbox-success) .checkbox-control{border-color:var(--success-color)}:host(.checkbox-success) .checkbox-input:checked+.checkbox-control{background:var(--success-color);border-color:var(--success-color)}.checkbox-error{font-size:var(--font-size-sm);color:var(--danger-color)}.checkbox-success{font-size:var(--font-size-sm);color:var(--success-color)}:host(.checkbox-small) .checkbox-control{width:var(--size-4);height:var(--size-4);min-width:var(--size-4);min-height:var(--size-4)}:host(.checkbox-small) .checkbox-text{font-size:var(--font-size-sm)}:host(.checkbox-large) .checkbox-control{width:var(--size-5-5);height:var(--size-5-5);min-width:var(--size-5-5);min-height:var(--size-5-5)}:host(.checkbox-large) .checkbox-text{font-size:var(--font-size-lg)}.checkbox-text{color:var(--text-color)}\n"] }]
1022
- }], ctorParameters: () => [], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }, { type: i0.Output, args: ["requiredChange"] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }, { type: i0.Output, args: ["indeterminateChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], success: [{ type: i0.Input, args: [{ isSignal: true, alias: "success", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], labelPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelPosition", required: false }] }] } });
1058
+ }, template: "<div class=\"checkbox-container\">\n <label class=\"checkbox-label\" [class.label-left]=\"labelPosition() === 'left'\">\n <input\n #checkboxInput\n type=\"checkbox\"\n class=\"checkbox-input\"\n [checked]=\"checked()\"\n [disabled]=\"disabled()\"\n [required]=\"required()\"\n [attr.aria-label]=\"computedAriaLabel()\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-checked]=\"ariaChecked()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n (change)=\"toggle()\"\n (keydown)=\"handleKeydown($event)\"\n />\n <span \n class=\"checkbox-control\"\n [class.error]=\"!!error()\"\n [class.success]=\"!!success()\"\n [class.indeterminate]=\"indeterminate()\"\n >\n @if (checked() && !indeterminate()) {\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\n }\n @if (indeterminate()) {\n <i class=\"ph ph-minus\" aria-hidden=\"true\"></i>\n }\n </span>\n <span class=\"checkbox-text\">\n @if (label()) {\n {{ label() }}\n } @else {\n <ng-content></ng-content>\n }\n </span>\n </label>\n\n @if (error()) {\n <div [id]=\"errorMessageId()\" class=\"checkbox-error\" role=\"alert\">\n {{ error() }}\n </div>\n }\n\n @if (success()) {\n <div [id]=\"successMessageId()\" class=\"checkbox-success\" role=\"status\">\n {{ success() }}\n </div>\n }\n</div>", styles: [".checkbox-container{display:inline-flex;flex-direction:column;gap:var(--spacing-xs)}.checkbox-label{display:inline-flex;align-items:center;gap:var(--spacing-sm);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--text-color)}.checkbox-label.label-left{flex-direction:row-reverse;justify-content:space-between}.checkbox-input{position:absolute;opacity:0;width:0;height:0}.checkbox-control{display:flex;align-items:center;justify-content:center;width:var(--size-4-5);height:var(--size-4-5);min-width:var(--size-4-5);min-height:var(--size-4-5);border:var(--border-width-2) solid var(--surface-border);border-radius:var(--border-radius);background:var(--surface-card);transition:background-color var(--animation-duration-fast) var(--animation-easing-default),border-color var(--animation-duration-fast) var(--animation-easing-default);color:var(--primary-color-text);position:relative;flex-shrink:0}.checkbox-input:checked+.checkbox-control{background:var(--primary-color);border-color:var(--primary-color)}.checkbox-input:focus-visible+.checkbox-control{outline:var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);outline-offset:var(--focus-outline-offset)}.checkbox-control i{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) scale(0);font-size:var(--font-size-xs);opacity:0;transition:opacity var(--animation-duration-fast) var(--animation-easing-default),transform var(--animation-duration-fast) var(--animation-easing-default)}.checkbox-input:checked+.checkbox-control i,.checkbox-control.indeterminate i{opacity:1;transform:translate(-50%,-50%) scale(1.1)}.checkbox-control.indeterminate{background:var(--primary-color);border-color:var(--primary-color)}:host(.checkbox-disabled) .checkbox-label{opacity:var(--opacity-disabled);cursor:not-allowed}:host(.checkbox-error) .checkbox-control{border-color:var(--danger-color)}:host(.checkbox-error) .checkbox-input:checked+.checkbox-control{background:var(--danger-color);border-color:var(--danger-color)}:host(.checkbox-success) .checkbox-control{border-color:var(--success-color)}:host(.checkbox-success) .checkbox-input:checked+.checkbox-control{background:var(--success-color);border-color:var(--success-color)}.checkbox-error{font-size:var(--font-size-sm);color:var(--danger-color)}.checkbox-success{font-size:var(--font-size-sm);color:var(--success-color)}:host(.checkbox-small) .checkbox-control{width:var(--size-4);height:var(--size-4);min-width:var(--size-4);min-height:var(--size-4)}:host(.checkbox-small) .checkbox-text{font-size:var(--font-size-sm)}:host(.checkbox-large) .checkbox-control{width:var(--size-5-5);height:var(--size-5-5);min-width:var(--size-5-5);min-height:var(--size-5-5)}:host(.checkbox-large) .checkbox-text{font-size:var(--font-size-lg)}.checkbox-text{color:var(--text-color)}\n"] }]
1059
+ }], ctorParameters: () => [], propDecorators: { checkboxInput: [{ type: i0.ViewChild, args: ['checkboxInput', { isSignal: true }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }, { type: i0.Output, args: ["requiredChange"] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }, { type: i0.Output, args: ["indeterminateChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], success: [{ type: i0.Input, args: [{ isSignal: true, alias: "success", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], labelPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelPosition", required: false }] }] } });
1023
1060
 
1024
1061
  class PshCollapseComponent {
1025
1062
  constructor() {
@@ -2136,6 +2173,16 @@ class PshModalComponent {
2136
2173
  * ```
2137
2174
  */
2138
2175
  this.styleClass = input('', ...(ngDevMode ? [{ debugName: "styleClass" }] : []));
2176
+ /**
2177
+ * Custom CSS class(es) to apply to the modal backdrop
2178
+ * Useful for stacked modals with different z-index or opacity
2179
+ *
2180
+ * @example
2181
+ * ```html
2182
+ * <psh-modal backdropClass="higher-z-index" />
2183
+ * ```
2184
+ */
2185
+ this.backdropClass = input('', ...(ngDevMode ? [{ debugName: "backdropClass" }] : []));
2139
2186
  /**
2140
2187
  * Emitted when the modal is closed
2141
2188
  */
@@ -2405,12 +2452,12 @@ class PshModalComponent {
2405
2452
  this.detachModalFromBody();
2406
2453
  }
2407
2454
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PshModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2408
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: PshModalComponent, isStandalone: true, selector: "psh-modal", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdrop: { classPropertyName: "closeOnBackdrop", publicName: "closeOnBackdrop", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, preventScroll: { classPropertyName: "preventScroll", publicName: "preventScroll", isSignal: true, isRequired: false, transformFunction: null }, showFooter: { classPropertyName: "showFooter", publicName: "showFooter", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, dismissLabel: { classPropertyName: "dismissLabel", publicName: "dismissLabel", isSignal: true, isRequired: false, transformFunction: null }, confirmLabel: { classPropertyName: "confirmLabel", publicName: "confirmLabel", isSignal: true, isRequired: false, transformFunction: null }, cancelLabel: { classPropertyName: "cancelLabel", publicName: "cancelLabel", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", closed: "closed", confirmed: "confirmed" }, queries: [{ propertyName: "customFooter", first: true, predicate: ["modalFooter"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "modalBackdrop", first: true, predicate: ["modalBackdrop"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n #modalBackdrop\n class=\"modal-backdrop\"\n [class.open]=\"open()\"\n (click)=\"handleBackdropClick($event)\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-hidden]=\"!open()\"\n [attr.aria-labelledby]=\"modalDialogId() + '-title'\"\n [attr.aria-describedby]=\"modalDescriptionId()\"\n [attr.data-state]=\"state()\"\n>\n <div\n [class]=\"'modal-container ' + styleClass()\"\n [class.small]=\"size() === 'small'\"\n [class.medium]=\"size() === 'medium'\"\n [class.large]=\"size() === 'large'\"\n [attr.id]=\"modalDialogId()\"\n role=\"document\"\n >\n <div class=\"modal-content-wrapper\">\n <div class=\"modal-header\">\n <h2 class=\"modal-title\" [attr.id]=\"modalDialogId() + '-title'\">\n <ng-content select=\"[modal-title]\">{{ title() }}</ng-content>\n </h2>\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"modal-close\"\n (click)=\"handleClose()\"\n [attr.aria-label]=\"dismissLabel()\"\n >\n <i class=\"ph ph-x\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n\n <div class=\"modal-content\" [attr.id]=\"modalDescriptionId()\">\n <ng-content>\n <p>\n Modal content goes here. You can replace this with your own content.\n </p>\n </ng-content>\n </div>\n\n @if (showFooter() && !hasCustomFooter()) {\n <div class=\"modal-footer\">\n <psh-button\n appearance=\"outline\"\n variant=\"primary\"\n size=\"medium\"\n [fullWidth]=\"isMobileScreen()\"\n (clicked)=\"handleClose()\">\n {{ cancelLabel() }}\n </psh-button>\n <psh-button\n appearance=\"filled\"\n variant=\"primary\"\n size=\"medium\"\n [fullWidth]=\"isMobileScreen()\"\n (clicked)=\"handleConfirm()\">\n {{ confirmLabel() }}\n </psh-button>\n </div>\n } @if (showFooter() && hasCustomFooter()) {\n <div class=\"modal-footer\">\n <!--\n Note: When using a custom footer, buttons should also use [fullWidth]=\"isMobileScreen()\"\n to ensure proper responsive behavior on mobile devices.\n Example:\n <psh-button [fullWidth]=\"isMobileScreen()\" (clicked)=\"action()\">Custom Button</psh-button>\n -->\n <ng-content select=\"[modal-footer]\" #modalFooter></ng-content>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":root{--modal-max-width-sm: 25rem;--modal-max-width-md: 37.5rem;--modal-max-width-lg: 50rem;--modal-backdrop-opacity: .6;--modal-backdrop-blur: 4px;--modal-animation-distance: 20px;--modal-animation-scale: .95}.modal-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(var(--surface-900-rgb),var(--modal-backdrop-opacity));-webkit-backdrop-filter:blur(var(--modal-backdrop-blur));backdrop-filter:blur(var(--modal-backdrop-blur));align-items:center;justify-content:center;z-index:var(--z-index-modal-backdrop);pointer-events:none;opacity:0;display:none;transition:opacity var(--animation-duration-default) var(--animation-easing-smooth)}.modal-backdrop.open{opacity:1;pointer-events:all;display:flex}.modal-content-wrapper{display:flex;flex-direction:column;max-height:90vh;min-height:0}.modal-container{background:var(--surface-card);border-radius:var(--border-radius-lg);box-shadow:var(--shadow-lg),0 0 0 1px rgba(var(--primary-color-rgb),.05);max-width:var(--modal-max-width-md);transform:translateY(var(--modal-animation-distance)) scale(var(--modal-animation-scale));transition:transform var(--animation-duration-default) var(--animation-easing-smooth),opacity var(--animation-duration-default) var(--animation-easing-smooth);border:1px solid var(--surface-border)}.modal-backdrop.open .modal-container{transform:translateY(0) scale(1)}.modal-container.small{width:50%;max-width:var(--modal-max-width-sm)}.modal-container.medium{width:70%;max-width:var(--modal-max-width-md)}.modal-container.large{width:90%;max-width:var(--modal-max-width-lg)}.modal-header{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-lg) var(--spacing-xl);border-bottom:1px solid var(--surface-border);background:var(--surface-ground);border-top-left-radius:var(--border-radius-lg);border-top-right-radius:var(--border-radius-lg)}.modal-title{font-size:var(--font-size-xl);font-weight:var(--font-weight-semibold);color:var(--text-color);margin:0;display:flex;align-items:center;gap:var(--spacing-sm)}.modal-close{background:transparent;border:none;color:var(--text-color-secondary);padding:var(--spacing-sm);cursor:pointer;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;transition:all var(--animation-duration-fast) var(--animation-easing-smooth);margin:var(--negative-spacing-sm)}.modal-close:hover{background:var(--surface-hover);color:var(--text-color);transform:scale(1.1)}.modal-close:focus-visible{outline:var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);outline-offset:var(--focus-outline-offset)}.modal-close i{font-size:var(--font-size-xl)}.modal-content{padding:var(--spacing-xl);overflow-y:auto;flex-grow:1;min-height:0;color:var(--text-color-secondary);line-height:var(--line-height-relaxed)}.modal-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--spacing-md);padding:var(--spacing-lg) var(--spacing-xl);border-top:1px solid var(--surface-border);background:var(--surface-ground);border-bottom-left-radius:var(--border-radius-lg);border-bottom-right-radius:var(--border-radius-lg)}@media (max-width: 768px){.modal-container{width:95%;margin:var(--spacing-md)}.modal-header{padding:var(--spacing-md) var(--spacing-lg)}.modal-title{font-size:var(--font-size-lg)}.modal-content{padding:var(--spacing-lg)}.modal-footer{padding:var(--spacing-md) var(--spacing-lg);flex-direction:column-reverse;gap:var(--spacing-sm)}}@media (max-width: 480px){.modal-container{width:100%;max-height:100vh;margin:0;border-radius:0}.modal-header{padding:var(--spacing-sm) var(--spacing-md);border-top-left-radius:0;border-top-right-radius:0}.modal-title{font-size:var(--font-size-base)}.modal-content{padding:var(--spacing-md);flex:0 1 auto}.modal-footer{padding:var(--spacing-sm) var(--spacing-md);border-bottom-left-radius:0;border-bottom-right-radius:0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PshButtonComponent, selector: "psh-button", inputs: ["appearance", "variant", "size", "disabled", "loading", "fullWidth", "iconPosition", "icon", "ariaLabel", "loadingText", "disabledText", "iconOnlyText", "type"], outputs: ["fullWidthChange", "clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2455
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: PshModalComponent, isStandalone: true, selector: "psh-modal", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdrop: { classPropertyName: "closeOnBackdrop", publicName: "closeOnBackdrop", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, preventScroll: { classPropertyName: "preventScroll", publicName: "preventScroll", isSignal: true, isRequired: false, transformFunction: null }, showFooter: { classPropertyName: "showFooter", publicName: "showFooter", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, dismissLabel: { classPropertyName: "dismissLabel", publicName: "dismissLabel", isSignal: true, isRequired: false, transformFunction: null }, confirmLabel: { classPropertyName: "confirmLabel", publicName: "confirmLabel", isSignal: true, isRequired: false, transformFunction: null }, cancelLabel: { classPropertyName: "cancelLabel", publicName: "cancelLabel", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null }, backdropClass: { classPropertyName: "backdropClass", publicName: "backdropClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", closed: "closed", confirmed: "confirmed" }, queries: [{ propertyName: "customFooter", first: true, predicate: ["modalFooter"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "modalBackdrop", first: true, predicate: ["modalBackdrop"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n #modalBackdrop\n [class]=\"'modal-backdrop ' + backdropClass()\"\n [class.open]=\"open()\"\n (click)=\"handleBackdropClick($event)\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-hidden]=\"!open()\"\n [attr.aria-labelledby]=\"modalDialogId() + '-title'\"\n [attr.aria-describedby]=\"modalDescriptionId()\"\n [attr.data-state]=\"state()\"\n>\n <div\n [class]=\"'modal-container ' + styleClass()\"\n [class.small]=\"size() === 'small'\"\n [class.medium]=\"size() === 'medium'\"\n [class.large]=\"size() === 'large'\"\n [attr.id]=\"modalDialogId()\"\n role=\"document\"\n >\n <div class=\"modal-content-wrapper\">\n <div class=\"modal-header\">\n <h2 class=\"modal-title\" [attr.id]=\"modalDialogId() + '-title'\">\n <ng-content select=\"[modal-title]\">{{ title() }}</ng-content>\n </h2>\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"modal-close\"\n (click)=\"handleClose()\"\n [attr.aria-label]=\"dismissLabel()\"\n >\n <i class=\"ph ph-x\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n\n <div class=\"modal-content\" [attr.id]=\"modalDescriptionId()\">\n <ng-content>\n <p>\n Modal content goes here. You can replace this with your own content.\n </p>\n </ng-content>\n </div>\n\n @if (showFooter() && !hasCustomFooter()) {\n <div class=\"modal-footer\">\n <psh-button\n appearance=\"outline\"\n variant=\"primary\"\n size=\"medium\"\n [fullWidth]=\"isMobileScreen()\"\n (clicked)=\"handleClose()\">\n {{ cancelLabel() }}\n </psh-button>\n <psh-button\n appearance=\"filled\"\n variant=\"primary\"\n size=\"medium\"\n [fullWidth]=\"isMobileScreen()\"\n (clicked)=\"handleConfirm()\">\n {{ confirmLabel() }}\n </psh-button>\n </div>\n } @if (showFooter() && hasCustomFooter()) {\n <div class=\"modal-footer\">\n <!--\n Note: When using a custom footer, buttons should also use [fullWidth]=\"isMobileScreen()\"\n to ensure proper responsive behavior on mobile devices.\n Example:\n <psh-button [fullWidth]=\"isMobileScreen()\" (clicked)=\"action()\">Custom Button</psh-button>\n -->\n <ng-content select=\"[modal-footer]\" #modalFooter></ng-content>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":root{--modal-max-width-sm: 25rem;--modal-max-width-md: 37.5rem;--modal-max-width-lg: 50rem;--modal-backdrop-opacity: .6;--modal-backdrop-blur: 4px;--modal-animation-distance: 20px;--modal-animation-scale: .95}.modal-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(var(--surface-900-rgb),var(--modal-backdrop-opacity));-webkit-backdrop-filter:blur(var(--modal-backdrop-blur));backdrop-filter:blur(var(--modal-backdrop-blur));align-items:center;justify-content:center;z-index:var(--z-index-modal-backdrop);pointer-events:none;opacity:0;display:none;transition:opacity var(--animation-duration-default) var(--animation-easing-smooth)}.modal-backdrop.open{opacity:1;pointer-events:all;display:flex}.modal-content-wrapper{display:flex;flex-direction:column;max-height:90vh;min-height:0}.modal-container{background:var(--surface-card);border-radius:var(--border-radius-lg);box-shadow:var(--shadow-lg),0 0 0 1px rgba(var(--primary-color-rgb),.05);max-width:var(--modal-max-width-md);transform:translateY(var(--modal-animation-distance)) scale(var(--modal-animation-scale));transition:transform var(--animation-duration-default) var(--animation-easing-smooth),opacity var(--animation-duration-default) var(--animation-easing-smooth);border:1px solid var(--surface-border)}.modal-backdrop.open .modal-container{transform:translateY(0) scale(1)}.modal-container.small{width:50%;max-width:var(--modal-max-width-sm)}.modal-container.medium{width:70%;max-width:var(--modal-max-width-md)}.modal-container.large{width:90%;max-width:var(--modal-max-width-lg)}.modal-header{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-lg) var(--spacing-xl);border-bottom:1px solid var(--surface-border);background:var(--surface-ground);border-top-left-radius:var(--border-radius-lg);border-top-right-radius:var(--border-radius-lg)}.modal-title{font-size:var(--font-size-xl);font-weight:var(--font-weight-semibold);color:var(--text-color);margin:0;display:flex;align-items:center;gap:var(--spacing-sm)}.modal-close{background:transparent;border:none;color:var(--text-color-secondary);padding:var(--spacing-sm);cursor:pointer;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;transition:all var(--animation-duration-fast) var(--animation-easing-smooth);margin:var(--negative-spacing-sm)}.modal-close:hover{background:var(--surface-hover);color:var(--text-color);transform:scale(1.1)}.modal-close:focus-visible{outline:var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);outline-offset:var(--focus-outline-offset)}.modal-close i{font-size:var(--font-size-xl)}.modal-content{padding:var(--spacing-xl);overflow-y:auto;flex-grow:1;min-height:0;color:var(--text-color-secondary);line-height:var(--line-height-relaxed)}.modal-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--spacing-md);padding:var(--spacing-lg) var(--spacing-xl);border-top:1px solid var(--surface-border);background:var(--surface-ground);border-bottom-left-radius:var(--border-radius-lg);border-bottom-right-radius:var(--border-radius-lg)}@media (max-width: 768px){.modal-container{width:95%;margin:var(--spacing-md)}.modal-header{padding:var(--spacing-md) var(--spacing-lg)}.modal-title{font-size:var(--font-size-lg)}.modal-content{padding:var(--spacing-lg)}.modal-footer{padding:var(--spacing-md) var(--spacing-lg);flex-direction:column-reverse;gap:var(--spacing-sm)}}@media (max-width: 480px){.modal-container{width:100%;max-height:100vh;margin:0;border-radius:0}.modal-header{padding:var(--spacing-sm) var(--spacing-md);border-top-left-radius:0;border-top-right-radius:0}.modal-title{font-size:var(--font-size-base)}.modal-content{padding:var(--spacing-md);flex:0 1 auto}.modal-footer{padding:var(--spacing-sm) var(--spacing-md);border-bottom-left-radius:0;border-bottom-right-radius:0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PshButtonComponent, selector: "psh-button", inputs: ["appearance", "variant", "size", "disabled", "loading", "fullWidth", "iconPosition", "icon", "ariaLabel", "loadingText", "disabledText", "iconOnlyText", "type"], outputs: ["fullWidthChange", "clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2409
2456
  }
2410
2457
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PshModalComponent, decorators: [{
2411
2458
  type: Component,
2412
- args: [{ selector: 'psh-modal', imports: [CommonModule, PshButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n #modalBackdrop\n class=\"modal-backdrop\"\n [class.open]=\"open()\"\n (click)=\"handleBackdropClick($event)\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-hidden]=\"!open()\"\n [attr.aria-labelledby]=\"modalDialogId() + '-title'\"\n [attr.aria-describedby]=\"modalDescriptionId()\"\n [attr.data-state]=\"state()\"\n>\n <div\n [class]=\"'modal-container ' + styleClass()\"\n [class.small]=\"size() === 'small'\"\n [class.medium]=\"size() === 'medium'\"\n [class.large]=\"size() === 'large'\"\n [attr.id]=\"modalDialogId()\"\n role=\"document\"\n >\n <div class=\"modal-content-wrapper\">\n <div class=\"modal-header\">\n <h2 class=\"modal-title\" [attr.id]=\"modalDialogId() + '-title'\">\n <ng-content select=\"[modal-title]\">{{ title() }}</ng-content>\n </h2>\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"modal-close\"\n (click)=\"handleClose()\"\n [attr.aria-label]=\"dismissLabel()\"\n >\n <i class=\"ph ph-x\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n\n <div class=\"modal-content\" [attr.id]=\"modalDescriptionId()\">\n <ng-content>\n <p>\n Modal content goes here. You can replace this with your own content.\n </p>\n </ng-content>\n </div>\n\n @if (showFooter() && !hasCustomFooter()) {\n <div class=\"modal-footer\">\n <psh-button\n appearance=\"outline\"\n variant=\"primary\"\n size=\"medium\"\n [fullWidth]=\"isMobileScreen()\"\n (clicked)=\"handleClose()\">\n {{ cancelLabel() }}\n </psh-button>\n <psh-button\n appearance=\"filled\"\n variant=\"primary\"\n size=\"medium\"\n [fullWidth]=\"isMobileScreen()\"\n (clicked)=\"handleConfirm()\">\n {{ confirmLabel() }}\n </psh-button>\n </div>\n } @if (showFooter() && hasCustomFooter()) {\n <div class=\"modal-footer\">\n <!--\n Note: When using a custom footer, buttons should also use [fullWidth]=\"isMobileScreen()\"\n to ensure proper responsive behavior on mobile devices.\n Example:\n <psh-button [fullWidth]=\"isMobileScreen()\" (clicked)=\"action()\">Custom Button</psh-button>\n -->\n <ng-content select=\"[modal-footer]\" #modalFooter></ng-content>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":root{--modal-max-width-sm: 25rem;--modal-max-width-md: 37.5rem;--modal-max-width-lg: 50rem;--modal-backdrop-opacity: .6;--modal-backdrop-blur: 4px;--modal-animation-distance: 20px;--modal-animation-scale: .95}.modal-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(var(--surface-900-rgb),var(--modal-backdrop-opacity));-webkit-backdrop-filter:blur(var(--modal-backdrop-blur));backdrop-filter:blur(var(--modal-backdrop-blur));align-items:center;justify-content:center;z-index:var(--z-index-modal-backdrop);pointer-events:none;opacity:0;display:none;transition:opacity var(--animation-duration-default) var(--animation-easing-smooth)}.modal-backdrop.open{opacity:1;pointer-events:all;display:flex}.modal-content-wrapper{display:flex;flex-direction:column;max-height:90vh;min-height:0}.modal-container{background:var(--surface-card);border-radius:var(--border-radius-lg);box-shadow:var(--shadow-lg),0 0 0 1px rgba(var(--primary-color-rgb),.05);max-width:var(--modal-max-width-md);transform:translateY(var(--modal-animation-distance)) scale(var(--modal-animation-scale));transition:transform var(--animation-duration-default) var(--animation-easing-smooth),opacity var(--animation-duration-default) var(--animation-easing-smooth);border:1px solid var(--surface-border)}.modal-backdrop.open .modal-container{transform:translateY(0) scale(1)}.modal-container.small{width:50%;max-width:var(--modal-max-width-sm)}.modal-container.medium{width:70%;max-width:var(--modal-max-width-md)}.modal-container.large{width:90%;max-width:var(--modal-max-width-lg)}.modal-header{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-lg) var(--spacing-xl);border-bottom:1px solid var(--surface-border);background:var(--surface-ground);border-top-left-radius:var(--border-radius-lg);border-top-right-radius:var(--border-radius-lg)}.modal-title{font-size:var(--font-size-xl);font-weight:var(--font-weight-semibold);color:var(--text-color);margin:0;display:flex;align-items:center;gap:var(--spacing-sm)}.modal-close{background:transparent;border:none;color:var(--text-color-secondary);padding:var(--spacing-sm);cursor:pointer;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;transition:all var(--animation-duration-fast) var(--animation-easing-smooth);margin:var(--negative-spacing-sm)}.modal-close:hover{background:var(--surface-hover);color:var(--text-color);transform:scale(1.1)}.modal-close:focus-visible{outline:var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);outline-offset:var(--focus-outline-offset)}.modal-close i{font-size:var(--font-size-xl)}.modal-content{padding:var(--spacing-xl);overflow-y:auto;flex-grow:1;min-height:0;color:var(--text-color-secondary);line-height:var(--line-height-relaxed)}.modal-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--spacing-md);padding:var(--spacing-lg) var(--spacing-xl);border-top:1px solid var(--surface-border);background:var(--surface-ground);border-bottom-left-radius:var(--border-radius-lg);border-bottom-right-radius:var(--border-radius-lg)}@media (max-width: 768px){.modal-container{width:95%;margin:var(--spacing-md)}.modal-header{padding:var(--spacing-md) var(--spacing-lg)}.modal-title{font-size:var(--font-size-lg)}.modal-content{padding:var(--spacing-lg)}.modal-footer{padding:var(--spacing-md) var(--spacing-lg);flex-direction:column-reverse;gap:var(--spacing-sm)}}@media (max-width: 480px){.modal-container{width:100%;max-height:100vh;margin:0;border-radius:0}.modal-header{padding:var(--spacing-sm) var(--spacing-md);border-top-left-radius:0;border-top-right-radius:0}.modal-title{font-size:var(--font-size-base)}.modal-content{padding:var(--spacing-md);flex:0 1 auto}.modal-footer{padding:var(--spacing-sm) var(--spacing-md);border-bottom-left-radius:0;border-bottom-right-radius:0}}\n"] }]
2413
- }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], showClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClose", required: false }] }], closeOnBackdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdrop", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], preventScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventScroll", required: false }] }], showFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFooter", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], dismissLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissLabel", required: false }] }], confirmLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmLabel", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], confirmed: [{ type: i0.Output, args: ["confirmed"] }], customFooter: [{ type: i0.ContentChild, args: ['modalFooter', { isSignal: true }] }], modalBackdrop: [{ type: i0.ViewChild, args: ['modalBackdrop', { isSignal: true }] }] } });
2459
+ args: [{ selector: 'psh-modal', imports: [CommonModule, PshButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n #modalBackdrop\n [class]=\"'modal-backdrop ' + backdropClass()\"\n [class.open]=\"open()\"\n (click)=\"handleBackdropClick($event)\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-hidden]=\"!open()\"\n [attr.aria-labelledby]=\"modalDialogId() + '-title'\"\n [attr.aria-describedby]=\"modalDescriptionId()\"\n [attr.data-state]=\"state()\"\n>\n <div\n [class]=\"'modal-container ' + styleClass()\"\n [class.small]=\"size() === 'small'\"\n [class.medium]=\"size() === 'medium'\"\n [class.large]=\"size() === 'large'\"\n [attr.id]=\"modalDialogId()\"\n role=\"document\"\n >\n <div class=\"modal-content-wrapper\">\n <div class=\"modal-header\">\n <h2 class=\"modal-title\" [attr.id]=\"modalDialogId() + '-title'\">\n <ng-content select=\"[modal-title]\">{{ title() }}</ng-content>\n </h2>\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"modal-close\"\n (click)=\"handleClose()\"\n [attr.aria-label]=\"dismissLabel()\"\n >\n <i class=\"ph ph-x\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n\n <div class=\"modal-content\" [attr.id]=\"modalDescriptionId()\">\n <ng-content>\n <p>\n Modal content goes here. You can replace this with your own content.\n </p>\n </ng-content>\n </div>\n\n @if (showFooter() && !hasCustomFooter()) {\n <div class=\"modal-footer\">\n <psh-button\n appearance=\"outline\"\n variant=\"primary\"\n size=\"medium\"\n [fullWidth]=\"isMobileScreen()\"\n (clicked)=\"handleClose()\">\n {{ cancelLabel() }}\n </psh-button>\n <psh-button\n appearance=\"filled\"\n variant=\"primary\"\n size=\"medium\"\n [fullWidth]=\"isMobileScreen()\"\n (clicked)=\"handleConfirm()\">\n {{ confirmLabel() }}\n </psh-button>\n </div>\n } @if (showFooter() && hasCustomFooter()) {\n <div class=\"modal-footer\">\n <!--\n Note: When using a custom footer, buttons should also use [fullWidth]=\"isMobileScreen()\"\n to ensure proper responsive behavior on mobile devices.\n Example:\n <psh-button [fullWidth]=\"isMobileScreen()\" (clicked)=\"action()\">Custom Button</psh-button>\n -->\n <ng-content select=\"[modal-footer]\" #modalFooter></ng-content>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":root{--modal-max-width-sm: 25rem;--modal-max-width-md: 37.5rem;--modal-max-width-lg: 50rem;--modal-backdrop-opacity: .6;--modal-backdrop-blur: 4px;--modal-animation-distance: 20px;--modal-animation-scale: .95}.modal-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(var(--surface-900-rgb),var(--modal-backdrop-opacity));-webkit-backdrop-filter:blur(var(--modal-backdrop-blur));backdrop-filter:blur(var(--modal-backdrop-blur));align-items:center;justify-content:center;z-index:var(--z-index-modal-backdrop);pointer-events:none;opacity:0;display:none;transition:opacity var(--animation-duration-default) var(--animation-easing-smooth)}.modal-backdrop.open{opacity:1;pointer-events:all;display:flex}.modal-content-wrapper{display:flex;flex-direction:column;max-height:90vh;min-height:0}.modal-container{background:var(--surface-card);border-radius:var(--border-radius-lg);box-shadow:var(--shadow-lg),0 0 0 1px rgba(var(--primary-color-rgb),.05);max-width:var(--modal-max-width-md);transform:translateY(var(--modal-animation-distance)) scale(var(--modal-animation-scale));transition:transform var(--animation-duration-default) var(--animation-easing-smooth),opacity var(--animation-duration-default) var(--animation-easing-smooth);border:1px solid var(--surface-border)}.modal-backdrop.open .modal-container{transform:translateY(0) scale(1)}.modal-container.small{width:50%;max-width:var(--modal-max-width-sm)}.modal-container.medium{width:70%;max-width:var(--modal-max-width-md)}.modal-container.large{width:90%;max-width:var(--modal-max-width-lg)}.modal-header{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-lg) var(--spacing-xl);border-bottom:1px solid var(--surface-border);background:var(--surface-ground);border-top-left-radius:var(--border-radius-lg);border-top-right-radius:var(--border-radius-lg)}.modal-title{font-size:var(--font-size-xl);font-weight:var(--font-weight-semibold);color:var(--text-color);margin:0;display:flex;align-items:center;gap:var(--spacing-sm)}.modal-close{background:transparent;border:none;color:var(--text-color-secondary);padding:var(--spacing-sm);cursor:pointer;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;transition:all var(--animation-duration-fast) var(--animation-easing-smooth);margin:var(--negative-spacing-sm)}.modal-close:hover{background:var(--surface-hover);color:var(--text-color);transform:scale(1.1)}.modal-close:focus-visible{outline:var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);outline-offset:var(--focus-outline-offset)}.modal-close i{font-size:var(--font-size-xl)}.modal-content{padding:var(--spacing-xl);overflow-y:auto;flex-grow:1;min-height:0;color:var(--text-color-secondary);line-height:var(--line-height-relaxed)}.modal-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--spacing-md);padding:var(--spacing-lg) var(--spacing-xl);border-top:1px solid var(--surface-border);background:var(--surface-ground);border-bottom-left-radius:var(--border-radius-lg);border-bottom-right-radius:var(--border-radius-lg)}@media (max-width: 768px){.modal-container{width:95%;margin:var(--spacing-md)}.modal-header{padding:var(--spacing-md) var(--spacing-lg)}.modal-title{font-size:var(--font-size-lg)}.modal-content{padding:var(--spacing-lg)}.modal-footer{padding:var(--spacing-md) var(--spacing-lg);flex-direction:column-reverse;gap:var(--spacing-sm)}}@media (max-width: 480px){.modal-container{width:100%;max-height:100vh;margin:0;border-radius:0}.modal-header{padding:var(--spacing-sm) var(--spacing-md);border-top-left-radius:0;border-top-right-radius:0}.modal-title{font-size:var(--font-size-base)}.modal-content{padding:var(--spacing-md);flex:0 1 auto}.modal-footer{padding:var(--spacing-sm) var(--spacing-md);border-bottom-left-radius:0;border-bottom-right-radius:0}}\n"] }]
2460
+ }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], showClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClose", required: false }] }], closeOnBackdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdrop", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], preventScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventScroll", required: false }] }], showFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFooter", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], dismissLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissLabel", required: false }] }], confirmLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmLabel", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }], backdropClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "backdropClass", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], confirmed: [{ type: i0.Output, args: ["confirmed"] }], customFooter: [{ type: i0.ContentChild, args: ['modalFooter', { isSignal: true }] }], modalBackdrop: [{ type: i0.ViewChild, args: ['modalBackdrop', { isSignal: true }] }] } });
2414
2461
 
2415
2462
  const PAGINATION_CONFIG = new InjectionToken('PAGINATION_CONFIG', {
2416
2463
  factory: () => ({