@compsych-ui-components/angular 7.0.13 → 7.0.15

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.
@@ -100,6 +100,8 @@ export class TextInputComponent {
100
100
  disabled = false;
101
101
  /** Makes the input read-only — focusable and copyable but not editable. Value is still submitted with the form. Supports boolean coercion. */
102
102
  readonly = false;
103
+ /** Visually hides the label while keeping it accessible to screen readers. Supports boolean coercion. */
104
+ hideLabel = false;
103
105
  /** Lucide icon name rendered inside the input before the text (e.g. `'search'`). */
104
106
  leadingIcon;
105
107
  /** Lucide icon name rendered inside the input after the text (e.g. `'eye'`). */
@@ -196,9 +198,9 @@ export class TextInputComponent {
196
198
  }
197
199
  }
198
200
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TextInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
199
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: TextInputComponent, isStandalone: true, selector: "compsych-text-input", inputs: { label: "label", placeholder: "placeholder", value: "value", type: "type", size: "size", name: "name", id: "id", ariaDescribedBy: "ariaDescribedBy", supportingText: "supportingText", errorMessage: "errorMessage", errorMessages: "errorMessages", labelButton: "labelButton", required: ["required", "required", booleanAttribute], error: ["error", "error", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], readonly: ["readonly", "readonly", booleanAttribute], leadingIcon: "leadingIcon", trailingIcon: "trailingIcon", trailingIconAriaLabel: "trailingIconAriaLabel" }, outputs: { valueChange: "valueChange", labelButtonClick: "labelButtonClick", trailingIconClick: "trailingIconClick" }, usesOnChanges: true, ngImport: i0, template: `
201
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: TextInputComponent, isStandalone: true, selector: "compsych-text-input", inputs: { label: "label", placeholder: "placeholder", value: "value", type: "type", size: "size", name: "name", id: "id", ariaDescribedBy: "ariaDescribedBy", supportingText: "supportingText", errorMessage: "errorMessage", errorMessages: "errorMessages", labelButton: "labelButton", required: ["required", "required", booleanAttribute], error: ["error", "error", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], readonly: ["readonly", "readonly", booleanAttribute], hideLabel: ["hideLabel", "hideLabel", booleanAttribute], leadingIcon: "leadingIcon", trailingIcon: "trailingIcon", trailingIconAriaLabel: "trailingIconAriaLabel" }, outputs: { valueChange: "valueChange", labelButtonClick: "labelButtonClick", trailingIconClick: "trailingIconClick" }, usesOnChanges: true, ngImport: i0, template: `
200
202
  <div [class]="wrapperClasses">
201
- <div class="label-row">
203
+ <div class="label-row" [class.sr-only]="hideLabel">
202
204
  <label class="label" [for]="inputId">
203
205
  {{ label }}
