@lifecorner/angular-ui-kit 3.26.0 → 3.27.1

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 (66) hide show
  1. package/fesm2022/lifecorner-angular-ui-kit-callout.mjs +2 -2
  2. package/fesm2022/lifecorner-angular-ui-kit-callout.mjs.map +1 -1
  3. package/fesm2022/lifecorner-angular-ui-kit-chat.mjs +2 -2
  4. package/fesm2022/lifecorner-angular-ui-kit-chat.mjs.map +1 -1
  5. package/fesm2022/lifecorner-angular-ui-kit-color-picker.mjs +2 -2
  6. package/fesm2022/lifecorner-angular-ui-kit-color-picker.mjs.map +1 -1
  7. package/fesm2022/lifecorner-angular-ui-kit-date-range-picker.mjs +2 -2
  8. package/fesm2022/lifecorner-angular-ui-kit-date-range-picker.mjs.map +1 -1
  9. package/fesm2022/lifecorner-angular-ui-kit-datepicker.mjs +2 -2
  10. package/fesm2022/lifecorner-angular-ui-kit-datepicker.mjs.map +1 -1
  11. package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs +47 -9
  12. package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs.map +1 -1
  13. package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs +2 -2
  14. package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs.map +1 -1
  15. package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs +2 -2
  16. package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs.map +1 -1
  17. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs +4 -4
  18. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs.map +1 -1
  19. package/fesm2022/lifecorner-angular-ui-kit-filter-bar.mjs +2 -2
  20. package/fesm2022/lifecorner-angular-ui-kit-filter-bar.mjs.map +1 -1
  21. package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs +2 -2
  22. package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs.map +1 -1
  23. package/fesm2022/lifecorner-angular-ui-kit-gantt-chart.mjs +2 -2
  24. package/fesm2022/lifecorner-angular-ui-kit-gantt-chart.mjs.map +1 -1
  25. package/fesm2022/lifecorner-angular-ui-kit-header.mjs +2 -2
  26. package/fesm2022/lifecorner-angular-ui-kit-header.mjs.map +1 -1
  27. package/fesm2022/lifecorner-angular-ui-kit-input.mjs +2 -2
  28. package/fesm2022/lifecorner-angular-ui-kit-input.mjs.map +1 -1
  29. package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs +2 -2
  30. package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs.map +1 -1
  31. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs +4 -4
  32. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs.map +1 -1
  33. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs +2 -2
  34. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs.map +1 -1
  35. package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs +2 -2
  36. package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs.map +1 -1
  37. package/fesm2022/lifecorner-angular-ui-kit-rich-text-editor.mjs +2 -2
  38. package/fesm2022/lifecorner-angular-ui-kit-rich-text-editor.mjs.map +1 -1
  39. package/fesm2022/lifecorner-angular-ui-kit-scatter-plot.mjs +2 -2
  40. package/fesm2022/lifecorner-angular-ui-kit-scatter-plot.mjs.map +1 -1
  41. package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs +2 -2
  42. package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs.map +1 -1
  43. package/fesm2022/lifecorner-angular-ui-kit-select.mjs +2 -2
  44. package/fesm2022/lifecorner-angular-ui-kit-select.mjs.map +1 -1
  45. package/fesm2022/lifecorner-angular-ui-kit-split-pane.mjs +2 -2
  46. package/fesm2022/lifecorner-angular-ui-kit-split-pane.mjs.map +1 -1
  47. package/fesm2022/lifecorner-angular-ui-kit-table.mjs +4 -4
  48. package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
  49. package/fesm2022/lifecorner-angular-ui-kit-tabs.mjs +2 -2
  50. package/fesm2022/lifecorner-angular-ui-kit-tabs.mjs.map +1 -1
  51. package/fesm2022/lifecorner-angular-ui-kit-tag-input.mjs +2 -2
  52. package/fesm2022/lifecorner-angular-ui-kit-tag-input.mjs.map +1 -1
  53. package/fesm2022/lifecorner-angular-ui-kit-textarea.mjs +2 -2
  54. package/fesm2022/lifecorner-angular-ui-kit-textarea.mjs.map +1 -1
  55. package/fesm2022/lifecorner-angular-ui-kit-toggle-group.mjs +2 -2
  56. package/fesm2022/lifecorner-angular-ui-kit-toggle-group.mjs.map +1 -1
  57. package/package.json +1 -1
  58. package/src/styles/_reset.scss +4 -2
  59. package/src/styles/_theme-dark.scss +154 -137
  60. package/src/styles/_theme-light.scss +158 -138
  61. package/src/styles/focus-indicators.spec.ts +205 -0
  62. package/src/styles/theme-contrast.spec.ts +14 -2
  63. package/src/styles/theme-modes.spec.ts +40 -0
  64. package/src/styles/touch-targets-contrast.spec.ts +286 -0
  65. package/types/lifecorner-angular-ui-kit-dependency-viewer.d.ts +33 -4
  66. package/types/lifecorner-angular-ui-kit-file-upload.d.ts +6 -1
@@ -248,7 +248,7 @@ var TagInputComponent = class TagInputComponent {
248
248
  }],
249
249
  ngImport: i0,
250
250
  template: "@if (label()) {\n <label class=\"lc-tag-input__label\" [for]=\"inputId\">{{ label() }}</label>\n}\n\n<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- container click only forwards focus to the input, which is focusable itself -->\n<div\n class=\"lc-tag-input__container\"\n [class.lc-tag-input__container--focused]=\"focused()\"\n [class.lc-tag-input__container--disabled]=\"isDisabled()\"\n (click)=\"focusInput()\"\n>\n @for (tag of tags(); track $index) {\n <span class=\"lc-tag-input__tag\">\n <span class=\"lc-tag-input__tag-text\">{{ tag }}</span>\n @if (removable() && !isDisabled()) {\n <button\n type=\"button\"\n class=\"lc-tag-input__tag-remove\"\n (click)=\"removeTag($index); $event.stopPropagation()\"\n [attr.aria-label]=\"removeTagAriaLabel() + ' ' + tag\"\n >×</button>\n }\n </span>\n }\n\n <input\n #inputEl\n type=\"text\"\n [id]=\"inputId\"\n class=\"lc-tag-input__input\"\n [attr.role]=\"hasSuggestions() ? 'combobox' : null\"\n [attr.aria-autocomplete]=\"hasSuggestions() ? 'list' : null\"\n [attr.aria-expanded]=\"hasSuggestions() ? showSuggestions() : null\"\n [attr.aria-controls]=\"showSuggestions() ? listboxId : null\"\n [attr.aria-activedescendant]=\"showSuggestions() && activeIndex() >= 0 ? optionId(activeIndex()) : null\"\n [placeholder]=\"inputPlaceholder()\"\n [value]=\"inputValue()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"!canAdd()\"\n [attr.aria-readonly]=\"!canAdd() ? true : null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n</div>\n\n@if (showSuggestions()) {\n <ul class=\"lc-tag-input__suggestions\" role=\"listbox\" [id]=\"listboxId\" [attr.aria-label]=\"label() || suggestionsAriaLabel()\">\n @for (sug of filteredSuggestions(); track sug; let i = $index) {\n <li\n class=\"lc-tag-input__suggestion\"\n [class.lc-tag-input__suggestion--active]=\"i === activeIndex()\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n (mouseenter)=\"activeIndex.set(i)\"\n (mousedown)=\"selectSuggestion(sug); $event.preventDefault()\"\n >{{ sug }}</li>\n }\n </ul>\n}\n",
