ps-helix 6.2.1 → 6.2.2

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.1-blue.svg)](https://www.npmjs.com/package/ps-helix)
5
+ [![npm version](https://img.shields.io/badge/npm-6.2.2-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.1"
109
+ "ps-helix": "^6.2.2"
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.1
1185
+ **Version**: 6.2.2
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
@@ -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, viewChild, effect, Injectable, DestroyRef, Directive, ViewContainerRef, ChangeDetectorRef, ViewChild, Injector, afterNextRender, HostListener, Renderer2, contentChild, linkedSignal, isDevMode, EventEmitter, Output, Input, contentChildren } from '@angular/core';
2
+ import { input, output, computed, ChangeDetectionStrategy, Component, model, inject, ElementRef, signal, PLATFORM_ID, ViewEncapsulation, InjectionToken, viewChild, isDevMode, Injectable, DestroyRef, Directive, ViewContainerRef, ChangeDetectorRef, effect, ViewChild, Injector, afterNextRender, HostListener, Renderer2, contentChild, linkedSignal, EventEmitter, Output, Input, contentChildren } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule, DOCUMENT, isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
5
5
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
@@ -1210,6 +1210,9 @@ class PshCheckboxComponent {
1210
1210
  this.config = inject(CHECKBOX_CONFIG);
1211
1211
  this.checkboxInput = viewChild('checkboxInput', /* @ts-ignore */
1212
1212
  ...(ngDevMode ? [{ debugName: "checkboxInput" }] : /* istanbul ignore next */ []));
1213
+ /** Holds the `label` input or the projected content — whichever provides the visible label. */
1214
+ this.labelSlot = viewChild('labelSlot', /* @ts-ignore */
1215
+ ...(ngDevMode ? [{ debugName: "labelSlot" }] : /* istanbul ignore next */ []));
1213
1216
  this.uniqueId = `psh-cb-${++checkboxIdCounter}`;
1214
1217
  this.onChange = (_) => { };
1215
1218
  this.onTouched = () => { };
@@ -1261,11 +1264,17 @@ class PshCheckboxComponent {
1261
1264
  return this.checked() ? 'checked' : 'unchecked';
1262
1265
  }, /* @ts-ignore */
1263
1266
  ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
1264
- effect(() => {
1265
- if (!this.label() && !this.ariaLabel()) {
1266
- console.warn('[psh-checkbox] Label manquant pour l\'accessibilité.');
1267
- }
1268
- });
1267
+ }
1268
+ ngAfterViewInit() {
1269
+ if (!isDevMode())
1270
+ return;
1271
+ // Read the rendered label slot rather than the `label` input alone: a projected
1272
+ // label (`<psh-checkbox>Accept terms</psh-checkbox>`) is just as accessible, and
1273
+ // `contentChild` cannot see a bare text node. Checked once, after the first render.
1274
+ const hasVisibleLabel = !!this.labelSlot()?.nativeElement.textContent?.trim();
1275
+ if (!hasVisibleLabel && !this.ariaLabel()) {
1276
+ console.warn('[psh-checkbox] No accessible label provided. Please use label input, ariaLabel input, or projected content.');
1277
+ }
1269
1278
  }
1270
1279
  toggle() {
1271
1280
  if (!this.disabled()) {
@@ -1296,7 +1305,7 @@ class PshCheckboxComponent {
1296
1305
  provide: NG_VALUE_ACCESSOR,
1297
1306
  useExisting: PshCheckboxComponent,
1298
1307
  multi: true
1299
- }], viewQueries: [{ propertyName: "checkboxInput", first: true, predicate: ["checkboxInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"checkbox-container\">\r\n <label class=\"checkbox-label\" [class.label-left]=\"labelPosition() === 'left'\">\r\n <input\r\n #checkboxInput\r\n type=\"checkbox\"\r\n class=\"checkbox-input\"\r\n [checked]=\"checked()\"\r\n [disabled]=\"disabled()\"\r\n [required]=\"required()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-checked]=\"ariaChecked()\"\r\n [attr.aria-required]=\"required() || null\"\r\n [attr.aria-invalid]=\"error() ? 'true' : null\"\r\n [attr.aria-describedby]=\"ariaDescribedBy()\"\r\n (change)=\"toggle()\"\r\n (keydown)=\"handleKeydown($event)\"\r\n />\r\n \r\n <span class=\"checkbox-control\">\r\n @if (checked() && !indeterminate()) {\r\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\r\n }\r\n @if (indeterminate()) {\r\n <i class=\"ph ph-minus\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n\r\n <span class=\"checkbox-text\">\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]=\"uniqueId + '-error'\" class=\"checkbox-message error\" role=\"alert\">\r\n {{ error() }}\r\n </div>\r\n } @else if (success()) {\r\n <div [id]=\"uniqueId + '-success'\" class=\"checkbox-message success\" role=\"status\">\r\n {{ success() }}\r\n </div>\r\n }\r\n</div>", styles: [":host{display:inline-flex;flex-direction:column}.checkbox-container{display:flex;flex-direction:column;gap:var(--form-field-gap)}.checkbox-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)}.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:1.25rem;height:1.25rem;border:.125rem solid var(--surface-border);border-radius:var(--border-radius);background:var(--surface-card);transition:all var(--animation-duration-fast) var(--animation-easing-default);color:#fff;position:relative;flex-shrink:0}:host.checkbox-checked .checkbox-control,:host.checkbox-indeterminate .checkbox-control{background:var(--primary-color);border-color:var(--primary-color)}.checkbox-input:focus-visible+.checkbox-control{outline:.125rem solid var(--focus-outline-color);outline-offset:.125rem}.checkbox-control i{font-size:.875rem;line-height:1}:host.checkbox-small .checkbox-control{width:1rem;height:1rem}:host.checkbox-large .checkbox-control{width:1.5rem;height:1.5rem}:host.checkbox-error .checkbox-control{border-color:var(--danger-color)}:host.checkbox-error.checkbox-checked .checkbox-control{background:var(--danger-color)}:host.checkbox-success .checkbox-control{border-color:var(--success-color)}:host.checkbox-success.checkbox-checked .checkbox-control{background:var(--success-color)}:host.checkbox-disabled{opacity:var(--opacity-disabled);cursor:not-allowed}:host.checkbox-disabled .checkbox-label{cursor:not-allowed}.checkbox-message{font-size:.875rem}.checkbox-message.error{color:var(--danger-color)}.checkbox-message.success{color:var(--success-color)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1308
+ }], viewQueries: [{ propertyName: "checkboxInput", first: true, predicate: ["checkboxInput"], descendants: true, isSignal: true }, { propertyName: "labelSlot", first: true, predicate: ["labelSlot"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"checkbox-container\">\r\n <label class=\"checkbox-label\" [class.label-left]=\"labelPosition() === 'left'\">\r\n <input\r\n #checkboxInput\r\n type=\"checkbox\"\r\n class=\"checkbox-input\"\r\n [checked]=\"checked()\"\r\n [disabled]=\"disabled()\"\r\n [required]=\"required()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-checked]=\"ariaChecked()\"\r\n [attr.aria-required]=\"required() || null\"\r\n [attr.aria-invalid]=\"error() ? 'true' : null\"\r\n [attr.aria-describedby]=\"ariaDescribedBy()\"\r\n (change)=\"toggle()\"\r\n (keydown)=\"handleKeydown($event)\"\r\n />\r\n \r\n <span class=\"checkbox-control\">\r\n @if (checked() && !indeterminate()) {\r\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\r\n }\r\n @if (indeterminate()) {\r\n <i class=\"ph ph-minus\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n\r\n <span class=\"checkbox-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]=\"uniqueId + '-error'\" class=\"checkbox-message error\" role=\"alert\">\r\n {{ error() }}\r\n </div>\r\n } @else if (success()) {\r\n <div [id]=\"uniqueId + '-success'\" class=\"checkbox-message success\" role=\"status\">\r\n {{ success() }}\r\n </div>\r\n }\r\n</div>", styles: [":host{display:inline-flex;flex-direction:column}.checkbox-container{display:flex;flex-direction:column;gap:var(--form-field-gap)}.checkbox-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)}.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:1.25rem;height:1.25rem;border:.125rem solid var(--surface-border);border-radius:var(--border-radius);background:var(--surface-card);transition:all var(--animation-duration-fast) var(--animation-easing-default);color:#fff;position:relative;flex-shrink:0}:host.checkbox-checked .checkbox-control,:host.checkbox-indeterminate .checkbox-control{background:var(--primary-color);border-color:var(--primary-color)}.checkbox-input:focus-visible+.checkbox-control{outline:.125rem solid var(--focus-outline-color);outline-offset:.125rem}.checkbox-control i{font-size:.875rem;line-height:1}:host.checkbox-small .checkbox-control{width:1rem;height:1rem}:host.checkbox-large .checkbox-control{width:1.5rem;height:1.5rem}:host.checkbox-error .checkbox-control{border-color:var(--danger-color)}:host.checkbox-error.checkbox-checked .checkbox-control{background:var(--danger-color)}:host.checkbox-success .checkbox-control{border-color:var(--success-color)}:host.checkbox-success.checkbox-checked .checkbox-control{background:var(--success-color)}:host.checkbox-disabled{opacity:var(--opacity-disabled);cursor:not-allowed}:host.checkbox-disabled .checkbox-label{cursor:not-allowed}.checkbox-message{font-size:.875rem}.checkbox-message.error{color:var(--danger-color)}.checkbox-message.success{color:var(--success-color)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1300
1309
  }
1301
1310
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshCheckboxComponent, decorators: [{
1302
1311
  type: Component,
@@ -1314,8 +1323,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
1314
1323
  '[class.checkbox-indeterminate]': 'indeterminate()',
1315
1324
  '[attr.data-state]': 'state()',
1316
1325
  '[attr.aria-checked]': 'ariaChecked()'
1317
- }, template: "<div class=\"checkbox-container\">\r\n <label class=\"checkbox-label\" [class.label-left]=\"labelPosition() === 'left'\">\r\n <input\r\n #checkboxInput\r\n type=\"checkbox\"\r\n class=\"checkbox-input\"\r\n [checked]=\"checked()\"\r\n [disabled]=\"disabled()\"\r\n [required]=\"required()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-checked]=\"ariaChecked()\"\r\n [attr.aria-required]=\"required() || null\"\r\n [attr.aria-invalid]=\"error() ? 'true' : null\"\r\n [attr.aria-describedby]=\"ariaDescribedBy()\"\r\n (change)=\"toggle()\"\r\n (keydown)=\"handleKeydown($event)\"\r\n />\r\n \r\n <span class=\"checkbox-control\">\r\n @if (checked() && !indeterminate()) {\r\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\r\n }\r\n @if (indeterminate()) {\r\n <i class=\"ph ph-minus\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n\r\n <span class=\"checkbox-text\">\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]=\"uniqueId + '-error'\" class=\"checkbox-message error\" role=\"alert\">\r\n {{ error() }}\r\n </div>\r\n } @else if (success()) {\r\n <div [id]=\"uniqueId + '-success'\" class=\"checkbox-message success\" role=\"status\">\r\n {{ success() }}\r\n </div>\r\n }\r\n</div>", styles: [":host{display:inline-flex;flex-direction:column}.checkbox-container{display:flex;flex-direction:column;gap:var(--form-field-gap)}.checkbox-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)}.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:1.25rem;height:1.25rem;border:.125rem solid var(--surface-border);border-radius:var(--border-radius);background:var(--surface-card);transition:all var(--animation-duration-fast) var(--animation-easing-default);color:#fff;position:relative;flex-shrink:0}:host.checkbox-checked .checkbox-control,:host.checkbox-indeterminate .checkbox-control{background:var(--primary-color);border-color:var(--primary-color)}.checkbox-input:focus-visible+.checkbox-control{outline:.125rem solid var(--focus-outline-color);outline-offset:.125rem}.checkbox-control i{font-size:.875rem;line-height:1}:host.checkbox-small .checkbox-control{width:1rem;height:1rem}:host.checkbox-large .checkbox-control{width:1.5rem;height:1.5rem}:host.checkbox-error .checkbox-control{border-color:var(--danger-color)}:host.checkbox-error.checkbox-checked .checkbox-control{background:var(--danger-color)}:host.checkbox-success .checkbox-control{border-color:var(--success-color)}:host.checkbox-success.checkbox-checked .checkbox-control{background:var(--success-color)}:host.checkbox-disabled{opacity:var(--opacity-disabled);cursor:not-allowed}:host.checkbox-disabled .checkbox-label{cursor:not-allowed}.checkbox-message{font-size:.875rem}.checkbox-message.error{color:var(--danger-color)}.checkbox-message.success{color:var(--success-color)}\n"] }]
1318
- }], 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"] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }, { type: i0.Output, args: ["indeterminateChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], 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 }] }] } });
1326
+ }, template: "<div class=\"checkbox-container\">\r\n <label class=\"checkbox-label\" [class.label-left]=\"labelPosition() === 'left'\">\r\n <input\r\n #checkboxInput\r\n type=\"checkbox\"\r\n class=\"checkbox-input\"\r\n [checked]=\"checked()\"\r\n [disabled]=\"disabled()\"\r\n [required]=\"required()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-checked]=\"ariaChecked()\"\r\n [attr.aria-required]=\"required() || null\"\r\n [attr.aria-invalid]=\"error() ? 'true' : null\"\r\n [attr.aria-describedby]=\"ariaDescribedBy()\"\r\n (change)=\"toggle()\"\r\n (keydown)=\"handleKeydown($event)\"\r\n />\r\n \r\n <span class=\"checkbox-control\">\r\n @if (checked() && !indeterminate()) {\r\n <i class=\"ph ph-check\" aria-hidden=\"true\"></i>\r\n }\r\n @if (indeterminate()) {\r\n <i class=\"ph ph-minus\" aria-hidden=\"true\"></i>\r\n }\r\n </span>\r\n\r\n <span class=\"checkbox-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]=\"uniqueId + '-error'\" class=\"checkbox-message error\" role=\"alert\">\r\n {{ error() }}\r\n </div>\r\n } @else if (success()) {\r\n <div [id]=\"uniqueId + '-success'\" class=\"checkbox-message success\" role=\"status\">\r\n {{ success() }}\r\n </div>\r\n }\r\n</div>", styles: [":host{display:inline-flex;flex-direction:column}.checkbox-container{display:flex;flex-direction:column;gap:var(--form-field-gap)}.checkbox-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)}.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:1.25rem;height:1.25rem;border:.125rem solid var(--surface-border);border-radius:var(--border-radius);background:var(--surface-card);transition:all var(--animation-duration-fast) var(--animation-easing-default);color:#fff;position:relative;flex-shrink:0}:host.checkbox-checked .checkbox-control,:host.checkbox-indeterminate .checkbox-control{background:var(--primary-color);border-color:var(--primary-color)}.checkbox-input:focus-visible+.checkbox-control{outline:.125rem solid var(--focus-outline-color);outline-offset:.125rem}.checkbox-control i{font-size:.875rem;line-height:1}:host.checkbox-small .checkbox-control{width:1rem;height:1rem}:host.checkbox-large .checkbox-control{width:1.5rem;height:1.5rem}:host.checkbox-error .checkbox-control{border-color:var(--danger-color)}:host.checkbox-error.checkbox-checked .checkbox-control{background:var(--danger-color)}:host.checkbox-success .checkbox-control{border-color:var(--success-color)}:host.checkbox-success.checkbox-checked .checkbox-control{background:var(--success-color)}:host.checkbox-disabled{opacity:var(--opacity-disabled);cursor:not-allowed}:host.checkbox-disabled .checkbox-label{cursor:not-allowed}.checkbox-message{font-size:.875rem}.checkbox-message.error{color:var(--danger-color)}.checkbox-message.success{color:var(--success-color)}\n"] }]
1327
+ }], propDecorators: { checkboxInput: [{ type: i0.ViewChild, args: ['checkboxInput', { isSignal: true }] }], labelSlot: [{ type: i0.ViewChild, args: ['labelSlot', { 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"] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }, { type: i0.Output, args: ["indeterminateChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], 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 }] }] } });
1319
1328
 