204
206
  @if (isRequired) {
@@ -254,13 +256,13 @@ export class TextInputComponent {
254
256
  </span>
255
257
  }
256
258
  </div>
257
- `, isInline: true, styles: [":host{display:block}.cmp-text-field{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4)}.label-row{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.label{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface)}.required-marker{color:var(--accent-primary-sys-primary)}.cmp-text-field.disabled .label{opacity:.48}.input-wrapper{position:relative}.input-icon{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;pointer-events:none;color:var(--surface-surface-sys-on-surface-variant)}.input-icon--leading{left:var(--spacing-padding-sys-padding-12)}.input-icon--trailing{right:var(--spacing-padding-sys-padding-12)}.cmp-text-field.medium .input-icon--leading,.cmp-text-field.large .input-icon--leading{left:var(--spacing-padding-sys-padding-16)}.cmp-text-field.medium .input-icon--trailing,.cmp-text-field.large .input-icon--trailing{right:var(--spacing-padding-sys-padding-16)}.input-icon--btn{background:none;border:none;margin:0;padding:0;cursor:pointer;pointer-events:auto;border-radius:var(--border-radius-sys-radius-xs, 2px)}.input-icon--btn:focus-visible{outline:2px solid var(--accent-primary-sys-primary);outline-offset:2px}.input-icon--btn:disabled{cursor:not-allowed}.cmp-text-field.disabled .input-icon{opacity:.48}.input{box-sizing:border-box;width:100%;margin:0;border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-sm);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);font-weight:var(--body-medium-sys-font-weight);line-height:var(--body-medium-sys-line-height);appearance:none;-webkit-appearance:none;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}.input::placeholder{color:var(--surface-surface-sys-on-surface-variant)}.input:hover:not(:disabled):not(:focus){border-color:var(--outline-sys-outline-variant);box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.input:focus{border-color:var(--accent-primary-sys-primary);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input.error:not(:disabled):not(:focus){border-color:var(--error-sys-error)}.input.error:focus{border-color:var(--error-sys-error);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input:disabled{background-color:var(--surface-surface-container-sys-surface-container-highest);color:var(--surface-surface-sys-on-surface-variant);border-color:var(--outline-sys-outline);cursor:not-allowed;opacity:.48}.supporting-text{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface-variant)}.supporting-text--error{color:var(--error-sys-error)}.cmp-text-field.disabled .supporting-text{opacity:.48}.cmp-text-field.small .input{height:44px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-12);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.cmp-text-field.medium .input{height:52px;padding:var(--spacing-padding-sys-padding-12) var(--spacing-padding-sys-padding-16);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.cmp-text-field.large .input{height:64px;padding:var(--spacing-padding-sys-padding-20) var(--spacing-padding-sys-padding-16);font-size:var(--body-large-sys-font-size);line-height:var(--body-large-sys-line-height)}.cmp-text-field.small .input.has-leading-icon{padding-left:calc(var(--spacing-padding-sys-padding-12) + 28px)}.cmp-text-field.small .input.has-trailing-icon{padding-right:calc(var(--spacing-padding-sys-padding-12) + 28px)}.cmp-text-field.medium .input.has-leading-icon,.cmp-text-field.large .input.has-leading-icon{padding-left:calc(var(--spacing-padding-sys-padding-16) + 28px)}.cmp-text-field.medium .input.has-trailing-icon,.cmp-text-field.large .input.has-trailing-icon{padding-right:calc(var(--spacing-padding-sys-padding-16) + 28px)}@media(forced-colors:active){.input{border:1px solid ButtonText;forced-color-adjust:none}.input:focus{outline:2px solid Highlight;outline-offset:2px}.input:disabled{color:GrayText;border-color:GrayText;background:ButtonFace;opacity:1}.cmp-text-field.disabled .label{color:GrayText;opacity:1}.supporting-text--error{color:LinkText}}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "compsych-button", inputs: ["label", "variant", "size", "leadingIcon", "trailingIcon", "type", "ariaLabel", "iconOnly", "disabled", "loading", "fullWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
259
+ `, isInline: true, styles: [":host{display:block}.cmp-text-field{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4)}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.label-row{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.label{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface)}.required-marker{color:var(--accent-primary-sys-primary)}.cmp-text-field.disabled .label{opacity:.48}.input-wrapper{position:relative}.input-icon{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;pointer-events:none;color:var(--surface-surface-sys-on-surface-variant)}.input-icon--leading{left:var(--spacing-padding-sys-padding-12)}.input-icon--trailing{right:var(--spacing-padding-sys-padding-12)}.cmp-text-field.medium .input-icon--leading,.cmp-text-field.large .input-icon--leading{left:var(--spacing-padding-sys-padding-16)}.cmp-text-field.medium .input-icon--trailing,.cmp-text-field.large .input-icon--trailing{right:var(--spacing-padding-sys-padding-16)}.input-icon--btn{background:none;border:none;margin:0;padding:0;cursor:pointer;pointer-events:auto;border-radius:var(--border-radius-sys-radius-xs, 2px)}.input-icon--btn:focus-visible{outline:2px solid var(--accent-primary-sys-primary);outline-offset:2px}.input-icon--btn:disabled{cursor:not-allowed}.cmp-text-field.disabled .input-icon{opacity:.48}.input{box-sizing:border-box;width:100%;margin:0;border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-sm);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);font-weight:var(--body-medium-sys-font-weight);line-height:var(--body-medium-sys-line-height);appearance:none;-webkit-appearance:none;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}.input::placeholder{color:var(--surface-surface-sys-on-surface-variant)}.input:hover:not(:disabled):not(:focus){border-color:var(--outline-sys-outline-variant);box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.input:focus{border-color:var(--accent-primary-sys-primary);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input.error:not(:disabled):not(:focus){border-color:var(--error-sys-error)}.input.error:focus{border-color:var(--error-sys-error);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input:disabled{background-color:var(--surface-surface-container-sys-surface-container-highest);color:var(--surface-surface-sys-on-surface-variant);border-color:var(--outline-sys-outline);cursor:not-allowed;opacity:.48}.supporting-text{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface-variant)}.supporting-text--error{color:var(--error-sys-error)}.cmp-text-field.disabled .supporting-text{opacity:.48}.cmp-text-field.small .input{height:44px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-12);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.cmp-text-field.medium .input{height:52px;padding:var(--spacing-padding-sys-padding-12) var(--spacing-padding-sys-padding-16);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.cmp-text-field.large .input{height:64px;padding:var(--spacing-padding-sys-padding-20) var(--spacing-padding-sys-padding-16);font-size:var(--body-large-sys-font-size);line-height:var(--body-large-sys-line-height)}.cmp-text-field.small .input.has-leading-icon{padding-left:calc(var(--spacing-padding-sys-padding-12) + 28px)}.cmp-text-field.small .input.has-trailing-icon{padding-right:calc(var(--spacing-padding-sys-padding-12) + 28px)}.cmp-text-field.medium .input.has-leading-icon,.cmp-text-field.large .input.has-leading-icon{padding-left:calc(var(--spacing-padding-sys-padding-16) + 28px)}.cmp-text-field.medium .input.has-trailing-icon,.cmp-text-field.large .input.has-trailing-icon{padding-right:calc(var(--spacing-padding-sys-padding-16) + 28px)}@media(forced-colors:active){.input{border:1px solid ButtonText;forced-color-adjust:none}.input:focus{outline:2px solid Highlight;outline-offset:2px}.input:disabled{color:GrayText;border-color:GrayText;background:ButtonFace;opacity:1}.cmp-text-field.disabled .label{color:GrayText;opacity:1}.supporting-text--error{color:LinkText}}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "compsych-button", inputs: ["label", "variant", "size", "leadingIcon", "trailingIcon", "type", "ariaLabel", "iconOnly", "disabled", "loading", "fullWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
258
260
  }
259
261
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TextInputComponent, decorators: [{
260
262
  type: Component,
261
263
  args: [{ selector: 'compsych-text-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ButtonComponent], template: `
262
264
  <div [class]="wrapperClasses">
263
- <div class="label-row">
265
+ <div class="label-row" [class.sr-only]="hideLabel">
264
266
  <label class="label" [for]="inputId">
265
267
  {{ label }}
266
268
  @if (isRequired) {
@@ -316,7 +318,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
316
318
  </span>
317
319
  }
318
320
  </div>
319
- `, styles: [":host{display:block}.cmp-text-field{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4)}.label-row{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.label{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface)}.required-marker{color:var(--accent-primary-sys-primary)}.cmp-text-field.disabled .label{opacity:.48}.input-wrapper{position:relative}.input-icon{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;pointer-events:none;color:var(--surface-surface-sys-on-surface-variant)}.input-icon--leading{left:var(--spacing-padding-sys-padding-12)}.input-icon--trailing{right:var(--spacing-padding-sys-padding-12)}.cmp-text-field.medium .input-icon--leading,.cmp-text-field.large .input-icon--leading{left:var(--spacing-padding-sys-padding-16)}.cmp-text-field.medium .input-icon--trailing,.cmp-text-field.large .input-icon--trailing{right:var(--spacing-padding-sys-padding-16)}.input-icon--btn{background:none;border:none;margin:0;padding:0;cursor:pointer;pointer-events:auto;border-radius:var(--border-radius-sys-radius-xs, 2px)}.input-icon--btn:focus-visible{outline:2px solid var(--accent-primary-sys-primary);outline-offset:2px}.input-icon--btn:disabled{cursor:not-allowed}.cmp-text-field.disabled .input-icon{opacity:.48}.input{box-sizing:border-box;width:100%;margin:0;border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-sm);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);font-weight:var(--body-medium-sys-font-weight);line-height:var(--body-medium-sys-line-height);appearance:none;-webkit-appearance:none;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}.input::placeholder{color:var(--surface-surface-sys-on-surface-variant)}.input:hover:not(:disabled):not(:focus){border-color:var(--outline-sys-outline-variant);box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.input:focus{border-color:var(--accent-primary-sys-primary);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input.error:not(:disabled):not(:focus){border-color:var(--error-sys-error)}.input.error:focus{border-color:var(--error-sys-error);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input:disabled{background-color:var(--surface-surface-container-sys-surface-container-highest);color:var(--surface-surface-sys-on-surface-variant);border-color:var(--outline-sys-outline);cursor:not-allowed;opacity:.48}.supporting-text{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface-variant)}.supporting-text--error{color:var(--error-sys-error)}.cmp-text-field.disabled .supporting-text{opacity:.48}.cmp-text-field.small .input{height:44px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-12);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.cmp-text-field.medium .input{height:52px;padding:var(--spacing-padding-sys-padding-12) var(--spacing-padding-sys-padding-16);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.cmp-text-field.large .input{height:64px;padding:var(--spacing-padding-sys-padding-20) var(--spacing-padding-sys-padding-16);font-size:var(--body-large-sys-font-size);line-height:var(--body-large-sys-line-height)}.cmp-text-field.small .input.has-leading-icon{padding-left:calc(var(--spacing-padding-sys-padding-12) + 28px)}.cmp-text-field.small .input.has-trailing-icon{padding-right:calc(var(--spacing-padding-sys-padding-12) + 28px)}.cmp-text-field.medium .input.has-leading-icon,.cmp-text-field.large .input.has-leading-icon{padding-left:calc(var(--spacing-padding-sys-padding-16) + 28px)}.cmp-text-field.medium .input.has-trailing-icon,.cmp-text-field.large .input.has-trailing-icon{padding-right:calc(var(--spacing-padding-sys-padding-16) + 28px)}@media(forced-colors:active){.input{border:1px solid ButtonText;forced-color-adjust:none}.input:focus{outline:2px solid Highlight;outline-offset:2px}.input:disabled{color:GrayText;border-color:GrayText;background:ButtonFace;opacity:1}.cmp-text-field.disabled .label{color:GrayText;opacity:1}.supporting-text--error{color:LinkText}}\n"] }]
321
+ `, styles: [":host{display:block}.cmp-text-field{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4)}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.label-row{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.label{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface)}.required-marker{color:var(--accent-primary-sys-primary)}.cmp-text-field.disabled .label{opacity:.48}.input-wrapper{position:relative}.input-icon{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;pointer-events:none;color:var(--surface-surface-sys-on-surface-variant)}.input-icon--leading{left:var(--spacing-padding-sys-padding-12)}.input-icon--trailing{right:var(--spacing-padding-sys-padding-12)}.cmp-text-field.medium .input-icon--leading,.cmp-text-field.large .input-icon--leading{left:var(--spacing-padding-sys-padding-16)}.cmp-text-field.medium .input-icon--trailing,.cmp-text-field.large .input-icon--trailing{right:var(--spacing-padding-sys-padding-16)}.input-icon--btn{background:none;border:none;margin:0;padding:0;cursor:pointer;pointer-events:auto;border-radius:var(--border-radius-sys-radius-xs, 2px)}.input-icon--btn:focus-visible{outline:2px solid var(--accent-primary-sys-primary);outline-offset:2px}.input-icon--btn:disabled{cursor:not-allowed}.cmp-text-field.disabled .input-icon{opacity:.48}.input{box-sizing:border-box;width:100%;margin:0;border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-sm);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);font-weight:var(--body-medium-sys-font-weight);line-height:var(--body-medium-sys-line-height);appearance:none;-webkit-appearance:none;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}.input::placeholder{color:var(--surface-surface-sys-on-surface-variant)}.input:hover:not(:disabled):not(:focus){border-color:var(--outline-sys-outline-variant);box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.input:focus{border-color:var(--accent-primary-sys-primary);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input.error:not(:disabled):not(:focus){border-color:var(--error-sys-error)}.input.error:focus{border-color:var(--error-sys-error);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input:disabled{background-color:var(--surface-surface-container-sys-surface-container-highest);color:var(--surface-surface-sys-on-surface-variant);border-color:var(--outline-sys-outline);cursor:not-allowed;opacity:.48}.supporting-text{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface-variant)}.supporting-text--error{color:var(--error-sys-error)}.cmp-text-field.disabled .supporting-text{opacity:.48}.cmp-text-field.small .input{height:44px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-12);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.cmp-text-field.medium .input{height:52px;padding:var(--spacing-padding-sys-padding-12) var(--spacing-padding-sys-padding-16);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.cmp-text-field.large .input{height:64px;padding:var(--spacing-padding-sys-padding-20) var(--spacing-padding-sys-padding-16);font-size:var(--body-large-sys-font-size);line-height:var(--body-large-sys-line-height)}.cmp-text-field.small .input.has-leading-icon{padding-left:calc(var(--spacing-padding-sys-padding-12) + 28px)}.cmp-text-field.small .input.has-trailing-icon{padding-right:calc(var(--spacing-padding-sys-padding-12) + 28px)}.cmp-text-field.medium .input.has-leading-icon,.cmp-text-field.large .input.has-leading-icon{padding-left:calc(var(--spacing-padding-sys-padding-16) + 28px)}.cmp-text-field.medium .input.has-trailing-icon,.cmp-text-field.large .input.has-trailing-icon{padding-right:calc(var(--spacing-padding-sys-padding-16) + 28px)}@media(forced-colors:active){.input{border:1px solid ButtonText;forced-color-adjust:none}.input:focus{outline:2px solid Highlight;outline-offset:2px}.input:disabled{color:GrayText;border-color:GrayText;background:ButtonFace;opacity:1}.cmp-text-field.disabled .label{color:GrayText;opacity:1}.supporting-text--error{color:LinkText}}\n"] }]
320
322
  }], ctorParameters: () => [], propDecorators: { label: [{
321
323
  type: Input
322
324
  }], placeholder: [{
@@ -353,6 +355,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
353
355
  }], readonly: [{
354
356
  type: Input,
355
357
  args: [{ transform: booleanAttribute }]
358
+ }], hideLabel: [{
359
+ type: Input,
360
+ args: [{ transform: booleanAttribute }]
356
361
  }], leadingIcon: [{
357
362
  type: Input
358
363
  }], trailingIcon: [{
@@ -1 +1 @@
1
- {"version":3,"file":"text-input.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/text-input/text-input.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n EventEmitter,\r\n Input,\r\n OnChanges,\r\n Output,\r\n booleanAttribute,\r\n inject,\r\n} from '@angular/core';\r\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\r\nimport { ControlValueAccessor, NgControl, Validators } from '@angular/forms';\r\nimport { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';\r\nimport { ButtonComponent, ButtonVariant } from '../button/button';\r\n\r\n/** Size variant of a `compsych-text-input`. Controls input height, padding, and font size. */\r\nexport type TextInputSize = 'small' | 'medium' | 'large';\r\n\r\n/** Native input type. */\r\nexport type TextInputType = 'text' | 'password';\r\n\r\n/** Configuration for the optional button rendered at the right of the label row. */\r\nexport interface TextInputLabelButton {\r\n /** Stable identifier emitted by `labelButtonClick` when clicked. */\r\n id: string;\r\n /** Text displayed on the button. */\r\n label: string;\r\n /** Visual style of the button. Defaults to `'text'`. */\r\n variant?: ButtonVariant;\r\n /** Lucide icon name shown before the label. */\r\n leadingIcon?: string;\r\n /** Lucide icon name shown after the label. */\r\n trailingIcon?: string;\r\n /** Hides the label and renders a square icon-only button. Requires `leadingIcon`. */\r\n iconOnly?: boolean;\r\n}\r\n\r\nlet nextId = 0;\r\n\r\n/**\r\n * A labelled text input that wraps a native `<input>` element.\r\n * Implements `ControlValueAccessor` so it works with both reactive forms\r\n * (`formControlName`, `[formControl]`) and template-driven forms (`ngModel`).\r\n * The error visual state is driven automatically by the bound `FormControl`'s\r\n * validity once the field has been touched, and can also be set explicitly via\r\n * the `error` input for use outside of Angular forms.\r\n */\r\n@Component({\r\n selector: 'compsych-text-input',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [ButtonComponent],\r\n template: `\r\n <div [class]=\"wrapperClasses\">\r\n <div class=\"label-row\">\r\n <label class=\"label\" [for]=\"inputId\">\r\n {{ label }}\r\n @if (isRequired) {\r\n <span class=\"required-marker\" aria-hidden=\"true\"> *</span>\r\n }\r\n </label>\r\n @if (parsedLabelButton) {\r\n <compsych-button\r\n [label]=\"parsedLabelButton.label\"\r\n [variant]=\"parsedLabelButton.variant ?? 'text'\"\r\n size=\"small\"\r\n [leadingIcon]=\"parsedLabelButton.leadingIcon\"\r\n [trailingIcon]=\"parsedLabelButton.trailingIcon\"\r\n [iconOnly]=\"!!parsedLabelButton.iconOnly\"\r\n [disabled]=\"disabled\"\r\n (click)=\"onLabelButtonClick()\"\r\n />\r\n }\r\n </div>\r\n <div class=\"input-wrapper\">\r\n @if (leadingIconSvg) {\r\n <span class=\"input-icon input-icon--leading\" aria-hidden=\"true\" [innerHTML]=\"leadingIconSvg\"></span>\r\n }\r\n <input\r\n class=\"input\"\r\n [class.has-leading-icon]=\"leadingIcon\"\r\n [class.has-trailing-icon]=\"trailingIcon\"\r\n [id]=\"inputId\"\r\n [name]=\"name || inputId\"\r\n [type]=\"type\"\r\n [placeholder]=\"placeholder\"\r\n [value]=\"value\"\r\n [disabled]=\"disabled\"\r\n [readOnly]=\"readonly\"\r\n [attr.required]=\"isRequired ? '' : null\"\r\n [class.error]=\"hasError\"\r\n [attr.aria-invalid]=\"hasError ? 'true' : null\"\r\n [attr.aria-describedby]=\"describedBy\"\r\n (input)=\"onInput($event)\"\r\n (blur)=\"onBlur()\"\r\n />\r\n @if (trailingIconSvg) {\r\n @if (trailingIconClick.observed) {\r\n <button class=\"input-icon input-icon--trailing input-icon--btn\" type=\"button\" [disabled]=\"disabled\" [attr.aria-label]=\"trailingIconAriaLabel || null\" [innerHTML]=\"trailingIconSvg\" (click)=\"trailingIconClick.emit()\"></button>\r\n } @else {\r\n <span class=\"input-icon input-icon--trailing\" aria-hidden=\"true\" [innerHTML]=\"trailingIconSvg\"></span>\r\n }\r\n }\r\n </div>\r\n @if (displayedSupportingText) {\r\n <span class=\"supporting-text\" [class.supporting-text--error]=\"hasError\" [id]=\"inputId + '-supporting'\">\r\n {{ displayedSupportingText }}\r\n </span>\r\n }\r\n </div>\r\n `,\r\n styleUrl: './text-input.css',\r\n})\r\nexport class TextInputComponent implements ControlValueAccessor, OnChanges {\r\n private readonly _autoId = `cmp-text-input-${++nextId}`;\r\n private readonly cdr = inject(ChangeDetectorRef);\r\n private readonly sanitizer = inject(DomSanitizer);\r\n private _onChange: (value: string) => void = () => {};\r\n private _onTouched: () => void = () => {};\r\n\r\n /** Injected when the component is used inside a reactive or template-driven form. */\r\n protected readonly ngControl: NgControl | null;\r\n\r\n constructor() {\r\n const ctrl = inject(NgControl, { optional: true, self: true });\r\n if (ctrl) ctrl.valueAccessor = this;\r\n this.ngControl = ctrl;\r\n }\r\n\r\n /** Label displayed above the input. */\r\n @Input() label = '';\r\n /** Placeholder text shown inside the input when empty. */\r\n @Input() placeholder = '';\r\n /** Current value of the input. When used in a form, the form controls the value. */\r\n @Input() value = '';\r\n /** Native input type. Defaults to `'text'`. Use `'password'` to mask the value. */\r\n @Input() type: TextInputType = 'text';\r\n /** Size variant — controls height, padding, and font size. Defaults to `'medium'`. */\r\n @Input() size: TextInputSize = 'medium';\r\n /** Native `name` attribute forwarded to the `<input>`. Defaults to the auto-generated id. */\r\n @Input() name?: string;\r\n /** Native `id` attribute for the `<input>`. Auto-generated if omitted. */\r\n @Input() id?: string;\r\n /**\r\n * Id(s) of external elements that describe the input, space-separated.\r\n * Forwarded to `aria-describedby` on the native `<input>`.\r\n */\r\n @Input() ariaDescribedBy?: string;\r\n /** Text displayed below the input. Shown in red when the input is in error state. */\r\n @Input() supportingText = '';\r\n /**\r\n * Static error text shown in red when the input is in error state.\r\n * Used when the input is outside a form (with `[error]=\"true\"`).\r\n * When inside a form, prefer `errorMessages` for per-validator messages.\r\n */\r\n @Input() errorMessage = '';\r\n /**\r\n * Map of Angular validator error keys to display strings.\r\n * The component reads `ngControl.errors` to determine which validator failed\r\n * and shows the matching message in red below the input.\r\n * Example: `{ required: 'Username is required', minlength: 'Min 8 characters' }`\r\n */\r\n @Input() errorMessages: Record<string, string> = {};\r\n\r\n private _labelButton: TextInputLabelButton | null = null;\r\n\r\n /**\r\n * JSON string configuring the optional button at the right of the label row.\r\n * Hidden when empty or omitted. Example: `'{\"id\":\"show\",\"label\":\"Show\"}'`\r\n * Clicking emits the button's `id` via the `labelButtonClick` output.\r\n */\r\n @Input() set labelButton(json: string) {\r\n if (!json) { this._labelButton = null; return; }\r\n try {\r\n this._labelButton = JSON.parse(json) as TextInputLabelButton;\r\n } catch {\r\n this._labelButton = null;\r\n }\r\n }\r\n\r\n /** @internal Template access to the parsed label button config. */\r\n protected get parsedLabelButton(): TextInputLabelButton | null {\r\n return this._labelButton;\r\n }\r\n\r\n /**\r\n * Adds a blue asterisk after the label. When used in a form, this is set\r\n * automatically if the bound `FormControl` has `Validators.required`.\r\n * Can also be set explicitly for non-form use. Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) required = false;\r\n /**\r\n * Applies the error visual state (red border) explicitly. When used in a form,\r\n * the error state is driven automatically from the `FormControl`'s validity once\r\n * the field is touched — this input is only needed outside of forms.\r\n * Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) error = false;\r\n /** Disables the input. When used in a form, also responds to `FormControl.disable()`. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) disabled = false;\r\n /** Makes the input read-only — focusable and copyable but not editable. Value is still submitted with the form. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) readonly = false;\r\n /** Lucide icon name rendered inside the input before the text (e.g. `'search'`). */\r\n @Input() leadingIcon?: string;\r\n /** Lucide icon name rendered inside the input after the text (e.g. `'eye'`). */\r\n @Input() trailingIcon?: string;\r\n /** Accessible label for the trailing icon button. Required when `trailingIconClick` is used. */\r\n @Input() trailingIconAriaLabel = '';\r\n\r\n protected leadingIconSvg: SafeHtml | null = null;\r\n protected trailingIconSvg: SafeHtml | null = null;\r\n\r\n ngOnChanges(): void {\r\n this.leadingIconSvg = this.leadingIcon ? this.buildIconSvg(this.leadingIcon) : null;\r\n this.trailingIconSvg = this.trailingIcon ? this.buildIconSvg(this.trailingIcon) : null;\r\n }\r\n\r\n private buildIconSvg(name: string): SafeHtml {\r\n const svg = (LUCIDE_ICONS[name] ?? '').replace('<svg ', '<svg width=\"20\" height=\"20\" ');\r\n return this.sanitizer.bypassSecurityTrustHtml(svg);\r\n }\r\n\r\n /** Emits the new string value whenever the user types. For use outside Angular forms; inside forms use the `FormControl` value stream. */\r\n @Output() readonly valueChange = new EventEmitter<string>();\r\n /** Emits the label button's `id` when it is clicked. */\r\n @Output() readonly labelButtonClick = new EventEmitter<string>();\r\n /** Emitted when the trailing icon button is clicked. When observed, the trailing icon renders as an interactive button. */\r\n @Output() readonly trailingIconClick = new EventEmitter<void>();\r\n\r\n get inputId(): string {\r\n return this.id ?? this._autoId;\r\n }\r\n\r\n /** True when the bound FormControl is invalid and touched, or when `error` is explicitly set. */\r\n get hasError(): boolean {\r\n return this.error || !!(this.ngControl?.invalid && this.ngControl.touched);\r\n }\r\n\r\n /** True when `required` is set explicitly, or when the bound FormControl has Validators.required. */\r\n get isRequired(): boolean {\r\n return this.required || !!(this.ngControl?.control?.hasValidator(Validators.required));\r\n }\r\n\r\n private get activeErrorMessage(): string {\r\n const errors = this.ngControl?.errors;\r\n if (!errors) return '';\r\n for (const key of Object.keys(errors)) {\r\n if (this.errorMessages[key]) return this.errorMessages[key];\r\n }\r\n return '';\r\n }\r\n\r\n get displayedSupportingText(): string {\r\n if (this.hasError) {\r\n return this.activeErrorMessage || this.errorMessage || this.supportingText;\r\n }\r\n return this.supportingText;\r\n }\r\n\r\n get describedBy(): string | null {\r\n const parts: string[] = [];\r\n if (this.ariaDescribedBy) parts.push(this.ariaDescribedBy);\r\n if (this.supportingText || this.errorMessage || Object.keys(this.errorMessages).length) {\r\n parts.push(this.inputId + '-supporting');\r\n }\r\n return parts.length ? parts.join(' ') : null;\r\n }\r\n\r\n get wrapperClasses(): string {\r\n return ['cmp-text-field', this.size, this.disabled ? 'disabled' : '']\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n // ── ControlValueAccessor ────────────────────────────────────────────────────\r\n\r\n writeValue(value: string | null): void {\r\n this.value = value ?? '';\r\n this.cdr.markForCheck();\r\n }\r\n\r\n registerOnChange(fn: (value: string) => void): void {\r\n this._onChange = fn;\r\n }\r\n\r\n registerOnTouched(fn: () => void): void {\r\n this._onTouched = fn;\r\n }\r\n\r\n /** Called by Angular when the bound FormControl is programmatically disabled or enabled. */\r\n setDisabledState(isDisabled: boolean): void {\r\n this.disabled = isDisabled;\r\n this.cdr.markForCheck();\r\n }\r\n\r\n // ── Event handlers ──────────────────────────────────────────────────────────\r\n\r\n protected onInput(event: Event): void {\r\n const value = (event.target as HTMLInputElement).value;\r\n this.value = value;\r\n this._onChange(value);\r\n this.valueChange.emit(value);\r\n }\r\n\r\n protected onBlur(): void {\r\n this._onTouched();\r\n this.cdr.markForCheck();\r\n }\r\n\r\n protected onLabelButtonClick(): void {\r\n if (this._labelButton) {\r\n this.labelButtonClick.emit(this._labelButton.id);\r\n }\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"text-input.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/text-input/text-input.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n EventEmitter,\r\n Input,\r\n OnChanges,\r\n Output,\r\n booleanAttribute,\r\n inject,\r\n} from '@angular/core';\r\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\r\nimport { ControlValueAccessor, NgControl, Validators } from '@angular/forms';\r\nimport { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';\r\nimport { ButtonComponent, ButtonVariant } from '../button/button';\r\n\r\n/** Size variant of a `compsych-text-input`. Controls input height, padding, and font size. */\r\nexport type TextInputSize = 'small' | 'medium' | 'large';\r\n\r\n/** Native input type. */\r\nexport type TextInputType = 'text' | 'password';\r\n\r\n/** Configuration for the optional button rendered at the right of the label row. */\r\nexport interface TextInputLabelButton {\r\n /** Stable identifier emitted by `labelButtonClick` when clicked. */\r\n id: string;\r\n /** Text displayed on the button. */\r\n label: string;\r\n /** Visual style of the button. Defaults to `'text'`. */\r\n variant?: ButtonVariant;\r\n /** Lucide icon name shown before the label. */\r\n leadingIcon?: string;\r\n /** Lucide icon name shown after the label. */\r\n trailingIcon?: string;\r\n /** Hides the label and renders a square icon-only button. Requires `leadingIcon`. */\r\n iconOnly?: boolean;\r\n}\r\n\r\nlet nextId = 0;\r\n\r\n/**\r\n * A labelled text input that wraps a native `<input>` element.\r\n * Implements `ControlValueAccessor` so it works with both reactive forms\r\n * (`formControlName`, `[formControl]`) and template-driven forms (`ngModel`).\r\n * The error visual state is driven automatically by the bound `FormControl`'s\r\n * validity once the field has been touched, and can also be set explicitly via\r\n * the `error` input for use outside of Angular forms.\r\n */\r\n@Component({\r\n selector: 'compsych-text-input',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [ButtonComponent],\r\n template: `\r\n <div [class]=\"wrapperClasses\">\r\n <div class=\"label-row\" [class.sr-only]=\"hideLabel\">\r\n <label class=\"label\" [for]=\"inputId\">\r\n {{ label }}\r\n @if (isRequired) {\r\n <span class=\"required-marker\" aria-hidden=\"true\"> *</span>\r\n }\r\n </label>\r\n @if (parsedLabelButton) {\r\n <compsych-button\r\n [label]=\"parsedLabelButton.label\"\r\n [variant]=\"parsedLabelButton.variant ?? 'text'\"\r\n size=\"small\"\r\n [leadingIcon]=\"parsedLabelButton.leadingIcon\"\r\n [trailingIcon]=\"parsedLabelButton.trailingIcon\"\r\n [iconOnly]=\"!!parsedLabelButton.iconOnly\"\r\n [disabled]=\"disabled\"\r\n (click)=\"onLabelButtonClick()\"\r\n />\r\n }\r\n </div>\r\n <div class=\"input-wrapper\">\r\n @if (leadingIconSvg) {\r\n <span class=\"input-icon input-icon--leading\" aria-hidden=\"true\" [innerHTML]=\"leadingIconSvg\"></span>\r\n }\r\n <input\r\n class=\"input\"\r\n [class.has-leading-icon]=\"leadingIcon\"\r\n [class.has-trailing-icon]=\"trailingIcon\"\r\n [id]=\"inputId\"\r\n [name]=\"name || inputId\"\r\n [type]=\"type\"\r\n [placeholder]=\"placeholder\"\r\n [value]=\"value\"\r\n [disabled]=\"disabled\"\r\n [readOnly]=\"readonly\"\r\n [attr.required]=\"isRequired ? '' : null\"\r\n [class.error]=\"hasError\"\r\n [attr.aria-invalid]=\"hasError ? 'true' : null\"\r\n [attr.aria-describedby]=\"describedBy\"\r\n (input)=\"onInput($event)\"\r\n (blur)=\"onBlur()\"\r\n />\r\n @if (trailingIconSvg) {\r\n @if (trailingIconClick.observed) {\r\n <button class=\"input-icon input-icon--trailing input-icon--btn\" type=\"button\" [disabled]=\"disabled\" [attr.aria-label]=\"trailingIconAriaLabel || null\" [innerHTML]=\"trailingIconSvg\" (click)=\"trailingIconClick.emit()\"></button>\r\n } @else {\r\n <span class=\"input-icon input-icon--trailing\" aria-hidden=\"true\" [innerHTML]=\"trailingIconSvg\"></span>\r\n }\r\n }\r\n </div>\r\n @if (displayedSupportingText) {\r\n <span class=\"supporting-text\" [class.supporting-text--error]=\"hasError\" [id]=\"inputId + '-supporting'\">\r\n {{ displayedSupportingText }}\r\n </span>\r\n }\r\n </div>\r\n `,\r\n styleUrl: './text-input.css',\r\n})\r\nexport class TextInputComponent implements ControlValueAccessor, OnChanges {\r\n private readonly _autoId = `cmp-text-input-${++nextId}`;\r\n private readonly cdr = inject(ChangeDetectorRef);\r\n private readonly sanitizer = inject(DomSanitizer);\r\n private _onChange: (value: string) => void = () => {};\r\n private _onTouched: () => void = () => {};\r\n\r\n /** Injected when the component is used inside a reactive or template-driven form. */\r\n protected readonly ngControl: NgControl | null;\r\n\r\n constructor() {\r\n const ctrl = inject(NgControl, { optional: true, self: true });\r\n if (ctrl) ctrl.valueAccessor = this;\r\n this.ngControl = ctrl;\r\n }\r\n\r\n /** Label displayed above the input. */\r\n @Input() label = '';\r\n /** Placeholder text shown inside the input when empty. */\r\n @Input() placeholder = '';\r\n /** Current value of the input. When used in a form, the form controls the value. */\r\n @Input() value = '';\r\n /** Native input type. Defaults to `'text'`. Use `'password'` to mask the value. */\r\n @Input() type: TextInputType = 'text';\r\n /** Size variant — controls height, padding, and font size. Defaults to `'medium'`. */\r\n @Input() size: TextInputSize = 'medium';\r\n /** Native `name` attribute forwarded to the `<input>`. Defaults to the auto-generated id. */\r\n @Input() name?: string;\r\n /** Native `id` attribute for the `<input>`. Auto-generated if omitted. */\r\n @Input() id?: string;\r\n /**\r\n * Id(s) of external elements that describe the input, space-separated.\r\n * Forwarded to `aria-describedby` on the native `<input>`.\r\n */\r\n @Input() ariaDescribedBy?: string;\r\n /** Text displayed below the input. Shown in red when the input is in error state. */\r\n @Input() supportingText = '';\r\n /**\r\n * Static error text shown in red when the input is in error state.\r\n * Used when the input is outside a form (with `[error]=\"true\"`).\r\n * When inside a form, prefer `errorMessages` for per-validator messages.\r\n */\r\n @Input() errorMessage = '';\r\n /**\r\n * Map of Angular validator error keys to display strings.\r\n * The component reads `ngControl.errors` to determine which validator failed\r\n * and shows the matching message in red below the input.\r\n * Example: `{ required: 'Username is required', minlength: 'Min 8 characters' }`\r\n */\r\n @Input() errorMessages: Record<string, string> = {};\r\n\r\n private _labelButton: TextInputLabelButton | null = null;\r\n\r\n /**\r\n * JSON string configuring the optional button at the right of the label row.\r\n * Hidden when empty or omitted. Example: `'{\"id\":\"show\",\"label\":\"Show\"}'`\r\n * Clicking emits the button's `id` via the `labelButtonClick` output.\r\n */\r\n @Input() set labelButton(json: string) {\r\n if (!json) { this._labelButton = null; return; }\r\n try {\r\n this._labelButton = JSON.parse(json) as TextInputLabelButton;\r\n } catch {\r\n this._labelButton = null;\r\n }\r\n }\r\n\r\n /** @internal Template access to the parsed label button config. */\r\n protected get parsedLabelButton(): TextInputLabelButton | null {\r\n return this._labelButton;\r\n }\r\n\r\n /**\r\n * Adds a blue asterisk after the label. When used in a form, this is set\r\n * automatically if the bound `FormControl` has `Validators.required`.\r\n * Can also be set explicitly for non-form use. Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) required = false;\r\n /**\r\n * Applies the error visual state (red border) explicitly. When used in a form,\r\n * the error state is driven automatically from the `FormControl`'s validity once\r\n * the field is touched — this input is only needed outside of forms.\r\n * Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) error = false;\r\n /** Disables the input. When used in a form, also responds to `FormControl.disable()`. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) disabled = false;\r\n /** Makes the input read-only — focusable and copyable but not editable. Value is still submitted with the form. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) readonly = false;\r\n /** Visually hides the label while keeping it accessible to screen readers. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) hideLabel = false;\r\n /** Lucide icon name rendered inside the input before the text (e.g. `'search'`). */\r\n @Input() leadingIcon?: string;\r\n /** Lucide icon name rendered inside the input after the text (e.g. `'eye'`). */\r\n @Input() trailingIcon?: string;\r\n /** Accessible label for the trailing icon button. Required when `trailingIconClick` is used. */\r\n @Input() trailingIconAriaLabel = '';\r\n\r\n protected leadingIconSvg: SafeHtml | null = null;\r\n protected trailingIconSvg: SafeHtml | null = null;\r\n\r\n ngOnChanges(): void {\r\n this.leadingIconSvg = this.leadingIcon ? this.buildIconSvg(this.leadingIcon) : null;\r\n this.trailingIconSvg = this.trailingIcon ? this.buildIconSvg(this.trailingIcon) : null;\r\n }\r\n\r\n private buildIconSvg(name: string): SafeHtml {\r\n const svg = (LUCIDE_ICONS[name] ?? '').replace('<svg ', '<svg width=\"20\" height=\"20\" ');\r\n return this.sanitizer.bypassSecurityTrustHtml(svg);\r\n }\r\n\r\n /** Emits the new string value whenever the user types. For use outside Angular forms; inside forms use the `FormControl` value stream. */\r\n @Output() readonly valueChange = new EventEmitter<string>();\r\n /** Emits the label button's `id` when it is clicked. */\r\n @Output() readonly labelButtonClick = new EventEmitter<string>();\r\n /** Emitted when the trailing icon button is clicked. When observed, the trailing icon renders as an interactive button. */\r\n @Output() readonly trailingIconClick = new EventEmitter<void>();\r\n\r\n get inputId(): string {\r\n return this.id ?? this._autoId;\r\n }\r\n\r\n /** True when the bound FormControl is invalid and touched, or when `error` is explicitly set. */\r\n get hasError(): boolean {\r\n return this.error || !!(this.ngControl?.invalid && this.ngControl.touched);\r\n }\r\n\r\n /** True when `required` is set explicitly, or when the bound FormControl has Validators.required. */\r\n get isRequired(): boolean {\r\n return this.required || !!(this.ngControl?.control?.hasValidator(Validators.required));\r\n }\r\n\r\n private get activeErrorMessage(): string {\r\n const errors = this.ngControl?.errors;\r\n if (!errors) return '';\r\n for (const key of Object.keys(errors)) {\r\n if (this.errorMessages[key]) return this.errorMessages[key];\r\n }\r\n return '';\r\n }\r\n\r\n get displayedSupportingText(): string {\r\n if (this.hasError) {\r\n return this.activeErrorMessage || this.errorMessage || this.supportingText;\r\n }\r\n return this.supportingText;\r\n }\r\n\r\n get describedBy(): string | null {\r\n const parts: string[] = [];\r\n if (this.ariaDescribedBy) parts.push(this.ariaDescribedBy);\r\n if (this.supportingText || this.errorMessage || Object.keys(this.errorMessages).length) {\r\n parts.push(this.inputId + '-supporting');\r\n }\r\n return parts.length ? parts.join(' ') : null;\r\n }\r\n\r\n get wrapperClasses(): string {\r\n return ['cmp-text-field', this.size, this.disabled ? 'disabled' : '']\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n // ── ControlValueAccessor ────────────────────────────────────────────────────\r\n\r\n writeValue(value: string | null): void {\r\n this.value = value ?? '';\r\n this.cdr.markForCheck();\r\n }\r\n\r\n registerOnChange(fn: (value: string) => void): void {\r\n this._onChange = fn;\r\n }\r\n\r\n registerOnTouched(fn: () => void): void {\r\n this._onTouched = fn;\r\n }\r\n\r\n /** Called by Angular when the bound FormControl is programmatically disabled or enabled. */\r\n setDisabledState(isDisabled: boolean): void {\r\n this.disabled = isDisabled;\r\n this.cdr.markForCheck();\r\n }\r\n\r\n // ── Event handlers ──────────────────────────────────────────────────────────\r\n\r\n protected onInput(event: Event): void {\r\n const value = (event.target as HTMLInputElement).value;\r\n this.value = value;\r\n this._onChange(value);\r\n this.valueChange.emit(value);\r\n }\r\n\r\n protected onBlur(): void {\r\n this._onTouched();\r\n this.cdr.markForCheck();\r\n }\r\n\r\n protected onLabelButtonClick(): void {\r\n if (this._labelButton) {\r\n this.labelButtonClick.emit(this._labelButton.id);\r\n }\r\n }\r\n}\r\n"]}
@@ -99,6 +99,8 @@ export declare class TextInputComponent implements ControlValueAccessor, OnChang
99
99
  disabled: boolean;
100
100
  /** Makes the input read-only — focusable and copyable but not editable. Value is still submitted with the form. Supports boolean coercion. */
101
101
  readonly: boolean;
102
+ /** Visually hides the label while keeping it accessible to screen readers. Supports boolean coercion. */
103
+ hideLabel: boolean;
102
104
  /** Lucide icon name rendered inside the input before the text (e.g. `'search'`). */
103
105
  leadingIcon?: string;
104
106
  /** Lucide icon name rendered inside the input after the text (e.g. `'eye'`). */
@@ -133,9 +135,10 @@ export declare class TextInputComponent implements ControlValueAccessor, OnChang
133
135
  protected onBlur(): void;
134
136
  protected onLabelButtonClick(): void;
135
137
  static ɵfac: i0.ɵɵFactoryDeclaration<TextInputComponent, never>;
136
- static ɵcmp: i0.ɵɵComponentDeclaration<TextInputComponent, "compsych-text-input", never, { "label": { "alias": "label"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "value": { "alias": "value"; "required": false; }; "type": { "alias": "type"; "required": false; }; "size": { "alias": "size"; "required": false; }; "name": { "alias": "name"; "required": false; }; "id": { "alias": "id"; "required": false; }; "ariaDescribedBy": { "alias": "ariaDescribedBy"; "required": false; }; "supportingText": { "alias": "supportingText"; "required": false; }; "errorMessage": { "alias": "errorMessage"; "required": false; }; "errorMessages": { "alias": "errorMessages"; "required": false; }; "labelButton": { "alias": "labelButton"; "required": false; }; "required": { "alias": "required"; "required": false; }; "error": { "alias": "error"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "leadingIcon": { "alias": "leadingIcon"; "required": false; }; "trailingIcon": { "alias": "trailingIcon"; "required": false; }; "trailingIconAriaLabel": { "alias": "trailingIconAriaLabel"; "required": false; }; }, { "valueChange": "valueChange"; "labelButtonClick": "labelButtonClick"; "trailingIconClick": "trailingIconClick"; }, never, never, true, never>;
138
+ static ɵcmp: i0.ɵɵComponentDeclaration<TextInputComponent, "compsych-text-input", never, { "label": { "alias": "label"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "value": { "alias": "value"; "required": false; }; "type": { "alias": "type"; "required": false; }; "size": { "alias": "size"; "required": false; }; "name": { "alias": "name"; "required": false; }; "id": { "alias": "id"; "required": false; }; "ariaDescribedBy": { "alias": "ariaDescribedBy"; "required": false; }; "supportingText": { "alias": "supportingText"; "required": false; }; "errorMessage": { "alias": "errorMessage"; "required": false; }; "errorMessages": { "alias": "errorMessages"; "required": false; }; "labelButton": { "alias": "labelButton"; "required": false; }; "required": { "alias": "required"; "required": false; }; "error": { "alias": "error"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "hideLabel": { "alias": "hideLabel"; "required": false; }; "leadingIcon": { "alias": "leadingIcon"; "required": false; }; "trailingIcon": { "alias": "trailingIcon"; "required": false; }; "trailingIconAriaLabel": { "alias": "trailingIconAriaLabel"; "required": false; }; }, { "valueChange": "valueChange"; "labelButtonClick": "labelButtonClick"; "trailingIconClick": "trailingIconClick"; }, never, never, true, never>;
137
139
  static ngAcceptInputType_required: unknown;
138
140
  static ngAcceptInputType_error: unknown;
139
141
  static ngAcceptInputType_disabled: unknown;
140
142
  static ngAcceptInputType_readonly: unknown;
143
+ static ngAcceptInputType_hideLabel: unknown;
141
144
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compsych-ui-components/angular",
3
- "version": "7.0.13",
3
+ "version": "7.0.15",
4
4
  "description": "Angular UI components for compsych",
5
5
  "publishConfig": {
6
6
  "access": "public",