251
- styles: [":host{display:block;position:relative}.lc-tag-input__label{display:block;margin-bottom:4px;font-size:var(--typography-font-size-sm, .875rem);font-weight:500;color:var(--color-text-secondary)}.lc-tag-input__container{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:6px 8px;min-height:40px;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);background:var(--color-surface-2);cursor:text;transition:border-color .15s,box-shadow .15s}.lc-tag-input__container:hover{border-color:var(--color-text-secondary)}.lc-tag-input__container--focused{border-color:var(--color-primary-500);box-shadow:0 0 0 2px #20849740}.lc-tag-input__container--disabled{opacity:.6;background:var(--color-surface-muted);cursor:default}.lc-tag-input__tag{display:inline-flex;align-items:center;gap:2px;padding:2px 8px;border-radius:var(--border-radius-full);background:#20849729;border:1px solid var(--color-border-strong);color:var(--color-primary-500);font-size:var(--typography-font-size-sm, .875rem);line-height:1.4;-webkit-user-select:none;user-select:none}.lc-tag-input__tag-text{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-tag-input__tag-remove{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border:none;border-radius:50%;background:transparent;color:currentColor;cursor:pointer;font-size:14px;line-height:1;padding:0;opacity:.7;transition:opacity .1s,background .1s}.lc-tag-input__tag-remove:hover{opacity:1;background:#20849733}.lc-tag-input__input{flex:1;min-width:60px;border:none;outline:none;background:transparent;font-size:var(--typography-font-size-sm, .875rem);color:var(--color-text-primary);padding:2px 0}.lc-tag-input__input::placeholder{color:var(--color-text-tertiary)}.lc-tag-input__input:read-only{cursor:default}.lc-tag-input__suggestions{position:absolute;z-index:50;top:100%;left:0;right:0;margin:4px 0 0;padding:4px 0;list-style:none;border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md);background-color:var(--color-surface);background-image:var(--gradient-surface);box-shadow:var(--elevation-4);max-height:180px;overflow-y:auto}.lc-tag-input__suggestion{padding:6px 12px;font-size:var(--typography-font-size-sm, .875rem);cursor:pointer;color:var(--color-text-secondary)}.lc-tag-input__suggestion:hover,.lc-tag-input__suggestion--active{background:var(--color-surface-hover);color:var(--color-text-primary)}\n"],
251
+ styles: [":host{display:block;position:relative}.lc-tag-input__label{display:block;margin-bottom:4px;font-size:var(--typography-font-size-sm, .875rem);font-weight:500;color:var(--color-text-secondary)}.lc-tag-input__container{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:6px 8px;min-height:40px;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);background:var(--color-surface-2);cursor:text;transition:border-color .15s,box-shadow .15s}.lc-tag-input__container:hover:not(.lc-tag-input__container--focused){border-color:var(--color-text-secondary)}.lc-tag-input__container--focused{border-color:var(--color-primary-500);box-shadow:0 0 0 2px #20849740}.lc-tag-input__container--disabled{opacity:.6;background:var(--color-surface-muted);cursor:default}.lc-tag-input__tag{display:inline-flex;align-items:center;gap:2px;padding:2px 8px;border-radius:var(--border-radius-full);background:#20849729;border:1px solid var(--color-border-strong);color:var(--color-on-primary-subtle);font-size:var(--typography-font-size-sm, .875rem);line-height:1.4;-webkit-user-select:none;user-select:none}.lc-tag-input__tag-text{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-tag-input__tag-remove{position:relative;display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border:none;border-radius:50%;background:transparent;color:currentColor;cursor:pointer;font-size:14px;line-height:1;padding:0;opacity:.7;transition:opacity .1s,background .1s}.lc-tag-input__tag-remove:before{content:\"\";position:absolute;inset:-.25rem}.lc-tag-input__tag-remove:hover{opacity:1;background:#20849733}.lc-tag-input__tag-remove:focus-visible{opacity:1;outline:2px solid var(--color-primary-500);outline-offset:2px}@media(forced-colors:active){.lc-tag-input__tag-remove:focus-visible{outline-color:Highlight}}.lc-tag-input__input{flex:1;min-width:60px;border:none;outline:none;background:transparent;font-size:var(--typography-font-size-sm, .875rem);color:var(--color-text-primary);padding:2px 0}.lc-tag-input__input::placeholder{color:var(--color-text-tertiary)}.lc-tag-input__input:read-only{cursor:default}.lc-tag-input__suggestions{position:absolute;z-index:50;top:100%;left:0;right:0;margin:4px 0 0;padding:4px 0;list-style:none;border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md);background-color:var(--color-surface);background-image:var(--gradient-surface);box-shadow:var(--elevation-4);max-height:180px;overflow-y:auto}.lc-tag-input__suggestion{padding:6px 12px;font-size:var(--typography-font-size-sm, .875rem);cursor:pointer;color:var(--color-text-secondary)}.lc-tag-input__suggestion:hover,.lc-tag-input__suggestion--active{background:var(--color-surface-hover);color:var(--color-text-primary)}\n"],
252
252
  changeDetection: i0.ChangeDetectionStrategy.OnPush
253
253
  });
254
254
  };
@@ -269,7 +269,7 @@ i0.ɵɵngDeclareClassMetadata({
269
269
  multi: true
270
270
  }],
271
271
  template: "@if (label()) {\n <label class=\"lc-tag-input__label\" [for]=\"inputId\">{{ label() }}</label>\n}\n\n<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- container click only forwards focus to the input, which is focusable itself -->\n<div\n class=\"lc-tag-input__container\"\n [class.lc-tag-input__container--focused]=\"focused()\"\n [class.lc-tag-input__container--disabled]=\"isDisabled()\"\n (click)=\"focusInput()\"\n>\n @for (tag of tags(); track $index) {\n <span class=\"lc-tag-input__tag\">\n <span class=\"lc-tag-input__tag-text\">{{ tag }}</span>\n @if (removable() && !isDisabled()) {\n <button\n type=\"button\"\n class=\"lc-tag-input__tag-remove\"\n (click)=\"removeTag($index); $event.stopPropagation()\"\n [attr.aria-label]=\"removeTagAriaLabel() + ' ' + tag\"\n >×</button>\n }\n </span>\n }\n\n <input\n #inputEl\n type=\"text\"\n [id]=\"inputId\"\n class=\"lc-tag-input__input\"\n [attr.role]=\"hasSuggestions() ? 'combobox' : null\"\n [attr.aria-autocomplete]=\"hasSuggestions() ? 'list' : null\"\n [attr.aria-expanded]=\"hasSuggestions() ? showSuggestions() : null\"\n [attr.aria-controls]=\"showSuggestions() ? listboxId : null\"\n [attr.aria-activedescendant]=\"showSuggestions() && activeIndex() >= 0 ? optionId(activeIndex()) : null\"\n [placeholder]=\"inputPlaceholder()\"\n [value]=\"inputValue()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"!canAdd()\"\n [attr.aria-readonly]=\"!canAdd() ? true : null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n</div>\n\n@if (showSuggestions()) {\n <ul class=\"lc-tag-input__suggestions\" role=\"listbox\" [id]=\"listboxId\" [attr.aria-label]=\"label() || suggestionsAriaLabel()\">\n @for (sug of filteredSuggestions(); track sug; let i = $index) {\n <li\n class=\"lc-tag-input__suggestion\"\n [class.lc-tag-input__suggestion--active]=\"i === activeIndex()\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n (mouseenter)=\"activeIndex.set(i)\"\n (mousedown)=\"selectSuggestion(sug); $event.preventDefault()\"\n >{{ sug }}</li>\n }\n </ul>\n}\n",