1320
1329
  class PshCollapseComponent {
1321
1330
  constructor() {
@@ -1325,14 +1334,18 @@ class PshCollapseComponent {
1325
1334
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
1326
1335
  this.variant = input('default', { ...(ngDevMode ? { debugName: "variant" } : /* istanbul ignore next */ {}), transform: (value) => {
1327
1336
  if (!['default', 'outline'].includes(value)) {
1328
- console.warn(`[psh-collapse] Invalid variant "${value}", falling back to "default"`);
1337
+ if (isDevMode()) {
1338
+ console.warn(`[psh-collapse] Invalid variant "${value}", falling back to "default"`);
1339
+ }
1329
1340
  return 'default';
1330
1341
  }
1331
1342
  return value;
1332
1343
  } });
1333
1344
  this.size = input('medium', { ...(ngDevMode ? { debugName: "size" } : /* istanbul ignore next */ {}), transform: (value) => {
1334
1345
  if (!['small', 'medium', 'large'].includes(value)) {
1335
- console.warn(`[psh-collapse] Invalid size "${value}", falling back to "medium"`);
1346
+ if (isDevMode()) {
1347
+ console.warn(`[psh-collapse] Invalid size "${value}", falling back to "medium"`);
1348
+ }
1336
1349
  return 'medium';
1337
1350
  }
1338
1351
  return value;
@@ -3890,11 +3903,13 @@ class PshRadioComponent {
3890
3903
  // so aria-describedby references whichever message is actually rendered.
3891
3904
  this.ariaDescribedBy = computed(() => this.errorMessageId() ?? this.successMessageId(), /* @ts-ignore */
3892
3905
  ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : /* istanbul ignore next */ []));
3893
- effect(() => {
3894
- if (!this.label() && !this.ariaLabel() && !this.hasProjectedContent()) {
3895
- console.warn('[psh-radio] No accessible label provided. Please use label input, ariaLabel input, or projected content.');
3896
- }
3897
- }, { allowSignalWrites: false });
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
+ });
3912
+ }
3898
3913
  }
3899
3914
  getState() {
3900
3915
  if (this.disabled())
@@ -3905,6 +3920,11 @@ class PshRadioComponent {
3905
3920
  return 'success';
3906
3921
  return this.checked() ? 'checked' : 'unchecked';
3907
3922
  }
3923
+ /**
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.
3927
+ */
3908
3928
  updateProjectedContent(hasContent) {
3909
3929
  this.hasProjectedContent.set(hasContent);
3910
3930
  }