@lifecorner/angular-ui-kit 3.11.4 → 3.12.0

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.
Files changed (45) hide show
  1. package/fesm2022/lifecorner-angular-ui-kit-accordion.mjs +6 -3
  2. package/fesm2022/lifecorner-angular-ui-kit-accordion.mjs.map +1 -1
  3. package/fesm2022/lifecorner-angular-ui-kit-alert.mjs +2 -2
  4. package/fesm2022/lifecorner-angular-ui-kit-alert.mjs.map +1 -1
  5. package/fesm2022/lifecorner-angular-ui-kit-calendar.mjs +11 -6
  6. package/fesm2022/lifecorner-angular-ui-kit-calendar.mjs.map +1 -1
  7. package/fesm2022/lifecorner-angular-ui-kit-confirm-dialog.mjs +2 -0
  8. package/fesm2022/lifecorner-angular-ui-kit-confirm-dialog.mjs.map +1 -1
  9. package/fesm2022/lifecorner-angular-ui-kit-copy-field.mjs +2 -0
  10. package/fesm2022/lifecorner-angular-ui-kit-copy-field.mjs.map +1 -1
  11. package/fesm2022/lifecorner-angular-ui-kit-email-input.mjs +2 -0
  12. package/fesm2022/lifecorner-angular-ui-kit-email-input.mjs.map +1 -1
  13. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs +7 -2
  14. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs.map +1 -1
  15. package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs +7 -2
  16. package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs.map +1 -1
  17. package/fesm2022/lifecorner-angular-ui-kit-header.mjs +4 -2
  18. package/fesm2022/lifecorner-angular-ui-kit-header.mjs.map +1 -1
  19. package/fesm2022/lifecorner-angular-ui-kit-input.mjs +42 -5
  20. package/fesm2022/lifecorner-angular-ui-kit-input.mjs.map +1 -1
  21. package/fesm2022/lifecorner-angular-ui-kit-internal-a11y.mjs +20 -0
  22. package/fesm2022/lifecorner-angular-ui-kit-internal-a11y.mjs.map +1 -0
  23. package/fesm2022/lifecorner-angular-ui-kit-pagination.mjs +5 -2
  24. package/fesm2022/lifecorner-angular-ui-kit-pagination.mjs.map +1 -1
  25. package/fesm2022/lifecorner-angular-ui-kit-rich-text-editor.mjs +6 -2
  26. package/fesm2022/lifecorner-angular-ui-kit-rich-text-editor.mjs.map +1 -1
  27. package/fesm2022/lifecorner-angular-ui-kit-stepper.mjs +11 -6
  28. package/fesm2022/lifecorner-angular-ui-kit-stepper.mjs.map +1 -1
  29. package/fesm2022/lifecorner-angular-ui-kit-table.mjs +2 -0
  30. package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
  31. package/fesm2022/lifecorner-angular-ui-kit-textarea.mjs +47 -7
  32. package/fesm2022/lifecorner-angular-ui-kit-textarea.mjs.map +1 -1
  33. package/package.json +5 -1
  34. package/types/lifecorner-angular-ui-kit-accordion.d.ts +31 -9
  35. package/types/lifecorner-angular-ui-kit-alert.d.ts +8 -1
  36. package/types/lifecorner-angular-ui-kit-calendar.d.ts +1 -0
  37. package/types/lifecorner-angular-ui-kit-file-upload.d.ts +18 -3
  38. package/types/lifecorner-angular-ui-kit-gallery.d.ts +17 -2
  39. package/types/lifecorner-angular-ui-kit-header.d.ts +12 -1
  40. package/types/lifecorner-angular-ui-kit-input.d.ts +47 -2
  41. package/types/lifecorner-angular-ui-kit-internal-a11y.d.ts +27 -0
  42. package/types/lifecorner-angular-ui-kit-pagination.d.ts +15 -2
  43. package/types/lifecorner-angular-ui-kit-rich-text-editor.d.ts +13 -1
  44. package/types/lifecorner-angular-ui-kit-stepper.d.ts +16 -5
  45. package/types/lifecorner-angular-ui-kit-textarea.d.ts +46 -8
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-textarea.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/textarea/textarea.component.ts","../../../../libs/angular-ui-kit/src/lib/textarea/textarea.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n ElementRef,\n OutputRef,\n PLATFORM_ID,\n afterRenderEffect,\n computed,\n forwardRef,\n inject,\n input,\n model,\n signal,\n viewChild,\n} from '@angular/core';\nimport { isPlatformServer } from '@angular/common';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nlet nextUniqueId = 0;\n\n/**\n * Textarea component for multi-line text input.\n *\n * Features:\n * - Variant styles (outline, filled)\n * - Size presets (xs, sm, md, lg)\n * - Auto-resize with configurable min/max rows\n * - Character count display with maxLength\n * - Validation error message and helper text\n * - Disabled and readonly states\n * - ControlValueAccessor integration for reactive forms\n * - Plain form posts without JavaScript: `name`, and a `value` that is\n * prerendered into the native `<textarea>`\n *\n * @example\n * ```html\n * <lc-textarea placeholder=\"Enter message\" [autoResize]=\"true\" [(ngModel)]=\"message\" />\n * <lc-textarea label=\"Message\" name=\"message\" [required]=\"true\" [value]=\"draft\" />\n * ```\n */\n@Component({\n selector: 'lc-textarea',\n standalone: true,\n imports: [],\n templateUrl: './textarea.component.html',\n styleUrl: './textarea.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => TextareaComponent),\n multi: true,\n },\n ],\n})\nexport class TextareaComponent implements ControlValueAccessor {\n private readonly textareaElement = viewChild<ElementRef<HTMLTextAreaElement>>('textarea');\n\n private readonly isServer = isPlatformServer(inject(PLATFORM_ID));\n\n /**\n * Visual variant of the textarea\n * @default 'outline'\n */\n readonly variant = input<'outline' | 'filled'>('outline');\n\n /**\n * Size of the textarea\n * @default 'md'\n */\n readonly size = input<'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the textarea is disabled\n * @default false\n */\n readonly disabled = input<boolean>(false);\n\n /**\n * Whether the textarea is in error state\n * @default false\n */\n readonly error = input<boolean>(false);\n\n /**\n * Whether the textarea is required\n * @default false\n */\n readonly required = input<boolean>(false);\n\n /**\n * Show the `*` after the label of a `required` field. `false` keeps the\n * required semantics (`required`, `aria-required`) without the asterisk —\n * for forms where every field is required.\n * @default true\n */\n readonly requiredIndicator = input<boolean>(true);\n\n /**\n * Accessible name of the `*` marker, for API parity with `lc-input`.\n * `lc-textarea` hides the `*` from assistive technology, because the native\n * `required` state is announced already and naming the `*` would say\n * \"required\" twice.\n * @default 'required'\n */\n readonly requiredAriaLabel = input<string>('required');\n\n /**\n * Whether the textarea is readonly\n * @default false\n */\n readonly readonly = input<boolean>(false);\n\n /**\n * Placeholder text\n * @default ''\n */\n readonly placeholder = input<string>('');\n\n /**\n * Label text displayed above the textarea\n * @default ''\n */\n readonly label = input<string>('');\n\n /**\n * Helper text displayed below the textarea\n * @default ''\n */\n readonly helperText = input<string>('');\n\n /**\n * Error message displayed when error is true\n * @default ''\n */\n readonly errorMessage = input<string>('');\n\n /**\n * ARIA label for accessibility\n * @default undefined\n */\n readonly ariaLabel = input<string | undefined>(undefined);\n\n /**\n * Native `name` of the inner `<textarea>`, so a form posts the text without\n * JavaScript. Unset = no attribute. A `disabled` textarea is not posted\n * (HTML); use `readonly` to post a fixed text.\n * @default undefined\n */\n readonly name = input<string>();\n\n /**\n * Number of visible text rows\n * @default 3\n */\n readonly rows = input<number>(3);\n\n /**\n * Maximum number of characters allowed\n * @default undefined\n */\n readonly maxLength = input<number | undefined>(undefined);\n\n /**\n * Whether to show character count\n * @default false\n */\n readonly showCharacterCount = input<boolean>(false);\n\n /**\n * Whether to automatically resize based on content\n * @default false\n */\n readonly autoResize = input<boolean>(false);\n\n /**\n * Minimum number of rows for auto-resize\n * @default 3\n */\n readonly minRows = input<number>(3);\n\n /**\n * Maximum number of rows for auto-resize\n * @default undefined\n */\n readonly maxRows = input<number | undefined>(undefined);\n\n /**\n * Current text, two-way bindable (`[value]` / `[(value)]`). It pre-fills the\n * native textarea, in prerendered HTML too, so a server-rendered page can\n * keep a visitor's text. Emits `valueChange` whenever it changes: once per\n * keystroke that changes the text, and when a bound form control or code\n * sets a different text. With a form control, the control's value wins;\n * don't bind both.\n * @default ''\n */\n readonly value = model<string>('');\n\n /**\n * The model's change output as a class property, so code that subscribes to\n * it (`textarea.valueChange.subscribe(…)`, `outputToObservable(…)`) keeps\n * compiling as in 3.9. Template bindings `(valueChange)` use the model.\n */\n readonly valueChange: OutputRef<string> = this.value;\n\n /** Per-instance id: links label, error and helper text to the control. */\n readonly textareaId = `lc-textarea-${nextUniqueId++}`;\n readonly errorId = `${this.textareaId}-error`;\n readonly helperId = `${this.textareaId}-helper`;\n\n /**\n * The native textarea's value. On the server a leading line break gets one\n * extra `\\n`: the HTML parser drops the first line break after `<textarea>`,\n * and the server's DOM serialiser doesn't add the one the HTML\n * serialisation rules require, so the prerendered page would lose it.\n */\n protected readonly renderedValue = computed(() => {\n const value = this.value();\n return this.isServer && /^[\\r\\n]/.test(value) ? `\\n${value}` : value;\n });\n\n // Internal disabled state (for ControlValueAccessor)\n private readonly formDisabled = signal<boolean>(false);\n\n /**\n * Computed disabled state from both input and form control\n */\n readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n // Computed values\n readonly currentCharacterCount = computed(() => this.value().length);\n\n readonly characterCountText = computed(() => {\n const current = this.currentCharacterCount();\n const max = this.maxLength();\n return max !== undefined ? `${current} / ${max}` : `${current}`;\n });\n\n readonly isOverLimit = computed(() => {\n const max = this.maxLength();\n return max !== undefined && this.currentCharacterCount() > max;\n });\n\n /** Whether an error message is shown (drives aria-describedby) */\n readonly showsError = computed(() => this.error() && !!this.errorMessage());\n\n /** Whether the helper text is shown (error message takes precedence) */\n readonly showsHelper = computed(() => !this.showsError() && !!this.helperText());\n\n readonly describedBy = computed(() => {\n const ids: string[] = [];\n if (this.showsError()) ids.push(this.errorId);\n if (this.showsHelper()) ids.push(this.helperId);\n return ids.length ? ids.join(' ') : null;\n });\n\n /**\n * Computed classes for the textarea element\n */\n readonly textareaClasses = computed(() => {\n const classes = ['lc-textarea', `lc-textarea--${this.variant()}`, `lc-textarea--${this.size()}`];\n\n if (this.isDisabled()) {\n classes.push('lc-textarea--disabled');\n }\n\n if (this.error()) {\n classes.push('lc-textarea--error');\n }\n\n if (this.readonly()) {\n classes.push('lc-textarea--readonly');\n }\n\n if (this.autoResize()) {\n classes.push('lc-textarea--auto-resize');\n }\n\n return classes.join(' ');\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (value: string) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n constructor() {\n // Re-measure after every render in which the value or the resize\n // constraints changed — this covers typing, form writes and input changes\n // without timers, and runs once the DOM already holds the new value.\n afterRenderEffect(() => {\n this.value();\n this.minRows();\n this.maxRows();\n if (this.autoResize()) {\n this.adjustHeight();\n }\n });\n }\n\n /**\n * Handle input changes\n */\n onInput(value: string): void {\n if (this.isDisabled() || this.readonly()) {\n return;\n }\n\n // model.set() emits `valueChange` exactly once per actual change\n this.value.set(value);\n this.onChange(value);\n }\n\n /**\n * Handle blur event\n */\n onBlur(): void {\n this.onTouched();\n }\n\n // ControlValueAccessor implementation\n writeValue(value: string | null | undefined): void {\n this.value.set(value || '');\n }\n\n registerOnChange(fn: (value: string) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n\n // Private helper methods\n private adjustHeight(): void {\n const textarea = this.textareaElement()?.nativeElement;\n if (!textarea) {\n return;\n }\n\n // Reset height to get accurate scrollHeight\n textarea.style.height = 'auto';\n\n const minHeight = this.rowsToHeight(textarea, this.minRows());\n const maxHeight = this.rowsToHeight(textarea, this.maxRows());\n let newHeight = textarea.scrollHeight;\n\n // Apply constraints\n if (minHeight !== undefined) {\n newHeight = Math.max(newHeight, minHeight);\n }\n if (maxHeight !== undefined) {\n newHeight = Math.min(newHeight, maxHeight);\n }\n\n textarea.style.height = `${newHeight}px`;\n }\n\n /** Pixel height for `rows` lines incl. vertical padding; undefined when it cannot be measured. */\n private rowsToHeight(textarea: HTMLTextAreaElement, rows: number | undefined): number | undefined {\n if (rows === undefined) {\n return undefined;\n }\n\n const style = getComputedStyle(textarea);\n const lineHeight = parseFloat(style.lineHeight);\n const paddingTop = parseFloat(style.paddingTop) || 0;\n const paddingBottom = parseFloat(style.paddingBottom) || 0;\n\n // 'normal' / unmeasurable line-height (e.g. jsdom) → no constraint\n if (!Number.isFinite(lineHeight)) {\n return undefined;\n }\n\n return lineHeight * rows + paddingTop + paddingBottom;\n }\n\n protected getInputValue(event: Event): string {\n return (event.target as HTMLTextAreaElement).value;\n }\n}\n","<div class=\"lc-textarea-wrapper\">\n @if (label()) {\n <label [for]=\"textareaId\" class=\"lc-textarea-label\">\n {{ label() }}\n @if (required() && requiredIndicator()) {\n <span class=\"lc-textarea-required-indicator\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n\n <div class=\"lc-textarea-input-wrapper\">\n <textarea\n #textarea\n [id]=\"textareaId\"\n [class]=\"textareaClasses()\"\n [value]=\"renderedValue()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [attr.name]=\"name() ?? null\"\n [attr.rows]=\"autoResize() ? null : rows()\"\n [attr.maxlength]=\"maxLength() ?? null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"error() || null\"\n [attr.aria-describedby]=\"describedBy()\"\n (input)=\"onInput(getInputValue($event))\"\n (blur)=\"onBlur()\"\n ></textarea>\n </div>\n\n <div class=\"lc-textarea-footer\">\n <div class=\"lc-textarea-messages\">\n @if (showsError()) {\n <span [id]=\"errorId\" class=\"lc-textarea-error-message\" role=\"alert\">{{ errorMessage() }}</span>\n } @else if (showsHelper()) {\n <span [id]=\"helperId\" class=\"lc-textarea-helper-text\">{{ helperText() }}</span>\n }\n </div>\n\n @if (showCharacterCount()) {\n <span\n class=\"lc-textarea-character-count\"\n [class.lc-textarea-character-count--over-limit]=\"isOverLimit()\"\n aria-live=\"polite\"\n >\n {{ characterCountText() }}\n </span>\n }\n </div>\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-textarea.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/textarea/textarea.component.ts","../../../../libs/angular-ui-kit/src/lib/textarea/textarea.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n ElementRef,\n OutputRef,\n PLATFORM_ID,\n afterRenderEffect,\n computed,\n forwardRef,\n inject,\n input,\n model,\n signal,\n viewChild,\n} from '@angular/core';\nimport { isPlatformServer } from '@angular/common';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\n/** Counter for generated ids: they follow creation order, so prerendered HTML is the same in every build. */\nlet nextUniqueId = 0;\n\n/**\n * Textarea component for multi-line text input.\n *\n * Features:\n * - Variant styles (outline, filled)\n * - Size presets (xs, sm, md, lg)\n * - Auto-resize with configurable min/max rows\n * - Character count display with maxLength\n * - Validation error message and helper text\n * - Disabled and readonly states\n * - ControlValueAccessor integration for reactive forms\n * - Plain form posts without JavaScript: `name`, and a `value` that is\n * prerendered into the native `<textarea>`\n *\n * @example\n * ```html\n * <lc-textarea placeholder=\"Enter message\" [autoResize]=\"true\" [(ngModel)]=\"message\" />\n * <lc-textarea label=\"Message\" name=\"message\" [required]=\"true\" [value]=\"draft\" />\n * ```\n */\n@Component({\n selector: 'lc-textarea',\n standalone: true,\n imports: [],\n templateUrl: './textarea.component.html',\n styleUrl: './textarea.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => TextareaComponent),\n multi: true,\n },\n ],\n})\nexport class TextareaComponent implements ControlValueAccessor {\n private readonly textareaElement = viewChild<ElementRef<HTMLTextAreaElement>>('textarea');\n\n private readonly isServer = isPlatformServer(inject(PLATFORM_ID));\n\n /**\n * Visual variant of the textarea\n * @default 'outline'\n */\n readonly variant = input<'outline' | 'filled'>('outline');\n\n /**\n * Size of the textarea\n * @default 'md'\n */\n readonly size = input<'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the textarea is disabled\n * @default false\n */\n readonly disabled = input<boolean>(false);\n\n /**\n * Whether the textarea is in error state\n * @default false\n */\n readonly error = input<boolean>(false);\n\n /**\n * Whether the textarea is required\n * @default false\n */\n readonly required = input<boolean>(false);\n\n /**\n * Show the `*` after the label of a `required` field. `false` keeps the\n * required semantics (`required`, `aria-required`) without the asterisk —\n * for forms where every field is required.\n * @default true\n */\n readonly requiredIndicator = input<boolean>(true);\n\n /**\n * Deprecated, no effect (it never had one in `lc-textarea`). The `*` is\n * decorative and hidden from assistive technology, because the native\n * `required` state is announced already, in the screen reader's language; a\n * name on the `*` would make the field say \"required\" twice. Kept so\n * existing bindings compile.\n * @deprecated No effect: the native `required` state is announced instead.\n * @default 'required'\n */\n readonly requiredAriaLabel = input<string>('required');\n\n /**\n * Id of the native `<textarea>`. The label's `for`, the error\n * (`<id>-error`), the helper text (`<id>-helper`) and `aria-describedby`\n * derive from it, so an error summary can link to the field\n * (`<a href=\"#contact-message\">`) and prerendered HTML is the same in every\n * build. Unique in the document, no spaces. Unset (or empty) = a generated\n * `lc-textarea-<n>`, which follows creation order (on the server it keeps\n * counting across prerendered routes): set it on prerendered pages.\n * @default generated (lc-textarea-<n>)\n */\n readonly controlId = input<string | undefined>(undefined);\n\n /**\n * Announce the error message the moment it appears (`role=\"alert\"` on the\n * message). Set `false` when an `lc-alert` error summary above the form\n * already announces the errors (a page re-rendered after a failed submit,\n * e.g. 422), so they aren't read twice. The message stays visible, stays\n * linked through `aria-describedby` (read with the field), and the control\n * keeps `aria-invalid`.\n * @default true\n */\n readonly announceError = input<boolean>(true);\n\n /**\n * Whether the textarea is readonly\n * @default false\n */\n readonly readonly = input<boolean>(false);\n\n /**\n * Placeholder text\n * @default ''\n */\n readonly placeholder = input<string>('');\n\n /**\n * Label text displayed above the textarea\n * @default ''\n */\n readonly label = input<string>('');\n\n /**\n * Helper text displayed below the textarea\n * @default ''\n */\n readonly helperText = input<string>('');\n\n /**\n * Error message displayed when error is true\n * @default ''\n */\n readonly errorMessage = input<string>('');\n\n /**\n * ARIA label for accessibility\n * @default undefined\n */\n readonly ariaLabel = input<string | undefined>(undefined);\n\n /**\n * Native `name` of the inner `<textarea>`, so a form posts the text without\n * JavaScript. Unset = no attribute. A `disabled` textarea is not posted\n * (HTML); use `readonly` to post a fixed text.\n * @default undefined\n */\n readonly name = input<string>();\n\n /**\n * Number of visible text rows\n * @default 3\n */\n readonly rows = input<number>(3);\n\n /**\n * Maximum number of characters allowed\n * @default undefined\n */\n readonly maxLength = input<number | undefined>(undefined);\n\n /**\n * Whether to show character count\n * @default false\n */\n readonly showCharacterCount = input<boolean>(false);\n\n /**\n * Whether to automatically resize based on content\n * @default false\n */\n readonly autoResize = input<boolean>(false);\n\n /**\n * Minimum number of rows for auto-resize\n * @default 3\n */\n readonly minRows = input<number>(3);\n\n /**\n * Maximum number of rows for auto-resize\n * @default undefined\n */\n readonly maxRows = input<number | undefined>(undefined);\n\n /**\n * Current text, two-way bindable (`[value]` / `[(value)]`). It pre-fills the\n * native textarea, in prerendered HTML too, so a server-rendered page can\n * keep a visitor's text. Emits `valueChange` whenever it changes: once per\n * keystroke that changes the text, and when a bound form control or code\n * sets a different text. With a form control, the control's value wins;\n * don't bind both.\n * @default ''\n */\n readonly value = model<string>('');\n\n /**\n * The model's change output as a class property, so code that subscribes to\n * it (`textarea.valueChange.subscribe(…)`, `outputToObservable(…)`) keeps\n * compiling as in 3.9. Template bindings `(valueChange)` use the model.\n */\n readonly valueChange: OutputRef<string> = this.value;\n\n /** Generated id, taken once per instance (also with `controlId`), so later ids don't shift. */\n private readonly generatedId = `lc-textarea-${nextUniqueId++}`;\n\n private readonly resolvedId = computed(() => this.controlId() || this.generatedId);\n\n /** Id of the native textarea: `controlId`, or the generated `lc-textarea-<n>`. Links label, error and helper text to it. */\n get textareaId(): string {\n return this.resolvedId();\n }\n\n /** Id of the error message: `<textareaId>-error`. */\n get errorId(): string {\n return `${this.resolvedId()}-error`;\n }\n\n /** Id of the helper text: `<textareaId>-helper`. */\n get helperId(): string {\n return `${this.resolvedId()}-helper`;\n }\n\n /**\n * The native textarea's value. On the server a leading line break gets one\n * extra `\\n`: the HTML parser drops the first line break after `<textarea>`,\n * and the server's DOM serialiser doesn't add the one the HTML\n * serialisation rules require, so the prerendered page would lose it.\n */\n protected readonly renderedValue = computed(() => {\n const value = this.value();\n return this.isServer && /^[\\r\\n]/.test(value) ? `\\n${value}` : value;\n });\n\n // Internal disabled state (for ControlValueAccessor)\n private readonly formDisabled = signal<boolean>(false);\n\n /**\n * Computed disabled state from both input and form control\n */\n readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n // Computed values\n readonly currentCharacterCount = computed(() => this.value().length);\n\n readonly characterCountText = computed(() => {\n const current = this.currentCharacterCount();\n const max = this.maxLength();\n return max !== undefined ? `${current} / ${max}` : `${current}`;\n });\n\n readonly isOverLimit = computed(() => {\n const max = this.maxLength();\n return max !== undefined && this.currentCharacterCount() > max;\n });\n\n /** Whether an error message is shown (drives aria-describedby) */\n readonly showsError = computed(() => this.error() && !!this.errorMessage());\n\n /** Whether the helper text is shown (error message takes precedence) */\n readonly showsHelper = computed(() => !this.showsError() && !!this.helperText());\n\n readonly describedBy = computed(() => {\n const ids: string[] = [];\n if (this.showsError()) ids.push(this.errorId);\n if (this.showsHelper()) ids.push(this.helperId);\n return ids.length ? ids.join(' ') : null;\n });\n\n /**\n * Computed classes for the textarea element\n */\n readonly textareaClasses = computed(() => {\n const classes = ['lc-textarea', `lc-textarea--${this.variant()}`, `lc-textarea--${this.size()}`];\n\n if (this.isDisabled()) {\n classes.push('lc-textarea--disabled');\n }\n\n if (this.error()) {\n classes.push('lc-textarea--error');\n }\n\n if (this.readonly()) {\n classes.push('lc-textarea--readonly');\n }\n\n if (this.autoResize()) {\n classes.push('lc-textarea--auto-resize');\n }\n\n return classes.join(' ');\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (value: string) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n constructor() {\n // Re-measure after every render in which the value or the resize\n // constraints changed — this covers typing, form writes and input changes\n // without timers, and runs once the DOM already holds the new value.\n afterRenderEffect(() => {\n this.value();\n this.minRows();\n this.maxRows();\n if (this.autoResize()) {\n this.adjustHeight();\n }\n });\n }\n\n /**\n * Handle input changes\n */\n onInput(value: string): void {\n if (this.isDisabled() || this.readonly()) {\n return;\n }\n\n // model.set() emits `valueChange` exactly once per actual change\n this.value.set(value);\n this.onChange(value);\n }\n\n /**\n * Handle blur event\n */\n onBlur(): void {\n this.onTouched();\n }\n\n // ControlValueAccessor implementation\n writeValue(value: string | null | undefined): void {\n this.value.set(value || '');\n }\n\n registerOnChange(fn: (value: string) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n\n // Private helper methods\n private adjustHeight(): void {\n const textarea = this.textareaElement()?.nativeElement;\n if (!textarea) {\n return;\n }\n\n // Reset height to get accurate scrollHeight\n textarea.style.height = 'auto';\n\n const minHeight = this.rowsToHeight(textarea, this.minRows());\n const maxHeight = this.rowsToHeight(textarea, this.maxRows());\n let newHeight = textarea.scrollHeight;\n\n // Apply constraints\n if (minHeight !== undefined) {\n newHeight = Math.max(newHeight, minHeight);\n }\n if (maxHeight !== undefined) {\n newHeight = Math.min(newHeight, maxHeight);\n }\n\n textarea.style.height = `${newHeight}px`;\n }\n\n /** Pixel height for `rows` lines incl. vertical padding; undefined when it cannot be measured. */\n private rowsToHeight(textarea: HTMLTextAreaElement, rows: number | undefined): number | undefined {\n if (rows === undefined) {\n return undefined;\n }\n\n const style = getComputedStyle(textarea);\n const lineHeight = parseFloat(style.lineHeight);\n const paddingTop = parseFloat(style.paddingTop) || 0;\n const paddingBottom = parseFloat(style.paddingBottom) || 0;\n\n // 'normal' / unmeasurable line-height (e.g. jsdom) → no constraint\n if (!Number.isFinite(lineHeight)) {\n return undefined;\n }\n\n return lineHeight * rows + paddingTop + paddingBottom;\n }\n\n protected getInputValue(event: Event): string {\n return (event.target as HTMLTextAreaElement).value;\n }\n}\n","<div class=\"lc-textarea-wrapper\">\n @if (label()) {\n <!-- No whitespace before the *: the gap is its margin, and it never wraps onto a line alone -->\n <label [for]=\"textareaId\" class=\"lc-textarea-label\">\n {{ label() }}@if (required() && requiredIndicator()) {<span class=\"lc-textarea-required-indicator\" aria-hidden=\"true\">*</span>}\n </label>\n }\n\n <div class=\"lc-textarea-input-wrapper\">\n <textarea\n #textarea\n [id]=\"textareaId\"\n [class]=\"textareaClasses()\"\n [value]=\"renderedValue()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [attr.name]=\"name() ?? null\"\n [attr.rows]=\"autoResize() ? null : rows()\"\n [attr.maxlength]=\"maxLength() ?? null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"error() || null\"\n [attr.aria-describedby]=\"describedBy()\"\n (input)=\"onInput(getInputValue($event))\"\n (blur)=\"onBlur()\"\n ></textarea>\n </div>\n\n <div class=\"lc-textarea-footer\">\n <div class=\"lc-textarea-messages\">\n @if (showsError()) {\n <span [id]=\"errorId\" class=\"lc-textarea-error-message\" [attr.role]=\"announceError() ? 'alert' : null\">{{ errorMessage() }}</span>\n } @else if (showsHelper()) {\n <span [id]=\"helperId\" class=\"lc-textarea-helper-text\">{{ helperText() }}</span>\n }\n </div>\n\n @if (showCharacterCount()) {\n <span\n class=\"lc-textarea-character-count\"\n [class.lc-textarea-character-count--over-limit]=\"isOverLimit()\"\n aria-live=\"polite\"\n >\n {{ characterCountText() }}\n </span>\n }\n </div>\n</div>\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifecorner/angular-ui-kit",
3
- "version": "3.11.4",
3
+ "version": "3.12.0",
4
4
  "description": "LifeCorner Design System – Angular UI component library",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -266,6 +266,10 @@
266
266
  "types": "./types/lifecorner-angular-ui-kit-input.d.ts",
267
267
  "default": "./fesm2022/lifecorner-angular-ui-kit-input.mjs"
268
268
  },
269
+ "./internal/a11y": {
270
+ "types": "./types/lifecorner-angular-ui-kit-internal-a11y.d.ts",
271
+ "default": "./fesm2022/lifecorner-angular-ui-kit-internal-a11y.mjs"
272
+ },
269
273
  "./internal/chart": {
270
274
  "types": "./types/lifecorner-angular-ui-kit-internal-chart.d.ts",
271
275
  "default": "./fesm2022/lifecorner-angular-ui-kit-internal-chart.mjs"
@@ -12,12 +12,18 @@ import { TemplateRef } from "@angular/core";
12
12
  * contain nested interactive elements (no button/link/input). Text, badges and
13
13
  * other non-interactive nodes only.
14
14
  *
15
+ * Name (WCAG 2.5.3 Label in Name): the header is named by the template's
16
+ * text; the accordion's `ariaLabel` (or `title`), if set, comes first through
17
+ * `aria-labelledby`. Separate the template's parts with whitespace
18
+ * (`&ngsp;`), so they read as words ("Item label Done 12:04"), and set a
19
+ * label only for context that the template doesn't show.
20
+ *
15
21
  * @example
16
22
  * ```html
17
23
  * <lc-accordion variant="flat" chevronPosition="leading">
18
24
  * <ng-template lcAccordionHeader>
19
- * <span class="title">Item label</span>
20
- * <lc-badge variant="success" size="sm">Done</lc-badge>
25
+ * <span class="title">Item label</span>&ngsp;
26
+ * <lc-badge variant="success" size="sm">Done</lc-badge>&ngsp;
21
27
  * <span style="margin-left: auto;">12:04</span>
22
28
  * </ng-template>
23
29
  * <div>Body…</div>
@@ -81,8 +87,8 @@ export type AccordionChevronPosition = 'leading' | 'trailing';
81
87
  * ```html
82
88
  * <lc-accordion variant="flat" chevronPosition="leading" [lazy]="true">
83
89
  * <ng-template lcAccordionHeader>
84
- * <span class="title">Item label</span>
85
- * <lc-badge variant="success" size="sm">Done</lc-badge>
90
+ * <span class="title">Item label</span>&ngsp;
91
+ * <lc-badge variant="success" size="sm">Done</lc-badge>&ngsp;
86
92
  * <span style="margin-left: auto;">12:04</span>
87
93
  * </ng-template>
88
94
  * <ng-template lcAccordionContent>
@@ -95,13 +101,18 @@ export declare class AccordionComponent {
95
101
  /**
96
102
  * Title displayed in the accordion header. Optional: when a
97
103
  * `lcAccordionHeader` template is projected it takes precedence, and `title`
98
- * (if set) becomes the header's accessible label fallback.
104
+ * (if set) starts the header's accessible name when `ariaLabel` is empty.
105
+ * @default ''
99
106
  */
100
107
  readonly title: import("@angular/core").InputSignal<string>;
101
108
  /**
102
- * Explicit accessible label for the header button. Use this when the header
103
- * is a rich template with no meaningful plain-text title. Falls back to
104
- * `title` when omitted.
109
+ * Start of the header button's accessible name; falls back to `title`. A
110
+ * plain `title` is appended unless contained (WCAG 2.5.3 Label in Name).
111
+ * With an `lcAccordionHeader` template, the name is this label (or `title`)
112
+ * followed by the template's text, through `aria-labelledby`. Separate the
113
+ * template's parts with whitespace (`&ngsp;`), and set a label only for
114
+ * context that the template doesn't show.
115
+ * @default ''
105
116
  */
106
117
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
107
118
  /** Whether the accordion is expanded (two-way binding) */
@@ -147,8 +158,19 @@ export declare class AccordionComponent {
147
158
  /** Latches to `true` the first time the panel is opened (for lazy bodies). */
148
159
  private readonly hasBeenOpened;
149
160
  constructor();
150
- /** Accessible label for the header button (explicit override, else title). */
161
+ /**
162
+ * Label that starts a rich header's name (`ariaLabel`, else `title`); empty
163
+ * without a header template.
164
+ */
165
+ protected readonly richHeaderLabel: import("@angular/core").Signal<string>;
166
+ /**
167
+ * Accessible label of a plain header button (AS-33, WCAG 2.5.3 Label in
168
+ * Name): the explicit override, else the title, then the visible title
169
+ * unless contained. A rich header has none: it is named by its content.
170
+ */
151
171
  protected readonly headerAriaLabel: import("@angular/core").Signal<string | null>;
172
+ /** A labelled rich header: its hidden label, then the template's text. */
173
+ protected readonly headerLabelledBy: import("@angular/core").Signal<string | null>;
152
174
  /**
153
175
  * Whether the `lcAccordionContent` body should currently be in the DOM.
154
176
  * - `destroyOnClose`: only while expanded.
@@ -19,7 +19,9 @@ export type AlertActionsLayout = 'inline' | 'stacked';
19
19
  * dismissed alert can be shown again
20
20
  * - Content projection for custom body
21
21
  * - Action slot for a button rendered right of the body (e.g. retry), or
22
- * several actions stacked below it (`actionsLayout="stacked"`)
22
+ * several actions stacked below it (`actionsLayout="stacked"`); 3.12: a text
23
+ * link (`<a slot="action" href="…">`) as the one way forward gets a
24
+ * 44 × 44 px target, link colour, an underline and a focus ring
23
25
  * - The variant icon is decorative unless `iconAriaLabel` names it (the
24
26
  * title and the colour carry the tone; no built-in English word)
25
27
  * - Accessible: `role="alert"` for error / warning, `role="status"` otherwise
@@ -34,6 +36,11 @@ export type AlertActionsLayout = 'inline' | 'stacked';
34
36
  * Die Liste konnte nicht geladen werden.
35
37
  * <lc-button slot="action" variant="outline" size="sm">Erneut versuchen</lc-button>
36
38
  * </lc-alert>
39
+ *
40
+ * <lc-alert variant="error" title="Senden gerade nicht möglich">
41
+ * Wir können Ihre Nachricht gerade nicht senden.
42
+ * <a slot="action" href="mailto:kontakt@beispiel.example">Schreiben Sie uns an kontakt@beispiel.example</a>
43
+ * </lc-alert>
37
44
  * ```
38
45
  */
39
46
  export declare class AlertComponent {
@@ -55,6 +55,7 @@ export declare class CalendarComponent {
55
55
  protected readonly weekDays: import("@angular/core").Signal<{
56
56
  date: Date;
57
57
  label: string;
58
+ ariaLabel: string;
58
59
  isToday: boolean;
59
60
  events: {
60
61
  start: Date;
@@ -40,13 +40,23 @@ export declare class FileUploadComponent {
40
40
  */
41
41
  disabled: import("@angular/core").InputSignal<boolean>;
42
42
  /**
43
- * Hint text shown in the dropzone.
43
+ * Hint text shown in the dropzone. The drop zone's accessible name is
44
+ * `hint`, then the shown accepted and max lines, comma-separated (WCAG 2.5.3
45
+ * Label in Name).
44
46
  * @default 'Drag & drop files here, or click to browse'
45
47
  */
46
48
  hint: import("@angular/core").InputSignal<string>;
47
- /** Prefix of the accepted file types line (`accept`). */
49
+ /**
50
+ * Prefix of the accepted file types line (`accept`). The shown line is part
51
+ * of the drop zone's accessible name, after `hint`.
52
+ * @default 'Accepted:'
53
+ */
48
54
  acceptedLabel: import("@angular/core").InputSignal<string>;
49
- /** Prefix of the maximum file size line (`maxSizeBytes`). */
55
+ /**
56
+ * Prefix of the maximum file size line (`maxSizeBytes`). The shown line is
57
+ * part of the drop zone's accessible name, after `hint`.
58
+ * @default 'Max:'
59
+ */
50
60
  maxSizeLabel: import("@angular/core").InputSignal<string>;
51
61
  /** Accessible name of a selected file's remove button. */
52
62
  removeFileAriaLabel: import("@angular/core").InputSignal<string>;
@@ -65,6 +75,11 @@ export declare class FileUploadComponent {
65
75
  protected selectedFiles: import("@angular/core").WritableSignal<FileUploadFile[]>;
66
76
  protected readonly fileInputRef: import("@angular/core").Signal<ElementRef<HTMLInputElement> | undefined>;
67
77
  protected dropzoneClasses: import("@angular/core").Signal<string>;
78
+ /**
79
+ * Name of the drop zone (AS-33, WCAG 2.5.3 Label in Name): `hint`, then the
80
+ * accepted and max lines it shows, comma-separated.
81
+ */
82
+ protected readonly dropzoneAriaLabel: import("@angular/core").Signal<string>;
68
83
  protected onDragOver(event: DragEvent): void;
69
84
  protected onDragLeave(event: DragEvent): void;
70
85
  protected onDrop(event: DragEvent): void;
@@ -48,7 +48,12 @@ export declare class GalleryComponent implements OnDestroy {
48
48
  readonly size: import("@angular/core").InputSignal<GallerySize>;
49
49
  /** Custom column count (overrides size preset) */
50
50
  readonly columns: import("@angular/core").InputSignal<number | null>;
51
- /** Whether to show captions below thumbnails */
51
+ /**
52
+ * Whether to show captions below thumbnails. A shown caption is part of the
53
+ * item's accessible name: `alt` (or `imageFallbackLabel` and its number),
54
+ * then the caption (WCAG 2.5.3 Label in Name).
55
+ * @default false
56
+ */
52
57
  readonly showCaptions: import("@angular/core").InputSignal<boolean>;
53
58
  /** Whether clicking a thumbnail opens the lightbox */
54
59
  readonly enableLightbox: import("@angular/core").InputSignal<boolean>;
@@ -74,7 +79,11 @@ export declare class GalleryComponent implements OnDestroy {
74
79
  readonly previousAriaLabel: import("@angular/core").InputSignal<string>;
75
80
  /** Accessible name of the next-image button. */
76
81
  readonly nextAriaLabel: import("@angular/core").InputSignal<string>;
77
- /** Accessible name of a thumbnail without `alt`, followed by its number. */
82
+ /**
83
+ * Accessible name of a thumbnail without `alt`, followed by its number. A
84
+ * shown caption follows (`Bild 3, Sonnenuntergang am See`).
85
+ * @default 'Image'
86
+ */
78
87
  readonly imageFallbackLabel: import("@angular/core").InputSignal<string>;
79
88
  /** Emitted when lightbox opens with item index */
80
89
  readonly lightboxOpen: import("@angular/core").OutputEmitterRef<number>;
@@ -134,6 +143,12 @@ export declare class GalleryComponent implements OnDestroy {
134
143
  protected onImageLoad(index: number): void;
135
144
  protected isLoaded(index: number): boolean;
136
145
  protected getThumbnail(item: GalleryItem): string;
146
+ /**
147
+ * Name of a thumbnail (AS-33, WCAG 2.5.3 Label in Name): `alt` (or
148
+ * `imageFallbackLabel` and its number), then the shown caption unless one
149
+ * contains the other.
150
+ */
151
+ protected itemAriaLabel(item: GalleryItem, index: number): string;
137
152
  protected trackByIndex(index: number): number;
138
153
  /**
139
154
  * Keyboard handling while the lightbox is open. Bound on the lightbox itself
@@ -231,7 +231,12 @@ export declare class HeaderComponent {
231
231
  readonly themeToDarkAriaLabel: import("@angular/core").InputSignal<string>;
232
232
  /** Label of the theme button while the dark theme is active. */
233
233
  readonly themeToLightAriaLabel: import("@angular/core").InputSignal<string>;
234
- /** Accessible name of the context button (`contextName`). */
234
+ /**
235
+ * Start of the context button's accessible name. The visible `contextLabel`
236
+ * and `contextName` follow it (WCAG 2.5.3 Label in Name), unless the label
237
+ * already contains them: `Context info, Tenant Acme Corp`.
238
+ * @default 'Context info'
239
+ */
235
240
  readonly contextAriaLabel: import("@angular/core").InputSignal<string>;
236
241
  /** Label of the Profile entry in the account menu. */
237
242
  readonly profileLabel: import("@angular/core").InputSignal<string>;
@@ -262,6 +267,12 @@ export declare class HeaderComponent {
262
267
  * as axe-core 4.14 does); an empty label becomes the initials alone.
263
268
  */
264
269
  protected readonly avatarTriggerAriaLabel: import("@angular/core").Signal<string>;
270
+ /**
271
+ * Name of the context button (AS-33, WCAG 2.5.3 Label in Name):
272
+ * `contextAriaLabel`, a comma and the visible `contextLabel` and
273
+ * `contextName`, so a voice-control user can say what they see.
274
+ */
275
+ protected readonly contextButtonAriaLabel: import("@angular/core").Signal<string>;
265
276
  /**
266
277
  * Get menu items for profile dropdown
267
278
  */
@@ -134,8 +134,36 @@ export declare class InputComponent implements ControlValueAccessor {
134
134
  readonly spellcheck: import("@angular/core").InputSignal<boolean | undefined>;
135
135
  /** Native `enterkeyhint`: the label of the on-screen keyboard's Enter key. Unset = no attribute. */
136
136
  readonly enterkeyhint: import("@angular/core").InputSignal<InputEnterKeyHint | undefined>;
137
- /** Accessible name of the `*` marker shown for `required` fields. */
137
+ /**
138
+ * Deprecated, no effect since 3.12. The `*` is decorative and hidden from
139
+ * assistive technology, because the native `required` state is announced
140
+ * already, in the screen reader's language; a name on the `*` made the field
141
+ * say "required" twice. Kept so existing bindings compile.
142
+ * @deprecated No effect since 3.12: the native `required` state is announced instead.
143
+ * @default 'required'
144
+ */
138
145
  readonly requiredAriaLabel: import("@angular/core").InputSignal<string>;
146
+ /**
147
+ * Id of the native `<input>`. The label's `for`, the error (`<id>-error`),
148
+ * the helper text (`<id>-helper`), the label id (`<id>-label`) and
149
+ * `aria-describedby` derive from it, so an error summary can link to the
150
+ * field (`<a href="#contact-email">`) and prerendered HTML is the same in
151
+ * every build. Unique in the document, no spaces. Unset (or empty) = a
152
+ * generated `lc-input-<n>`, which follows creation order (on the server it
153
+ * keeps counting across prerendered routes): set it on prerendered pages.
154
+ * @default generated (lc-input-<n>)
155
+ */
156
+ readonly controlId: import("@angular/core").InputSignal<string | undefined>;
157
+ /**
158
+ * Announce the error message the moment it appears (`role="alert"` on the
159
+ * message). Set `false` when an `lc-alert` error summary above the form
160
+ * already announces the errors (a page re-rendered after a failed submit,
161
+ * e.g. 422), so they aren't read twice. The message stays visible, stays
162
+ * linked through `aria-describedby` (read with the field), and the control
163
+ * keeps `aria-invalid`.
164
+ * @default true
165
+ */
166
+ readonly announceError: import("@angular/core").InputSignal<boolean>;
139
167
  /**
140
168
  * Current value (two-way bindable: `[value]` / `[(value)]`). Emits
141
169
  * `valueChange` whenever it changes — once per user keystroke, on `clear()`
@@ -154,7 +182,14 @@ export declare class InputComponent implements ControlValueAccessor {
154
182
  * Emitted when Enter key is pressed
155
183
  */
156
184
  readonly enterPressed: import("@angular/core").OutputEmitterRef<void>;
157
- readonly inputId: string;
185
+ /** Generated id, taken once per instance (also with `controlId`), so later ids don't shift. */
186
+ private readonly generatedId;
187
+ private readonly resolvedId;
188
+ /**
189
+ * Resolved id of the native control: `controlId`, or the generated
190
+ * `lc-input-<n>`. Read-only, same meaning as before 3.12.
191
+ */
192
+ get inputId(): string;
158
193
  private readonly inputElement;
159
194
  private readonly readonlyValueElement;
160
195
  private readonly _formDisabled;
@@ -332,6 +367,16 @@ export declare class InputComponent implements ControlValueAccessor {
332
367
  "required": false;
333
368
  "isSignal": true;
334
369
  };
370
+ "controlId": {
371
+ "alias": "controlId";
372
+ "required": false;
373
+ "isSignal": true;
374
+ };
375
+ "announceError": {
376
+ "alias": "announceError";
377
+ "required": false;
378
+ "isSignal": true;
379
+ };
335
380
  "value": {
336
381
  "alias": "value";
337
382
  "required": false;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Accessible names that contain the visible text (WCAG 2.5.3 Label in Name,
3
+ * AS-33). The containment test follows axe-core 4.14's
4
+ * `label-content-name-mismatch`: both strings are compatibility-decomposed,
5
+ * parentheticals are dropped, the rest is lower-cased and split into words of
6
+ * letters and digits, and the visible text's words must occur as one
7
+ * contiguous run in the name's words.
8
+ */
9
+ /**
10
+ * The words of `s` as axe-core 4.14 compares them for Label in Name.
11
+ * `toLowerCase`, not `toLocaleLowerCase`: server and browser must agree.
12
+ */
13
+ export declare function labelWords(s: string): string[];
14
+ /** Whether `needle` occurs in `haystack` as one contiguous run of words. */
15
+ export declare function containsWords(haystack: readonly string[], needle: readonly string[]): boolean;
16
+ /**
17
+ * Accessible name that contains the visible `text` (WCAG 2.5.3): `label`,
18
+ * then `, text`, unless one already contains the other.
19
+ *
20
+ * - Text without letters or digits, or one non-digit character (a symbol or
21
+ * an icon glyph, which axe reports as "incomplete"): `label`.
22
+ * - The label already contains the text: `label`, unchanged.
23
+ * - An empty label: `text`.
24
+ * - The text contains the label: `text`, so nothing is read twice.
25
+ * - Otherwise `label, text`.
26
+ */
27
+ export declare function nameWithText(label: string, text: string): string;
@@ -35,9 +35,18 @@ export declare class PaginationComponent {
35
35
  readonly previousLabel: import("@angular/core").InputSignal<string>;
36
36
  /** Visible text of the next button */
37
37
  readonly nextLabel: import("@angular/core").InputSignal<string>;
38
- /** Accessible name of the previous button (the visible text is hidden on small screens) */
38
+ /**
39
+ * Accessible name of the previous button (the visible text is hidden on
40
+ * small screens). `previousLabel` is appended when this doesn't contain it
41
+ * (WCAG 2.5.3 Label in Name), e.g. `Vorherige Seite, Zurück`.
42
+ * @default 'Previous page'
43
+ */
39
44
  readonly previousAriaLabel: import("@angular/core").InputSignal<string>;
40
- /** Accessible name of the next button */
45
+ /**
46
+ * Accessible name of the next button. `nextLabel` is appended when this
47
+ * doesn't contain it (WCAG 2.5.3 Label in Name), e.g. `Nächste Seite, Weiter`.
48
+ * @default 'Next page'
49
+ */
41
50
  readonly nextAriaLabel: import("@angular/core").InputSignal<string>;
42
51
  /** Accessible name of a page button; `{page}` is replaced with the page number */
43
52
  readonly pageAriaLabel: import("@angular/core").InputSignal<string>;
@@ -47,6 +56,10 @@ export declare class PaginationComponent {
47
56
  */
48
57
  readonly infoText: import("@angular/core").InputSignal<string>;
49
58
  readonly pageChange: import("@angular/core").OutputEmitterRef<number>;
59
+ /** Name of the previous button: its label, then the visible text unless contained (AS-33). */
60
+ protected readonly previousButtonAriaLabel: import("@angular/core").Signal<string>;
61
+ /** Name of the next button: its label, then the visible text unless contained (AS-33). */
62
+ protected readonly nextButtonAriaLabel: import("@angular/core").Signal<string>;
50
63
  totalPages: import("@angular/core").Signal<number>;
51
64
  firstItemIndex: import("@angular/core").Signal<number>;
52
65
  lastItemIndex: import("@angular/core").Signal<number>;
@@ -47,7 +47,13 @@ export declare class RichTextEditorComponent implements ControlValueAccessor {
47
47
  toolbar: import("@angular/core").InputSignal<ToolbarConfig>;
48
48
  /** Whether to show word count */
49
49
  showWordCount: import("@angular/core").InputSignal<boolean>;
50
- /** Tooltips and accessible names of the toolbar actions, by action (`{ bold: 'Fett' }`); missing ones keep the English default. */
50
+ /**
51
+ * Tooltips and accessible names of the toolbar actions, by action
52
+ * (`{ bold: 'Fett' }`); missing ones keep the English default. A visible
53
+ * button label with letters or digits that the title doesn't contain is
54
+ * appended (`Heading 1, H1`, WCAG 2.5.3 Label in Name).
55
+ * @default {}
56
+ */
51
57
  actionTitles: import("@angular/core").InputSignal<Partial<Record<ToolbarAction, string>>>;
52
58
  /** Label of the rich-text mode button. */
53
59
  richModeLabel: import("@angular/core").InputSignal<string>;
@@ -118,6 +124,12 @@ export declare class RichTextEditorComponent implements ControlValueAccessor {
118
124
  private getSelection;
119
125
  protected getActionLabel(action: ToolbarAction): string;
120
126
  protected getActionTitle(action: ToolbarAction): string;
127
+ /**
128
+ * Tooltip and accessible name of a toolbar button (AS-33, WCAG 2.5.3 Label
129
+ * in Name): the title, then the visible label when it has letters or digits
130
+ * the title doesn't contain (`Heading 1, H1`). Symbol labels keep the title.
131
+ */
132
+ protected getActionName(action: ToolbarAction): string;
121
133
  /**
122
134
  * Converts the editor's markdown subset to HTML. The source is HTML-escaped
123
135
  * *first*, so nothing in it can open a tag or an attribute; the conversions
@@ -35,12 +35,18 @@ export declare class StepperComponent {
35
35
  readonly linear: import("@angular/core").InputSignal<boolean>;
36
36
  /** Orientation */
37
37
  readonly orientation: import("@angular/core").InputSignal<"horizontal" | "vertical">;
38
- /** Text shown under optional steps */
38
+ /**
39
+ * Text shown under optional steps; it also ends the step's accessible name.
40
+ * @default 'Optional'
41
+ */
39
42
  readonly optionalLabel: import("@angular/core").InputSignal<string>;
40
43
  /**
41
44
  * Accessible name template for each step button. Placeholders: `{n}`
42
- * (1-based step number), `{m}` (step count), `{label}`. Optional steps get
43
- * `optionalLabel` appended.
45
+ * (1-based step number), `{m}` (step count), `{label}`. The step's visible
46
+ * description and `optionalLabel` follow the filled template,
47
+ * comma-separated (`Step 2 of 3: Profile, Set up your profile`). Keep
48
+ * `{label}` last, so that the name contains the visible text (WCAG 2.5.3).
49
+ * @default 'Step {n} of {m}: {label}'
44
50
  */
45
51
  readonly stepAriaLabel: import("@angular/core").InputSignal<string>;
46
52
  /** Emitted when active step changes */
@@ -60,9 +66,14 @@ export declare class StepperComponent {
60
66
  * only completed steps (and the active one) are reachable.
61
67
  */
62
68
  isStepNavigable(index: number): boolean;
63
- /** Accessible name for the step button at `index` */
69
+ /**
70
+ * Accessible name for the step button at `index` (AS-33, WCAG 2.5.3 Label
71
+ * in Name): the filled `stepAriaLabel`, then the visible description and
72
+ * `optionalLabel`, comma-separated, so the name contains the visible text.
73
+ * A template that doesn't end with `{label}` gets the label after it.
74
+ */
64
75
  getStepAriaLabel(index: number): string;
65
- /** Id of the description element for `aria-describedby` (or null) */
76
+ /** Id of the description element (or null); the description is part of the step's name. */
66
77
  getDescriptionId(index: number): string | null;
67
78
  /** Navigate to a specific step (only if navigable, see `isStepNavigable`) */
68
79
  goToStep(index: number): void;
@@ -57,13 +57,36 @@ export declare class TextareaComponent implements ControlValueAccessor {
57
57
  */
58
58
  readonly requiredIndicator: import("@angular/core").InputSignal<boolean>;
59
59
  /**
60
- * Accessible name of the `*` marker, for API parity with `lc-input`.
61
- * `lc-textarea` hides the `*` from assistive technology, because the native
62
- * `required` state is announced already and naming the `*` would say
63
- * "required" twice.
60
+ * Deprecated, no effect (it never had one in `lc-textarea`). The `*` is
61
+ * decorative and hidden from assistive technology, because the native
62
+ * `required` state is announced already, in the screen reader's language; a
63
+ * name on the `*` would make the field say "required" twice. Kept so
64
+ * existing bindings compile.
65
+ * @deprecated No effect: the native `required` state is announced instead.
64
66
  * @default 'required'
65
67
  */
66
68
  readonly requiredAriaLabel: import("@angular/core").InputSignal<string>;
69
+ /**
70
+ * Id of the native `<textarea>`. The label's `for`, the error
71
+ * (`<id>-error`), the helper text (`<id>-helper`) and `aria-describedby`
72
+ * derive from it, so an error summary can link to the field
73
+ * (`<a href="#contact-message">`) and prerendered HTML is the same in every
74
+ * build. Unique in the document, no spaces. Unset (or empty) = a generated
75
+ * `lc-textarea-<n>`, which follows creation order (on the server it keeps
76
+ * counting across prerendered routes): set it on prerendered pages.
77
+ * @default generated (lc-textarea-<n>)
78
+ */
79
+ readonly controlId: import("@angular/core").InputSignal<string | undefined>;
80
+ /**
81
+ * Announce the error message the moment it appears (`role="alert"` on the
82
+ * message). Set `false` when an `lc-alert` error summary above the form
83
+ * already announces the errors (a page re-rendered after a failed submit,
84
+ * e.g. 422), so they aren't read twice. The message stays visible, stays
85
+ * linked through `aria-describedby` (read with the field), and the control
86
+ * keeps `aria-invalid`.
87
+ * @default true
88
+ */
89
+ readonly announceError: import("@angular/core").InputSignal<boolean>;
67
90
  /**
68
91
  * Whether the textarea is readonly
69
92
  * @default false
@@ -147,10 +170,15 @@ export declare class TextareaComponent implements ControlValueAccessor {
147
170
  * compiling as in 3.9. Template bindings `(valueChange)` use the model.
148
171
  */
149
172
  readonly valueChange: OutputRef<string>;
150
- /** Per-instance id: links label, error and helper text to the control. */
151
- readonly textareaId: string;
152
- readonly errorId: string;
153
- readonly helperId: string;
173
+ /** Generated id, taken once per instance (also with `controlId`), so later ids don't shift. */
174
+ private readonly generatedId;
175
+ private readonly resolvedId;
176
+ /** Id of the native textarea: `controlId`, or the generated `lc-textarea-<n>`. Links label, error and helper text to it. */
177
+ get textareaId(): string;
178
+ /** Id of the error message: `<textareaId>-error`. */
179
+ get errorId(): string;
180
+ /** Id of the helper text: `<textareaId>-helper`. */
181
+ get helperId(): string;
154
182
  /**
155
183
  * The native textarea's value. On the server a leading line break gets one
156
184
  * extra `\n`: the HTML parser drops the first line break after `<textarea>`,
@@ -231,6 +259,16 @@ export declare class TextareaComponent implements ControlValueAccessor {
231
259
  "required": false;
232
260
  "isSignal": true;
233
261
  };
262
+ "controlId": {
263
+ "alias": "controlId";
264
+ "required": false;
265
+ "isSignal": true;
266
+ };
267
+ "announceError": {
268
+ "alias": "announceError";
269
+ "required": false;
270
+ "isSignal": true;
271
+ };
234
272
  "readonly": {
235
273
  "alias": "readonly";
236
274
  "required": false;