272
- styles: [":host{display:block;position:relative}.lc-tag-input__label{display:block;margin-bottom:4px;font-size:var(--typography-font-size-sm, .875rem);font-weight:500;color:var(--color-text-secondary)}.lc-tag-input__container{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:6px 8px;min-height:40px;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);background:var(--color-surface-2);cursor:text;transition:border-color .15s,box-shadow .15s}.lc-tag-input__container:hover{border-color:var(--color-text-secondary)}.lc-tag-input__container--focused{border-color:var(--color-primary-500);box-shadow:0 0 0 2px #20849740}.lc-tag-input__container--disabled{opacity:.6;background:var(--color-surface-muted);cursor:default}.lc-tag-input__tag{display:inline-flex;align-items:center;gap:2px;padding:2px 8px;border-radius:var(--border-radius-full);background:#20849729;border:1px solid var(--color-border-strong);color:var(--color-primary-500);font-size:var(--typography-font-size-sm, .875rem);line-height:1.4;-webkit-user-select:none;user-select:none}.lc-tag-input__tag-text{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-tag-input__tag-remove{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border:none;border-radius:50%;background:transparent;color:currentColor;cursor:pointer;font-size:14px;line-height:1;padding:0;opacity:.7;transition:opacity .1s,background .1s}.lc-tag-input__tag-remove:hover{opacity:1;background:#20849733}.lc-tag-input__input{flex:1;min-width:60px;border:none;outline:none;background:transparent;font-size:var(--typography-font-size-sm, .875rem);color:var(--color-text-primary);padding:2px 0}.lc-tag-input__input::placeholder{color:var(--color-text-tertiary)}.lc-tag-input__input:read-only{cursor:default}.lc-tag-input__suggestions{position:absolute;z-index:50;top:100%;left:0;right:0;margin:4px 0 0;padding:4px 0;list-style:none;border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md);background-color:var(--color-surface);background-image:var(--gradient-surface);box-shadow:var(--elevation-4);max-height:180px;overflow-y:auto}.lc-tag-input__suggestion{padding:6px 12px;font-size:var(--typography-font-size-sm, .875rem);cursor:pointer;color:var(--color-text-secondary)}.lc-tag-input__suggestion:hover,.lc-tag-input__suggestion--active{background:var(--color-surface-hover);color:var(--color-text-primary)}\n"]
272
+ styles: [":host{display:block;position:relative}.lc-tag-input__label{display:block;margin-bottom:4px;font-size:var(--typography-font-size-sm, .875rem);font-weight:500;color:var(--color-text-secondary)}.lc-tag-input__container{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:6px 8px;min-height:40px;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);background:var(--color-surface-2);cursor:text;transition:border-color .15s,box-shadow .15s}.lc-tag-input__container:hover:not(.lc-tag-input__container--focused){border-color:var(--color-text-secondary)}.lc-tag-input__container--focused{border-color:var(--color-primary-500);box-shadow:0 0 0 2px #20849740}.lc-tag-input__container--disabled{opacity:.6;background:var(--color-surface-muted);cursor:default}.lc-tag-input__tag{display:inline-flex;align-items:center;gap:2px;padding:2px 8px;border-radius:var(--border-radius-full);background:#20849729;border:1px solid var(--color-border-strong);color:var(--color-on-primary-subtle);font-size:var(--typography-font-size-sm, .875rem);line-height:1.4;-webkit-user-select:none;user-select:none}.lc-tag-input__tag-text{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-tag-input__tag-remove{position:relative;display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border:none;border-radius:50%;background:transparent;color:currentColor;cursor:pointer;font-size:14px;line-height:1;padding:0;opacity:.7;transition:opacity .1s,background .1s}.lc-tag-input__tag-remove:before{content:\"\";position:absolute;inset:-.25rem}.lc-tag-input__tag-remove:hover{opacity:1;background:#20849733}.lc-tag-input__tag-remove:focus-visible{opacity:1;outline:2px solid var(--color-primary-500);outline-offset:2px}@media(forced-colors:active){.lc-tag-input__tag-remove:focus-visible{outline-color:Highlight}}.lc-tag-input__input{flex:1;min-width:60px;border:none;outline:none;background:transparent;font-size:var(--typography-font-size-sm, .875rem);color:var(--color-text-primary);padding:2px 0}.lc-tag-input__input::placeholder{color:var(--color-text-tertiary)}.lc-tag-input__input:read-only{cursor:default}.lc-tag-input__suggestions{position:absolute;z-index:50;top:100%;left:0;right:0;margin:4px 0 0;padding:4px 0;list-style:none;border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md);background-color:var(--color-surface);background-image:var(--gradient-surface);box-shadow:var(--elevation-4);max-height:180px;overflow-y:auto}.lc-tag-input__suggestion{padding:6px 12px;font-size:var(--typography-font-size-sm, .875rem);cursor:pointer;color:var(--color-text-secondary)}.lc-tag-input__suggestion:hover,.lc-tag-input__suggestion--active{background:var(--color-surface-hover);color:var(--color-text-primary)}\n"]
273
273
  }]
274
274
  }],
