ps-helix 6.2.2 → 6.2.4

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 22+ featuring modern design patterns, accessibility-first development, and optimal developer experience.
4
4
 
5
- [![npm version](https://img.shields.io/badge/npm-6.2.2-blue.svg)](https://www.npmjs.com/package/ps-helix)
5
+ [![npm version](https://img.shields.io/badge/npm-6.2.4-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-22.0.0-red.svg)](https://angular.dev/)
8
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-6.0.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": "^6.2.2"
109
+ "ps-helix": "^6.2.4"
110
110
  }
111
111
  }
112
112
  ```
@@ -1182,7 +1182,7 @@ Copyright (c) 2025 PACK Solutions
1182
1182
 
1183
1183
  ---
1184
1184
 
1185
- **Version**: 6.2.2
1185
+ **Version**: 6.2.4
1186
1186
  **Built with**: Angular 22.0.0, TypeScript 6.0.0, Phosphor Icons 2.0.3
1187
1187
  **Author**: Fabrice PEREZ | Design Engineer at PACK Solutions
1188
1188
  **Last Updated**: January 2026
@@ -3841,12 +3841,13 @@ const RADIO_STYLES = new InjectionToken('RADIO_STYLES', {
3841
3841
  */
3842
3842
  let radioIdCounter = 0;
3843
3843
  class PshRadioComponent {
3844
- set checkedInput(v) { this.checked.set(v); }
3845
- set disabledInput(v) { this.disabled.set(v); }
3846
3844
  constructor() {
3847
3845
  this.config = inject(RADIO_CONFIG);
3848
3846
  this.styles = inject(RADIO_STYLES, { optional: true }) ?? [];
3849
3847
  this.uniqueId = `radio-${++radioIdCounter}`;
3848
+ /** Holds the `label` input or the projected content — whichever provides the visible label. */
3849
+ this.labelSlot = viewChild('labelSlot', /* @ts-ignore */
3850
+ ...(ngDevMode ? [{ debugName: "labelSlot" }] : /* istanbul ignore next */ []));
3850
3851
  // Plain signals + manual @Input/@Output to prevent auto-emission
3851
3852
  // when parent sets [checked] or [disabled] via template binding.
3852
3853
  // model() would auto-emit checkedChange/disabledChange on .set().
@@ -3892,7 +3893,10 @@ class PshRadioComponent {
3892
3893
  const labelText = this.label();
3893
3894
  if (labelText)
3894
3895
  return labelText;
3895
- return this.hasProjectedContent() ? undefined : 'Radio';
3896
+ // No `aria-label` at all when nothing is declared: the `<label>` wrapping the input
3897
+ // already names it from a projected label. A hard-coded fallback would override that
3898
+ // visible text for screen readers (WCAG 2.5.3 "Label in Name").
3899
+ return undefined;
3896
3900
  }, /* @ts-ignore */
3897
3901
  ...(ngDevMode ? [{ debugName: "computedAriaLabel" }] : /* istanbul ignore next */ []));
3898
3902
  this.errorMessageId = computed(() => this.error() ? `${this.uniqueId}-error` : undefined, /* @ts-ignore */
@@ -3903,12 +3907,18 @@ class PshRadioComponent {
3903
3907
  // so aria-describedby references whichever message is actually rendered.
3904
3908
  this.ariaDescribedBy = computed(() => this.errorMessageId() ?? this.successMessageId(), /* @ts-ignore */
3905
3909
  ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : /* istanbul ignore next */ []));
3906
- if (isDevMode()) {
3907
- effect(() => {
3908
- if (!this.label() && !this.ariaLabel() && !this.hasProjectedContent()) {
3909
- console.warn('[psh-radio] No accessible label provided. Please use label input, ariaLabel input, or projected content.');
3910
- }
3911
- });
3910
+ }
3911
+ set checkedInput(v) { this.checked.set(v); }
3912
+ set disabledInput(v) { this.disabled.set(v); }
3913
+ ngAfterViewInit() {
3914
+ if (!isDevMode())
3915
+ return;
3916
+ // Read the rendered label slot rather than the `label` input alone: a projected
3917
+ // label (`<psh-radio>Accept terms</psh-radio>`) is just as accessible, and
3918
+ // `contentChild` cannot see a bare text node. Checked once, after the first render.
3919
+ const hasVisibleLabel = !!this.labelSlot()?.nativeElement.textContent?.trim();
3920
+ if (!hasVisibleLabel && !this.ariaLabel() && !this.hasProjectedContent()) {
3921
+ console.warn('[psh-radio] No accessible label provided. Please use label input, ariaLabel input, or projected content.');
3912
3922
  }
3913
3923
  }
3914
3924
  getState() {
@@ -3921,9 +3931,9 @@ class PshRadioComponent {
3921
3931
  return this.checked() ? 'checked' : 'unchecked';
3922
3932
  }
3923
3933
  /**
3924
- * Declares whether a label is projected (`<psh-radio>Label</psh-radio>`). Not detected
3925
- * automatically yet: until a consumer calls this, a projected label still triggers the
3926
- * dev-only accessibility warning and `aria-label="Radio"` overrides the visible text.
3934
+ * Declares whether a label is projected. No longer required — projected content is now
3935
+ * detected from the rendered label slot. Kept for backwards compatibility: calling it
3936
+ * with `true` still suppresses the dev-only accessibility warning.
3927
3937
  */
3928
3938
  updateProjectedContent(hasContent) {
3929
3939
  this.hasProjectedContent.set(hasContent);
@@ -3969,7 +3979,7 @@ class PshRadioComponent {
3969
3979
  // Implementation requires radio group context
3970
3980
  }
3971
3981
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshRadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3972
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: PshRadioComponent, isStandalone: true, selector: "psh-radio", inputs: { checkedInput: { classPropertyName: "checkedInput", publicName: "checked", isSignal: false, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", 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 }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", 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: { checkedChange: "checkedChange", disabledChange: "disabledChange", valueChange: "valueChange" }, host: { properties: { "class.radio-disabled": "disabled()", "class.radio-error": "!!error()", "class.radio-success": "!!success()", "class.radio-small": "size() === \"small\"", "class.radio-large": "size() === \"large\"", "attr.data-state": "state()" } }, ngImport: i0, template: "<div class=\"radio-container\">\r\n <label class=\"radio-label\" [class.label-left]=\"labelPosition() === 'left'\">\r\n <input\r\n type=\"radio\"\r\n class=\"radio-input\"\r\n [checked]=\"checked()\"\r\n [disabled]=\"disabled()\"\r\n [required]=\"required()\"\r\n [name]=\"name()\"\r\n [attr.value]=\"value()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-required]=\"required() || null\"\r\n [attr.aria-invalid]=\"error() ? 'true' : null\"\r\n [attr.aria-checked]=\"checked()\"\r\n [attr.aria-describedby]=\"ariaDescribedBy()\"\r\n (change)=\"handleChange()\"\r\n (keydown)=\"handleKeydown($event)\"\r\n />\r\n <span \r\n class=\"radio-control\"\r\n [class.error]=\"!!error()\"\r\n [class.success]=\"!!success()\"\r\n >\r\n <span class=\"radio-dot\"></span>\r\n </span>\r\n <span class=\"radio-text\">\r\n @if (label()) {\r\n {{ label() }}\r\n } @else {\r\n <ng-content>Radio</ng-content>\r\n }\r\n </span>\r\n </label>\r\n\r\n @if (error()) {\r\n <div [id]=\"errorMessageId()\" class=\"radio-error\" role=\"alert\">\r\n {{ error() }}\r\n </div>\r\n } @else if (success()) {\r\n <div [id]=\"successMessageId()\" class=\"radio-success\" role=\"status\">\r\n {{ success() }}\r\n </div>\r\n }\r\n</div>", styles: [".radio-container{display:inline-flex;flex-direction:column;gap:var(--form-field-gap)}.radio-label{display:inline-flex;align-items:center;gap:var(--form-label-gap);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--text-color)}.radio-label.label-left{flex-direction:row-reverse;justify-content:space-between}.radio-input{position:absolute;opacity:0;width:0;height:0}.radio-control{display:flex;align-items:center;justify-content:center;width:var(--size-5);height:var(--size-5);min-width:var(--size-5);min-height:var(--size-5);border:var(--border-width-2) solid var(--surface-border);border-radius:var(--radius-full);background:var(--surface-card);transition:all var(--animation-duration-fast) var(--animation-easing-default);flex-shrink:0}.radio-dot{width:var(--size-2);height:var(--size-2);border-radius:var(--radius-full);background:var(--primary-color-text);opacity:0;transform:scale(.8);transition:all var(--animation-duration-fast) var(--animation-easing-default)}.radio-input:checked+.radio-control{background:var(--primary-color);border-color:var(--primary-color)}.radio-input:checked+.radio-control .radio-dot{opacity:1;transform:scale(1)}.radio-input:focus-visible+.radio-control{box-shadow:0 0 0 var(--focus-outline-width) var(--focus-ring-color)}:host(.radio-disabled) .radio-label{opacity:var(--opacity-disabled);cursor:not-allowed}:host(.radio-error) .radio-control{border-color:var(--danger-color)}:host(.radio-error) .radio-input:checked+.radio-control{background:var(--danger-color);border-color:var(--danger-color)}:host(.radio-success) .radio-control{border-color:var(--success-color)}:host(.radio-success) .radio-input:checked+.radio-control{background:var(--success-color);border-color:var(--success-color)}.radio-error{font-size:var(--font-size-sm);color:var(--danger-color)}.radio-success{font-size:var(--font-size-sm);color:var(--success-color)}:host(.radio-small) .radio-control{width:var(--size-4);height:var(--size-4);min-width:var(--size-4);min-height:var(--size-4)}:host(.radio-small) .radio-dot{width:var(--size-1-5);height:var(--size-1-5)}:host(.radio-small) .radio-text{font-size:var(--font-size-sm)}:host(.radio-large) .radio-control{width:var(--size-6);height:var(--size-6);min-width:var(--size-6);min-height:var(--size-6)}:host(.radio-large) .radio-dot{width:var(--size-3);height:var(--size-3)}:host(.radio-large) .radio-text{font-size:var(--font-size-lg)}.radio-text{color:var(--text-color)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3982
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: PshRadioComponent, isStandalone: true, selector: "psh-radio", inputs: { checkedInput: { classPropertyName: "checkedInput", publicName: "checked", isSignal: false, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", 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 }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", 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: { checkedChange: "checkedChange", disabledChange: "disabledChange", valueChange: "valueChange" }, host: { properties: { "class.radio-disabled": "disabled()", "class.radio-error": "!!error()", "class.radio-success": "!!success()", "class.radio-small": "size() === \"small\"", "class.radio-large": "size() === \"large\"", "attr.data-state": "state()" } }, viewQueries: [{ propertyName: "labelSlot", first: true, predicate: ["labelSlot"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"radio-container\">\r\n <label class=\"radio-label\" [class.label-left]=\"labelPosition() === 'left'\">\r\n <input\r\n type=\"radio\"\r\n class=\"radio-input\"\r\n [checked]=\"checked()\"\r\n [disabled]=\"disabled()\"\r\n [required]=\"required()\"\r\n [name]=\"name()\"\r\n [attr.value]=\"value()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-required]=\"required() || null\"\r\n [attr.aria-invalid]=\"error() ? 'true' : null\"\r\n [attr.aria-checked]=\"checked()\"\r\n [attr.aria-describedby]=\"ariaDescribedBy()\"\r\n (change)=\"handleChange()\"\r\n (keydown)=\"handleKeydown($event)\"\r\n />\r\n <span \r\n class=\"radio-control\"\r\n [class.error]=\"!!error()\"\r\n [class.success]=\"!!success()\"\r\n >\r\n <span class=\"radio-dot\"></span>\r\n </span>\r\n <span class=\"radio-text\" #labelSlot>\r\n @if (label()) {\r\n {{ label() }}\r\n } @else {\r\n <ng-content></ng-content>\r\n }\r\n </span>\r\n </label>\r\n\r\n @if (error()) {\r\n <div [id]=\"errorMessageId()\" class=\"radio-error\" role=\"alert\">\r\n {{ error() }}\r\n </div>\r\n } @else if (success()) {\r\n <div [id]=\"successMessageId()\" class=\"radio-success\" role=\"status\">\r\n {{ success() }}\r\n </div>\r\n }\r\n</div>", styles: [".radio-container{display:inline-flex;flex-direction:column;gap:var(--form-field-gap)}.radio-label{display:inline-flex;align-items:center;gap:var(--form-label-gap);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--text-color)}.radio-label.label-left{flex-direction:row-reverse;justify-content:space-between}.radio-input{position:absolute;opacity:0;width:0;height:0}.radio-control{display:flex;align-items:center;justify-content:center;width:var(--size-5);height:var(--size-5);min-width:var(--size-5);min-height:var(--size-5);border:var(--border-width-2) solid var(--surface-border);border-radius:var(--radius-full);background:var(--surface-card);transition:all var(--animation-duration-fast) var(--animation-easing-default);flex-shrink:0}.radio-dot{width:var(--size-2);height:var(--size-2);border-radius:var(--radius-full);background:var(--primary-color-text);opacity:0;transform:scale(.8);transition:all var(--animation-duration-fast) var(--animation-easing-default)}.radio-input:checked+.radio-control{background:var(--primary-color);border-color:var(--primary-color)}.radio-input:checked+.radio-control .radio-dot{opacity:1;transform:scale(1)}.radio-input:focus-visible+.radio-control{box-shadow:0 0 0 var(--focus-outline-width) var(--focus-ring-color)}:host(.radio-disabled) .radio-label{opacity:var(--opacity-disabled);cursor:not-allowed}:host(.radio-error) .radio-control{border-color:var(--danger-color)}:host(.radio-error) .radio-input:checked+.radio-control{background:var(--danger-color);border-color:var(--danger-color)}:host(.radio-success) .radio-control{border-color:var(--success-color)}:host(.radio-success) .radio-input:checked+.radio-control{background:var(--success-color);border-color:var(--success-color)}.radio-error{font-size:var(--font-size-sm);color:var(--danger-color)}.radio-success{font-size:var(--font-size-sm);color:var(--success-color)}:host(.radio-small) .radio-control{width:var(--size-4);height:var(--size-4);min-width:var(--size-4);min-height:var(--size-4)}:host(.radio-small) .radio-dot{width:var(--size-1-5);height:var(--size-1-5)}:host(.radio-small) .radio-text{font-size:var(--font-size-sm)}:host(.radio-large) .radio-control{width:var(--size-6);height:var(--size-6);min-width:var(--size-6);min-height:var(--size-6)}:host(.radio-large) .radio-dot{width:var(--size-3);height:var(--size-3)}:host(.radio-large) .radio-text{font-size:var(--font-size-lg)}.radio-text{color:var(--text-color)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3973
3983
  }
3974
3984
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshRadioComponent, decorators: [{
3975
3985
  type: Component,
@@ -3980,8 +3990,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
3980
3990
  '[class.radio-small]': 'size() === "small"',
3981
3991
  '[class.radio-large]': 'size() === "large"',
3982
3992
  '[attr.data-state]': 'state()'
3983
- }, template: "<div class=\"radio-container\">\r\n <label class=\"radio-label\" [class.label-left]=\"labelPosition() === 'left'\">\r\n <input\r\n type=\"radio\"\r\n class=\"radio-input\"\r\n [checked]=\"checked()\"\r\n [disabled]=\"disabled()\"\r\n [required]=\"required()\"\r\n [name]=\"name()\"\r\n [attr.value]=\"value()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-required]=\"required() || null\"\r\n [attr.aria-invalid]=\"error() ? 'true' : null\"\r\n [attr.aria-checked]=\"checked()\"\r\n [attr.aria-describedby]=\"ariaDescribedBy()\"\r\n (change)=\"handleChange()\"\r\n (keydown)=\"handleKeydown($event)\"\r\n />\r\n <span \r\n class=\"radio-control\"\r\n [class.error]=\"!!error()\"\r\n [class.success]=\"!!success()\"\r\n >\r\n <span class=\"radio-dot\"></span>\r\n </span>\r\n <span class=\"radio-text\">\r\n @if (label()) {\r\n {{ label() }}\r\n } @else {\r\n <ng-content>Radio</ng-content>\r\n }\r\n </span>\r\n </label>\r\n\r\n @if (error()) {\r\n <div [id]=\"errorMessageId()\" class=\"radio-error\" role=\"alert\">\r\n {{ error() }}\r\n </div>\r\n } @else if (success()) {\r\n <div [id]=\"successMessageId()\" class=\"radio-success\" role=\"status\">\r\n {{ success() }}\r\n </div>\r\n }\r\n</div>", styles: [".radio-container{display:inline-flex;flex-direction:column;gap:var(--form-field-gap)}.radio-label{display:inline-flex;align-items:center;gap:var(--form-label-gap);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--text-color)}.radio-label.label-left{flex-direction:row-reverse;justify-content:space-between}.radio-input{position:absolute;opacity:0;width:0;height:0}.radio-control{display:flex;align-items:center;justify-content:center;width:var(--size-5);height:var(--size-5);min-width:var(--size-5);min-height:var(--size-5);border:var(--border-width-2) solid var(--surface-border);border-radius:var(--radius-full);background:var(--surface-card);transition:all var(--animation-duration-fast) var(--animation-easing-default);flex-shrink:0}.radio-dot{width:var(--size-2);height:var(--size-2);border-radius:var(--radius-full);background:var(--primary-color-text);opacity:0;transform:scale(.8);transition:all var(--animation-duration-fast) var(--animation-easing-default)}.radio-input:checked+.radio-control{background:var(--primary-color);border-color:var(--primary-color)}.radio-input:checked+.radio-control .radio-dot{opacity:1;transform:scale(1)}.radio-input:focus-visible+.radio-control{box-shadow:0 0 0 var(--focus-outline-width) var(--focus-ring-color)}:host(.radio-disabled) .radio-label{opacity:var(--opacity-disabled);cursor:not-allowed}:host(.radio-error) .radio-control{border-color:var(--danger-color)}:host(.radio-error) .radio-input:checked+.radio-control{background:var(--danger-color);border-color:var(--danger-color)}:host(.radio-success) .radio-control{border-color:var(--success-color)}:host(.radio-success) .radio-input:checked+.radio-control{background:var(--success-color);border-color:var(--success-color)}.radio-error{font-size:var(--font-size-sm);color:var(--danger-color)}.radio-success{font-size:var(--font-size-sm);color:var(--success-color)}:host(.radio-small) .radio-control{width:var(--size-4);height:var(--size-4);min-width:var(--size-4);min-height:var(--size-4)}:host(.radio-small) .radio-dot{width:var(--size-1-5);height:var(--size-1-5)}:host(.radio-small) .radio-text{font-size:var(--font-size-sm)}:host(.radio-large) .radio-control{width:var(--size-6);height:var(--size-6);min-width:var(--size-6);min-height:var(--size-6)}:host(.radio-large) .radio-dot{width:var(--size-3);height:var(--size-3)}:host(.radio-large) .radio-text{font-size:var(--font-size-lg)}.radio-text{color:var(--text-color)}\n"] }]
3984
- }], ctorParameters: () => [], propDecorators: { checkedInput: [{
3993
+ }, template: "<div class=\"radio-container\">\r\n <label class=\"radio-label\" [class.label-left]=\"labelPosition() === 'left'\">\r\n <input\r\n type=\"radio\"\r\n class=\"radio-input\"\r\n [checked]=\"checked()\"\r\n [disabled]=\"disabled()\"\r\n [required]=\"required()\"\r\n [name]=\"name()\"\r\n [attr.value]=\"value()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-required]=\"required() || null\"\r\n [attr.aria-invalid]=\"error() ? 'true' : null\"\r\n [attr.aria-checked]=\"checked()\"\r\n [attr.aria-describedby]=\"ariaDescribedBy()\"\r\n (change)=\"handleChange()\"\r\n (keydown)=\"handleKeydown($event)\"\r\n />\r\n <span \r\n class=\"radio-control\"\r\n [class.error]=\"!!error()\"\r\n [class.success]=\"!!success()\"\r\n >\r\n <span class=\"radio-dot\"></span>\r\n </span>\r\n <span class=\"radio-text\" #labelSlot>\r\n @if (label()) {\r\n {{ label() }}\r\n } @else {\r\n <ng-content></ng-content>\r\n }\r\n </span>\r\n </label>\r\n\r\n @if (error()) {\r\n <div [id]=\"errorMessageId()\" class=\"radio-error\" role=\"alert\">\r\n {{ error() }}\r\n </div>\r\n } @else if (success()) {\r\n <div [id]=\"successMessageId()\" class=\"radio-success\" role=\"status\">\r\n {{ success() }}\r\n </div>\r\n }\r\n</div>", styles: [".radio-container{display:inline-flex;flex-direction:column;gap:var(--form-field-gap)}.radio-label{display:inline-flex;align-items:center;gap:var(--form-label-gap);cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--text-color)}.radio-label.label-left{flex-direction:row-reverse;justify-content:space-between}.radio-input{position:absolute;opacity:0;width:0;height:0}.radio-control{display:flex;align-items:center;justify-content:center;width:var(--size-5);height:var(--size-5);min-width:var(--size-5);min-height:var(--size-5);border:var(--border-width-2) solid var(--surface-border);border-radius:var(--radius-full);background:var(--surface-card);transition:all var(--animation-duration-fast) var(--animation-easing-default);flex-shrink:0}.radio-dot{width:var(--size-2);height:var(--size-2);border-radius:var(--radius-full);background:var(--primary-color-text);opacity:0;transform:scale(.8);transition:all var(--animation-duration-fast) var(--animation-easing-default)}.radio-input:checked+.radio-control{background:var(--primary-color);border-color:var(--primary-color)}.radio-input:checked+.radio-control .radio-dot{opacity:1;transform:scale(1)}.radio-input:focus-visible+.radio-control{box-shadow:0 0 0 var(--focus-outline-width) var(--focus-ring-color)}:host(.radio-disabled) .radio-label{opacity:var(--opacity-disabled);cursor:not-allowed}:host(.radio-error) .radio-control{border-color:var(--danger-color)}:host(.radio-error) .radio-input:checked+.radio-control{background:var(--danger-color);border-color:var(--danger-color)}:host(.radio-success) .radio-control{border-color:var(--success-color)}:host(.radio-success) .radio-input:checked+.radio-control{background:var(--success-color);border-color:var(--success-color)}.radio-error{font-size:var(--font-size-sm);color:var(--danger-color)}.radio-success{font-size:var(--font-size-sm);color:var(--success-color)}:host(.radio-small) .radio-control{width:var(--size-4);height:var(--size-4);min-width:var(--size-4);min-height:var(--size-4)}:host(.radio-small) .radio-dot{width:var(--size-1-5);height:var(--size-1-5)}:host(.radio-small) .radio-text{font-size:var(--font-size-sm)}:host(.radio-large) .radio-control{width:var(--size-6);height:var(--size-6);min-width:var(--size-6);min-height:var(--size-6)}:host(.radio-large) .radio-dot{width:var(--size-3);height:var(--size-3)}:host(.radio-large) .radio-text{font-size:var(--font-size-lg)}.radio-text{color:var(--text-color)}\n"] }]
3994
+ }], propDecorators: { labelSlot: [{ type: i0.ViewChild, args: ['labelSlot', { isSignal: true }] }], checkedInput: [{
3985
3995
  type: Input,
3986
3996
  args: ['checked']
3987
3997
  }], disabledInput: [{
@@ -5863,9 +5873,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
5863
5873
  /** @deprecated Use PshToastService instead */
5864
5874
  const ToastService = PshToastService;
5865
5875
 
5866
- /** @deprecated Use PshToastComponent instead */
5867
- const ToastComponent = PshToastComponent;
5868
-
5869
5876
  class PshToastComponent {
5870
5877
  constructor() {
5871
5878
  this.toastService = inject(PshToastService);
@@ -5967,6 +5974,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
5967
5974
  '(document:keydown.escape)': 'handleEscapeKey()'
5968
5975
  }, template: "<div\r\n class=\"toast-container\"\r\n [class]=\"position()\"\r\n role=\"region\"\r\n aria-label=\"Notifications\"\r\n>\r\n @for (toast of toasts(); track toast.id) {\r\n <div\r\n class=\"toast-item\"\r\n [class]=\"toast.type\"\r\n role=\"alert\"\r\n aria-live=\"polite\"\r\n aria-atomic=\"true\"\r\n [style.--progress-duration]=\"toast.duration + 'ms'\"\r\n (mouseenter)=\"handleMouseEnter(toast.id!)\"\r\n (mouseleave)=\"handleMouseLeave(toast.id!)\"\r\n >\r\n @if (config.showIcon !== false) {\r\n <i\r\n class=\"ph ph-{{ toast.icon || getDefaultIcon(toast.type) }} toast-icon\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n }\r\n <div class=\"toast-content\">\r\n {{ toast.message }}\r\n </div>\r\n @if (shouldShowCloseButton(toast)) {\r\n <button\r\n type=\"button\"\r\n class=\"toast-close\"\r\n (click)=\"handleDismiss(toast.id)\"\r\n [attr.aria-label]=\"getCloseButtonAriaLabel(toast)\"\r\n >\r\n <i class=\"ph ph-x\" aria-hidden=\"true\"></i>\r\n </button>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".toast-container{--toast-min-width: 18.75rem;--toast-max-width: 31.25rem;--toast-progress-height: 3px;--toast-icon-size: 1.5rem;position:fixed;display:flex;flex-direction:column;gap:var(--spacing-sm);padding:var(--spacing-md);z-index:var(--z-index-toast);pointer-events:none;max-width:100%}.toast-item{min-width:var(--toast-min-width);max-width:var(--toast-max-width);padding:var(--spacing-md);background:var(--surface-0);border-radius:var(--border-radius-lg);box-shadow:var(--shadow-lg);display:flex;align-items:center;gap:var(--spacing-md);animation:slideIn var(--animation-duration-default) var(--animation-easing-default);position:relative;overflow:hidden;pointer-events:all;border:1px solid var(--surface-border);width:100%}@media(prefers-reduced-motion:reduce){.toast-item{animation:fadeIn var(--animation-duration-fast) var(--animation-easing-default)}}.toast-item:after{content:\"\";position:absolute;bottom:0;left:0;height:var(--toast-progress-height);background:currentColor;opacity:.2;animation:progress var(--progress-duration) linear forwards}@media(prefers-reduced-motion:reduce){.toast-item:after{animation:none}}.toast-item.info{background:var(--surface-0);border-color:var(--primary-color);color:var(--primary-color);box-shadow:0 4px 12px rgba(var(--primary-color-rgb),.1)}.toast-item.success{background:var(--surface-0);border-color:var(--success-color);color:var(--success-color);box-shadow:0 4px 12px rgba(var(--success-color-rgb),.1)}.toast-item.warning{background:var(--surface-0);border-color:var(--warning-color);color:var(--warning-color);box-shadow:0 4px 12px rgba(var(--warning-color-rgb),.1)}.toast-item.danger{background:var(--surface-0);border-color:var(--danger-color);color:var(--danger-color);box-shadow:0 4px 12px rgba(var(--danger-color-rgb),.1)}.toast-icon{font-size:var(--toast-icon-size);flex-shrink:0;display:flex;align-items:center;justify-content:center}.toast-content{flex:1;color:inherit;font-size:var(--font-size-base);line-height:var(--line-height-relaxed);font-weight:500;margin:0;padding-right:var(--spacing-md);overflow:hidden;text-overflow:ellipsis}.toast-close{background:transparent;border:none;color:currentColor;padding:var(--spacing-xs);cursor:pointer;opacity:.7;transition:all var(--animation-duration-normal) var(--animation-easing-default);border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;margin:calc(var(--spacing-xs) * -1);flex-shrink:0;width:var(--spacing-2xl);height:var(--spacing-2xl)}.toast-close:hover{opacity:1;background:var(--surface-hover)}.toast-close:focus{outline:none;box-shadow:0 0 0 var(--focus-outline-width) var(--focus-ring-color)}.toast-close i{font-size:1.1rem}.top-right{top:0;right:0}.top-left{top:0;left:0}.bottom-right{bottom:0;right:0}.bottom-left{bottom:0;left:0}@keyframes slideIn{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes progress{0%{width:100%}to{width:0%}}@media(max-width:576px){.toast-container{padding:var(--spacing-sm);width:100%;left:0!important;right:0!important}.toast-item{min-width:0;margin:0 var(--spacing-sm);max-width:calc(100vw - var(--spacing-md))}.toast-content{font-size:var(--font-size-sm);padding-right:var(--spacing-sm)}.toast-close{width:var(--spacing-xl);height:var(--spacing-xl)}.toast-close i{font-size:1rem}}@media(max-width:480px){.toast-container{padding:var(--spacing-xs)}.toast-item{margin:0 var(--spacing-xs);padding:var(--spacing-sm)}.toast-icon{font-size:1.25rem}.toast-content{font-size:var(--font-size-xs)}}\n"] }]
5969
5976
  }], ctorParameters: () => [] });
5977
+ /** @deprecated Use PshToastComponent instead */
5978
+ const ToastComponent = PshToastComponent;
5970
5979
 
5971
5980
  function hexToRgb(hex) {
5972
5981
  let value = hex.replace(/^#/, '').trim();