275
275
  propDecorators: {
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-tag-input.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/tag-input/tag-input.component.ts","../../../../libs/angular-ui-kit/src/lib/tag-input/tag-input.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n signal,\n computed,\n output,\n forwardRef,\n ElementRef,\n viewChild,\n} from '@angular/core';\nimport { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';\n\n@Component({\n selector: 'lc-tag-input',\n standalone: true,\n templateUrl: './tag-input.component.html',\n styleUrls: ['./tag-input.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => TagInputComponent),\n multi: true,\n },\n ],\n})\nexport class TagInputComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n readonly placeholder = input('Add tag…');\n readonly maxTags = input(Infinity);\n readonly allowDuplicates = input(false);\n readonly removable = input(true);\n readonly disabled = input(false);\n readonly separator = input<'enter' | 'comma' | 'both'>('both');\n readonly suggestions = input<string[]>([]);\n readonly label = input('');\n\n /** Placeholder once `maxTags` is reached; `{max}` is replaced by the limit. */\n readonly maxTagsReachedLabel = input('Maximum of {max} tags reached');\n\n /** Accessible name prefix of a tag's remove button, followed by the tag. */\n readonly removeTagAriaLabel = input('Remove');\n\n /** Accessible name of the suggestion list when no `label` is set. */\n readonly suggestionsAriaLabel = input('Suggestions');\n\n readonly tagAdded = output<string>();\n readonly tagRemoved = output<string>();\n\n /** Per-instance id wiring the label, the suggestion listbox and its options to the input. */\n readonly inputId = `lc-tag-input-${++TagInputComponent.nextId}`;\n protected readonly listboxId = `${this.inputId}-listbox`;\n\n protected tags = signal<string[]>([]);\n protected inputValue = signal('');\n protected focused = signal(false);\n\n /** Index of the keyboard-highlighted suggestion; -1 = none. */\n protected activeIndex = signal(-1);\n\n /** Set by Escape; hides the list until the text changes again. */\n private readonly suggestionsDismissed = signal(false);\n\n /** Disabled through the form control (`setDisabledState`), as opposed to the input. */\n private readonly formDisabled = signal(false);\n\n /** Disabled by either route — the one flag the template consults. */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n protected readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputEl');\n\n protected readonly filteredSuggestions = computed(() => {\n const val = this.inputValue().toLowerCase().trim();\n if (!val) return [];\n return this.suggestions().filter(\n s => s.toLowerCase().includes(val) && (this.allowDuplicates() || !this.tags().includes(s))\n );\n });\n\n /** Combobox semantics only make sense when there is a list to open. */\n protected readonly hasSuggestions = computed(() => this.suggestions().length > 0);\n\n protected readonly showSuggestions = computed(\n () => this.focused() && !this.suggestionsDismissed() && this.filteredSuggestions().length > 0\n );\n\n protected readonly canAdd = computed(() => this.tags().length < this.maxTags());\n\n /**\n * The input stays mounted when the limit is reached (removing it would drop\n * focus to `<body>`); it becomes read-only and says why.\n */\n protected readonly inputPlaceholder = computed(() => {\n if (!this.canAdd()) return this.maxTagsReachedLabel().replace('{max}', String(this.maxTags()));\n return this.tags().length ? '' : this.placeholder();\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (val: string[]) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n writeValue(value: string[] | null): void {\n this.tags.set(value ?? []);\n }\n\n registerOnChange(fn: (val: 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 protected optionId(index: number): string {\n return `${this.listboxId}-option-${index}`;\n }\n\n protected addTag(value: string): void {\n const tag = value.trim();\n if (!tag || !this.canAdd()) return;\n if (!this.allowDuplicates() && this.tags().includes(tag)) return;\n\n this.tags.update(t => [...t, tag]);\n this.inputValue.set('');\n this.activeIndex.set(-1);\n this.onChange(this.tags());\n this.tagAdded.emit(tag);\n }\n\n protected removeTag(index: number): void {\n const removed = this.tags()[index];\n this.tags.update(t => t.filter((_, i) => i !== index));\n this.onChange(this.tags());\n this.tagRemoved.emit(removed);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n const sep = this.separator();\n const val = this.inputValue();\n const listOpen = this.showSuggestions();\n\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowUp': {\n const count = this.filteredSuggestions().length;\n if (!count) return;\n event.preventDefault();\n this.suggestionsDismissed.set(false);\n const delta = event.key === 'ArrowDown' ? 1 : -1;\n const next = (this.activeIndex() + delta + count) % count;\n this.activeIndex.set(next);\n document.getElementById(this.optionId(next))?.scrollIntoView?.({ block: 'nearest' });\n return;\n }\n case 'Escape':\n if (listOpen) {\n event.preventDefault();\n this.suggestionsDismissed.set(true);\n this.activeIndex.set(-1);\n }\n return;\n case 'Enter':\n if (listOpen && this.activeIndex() >= 0) {\n event.preventDefault();\n this.addTag(this.filteredSuggestions()[this.activeIndex()]);\n } else if (sep === 'enter' || sep === 'both') {\n event.preventDefault();\n this.addTag(val);\n }\n return;\n case ',':\n if (sep === 'comma' || sep === 'both') {\n event.preventDefault();\n this.addTag(val);\n }\n return;\n case 'Backspace':\n if (!val && this.removable() && this.tags().length > 0) {\n this.removeTag(this.tags().length - 1);\n }\n return;\n }\n }\n\n protected onInput(event: Event): void {\n this.inputValue.set((event.target as HTMLInputElement).value);\n this.activeIndex.set(-1);\n this.suggestionsDismissed.set(false);\n }\n\n protected onFocus(): void {\n this.focused.set(true);\n }\n\n protected onBlur(): void {\n this.focused.set(false);\n this.activeIndex.set(-1);\n this.onTouched();\n const val = this.inputValue();\n if (val.trim()) this.addTag(val);\n }\n\n protected selectSuggestion(value: string): void {\n this.addTag(value);\n this.inputEl()?.nativeElement.focus();\n }\n\n protected focusInput(): void {\n this.inputEl()?.nativeElement.focus();\n }\n}\n","@if (label()) {\n <label class=\"lc-tag-input__label\" [for]=\"inputId\">{{ label() }}</label>\n}\n\n<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- container click only forwards focus to the input, which is focusable itself -->\n<div\n class=\"lc-tag-input__container\"\n [class.lc-tag-input__container--focused]=\"focused()\"\n [class.lc-tag-input__container--disabled]=\"isDisabled()\"\n (click)=\"focusInput()\"\n>\n @for (tag of tags(); track $index) {\n <span class=\"lc-tag-input__tag\">\n <span class=\"lc-tag-input__tag-text\">{{ tag }}</span>\n @if (removable() && !isDisabled()) {\n <button\n type=\"button\"\n class=\"lc-tag-input__tag-remove\"\n (click)=\"removeTag($index); $event.stopPropagation()\"\n [attr.aria-label]=\"removeTagAriaLabel() + ' ' + tag\"\n >×</button>\n }\n </span>\n }\n\n <input\n #inputEl\n type=\"text\"\n [id]=\"inputId\"\n class=\"lc-tag-input__input\"\n [attr.role]=\"hasSuggestions() ? 'combobox' : null\"\n [attr.aria-autocomplete]=\"hasSuggestions() ? 'list' : null\"\n [attr.aria-expanded]=\"hasSuggestions() ? showSuggestions() : null\"\n [attr.aria-controls]=\"showSuggestions() ? listboxId : null\"\n [attr.aria-activedescendant]=\"showSuggestions() && activeIndex() >= 0 ? optionId(activeIndex()) : null\"\n [placeholder]=\"inputPlaceholder()\"\n [value]=\"inputValue()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"!canAdd()\"\n [attr.aria-readonly]=\"!canAdd() ? true : null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n</div>\n\n@if (showSuggestions()) {\n <ul class=\"lc-tag-input__suggestions\" role=\"listbox\" [id]=\"listboxId\" [attr.aria-label]=\"label() || suggestionsAriaLabel()\">\n @for (sug of filteredSuggestions(); track sug; let i = $index) {\n <li\n class=\"lc-tag-input__suggestion\"\n [class.lc-tag-input__suggestion--active]=\"i === activeIndex()\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n (mouseenter)=\"activeIndex.set(i)\"\n (mousedown)=\"selectSuggestion(sug); $event.preventDefault()\"\n >{{ sug }}</li>\n }\n </ul>\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-tag-input.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/tag-input/tag-input.component.ts","../../../../libs/angular-ui-kit/src/lib/tag-input/tag-input.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n signal,\n computed,\n output,\n forwardRef,\n ElementRef,\n viewChild,\n} from '@angular/core';\nimport { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';\n\n@Component({\n selector: 'lc-tag-input',\n standalone: true,\n templateUrl: './tag-input.component.html',\n styleUrls: ['./tag-input.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => TagInputComponent),\n multi: true,\n },\n ],\n})\nexport class TagInputComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n readonly placeholder = input('Add tag…');\n readonly maxTags = input(Infinity);\n readonly allowDuplicates = input(false);\n readonly removable = input(true);\n readonly disabled = input(false);\n readonly separator = input<'enter' | 'comma' | 'both'>('both');\n readonly suggestions = input<string[]>([]);\n readonly label = input('');\n\n /** Placeholder once `maxTags` is reached; `{max}` is replaced by the limit. */\n readonly maxTagsReachedLabel = input('Maximum of {max} tags reached');\n\n /** Accessible name prefix of a tag's remove button, followed by the tag. */\n readonly removeTagAriaLabel = input('Remove');\n\n /** Accessible name of the suggestion list when no `label` is set. */\n readonly suggestionsAriaLabel = input('Suggestions');\n\n readonly tagAdded = output<string>();\n readonly tagRemoved = output<string>();\n\n /** Per-instance id wiring the label, the suggestion listbox and its options to the input. */\n readonly inputId = `lc-tag-input-${++TagInputComponent.nextId}`;\n protected readonly listboxId = `${this.inputId}-listbox`;\n\n protected tags = signal<string[]>([]);\n protected inputValue = signal('');\n protected focused = signal(false);\n\n /** Index of the keyboard-highlighted suggestion; -1 = none. */\n protected activeIndex = signal(-1);\n\n /** Set by Escape; hides the list until the text changes again. */\n private readonly suggestionsDismissed = signal(false);\n\n /** Disabled through the form control (`setDisabledState`), as opposed to the input. */\n private readonly formDisabled = signal(false);\n\n /** Disabled by either route — the one flag the template consults. */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n protected readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputEl');\n\n protected readonly filteredSuggestions = computed(() => {\n const val = this.inputValue().toLowerCase().trim();\n if (!val) return [];\n return this.suggestions().filter(\n s => s.toLowerCase().includes(val) && (this.allowDuplicates() || !this.tags().includes(s))\n );\n });\n\n /** Combobox semantics only make sense when there is a list to open. */\n protected readonly hasSuggestions = computed(() => this.suggestions().length > 0);\n\n protected readonly showSuggestions = computed(\n () => this.focused() && !this.suggestionsDismissed() && this.filteredSuggestions().length > 0\n );\n\n protected readonly canAdd = computed(() => this.tags().length < this.maxTags());\n\n /**\n * The input stays mounted when the limit is reached (removing it would drop\n * focus to `<body>`); it becomes read-only and says why.\n */\n protected readonly inputPlaceholder = computed(() => {\n if (!this.canAdd()) return this.maxTagsReachedLabel().replace('{max}', String(this.maxTags()));\n return this.tags().length ? '' : this.placeholder();\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (val: string[]) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n writeValue(value: string[] | null): void {\n this.tags.set(value ?? []);\n }\n\n registerOnChange(fn: (val: 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 protected optionId(index: number): string {\n return `${this.listboxId}-option-${index}`;\n }\n\n protected addTag(value: string): void {\n const tag = value.trim();\n if (!tag || !this.canAdd()) return;\n if (!this.allowDuplicates() && this.tags().includes(tag)) return;\n\n this.tags.update(t => [...t, tag]);\n this.inputValue.set('');\n this.activeIndex.set(-1);\n this.onChange(this.tags());\n this.tagAdded.emit(tag);\n }\n\n protected removeTag(index: number): void {\n const removed = this.tags()[index];\n this.tags.update(t => t.filter((_, i) => i !== index));\n this.onChange(this.tags());\n this.tagRemoved.emit(removed);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n const sep = this.separator();\n const val = this.inputValue();\n const listOpen = this.showSuggestions();\n\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowUp': {\n const count = this.filteredSuggestions().length;\n if (!count) return;\n event.preventDefault();\n this.suggestionsDismissed.set(false);\n const delta = event.key === 'ArrowDown' ? 1 : -1;\n const next = (this.activeIndex() + delta + count) % count;\n this.activeIndex.set(next);\n document.getElementById(this.optionId(next))?.scrollIntoView?.({ block: 'nearest' });\n return;\n }\n case 'Escape':\n if (listOpen) {\n event.preventDefault();\n this.suggestionsDismissed.set(true);\n this.activeIndex.set(-1);\n }\n return;\n case 'Enter':\n if (listOpen && this.activeIndex() >= 0) {\n event.preventDefault();\n this.addTag(this.filteredSuggestions()[this.activeIndex()]);\n } else if (sep === 'enter' || sep === 'both') {\n event.preventDefault();\n this.addTag(val);\n }\n return;\n case ',':\n if (sep === 'comma' || sep === 'both') {\n event.preventDefault();\n this.addTag(val);\n }\n return;\n case 'Backspace':\n if (!val && this.removable() && this.tags().length > 0) {\n this.removeTag(this.tags().length - 1);\n }\n return;\n }\n }\n\n protected onInput(event: Event): void {\n this.inputValue.set((event.target as HTMLInputElement).value);\n this.activeIndex.set(-1);\n this.suggestionsDismissed.set(false);\n }\n\n protected onFocus(): void {\n this.focused.set(true);\n }\n\n protected onBlur(): void {\n this.focused.set(false);\n this.activeIndex.set(-1);\n this.onTouched();\n const val = this.inputValue();\n if (val.trim()) this.addTag(val);\n }\n\n protected selectSuggestion(value: string): void {\n this.addTag(value);\n this.inputEl()?.nativeElement.focus();\n }\n\n protected focusInput(): void {\n this.inputEl()?.nativeElement.focus();\n }\n}\n","@if (label()) {\n <label class=\"lc-tag-input__label\" [for]=\"inputId\">{{ label() }}</label>\n}\n\n<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- container click only forwards focus to the input, which is focusable itself -->\n<div\n class=\"lc-tag-input__container\"\n [class.lc-tag-input__container--focused]=\"focused()\"\n [class.lc-tag-input__container--disabled]=\"isDisabled()\"\n (click)=\"focusInput()\"\n>\n @for (tag of tags(); track $index) {\n <span class=\"lc-tag-input__tag\">\n <span class=\"lc-tag-input__tag-text\">{{ tag }}</span>\n @if (removable() && !isDisabled()) {\n <button\n type=\"button\"\n class=\"lc-tag-input__tag-remove\"\n (click)=\"removeTag($index); $event.stopPropagation()\"\n [attr.aria-label]=\"removeTagAriaLabel() + ' ' + tag\"\n >×</button>\n }\n </span>\n }\n\n <input\n #inputEl\n type=\"text\"\n [id]=\"inputId\"\n class=\"lc-tag-input__input\"\n [attr.role]=\"hasSuggestions() ? 'combobox' : null\"\n [attr.aria-autocomplete]=\"hasSuggestions() ? 'list' : null\"\n [attr.aria-expanded]=\"hasSuggestions() ? showSuggestions() : null\"\n [attr.aria-controls]=\"showSuggestions() ? listboxId : null\"\n [attr.aria-activedescendant]=\"showSuggestions() && activeIndex() >= 0 ? optionId(activeIndex()) : null\"\n [placeholder]=\"inputPlaceholder()\"\n [value]=\"inputValue()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"!canAdd()\"\n [attr.aria-readonly]=\"!canAdd() ? true : null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n</div>\n\n@if (showSuggestions()) {\n <ul class=\"lc-tag-input__suggestions\" role=\"listbox\" [id]=\"listboxId\" [attr.aria-label]=\"label() || suggestionsAriaLabel()\">\n @for (sug of filteredSuggestions(); track sug; let i = $index) {\n <li\n class=\"lc-tag-input__suggestion\"\n [class.lc-tag-input__suggestion--active]=\"i === activeIndex()\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n (mouseenter)=\"activeIndex.set(i)\"\n (mousedown)=\"selectSuggestion(sug); $event.preventDefault()\"\n >{{ sug }}</li>\n }\n </ul>\n}\n"],"mappings":"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"}
@@ -322,7 +322,7 @@ var TextareaComponent = class TextareaComponent {
322
322
  }],
323
323
  ngImport: i0,
324
324
  template: "<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",
325
- styles: [".lc-textarea-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-sm, var(--spacing-2));width:100%}.lc-textarea-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary)}.lc-textarea{width:100%;font-family:var(--font-family-base);font-size:var(--font-size-base);line-height:var(--line-height-base);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);resize:vertical;outline:none}.lc-textarea::placeholder{color:var(--color-text-tertiary)}.lc-textarea:focus{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--xs{padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-xs);min-height:4rem}.lc-textarea--sm{padding:var(--spacing-2-5) var(--spacing-3-5);font-size:var(--font-size-sm);min-height:5rem}.lc-textarea--md{padding:var(--spacing-3) var(--spacing-4);font-size:var(--font-size-base);min-height:6rem}.lc-textarea--lg{padding:var(--spacing-3-5) var(--spacing-5);font-size:var(--font-size-lg);min-height:7rem}.lc-textarea--outline{background-color:var(--color-surface);border-color:var(--color-field-border)}.lc-textarea--outline:hover:not(:disabled):not(:read-only){border-color:var(--color-primary-400)}.lc-textarea--outline:focus{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--filled{background-color:var(--color-surface-sunken);border-color:transparent}.lc-textarea--filled:hover:not(:disabled):not(:read-only){background-color:var(--color-surface-hover)}.lc-textarea--filled:focus{background-color:var(--color-surface);border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--disabled{cursor:not-allowed;opacity:.6;background-color:var(--color-surface-muted);color:var(--color-text-tertiary)}.lc-textarea--readonly{cursor:default;background-color:var(--color-surface-muted)}.lc-textarea--error{border-color:var(--color-error-600)}.lc-textarea--error:focus{border-color:var(--color-error-600);outline:2px solid var(--color-error-600);outline-offset:0;box-shadow:none}.lc-textarea--auto-resize{resize:none;overflow:hidden}.lc-textarea-helper-text{font-size:var(--font-size-xs);color:var(--color-text-secondary)}.lc-textarea-error-message{font-size:var(--font-size-xs);color:var(--color-text-error)}.lc-textarea-char-count,.lc-textarea-character-count{font-size:var(--font-size-xs);color:var(--color-text-tertiary);text-align:right}.lc-textarea-char-count--warning,.lc-textarea-character-count--warning{color:var(--color-warning-600)}.lc-textarea-char-count--error,.lc-textarea-char-count--over-limit,.lc-textarea-character-count--error,.lc-textarea-character-count--over-limit{color:var(--color-text-error)}.lc-textarea-footer{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--lc-density-gap-sm, var(--spacing-2))}.lc-textarea-messages{flex:1}.lc-textarea-input-wrapper{position:relative;width:100%}.lc-textarea-required-indicator{color:var(--color-text-error);margin-inline-start:var(--spacing-1)}@media(forced-colors:active){.lc-textarea--error:focus{outline-color:Highlight}}\n"],
325
+ styles: [".lc-textarea-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-sm, var(--spacing-2));width:100%}.lc-textarea-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary)}.lc-textarea{width:100%;font-family:var(--font-family-base);font-size:var(--font-size-base);line-height:var(--line-height-base);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);resize:vertical;outline:none}.lc-textarea::placeholder{color:var(--color-text-tertiary)}.lc-textarea:focus{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--xs{padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-xs);min-height:4rem}.lc-textarea--sm{padding:var(--spacing-2-5) var(--spacing-3-5);font-size:var(--font-size-sm);min-height:5rem}.lc-textarea--md{padding:var(--spacing-3) var(--spacing-4);font-size:var(--font-size-base);min-height:6rem}.lc-textarea--lg{padding:var(--spacing-3-5) var(--spacing-5);font-size:var(--font-size-lg);min-height:7rem}.lc-textarea--outline{background-color:var(--color-surface);border-color:var(--color-field-border)}.lc-textarea--outline:hover:not(:disabled):not(:read-only):not(.lc-textarea--error):not(:focus){border-color:var(--color-text-secondary)}.lc-textarea--outline:focus{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--filled{background-color:var(--color-surface-sunken);border-color:transparent}.lc-textarea--filled:hover:not(:disabled):not(:read-only):not(.lc-textarea--error):not(:focus){background-color:var(--color-surface-hover)}.lc-textarea--filled:focus{background-color:var(--color-surface);border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--disabled{cursor:not-allowed;opacity:.6;background-color:var(--color-surface-muted);color:var(--color-text-tertiary)}.lc-textarea--readonly{cursor:default;background-color:var(--color-surface-muted)}.lc-textarea--error{border-color:var(--color-error-600)}.lc-textarea--error:focus{border-color:var(--color-error-600);outline:2px solid var(--color-error-600);outline-offset:0;box-shadow:none}.lc-textarea--auto-resize{resize:none;overflow:hidden}.lc-textarea-helper-text{font-size:var(--font-size-xs);color:var(--color-text-secondary)}.lc-textarea-error-message{font-size:var(--font-size-xs);color:var(--color-text-error)}.lc-textarea-char-count,.lc-textarea-character-count{font-size:var(--font-size-xs);color:var(--color-text-tertiary);text-align:right}.lc-textarea-char-count--warning,.lc-textarea-character-count--warning{color:var(--color-warning-600)}.lc-textarea-char-count--error,.lc-textarea-char-count--over-limit,.lc-textarea-character-count--error,.lc-textarea-character-count--over-limit{color:var(--color-text-error)}.lc-textarea-footer{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--lc-density-gap-sm, var(--spacing-2))}.lc-textarea-messages{flex:1}.lc-textarea-input-wrapper{position:relative;width:100%}.lc-textarea-required-indicator{color:var(--color-text-error);margin-inline-start:var(--spacing-1)}@media(forced-colors:active){.lc-textarea--error:focus{outline-color:Highlight}}\n"],
326
326
  changeDetection: i0.ChangeDetectionStrategy.OnPush
327
327
  });
328
328
  };
@@ -344,7 +344,7 @@ i0.ɵɵngDeclareClassMetadata({
344
344
  multi: true
345
345
  }],
346
346
  template: "<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",
347
- styles: [".lc-textarea-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-sm, var(--spacing-2));width:100%}.lc-textarea-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary)}.lc-textarea{width:100%;font-family:var(--font-family-base);font-size:var(--font-size-base);line-height:var(--line-height-base);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);resize:vertical;outline:none}.lc-textarea::placeholder{color:var(--color-text-tertiary)}.lc-textarea:focus{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--xs{padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-xs);min-height:4rem}.lc-textarea--sm{padding:var(--spacing-2-5) var(--spacing-3-5);font-size:var(--font-size-sm);min-height:5rem}.lc-textarea--md{padding:var(--spacing-3) var(--spacing-4);font-size:var(--font-size-base);min-height:6rem}.lc-textarea--lg{padding:var(--spacing-3-5) var(--spacing-5);font-size:var(--font-size-lg);min-height:7rem}.lc-textarea--outline{background-color:var(--color-surface);border-color:var(--color-field-border)}.lc-textarea--outline:hover:not(:disabled):not(:read-only){border-color:var(--color-primary-400)}.lc-textarea--outline:focus{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--filled{background-color:var(--color-surface-sunken);border-color:transparent}.lc-textarea--filled:hover:not(:disabled):not(:read-only){background-color:var(--color-surface-hover)}.lc-textarea--filled:focus{background-color:var(--color-surface);border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--disabled{cursor:not-allowed;opacity:.6;background-color:var(--color-surface-muted);color:var(--color-text-tertiary)}.lc-textarea--readonly{cursor:default;background-color:var(--color-surface-muted)}.lc-textarea--error{border-color:var(--color-error-600)}.lc-textarea--error:focus{border-color:var(--color-error-600);outline:2px solid var(--color-error-600);outline-offset:0;box-shadow:none}.lc-textarea--auto-resize{resize:none;overflow:hidden}.lc-textarea-helper-text{font-size:var(--font-size-xs);color:var(--color-text-secondary)}.lc-textarea-error-message{font-size:var(--font-size-xs);color:var(--color-text-error)}.lc-textarea-char-count,.lc-textarea-character-count{font-size:var(--font-size-xs);color:var(--color-text-tertiary);text-align:right}.lc-textarea-char-count--warning,.lc-textarea-character-count--warning{color:var(--color-warning-600)}.lc-textarea-char-count--error,.lc-textarea-char-count--over-limit,.lc-textarea-character-count--error,.lc-textarea-character-count--over-limit{color:var(--color-text-error)}.lc-textarea-footer{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--lc-density-gap-sm, var(--spacing-2))}.lc-textarea-messages{flex:1}.lc-textarea-input-wrapper{position:relative;width:100%}.lc-textarea-required-indicator{color:var(--color-text-error);margin-inline-start:var(--spacing-1)}@media(forced-colors:active){.lc-textarea--error:focus{outline-color:Highlight}}\n"]
347
+ styles: [".lc-textarea-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-sm, var(--spacing-2));width:100%}.lc-textarea-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary)}.lc-textarea{width:100%;font-family:var(--font-family-base);font-size:var(--font-size-base);line-height:var(--line-height-base);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);resize:vertical;outline:none}.lc-textarea::placeholder{color:var(--color-text-tertiary)}.lc-textarea:focus{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--xs{padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-xs);min-height:4rem}.lc-textarea--sm{padding:var(--spacing-2-5) var(--spacing-3-5);font-size:var(--font-size-sm);min-height:5rem}.lc-textarea--md{padding:var(--spacing-3) var(--spacing-4);font-size:var(--font-size-base);min-height:6rem}.lc-textarea--lg{padding:var(--spacing-3-5) var(--spacing-5);font-size:var(--font-size-lg);min-height:7rem}.lc-textarea--outline{background-color:var(--color-surface);border-color:var(--color-field-border)}.lc-textarea--outline:hover:not(:disabled):not(:read-only):not(.lc-textarea--error):not(:focus){border-color:var(--color-text-secondary)}.lc-textarea--outline:focus{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--filled{background-color:var(--color-surface-sunken);border-color:transparent}.lc-textarea--filled:hover:not(:disabled):not(:read-only):not(.lc-textarea--error):not(:focus){background-color:var(--color-surface-hover)}.lc-textarea--filled:focus{background-color:var(--color-surface);border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-textarea--disabled{cursor:not-allowed;opacity:.6;background-color:var(--color-surface-muted);color:var(--color-text-tertiary)}.lc-textarea--readonly{cursor:default;background-color:var(--color-surface-muted)}.lc-textarea--error{border-color:var(--color-error-600)}.lc-textarea--error:focus{border-color:var(--color-error-600);outline:2px solid var(--color-error-600);outline-offset:0;box-shadow:none}.lc-textarea--auto-resize{resize:none;overflow:hidden}.lc-textarea-helper-text{font-size:var(--font-size-xs);color:var(--color-text-secondary)}.lc-textarea-error-message{font-size:var(--font-size-xs);color:var(--color-text-error)}.lc-textarea-char-count,.lc-textarea-character-count{font-size:var(--font-size-xs);color:var(--color-text-tertiary);text-align:right}.lc-textarea-char-count--warning,.lc-textarea-character-count--warning{color:var(--color-warning-600)}.lc-textarea-char-count--error,.lc-textarea-char-count--over-limit,.lc-textarea-character-count--error,.lc-textarea-character-count--over-limit{color:var(--color-text-error)}.lc-textarea-footer{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--lc-density-gap-sm, var(--spacing-2))}.lc-textarea-messages{flex:1}.lc-textarea-input-wrapper{position:relative;width:100%}.lc-textarea-required-indicator{color:var(--color-text-error);margin-inline-start:var(--spacing-1)}@media(forced-colors:active){.lc-textarea--error:focus{outline-color:Highlight}}\n"]
348
348
  }]
349
349
  }],
350
350
  ctorParameters: () => [],
@@ -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\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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"}
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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"}
@@ -130,7 +130,7 @@ var ToggleGroupComponent = class ToggleGroupComponent {
130
130
  }],
131
131
  ngImport: i0,
132
132
  template: "<div\n class=\"lc-toggle-group\"\n [ngClass]=\"sizeClass()\"\n role=\"radiogroup\"\n [attr.aria-label]=\"ariaLabelledBy() ? null : ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || null\"\n>\n @for (option of options(); track option.value; let i = $index) {\n <button\n #toggleBtn\n type=\"button\"\n role=\"radio\"\n [class]=\"getButtonClasses(option)\"\n [disabled]=\"option.disabled ?? false\"\n [attr.aria-checked]=\"isActive(option)\"\n [attr.tabindex]=\"tabIndexFor(option)\"\n (click)=\"select(option)\"\n (keydown)=\"onKeydown($event, i)\"\n >\n {{ option.label }}\n @if (dotTone(option); as tone) {\n <span\n class=\"lc-toggle-group__dot lc-toggle-group__dot--{{ tone }}\"\n aria-hidden=\"true\"\n ></span>\n }\n </button>\n }\n</div>\n",
133
- styles: [".lc-toggle-group{display:inline-flex;border-radius:var(--border-radius-md);overflow:hidden;border:1px solid var(--color-border);background-color:var(--color-surface-2)}.lc-toggle-group__btn{display:inline-flex;align-items:center;gap:.375rem;border:none;background:transparent;cursor:pointer;font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary);transition:background-color .15s ease,color .15s ease;white-space:nowrap}.lc-toggle-group__btn:hover:not(:disabled):not(.lc-toggle-group__btn--active){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-toggle-group__btn:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--color-primary-500)}.lc-toggle-group__btn--active{background-color:var(--color-primary-fill);color:var(--color-on-primary)}.lc-toggle-group__btn--active:focus-visible{box-shadow:inset 0 0 0 2px var(--color-on-primary)}.lc-toggle-group__btn--disabled{opacity:.5;cursor:not-allowed}.lc-toggle-group__btn+.lc-toggle-group__btn{border-left:1px solid var(--color-border)}.lc-toggle-group__dot{flex:0 0 auto;width:.5em;height:.5em;border-radius:50%}.lc-toggle-group__dot--warning{background-color:var(--color-warning)}.lc-toggle-group__dot--error{background-color:var(--color-error)}.lc-toggle-group__dot--success{background-color:var(--color-success)}.lc-toggle-group--sm .lc-toggle-group__btn{padding:.25rem .5rem;font-size:var(--typography-font-size-xs, .75rem)}.lc-toggle-group--md .lc-toggle-group__btn{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem)}.lc-toggle-group--lg .lc-toggle-group__btn{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem)}\n"],
133
+ styles: [".lc-toggle-group{display:inline-flex;border-radius:var(--border-radius-md);overflow:hidden;border:1px solid var(--color-border);background-color:var(--color-surface-2)}.lc-toggle-group__btn{display:inline-flex;align-items:center;gap:.375rem;border:none;background:transparent;cursor:pointer;font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary);transition:background-color .15s ease,color .15s ease;white-space:nowrap}.lc-toggle-group__btn:hover:not(:disabled):not(.lc-toggle-group__btn--active){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-toggle-group__btn:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--color-primary-500)}.lc-toggle-group__btn--active{background-color:var(--color-primary-fill);color:var(--color-on-primary)}.lc-toggle-group__btn--active:focus-visible{box-shadow:inset 0 0 0 2px var(--color-on-primary)}.lc-toggle-group__btn--disabled{opacity:.5;cursor:not-allowed}.lc-toggle-group__btn+.lc-toggle-group__btn{border-left:1px solid var(--color-border)}.lc-toggle-group__dot{flex:0 0 auto;width:.5em;height:.5em;border-radius:50%}.lc-toggle-group__dot--warning{background-color:var(--color-warning)}.lc-toggle-group__dot--error{background-color:var(--color-error)}.lc-toggle-group__dot--success{background-color:var(--color-success)}.lc-toggle-group--sm .lc-toggle-group__btn{padding:.25rem .5rem;font-size:var(--typography-font-size-xs, .75rem)}.lc-toggle-group--md .lc-toggle-group__btn{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem)}.lc-toggle-group--lg .lc-toggle-group__btn{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem)}@media(forced-colors:active){.lc-toggle-group__btn:focus-visible{outline:2px solid Highlight;outline-offset:-2px}}\n"],
134
134
  dependencies: [{
135
135
  kind: "directive",
136
136
  type: NgClass,
@@ -153,7 +153,7 @@ i0.ɵɵngDeclareClassMetadata({
153
153
  imports: [NgClass],
154
154
  changeDetection: ChangeDetectionStrategy.OnPush,
155
155
  template: "<div\n class=\"lc-toggle-group\"\n [ngClass]=\"sizeClass()\"\n role=\"radiogroup\"\n [attr.aria-label]=\"ariaLabelledBy() ? null : ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || null\"\n>\n @for (option of options(); track option.value; let i = $index) {\n <button\n #toggleBtn\n type=\"button\"\n role=\"radio\"\n [class]=\"getButtonClasses(option)\"\n [disabled]=\"option.disabled ?? false\"\n [attr.aria-checked]=\"isActive(option)\"\n [attr.tabindex]=\"tabIndexFor(option)\"\n (click)=\"select(option)\"\n (keydown)=\"onKeydown($event, i)\"\n >\n {{ option.label }}\n @if (dotTone(option); as tone) {\n <span\n class=\"lc-toggle-group__dot lc-toggle-group__dot--{{ tone }}\"\n aria-hidden=\"true\"\n ></span>\n }\n </button>\n }\n</div>\n",
156
- styles: [".lc-toggle-group{display:inline-flex;border-radius:var(--border-radius-md);overflow:hidden;border:1px solid var(--color-border);background-color:var(--color-surface-2)}.lc-toggle-group__btn{display:inline-flex;align-items:center;gap:.375rem;border:none;background:transparent;cursor:pointer;font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary);transition:background-color .15s ease,color .15s ease;white-space:nowrap}.lc-toggle-group__btn:hover:not(:disabled):not(.lc-toggle-group__btn--active){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-toggle-group__btn:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--color-primary-500)}.lc-toggle-group__btn--active{background-color:var(--color-primary-fill);color:var(--color-on-primary)}.lc-toggle-group__btn--active:focus-visible{box-shadow:inset 0 0 0 2px var(--color-on-primary)}.lc-toggle-group__btn--disabled{opacity:.5;cursor:not-allowed}.lc-toggle-group__btn+.lc-toggle-group__btn{border-left:1px solid var(--color-border)}.lc-toggle-group__dot{flex:0 0 auto;width:.5em;height:.5em;border-radius:50%}.lc-toggle-group__dot--warning{background-color:var(--color-warning)}.lc-toggle-group__dot--error{background-color:var(--color-error)}.lc-toggle-group__dot--success{background-color:var(--color-success)}.lc-toggle-group--sm .lc-toggle-group__btn{padding:.25rem .5rem;font-size:var(--typography-font-size-xs, .75rem)}.lc-toggle-group--md .lc-toggle-group__btn{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem)}.lc-toggle-group--lg .lc-toggle-group__btn{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem)}\n"]
156
+ styles: [".lc-toggle-group{display:inline-flex;border-radius:var(--border-radius-md);overflow:hidden;border:1px solid var(--color-border);background-color:var(--color-surface-2)}.lc-toggle-group__btn{display:inline-flex;align-items:center;gap:.375rem;border:none;background:transparent;cursor:pointer;font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary);transition:background-color .15s ease,color .15s ease;white-space:nowrap}.lc-toggle-group__btn:hover:not(:disabled):not(.lc-toggle-group__btn--active){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-toggle-group__btn:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--color-primary-500)}.lc-toggle-group__btn--active{background-color:var(--color-primary-fill);color:var(--color-on-primary)}.lc-toggle-group__btn--active:focus-visible{box-shadow:inset 0 0 0 2px var(--color-on-primary)}.lc-toggle-group__btn--disabled{opacity:.5;cursor:not-allowed}.lc-toggle-group__btn+.lc-toggle-group__btn{border-left:1px solid var(--color-border)}.lc-toggle-group__dot{flex:0 0 auto;width:.5em;height:.5em;border-radius:50%}.lc-toggle-group__dot--warning{background-color:var(--color-warning)}.lc-toggle-group__dot--error{background-color:var(--color-error)}.lc-toggle-group__dot--success{background-color:var(--color-success)}.lc-toggle-group--sm .lc-toggle-group__btn{padding:.25rem .5rem;font-size:var(--typography-font-size-xs, .75rem)}.lc-toggle-group--md .lc-toggle-group__btn{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem)}.lc-toggle-group--lg .lc-toggle-group__btn{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem)}@media(forced-colors:active){.lc-toggle-group__btn:focus-visible{outline:2px solid Highlight;outline-offset:-2px}}\n"]
157
157
  }]
158
158
  }],
159
159
  propDecorators: {
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-toggle-group.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/toggle-group/toggle-group.component.ts","../../../../libs/angular-ui-kit/src/lib/toggle-group/toggle-group.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n ElementRef,\n input,\n output,\n computed,\n model,\n viewChildren,\n} from '@angular/core';\nimport { NgClass } from '@angular/common';\n\nexport type ToggleOptionDot = 'warning' | 'error' | 'success';\n\nexport interface ToggleOption {\n readonly value: string;\n readonly label: string;\n readonly disabled?: boolean;\n /**\n * Small status dot rendered to the right of the label (e.g. an unread\n * indicator). `true` uses the default tone (`warning`); pass a tone name\n * to pick a semantic color. Does not change the option's height.\n */\n readonly dot?: boolean | ToggleOptionDot;\n}\n\n/**\n * Toggle group component for single-option selection from a set.\n *\n * Features:\n * - Multiple segmented toggle buttons\n * - Active state highlighting\n * - Size variants (sm, md, lg)\n * - Two-way selected value binding\n * - Per-option disabled state\n * - Exposed as a radiogroup: Arrow/Home/End keys move the selection\n * - Dark mode support via CSS custom properties\n *\n * @example\n * ```html\n * <lc-toggle-group\n * [options]=\"[{value:'1D',label:'1D'},{value:'1W',label:'1W'}]\"\n * [(selected)]=\"interval\"\n * />\n * ```\n */\n@Component({\n selector: 'lc-toggle-group',\n standalone: true,\n imports: [NgClass],\n templateUrl: './toggle-group.component.html',\n styleUrl: './toggle-group.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ToggleGroupComponent {\n /** Available toggle options */\n options = input.required<readonly ToggleOption[]>();\n\n /** Currently selected value (two-way binding) */\n selected = model<string>('');\n\n /** Size variant */\n size = input<'sm' | 'md' | 'lg'>('md');\n\n /** Accessible name of the group */\n readonly ariaLabel = input<string>();\n\n /** id(s) of external element(s) labelling the group (wins over `ariaLabel`) */\n readonly ariaLabelledBy = input<string>();\n\n /** Emitted when selection changes */\n selectionChange = output<string>();\n\n /** Computed size class */\n sizeClass = computed(() => `lc-toggle-group--${this.size()}`);\n\n /**\n * Roving tabindex: the selected option is the group's single tab stop; when\n * nothing (or a disabled option) is selected, the first enabled option is.\n */\n readonly focusableValue = computed(() => {\n const options = this.options();\n const selected = options.find((o) => o.value === this.selected() && !o.disabled);\n return (selected ?? options.find((o) => !o.disabled))?.value ?? null;\n });\n\n private readonly buttons = viewChildren<ElementRef<HTMLButtonElement>>('toggleBtn');\n\n /** Select an option */\n select(option: ToggleOption): void {\n if (option.disabled) return;\n this.selected.set(option.value);\n this.selectionChange.emit(option.value);\n }\n\n /** Check if an option is active */\n isActive(option: ToggleOption): boolean {\n return this.selected() === option.value;\n }\n\n /** tabindex for an option button (roving tabindex) */\n tabIndexFor(option: ToggleOption): number {\n return option.value === this.focusableValue() ? 0 : -1;\n }\n\n /** Arrow/Home/End keys move focus and selection like a native radio group */\n onKeydown(event: KeyboardEvent, index: number): void {\n const options = this.options();\n let target: number | undefined;\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowDown':\n target = this.nextEnabled(index, 1);\n break;\n case 'ArrowLeft':\n case 'ArrowUp':\n target = this.nextEnabled(index, -1);\n break;\n case 'Home':\n target = this.nextEnabled(-1, 1);\n break;\n case 'End':\n target = this.nextEnabled(options.length, -1);\n break;\n default:\n return;\n }\n\n event.preventDefault();\n if (target === undefined || target === index) return;\n\n this.select(options[target]);\n this.buttons()[target]?.nativeElement.focus();\n }\n\n /** Get button classes */\n getButtonClasses(option: ToggleOption): string {\n const classes = ['lc-toggle-group__btn'];\n\n if (this.isActive(option)) {\n classes.push('lc-toggle-group__btn--active');\n }\n if (option.disabled) {\n classes.push('lc-toggle-group__btn--disabled');\n }\n\n return classes.join(' ');\n }\n\n /** Resolve the dot tone for an option (null when no dot is shown) */\n dotTone(option: ToggleOption): ToggleOptionDot | null {\n if (!option.dot) return null;\n return option.dot === true ? 'warning' : option.dot;\n }\n\n /** Index of the next enabled option in `direction`, wrapping around */\n private nextEnabled(from: number, direction: 1 | -1): number | undefined {\n const options = this.options();\n for (let step = 1; step <= options.length; step++) {\n const i = (from + direction * step + options.length) % options.length;\n if (!options[i].disabled) return i;\n }\n return undefined;\n }\n}\n","<div\n class=\"lc-toggle-group\"\n [ngClass]=\"sizeClass()\"\n role=\"radiogroup\"\n [attr.aria-label]=\"ariaLabelledBy() ? null : ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || null\"\n>\n @for (option of options(); track option.value; let i = $index) {\n <button\n #toggleBtn\n type=\"button\"\n role=\"radio\"\n [class]=\"getButtonClasses(option)\"\n [disabled]=\"option.disabled ?? false\"\n [attr.aria-checked]=\"isActive(option)\"\n [attr.tabindex]=\"tabIndexFor(option)\"\n (click)=\"select(option)\"\n (keydown)=\"onKeydown($event, i)\"\n >\n {{ option.label }}\n @if (dotTone(option); as tone) {\n <span\n class=\"lc-toggle-group__dot lc-toggle-group__dot--{{ tone }}\"\n aria-hidden=\"true\"\n ></span>\n }\n </button>\n }\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-toggle-group.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/toggle-group/toggle-group.component.ts","../../../../libs/angular-ui-kit/src/lib/toggle-group/toggle-group.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n ElementRef,\n input,\n output,\n computed,\n model,\n viewChildren,\n} from '@angular/core';\nimport { NgClass } from '@angular/common';\n\nexport type ToggleOptionDot = 'warning' | 'error' | 'success';\n\nexport interface ToggleOption {\n readonly value: string;\n readonly label: string;\n readonly disabled?: boolean;\n /**\n * Small status dot rendered to the right of the label (e.g. an unread\n * indicator). `true` uses the default tone (`warning`); pass a tone name\n * to pick a semantic color. Does not change the option's height.\n */\n readonly dot?: boolean | ToggleOptionDot;\n}\n\n/**\n * Toggle group component for single-option selection from a set.\n *\n * Features:\n * - Multiple segmented toggle buttons\n * - Active state highlighting\n * - Size variants (sm, md, lg)\n * - Two-way selected value binding\n * - Per-option disabled state\n * - Exposed as a radiogroup: Arrow/Home/End keys move the selection\n * - Dark mode support via CSS custom properties\n *\n * @example\n * ```html\n * <lc-toggle-group\n * [options]=\"[{value:'1D',label:'1D'},{value:'1W',label:'1W'}]\"\n * [(selected)]=\"interval\"\n * />\n * ```\n */\n@Component({\n selector: 'lc-toggle-group',\n standalone: true,\n imports: [NgClass],\n templateUrl: './toggle-group.component.html',\n styleUrl: './toggle-group.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ToggleGroupComponent {\n /** Available toggle options */\n options = input.required<readonly ToggleOption[]>();\n\n /** Currently selected value (two-way binding) */\n selected = model<string>('');\n\n /** Size variant */\n size = input<'sm' | 'md' | 'lg'>('md');\n\n /** Accessible name of the group */\n readonly ariaLabel = input<string>();\n\n /** id(s) of external element(s) labelling the group (wins over `ariaLabel`) */\n readonly ariaLabelledBy = input<string>();\n\n /** Emitted when selection changes */\n selectionChange = output<string>();\n\n /** Computed size class */\n sizeClass = computed(() => `lc-toggle-group--${this.size()}`);\n\n /**\n * Roving tabindex: the selected option is the group's single tab stop; when\n * nothing (or a disabled option) is selected, the first enabled option is.\n */\n readonly focusableValue = computed(() => {\n const options = this.options();\n const selected = options.find((o) => o.value === this.selected() && !o.disabled);\n return (selected ?? options.find((o) => !o.disabled))?.value ?? null;\n });\n\n private readonly buttons = viewChildren<ElementRef<HTMLButtonElement>>('toggleBtn');\n\n /** Select an option */\n select(option: ToggleOption): void {\n if (option.disabled) return;\n this.selected.set(option.value);\n this.selectionChange.emit(option.value);\n }\n\n /** Check if an option is active */\n isActive(option: ToggleOption): boolean {\n return this.selected() === option.value;\n }\n\n /** tabindex for an option button (roving tabindex) */\n tabIndexFor(option: ToggleOption): number {\n return option.value === this.focusableValue() ? 0 : -1;\n }\n\n /** Arrow/Home/End keys move focus and selection like a native radio group */\n onKeydown(event: KeyboardEvent, index: number): void {\n const options = this.options();\n let target: number | undefined;\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowDown':\n target = this.nextEnabled(index, 1);\n break;\n case 'ArrowLeft':\n case 'ArrowUp':\n target = this.nextEnabled(index, -1);\n break;\n case 'Home':\n target = this.nextEnabled(-1, 1);\n break;\n case 'End':\n target = this.nextEnabled(options.length, -1);\n break;\n default:\n return;\n }\n\n event.preventDefault();\n if (target === undefined || target === index) return;\n\n this.select(options[target]);\n this.buttons()[target]?.nativeElement.focus();\n }\n\n /** Get button classes */\n getButtonClasses(option: ToggleOption): string {\n const classes = ['lc-toggle-group__btn'];\n\n if (this.isActive(option)) {\n classes.push('lc-toggle-group__btn--active');\n }\n if (option.disabled) {\n classes.push('lc-toggle-group__btn--disabled');\n }\n\n return classes.join(' ');\n }\n\n /** Resolve the dot tone for an option (null when no dot is shown) */\n dotTone(option: ToggleOption): ToggleOptionDot | null {\n if (!option.dot) return null;\n return option.dot === true ? 'warning' : option.dot;\n }\n\n /** Index of the next enabled option in `direction`, wrapping around */\n private nextEnabled(from: number, direction: 1 | -1): number | undefined {\n const options = this.options();\n for (let step = 1; step <= options.length; step++) {\n const i = (from + direction * step + options.length) % options.length;\n if (!options[i].disabled) return i;\n }\n return undefined;\n }\n}\n","<div\n class=\"lc-toggle-group\"\n [ngClass]=\"sizeClass()\"\n role=\"radiogroup\"\n [attr.aria-label]=\"ariaLabelledBy() ? null : ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || null\"\n>\n @for (option of options(); track option.value; let i = $index) {\n <button\n #toggleBtn\n type=\"button\"\n role=\"radio\"\n [class]=\"getButtonClasses(option)\"\n [disabled]=\"option.disabled ?? false\"\n [attr.aria-checked]=\"isActive(option)\"\n [attr.tabindex]=\"tabIndexFor(option)\"\n (click)=\"select(option)\"\n (keydown)=\"onKeydown($event, i)\"\n >\n {{ option.label }}\n @if (dotTone(option); as tone) {\n <span\n class=\"lc-toggle-group__dot lc-toggle-group__dot--{{ tone }}\"\n aria-hidden=\"true\"\n ></span>\n }\n </button>\n }\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.26.0",
3
+ "version": "3.27.1",
4
4
  "description": "LifeCorner Design System – Angular UI component library",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -170,8 +170,10 @@ button {
170
170
  margin: 0;
171
171
  font: inherit;
172
172
  cursor: pointer;
173
-
174
- &:focus {
173
+
174
+ // Only mouse focus loses the ring (request 042, F-18): a keyboard-focused
175
+ // button with no `:focus-visible` rule of its own keeps the browser ring.
176
+ &:focus:not(:focus-visible) {
175
177
  outline: none;
176
178
  }
177
179
  }