@lifecorner/angular-ui-kit 3.24.1 → 3.25.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 (31) hide show
  1. package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs +2 -2
  2. package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs.map +1 -1
  3. package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs +2 -2
  4. package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs.map +1 -1
  5. package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs +7 -7
  6. package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs.map +1 -1
  7. package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs +12 -4
  8. package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs.map +1 -1
  9. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs +2 -2
  10. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs.map +1 -1
  11. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs +2 -2
  12. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs.map +1 -1
  13. package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs +2 -2
  14. package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs.map +1 -1
  15. package/fesm2022/lifecorner-angular-ui-kit-password-input.mjs +2 -2
  16. package/fesm2022/lifecorner-angular-ui-kit-password-input.mjs.map +1 -1
  17. package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs +2 -2
  18. package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs.map +1 -1
  19. package/fesm2022/lifecorner-angular-ui-kit-select.mjs +2 -2
  20. package/fesm2022/lifecorner-angular-ui-kit-select.mjs.map +1 -1
  21. package/fesm2022/lifecorner-angular-ui-kit-slider.mjs +2 -2
  22. package/fesm2022/lifecorner-angular-ui-kit-slider.mjs.map +1 -1
  23. package/fesm2022/lifecorner-angular-ui-kit-textarea.mjs +2 -2
  24. package/fesm2022/lifecorner-angular-ui-kit-textarea.mjs.map +1 -1
  25. package/fesm2022/lifecorner-angular-ui-kit-tree-view.mjs +2 -2
  26. package/fesm2022/lifecorner-angular-ui-kit-tree-view.mjs.map +1 -1
  27. package/fesm2022/lifecorner-angular-ui-kit-verification-code-input.mjs +2 -2
  28. package/fesm2022/lifecorner-angular-ui-kit-verification-code-input.mjs.map +1 -1
  29. package/package.json +1 -1
  30. package/src/styles/theme-contrast.spec.ts +235 -7
  31. package/types/lifecorner-angular-ui-kit-description-list.d.ts +17 -5
@@ -427,7 +427,7 @@ var CodeBlockComponent = class CodeBlockComponent {
427
427
  },
428
428
  ngImport: i0,
429
429
  template: "<div [class]=\"codeClasses()\">\n @if (showHeader()) {\n <div class=\"code-block__header\">\n <span class=\"code-block__language\">{{ headerLabel() }}</span>\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"code-block__copy\"\n [attr.aria-label]=\"copied() ? copiedLabel() : copyAriaLabel()\"\n (click)=\"copyToClipboard()\"\n >\n @if (copied()) {\n <lc-icon name=\"check\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copiedLabel() }}</span>\n } @else {\n <lc-icon name=\"clipboard-document\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copyLabel() }}</span>\n }\n </button>\n <span class=\"code-block__live\" role=\"status\" aria-live=\"polite\">{{ copied() ? copiedAnnouncement() : '' }}</span>\n }\n </div>\n }\n <div\n class=\"code-block__body\"\n [attr.tabindex]=\"wrap() ? null : 0\"\n [attr.role]=\"wrap() ? null : 'region'\"\n [attr.aria-label]=\"wrap() ? null : regionName()\"\n >\n <pre class=\"code-block__pre\"><code class=\"code-block__code\">@for (line of highlightedLines(); track $index; let i = $index) {<span class=\"code-block__line\">@if (showLineNumbers()) {<span class=\"code-block__line-number\">{{ lineNumbers()[i] }}</span>}<span class=\"code-block__line-content\">@for (segment of line; track $index) {@if (segment.className) {<span [attr.class]=\"segment.className\">{{ segment.text }}</span>}@else{{{ segment.text }}}}</span></span>\n}</code></pre>\n </div>\n</div>\n",
430
- styles: [".code-block{--lc-code-block-bg: #0c1722;--lc-code-block-ink: #eef4f6;--lc-code-block-muted: #93a4ad;--lc-code-block-muted-strong: #c4d2d9;--lc-code-block-border: #24343f;position:relative;border:1px solid var(--lc-code-block-border);border-radius:var(--border-radius-md);overflow:hidden;font-family:var(--typography-font-family-mono, \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace);font-size:var(--typography-font-size-sm, .875rem);line-height:1.1;background-color:var(--lc-code-block-bg);color:var(--lc-code-block-ink)}.code-block__header{display:flex;justify-content:space-between;align-items:center;min-height:2.75rem;padding:0 .25rem 0 1rem;background-color:#00000040;border-bottom:1px solid var(--lc-code-block-border)}.code-block__language{font-size:var(--typography-font-size-xs, .75rem);color:var(--lc-code-block-muted);text-transform:lowercase;-webkit-user-select:none;user-select:none}.code-block__copy{display:inline-flex;align-items:center;justify-content:center;gap:.25rem;min-height:2.75rem;min-width:2.75rem;padding:0 .75rem;border:none;border-radius:var(--border-radius-sm);background:transparent;color:var(--lc-code-block-muted);font-size:var(--typography-font-size-xs, .75rem);font-family:inherit;cursor:pointer;transition:color .15s ease,background-color .15s ease}.code-block__copy:hover{color:var(--lc-code-block-muted-strong);background-color:#ffffff0f}.code-block__copy:focus-visible{outline:none;color:var(--lc-code-block-muted-strong);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 40%,transparent)}.code-block__live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.code-block__body{overflow-x:auto}.code-block__body:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-4px}.code-block__body:focus-visible{border-radius:0 0 calc(var(--border-radius-md) - 1px) calc(var(--border-radius-md) - 1px)}.code-block__body:first-child:focus-visible{border-radius:calc(var(--border-radius-md) - 1px)}@media(forced-colors:active){.code-block__body:focus-visible{outline-color:Highlight}}.code-block__pre{margin:0;padding:1rem;background:transparent;color:inherit;border-radius:0;overflow:visible}.code-block__code{display:block}.code-block__line{display:flex}.code-block__line-number{display:inline-block;min-width:2.5rem;padding-right:1rem;text-align:right;color:var(--lc-code-block-muted);-webkit-user-select:none;user-select:none;flex-shrink:0}.code-block__line-content{flex:1;white-space:pre}.code-block__line-content ::ng-deep .token-keyword{color:#c678dd}.code-block__line-content ::ng-deep .token-string{color:#98c379}.code-block__line-content ::ng-deep .token-number{color:#d19a66}.code-block__line-content ::ng-deep .token-comment{color:var(--lc-code-block-muted);font-style:italic}.code-block__line-content ::ng-deep .token-type{color:#e5c07b}.code-block__line-content ::ng-deep .token-constant{color:#d19a66}.code-block__line-content ::ng-deep .token-decorator{color:#61afef}.code-block__line-content ::ng-deep .token-tag{color:#e06c75}.code-block__line-content ::ng-deep .token-attr{color:#d19a66}.code-block__line-content ::ng-deep .token-selector{color:#e06c75}.code-block__line-content ::ng-deep .token-property{color:#61afef}.code-block__line-content ::ng-deep .token-unit{color:#d19a66}.code-block__line-content ::ng-deep .token-variable{color:#e06c75}.code-block__line-content ::ng-deep .token-builtin{color:#61afef}.code-block__line-content ::ng-deep .token-flag{color:#56b6c2}.code-block--wrap .code-block__line-content{white-space:pre-wrap;overflow-wrap:anywhere;min-width:0}\n"],
430
+ styles: [".code-block{--lc-code-block-bg: #0c1722;--lc-code-block-ink: #eef4f6;--lc-code-block-muted: #93a4ad;--lc-code-block-muted-strong: #c4d2d9;--lc-code-block-border: #24343f;position:relative;border:1px solid var(--lc-code-block-border);border-radius:var(--border-radius-md);overflow:hidden;font-family:var(--typography-font-family-mono, \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace);font-size:var(--typography-font-size-sm, .875rem);line-height:1.1;background-color:var(--lc-code-block-bg);color:var(--lc-code-block-ink)}.code-block__header{display:flex;justify-content:space-between;align-items:center;min-height:2.75rem;padding:0 .25rem 0 1rem;background-color:#00000040;border-bottom:1px solid var(--lc-code-block-border)}.code-block__language{font-size:var(--typography-font-size-xs, .75rem);color:var(--lc-code-block-muted);text-transform:lowercase;-webkit-user-select:none;user-select:none}.code-block__copy{display:inline-flex;align-items:center;justify-content:center;gap:.25rem;min-height:2.75rem;min-width:2.75rem;padding:0 .75rem;border:none;border-radius:var(--border-radius-sm);background:transparent;color:var(--lc-code-block-muted);font-size:var(--typography-font-size-xs, .75rem);font-family:inherit;cursor:pointer;transition:color .15s ease,background-color .15s ease}.code-block__copy:hover{color:var(--lc-code-block-muted-strong);background-color:#ffffff0f}.code-block__copy:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.code-block__copy:focus-visible{color:var(--lc-code-block-muted-strong)}.code-block__live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.code-block__body{overflow-x:auto}.code-block__body:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-4px}.code-block__body:focus-visible{border-radius:0 0 calc(var(--border-radius-md) - 1px) calc(var(--border-radius-md) - 1px)}.code-block__body:first-child:focus-visible{border-radius:calc(var(--border-radius-md) - 1px)}@media(forced-colors:active){.code-block__body:focus-visible{outline-color:Highlight}}.code-block__pre{margin:0;padding:1rem;background:transparent;color:inherit;border-radius:0;overflow:visible}.code-block__code{display:block}.code-block__line{display:flex}.code-block__line-number{display:inline-block;min-width:2.5rem;padding-right:1rem;text-align:right;color:var(--lc-code-block-muted);-webkit-user-select:none;user-select:none;flex-shrink:0}.code-block__line-content{flex:1;white-space:pre}.code-block__line-content ::ng-deep .token-keyword{color:#c678dd}.code-block__line-content ::ng-deep .token-string{color:#98c379}.code-block__line-content ::ng-deep .token-number{color:#d19a66}.code-block__line-content ::ng-deep .token-comment{color:var(--lc-code-block-muted);font-style:italic}.code-block__line-content ::ng-deep .token-type{color:#e5c07b}.code-block__line-content ::ng-deep .token-constant{color:#d19a66}.code-block__line-content ::ng-deep .token-decorator{color:#61afef}.code-block__line-content ::ng-deep .token-tag{color:#e06c75}.code-block__line-content ::ng-deep .token-attr{color:#d19a66}.code-block__line-content ::ng-deep .token-selector{color:#e06c75}.code-block__line-content ::ng-deep .token-property{color:#61afef}.code-block__line-content ::ng-deep .token-unit{color:#d19a66}.code-block__line-content ::ng-deep .token-variable{color:#e06c75}.code-block__line-content ::ng-deep .token-builtin{color:#61afef}.code-block__line-content ::ng-deep .token-flag{color:#56b6c2}.code-block--wrap .code-block__line-content{white-space:pre-wrap;overflow-wrap:anywhere;min-width:0}@media(forced-colors:active){.code-block__copy:focus-visible{outline-color:Highlight}}\n"],
431
431
  dependencies: [{
432
432
  kind: "component",
433
433
  type: IconComponent,
@@ -460,7 +460,7 @@ i0.ɵɵngDeclareClassMetadata({
460
460
  viewProviders: [ɵprovideKitIcons([ICON_CHECK, ICON_CLIPBOARD])],
461
461
  changeDetection: ChangeDetectionStrategy.OnPush,
462
462
  template: "<div [class]=\"codeClasses()\">\n @if (showHeader()) {\n <div class=\"code-block__header\">\n <span class=\"code-block__language\">{{ headerLabel() }}</span>\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"code-block__copy\"\n [attr.aria-label]=\"copied() ? copiedLabel() : copyAriaLabel()\"\n (click)=\"copyToClipboard()\"\n >\n @if (copied()) {\n <lc-icon name=\"check\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copiedLabel() }}</span>\n } @else {\n <lc-icon name=\"clipboard-document\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copyLabel() }}</span>\n }\n </button>\n <span class=\"code-block__live\" role=\"status\" aria-live=\"polite\">{{ copied() ? copiedAnnouncement() : '' }}</span>\n }\n </div>\n }\n <div\n class=\"code-block__body\"\n [attr.tabindex]=\"wrap() ? null : 0\"\n [attr.role]=\"wrap() ? null : 'region'\"\n [attr.aria-label]=\"wrap() ? null : regionName()\"\n >\n <pre class=\"code-block__pre\"><code class=\"code-block__code\">@for (line of highlightedLines(); track $index; let i = $index) {<span class=\"code-block__line\">@if (showLineNumbers()) {<span class=\"code-block__line-number\">{{ lineNumbers()[i] }}</span>}<span class=\"code-block__line-content\">@for (segment of line; track $index) {@if (segment.className) {<span [attr.class]=\"segment.className\">{{ segment.text }}</span>}@else{{{ segment.text }}}}</span></span>\n}</code></pre>\n </div>\n</div>\n",
463
- styles: [".code-block{--lc-code-block-bg: #0c1722;--lc-code-block-ink: #eef4f6;--lc-code-block-muted: #93a4ad;--lc-code-block-muted-strong: #c4d2d9;--lc-code-block-border: #24343f;position:relative;border:1px solid var(--lc-code-block-border);border-radius:var(--border-radius-md);overflow:hidden;font-family:var(--typography-font-family-mono, \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace);font-size:var(--typography-font-size-sm, .875rem);line-height:1.1;background-color:var(--lc-code-block-bg);color:var(--lc-code-block-ink)}.code-block__header{display:flex;justify-content:space-between;align-items:center;min-height:2.75rem;padding:0 .25rem 0 1rem;background-color:#00000040;border-bottom:1px solid var(--lc-code-block-border)}.code-block__language{font-size:var(--typography-font-size-xs, .75rem);color:var(--lc-code-block-muted);text-transform:lowercase;-webkit-user-select:none;user-select:none}.code-block__copy{display:inline-flex;align-items:center;justify-content:center;gap:.25rem;min-height:2.75rem;min-width:2.75rem;padding:0 .75rem;border:none;border-radius:var(--border-radius-sm);background:transparent;color:var(--lc-code-block-muted);font-size:var(--typography-font-size-xs, .75rem);font-family:inherit;cursor:pointer;transition:color .15s ease,background-color .15s ease}.code-block__copy:hover{color:var(--lc-code-block-muted-strong);background-color:#ffffff0f}.code-block__copy:focus-visible{outline:none;color:var(--lc-code-block-muted-strong);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 40%,transparent)}.code-block__live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.code-block__body{overflow-x:auto}.code-block__body:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-4px}.code-block__body:focus-visible{border-radius:0 0 calc(var(--border-radius-md) - 1px) calc(var(--border-radius-md) - 1px)}.code-block__body:first-child:focus-visible{border-radius:calc(var(--border-radius-md) - 1px)}@media(forced-colors:active){.code-block__body:focus-visible{outline-color:Highlight}}.code-block__pre{margin:0;padding:1rem;background:transparent;color:inherit;border-radius:0;overflow:visible}.code-block__code{display:block}.code-block__line{display:flex}.code-block__line-number{display:inline-block;min-width:2.5rem;padding-right:1rem;text-align:right;color:var(--lc-code-block-muted);-webkit-user-select:none;user-select:none;flex-shrink:0}.code-block__line-content{flex:1;white-space:pre}.code-block__line-content ::ng-deep .token-keyword{color:#c678dd}.code-block__line-content ::ng-deep .token-string{color:#98c379}.code-block__line-content ::ng-deep .token-number{color:#d19a66}.code-block__line-content ::ng-deep .token-comment{color:var(--lc-code-block-muted);font-style:italic}.code-block__line-content ::ng-deep .token-type{color:#e5c07b}.code-block__line-content ::ng-deep .token-constant{color:#d19a66}.code-block__line-content ::ng-deep .token-decorator{color:#61afef}.code-block__line-content ::ng-deep .token-tag{color:#e06c75}.code-block__line-content ::ng-deep .token-attr{color:#d19a66}.code-block__line-content ::ng-deep .token-selector{color:#e06c75}.code-block__line-content ::ng-deep .token-property{color:#61afef}.code-block__line-content ::ng-deep .token-unit{color:#d19a66}.code-block__line-content ::ng-deep .token-variable{color:#e06c75}.code-block__line-content ::ng-deep .token-builtin{color:#61afef}.code-block__line-content ::ng-deep .token-flag{color:#56b6c2}.code-block--wrap .code-block__line-content{white-space:pre-wrap;overflow-wrap:anywhere;min-width:0}\n"]
463
+ styles: [".code-block{--lc-code-block-bg: #0c1722;--lc-code-block-ink: #eef4f6;--lc-code-block-muted: #93a4ad;--lc-code-block-muted-strong: #c4d2d9;--lc-code-block-border: #24343f;position:relative;border:1px solid var(--lc-code-block-border);border-radius:var(--border-radius-md);overflow:hidden;font-family:var(--typography-font-family-mono, \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace);font-size:var(--typography-font-size-sm, .875rem);line-height:1.1;background-color:var(--lc-code-block-bg);color:var(--lc-code-block-ink)}.code-block__header{display:flex;justify-content:space-between;align-items:center;min-height:2.75rem;padding:0 .25rem 0 1rem;background-color:#00000040;border-bottom:1px solid var(--lc-code-block-border)}.code-block__language{font-size:var(--typography-font-size-xs, .75rem);color:var(--lc-code-block-muted);text-transform:lowercase;-webkit-user-select:none;user-select:none}.code-block__copy{display:inline-flex;align-items:center;justify-content:center;gap:.25rem;min-height:2.75rem;min-width:2.75rem;padding:0 .75rem;border:none;border-radius:var(--border-radius-sm);background:transparent;color:var(--lc-code-block-muted);font-size:var(--typography-font-size-xs, .75rem);font-family:inherit;cursor:pointer;transition:color .15s ease,background-color .15s ease}.code-block__copy:hover{color:var(--lc-code-block-muted-strong);background-color:#ffffff0f}.code-block__copy:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.code-block__copy:focus-visible{color:var(--lc-code-block-muted-strong)}.code-block__live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.code-block__body{overflow-x:auto}.code-block__body:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-4px}.code-block__body:focus-visible{border-radius:0 0 calc(var(--border-radius-md) - 1px) calc(var(--border-radius-md) - 1px)}.code-block__body:first-child:focus-visible{border-radius:calc(var(--border-radius-md) - 1px)}@media(forced-colors:active){.code-block__body:focus-visible{outline-color:Highlight}}.code-block__pre{margin:0;padding:1rem;background:transparent;color:inherit;border-radius:0;overflow:visible}.code-block__code{display:block}.code-block__line{display:flex}.code-block__line-number{display:inline-block;min-width:2.5rem;padding-right:1rem;text-align:right;color:var(--lc-code-block-muted);-webkit-user-select:none;user-select:none;flex-shrink:0}.code-block__line-content{flex:1;white-space:pre}.code-block__line-content ::ng-deep .token-keyword{color:#c678dd}.code-block__line-content ::ng-deep .token-string{color:#98c379}.code-block__line-content ::ng-deep .token-number{color:#d19a66}.code-block__line-content ::ng-deep .token-comment{color:var(--lc-code-block-muted);font-style:italic}.code-block__line-content ::ng-deep .token-type{color:#e5c07b}.code-block__line-content ::ng-deep .token-constant{color:#d19a66}.code-block__line-content ::ng-deep .token-decorator{color:#61afef}.code-block__line-content ::ng-deep .token-tag{color:#e06c75}.code-block__line-content ::ng-deep .token-attr{color:#d19a66}.code-block__line-content ::ng-deep .token-selector{color:#e06c75}.code-block__line-content ::ng-deep .token-property{color:#61afef}.code-block__line-content ::ng-deep .token-unit{color:#d19a66}.code-block__line-content ::ng-deep .token-variable{color:#e06c75}.code-block__line-content ::ng-deep .token-builtin{color:#61afef}.code-block__line-content ::ng-deep .token-flag{color:#56b6c2}.code-block--wrap .code-block__line-content{white-space:pre-wrap;overflow-wrap:anywhere;min-width:0}@media(forced-colors:active){.code-block__copy:focus-visible{outline-color:Highlight}}\n"]
464
464
  }]
465
465
  }],
466
466
  propDecorators: {
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-code-block.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/code-block/code-block.component.ts","../../../../libs/angular-ui-kit/src/lib/code-block/code-block.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n computed,\n signal,\n} from '@angular/core';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHECK, ICON_CLIPBOARD } from '@lifecorner/angular-ui-kit/icons';\n\nexport type CodeBlockLanguage =\n | 'typescript'\n | 'javascript'\n | 'html'\n | 'css'\n | 'scss'\n | 'json'\n | 'bash'\n | 'python'\n | 'java'\n | 'text';\n\ninterface TokenRule {\n pattern: RegExp;\n className: string;\n}\n\nconst SHARED_RULES: TokenRule[] = [\n // Strings (double/single/backtick)\n { pattern: /`(?:[^`\\\\]|\\\\.)*`/g, className: 'token-string' },\n { pattern: /\"(?:[^\"\\\\]|\\\\.)*\"/g, className: 'token-string' },\n { pattern: /'(?:[^'\\\\]|\\\\.)*'/g, className: 'token-string' },\n // Numbers\n { pattern: /\\b\\d+(?:\\.\\d+)?\\b/g, className: 'token-number' },\n];\n\nconst LANGUAGE_RULES: Record<string, TokenRule[]> = {\n typescript: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:import|export|from|const|let|var|function|return|if|else|for|while|class|interface|type|enum|extends|implements|new|this|super|async|await|yield|of|in|as|is|readonly|public|private|protected|static|abstract|override|get|set|constructor|declare|namespace|module|require)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:string|number|boolean|void|null|undefined|never|any|unknown|object|symbol|bigint)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:true|false|null|undefined|NaN|Infinity)\\b/g, className: 'token-constant' },\n { pattern: /(?<=\\b(?:class|interface|type|enum|extends|implements)\\s+)\\w+/g, className: 'token-type' },\n ],\n javascript: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\b(?:import|export|from|const|let|var|function|return|if|else|for|while|class|extends|new|this|super|async|await|yield|of|in|typeof|instanceof|delete|throw|try|catch|finally|switch|case|default|break|continue|do)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:true|false|null|undefined|NaN|Infinity)\\b/g, className: 'token-constant' },\n ],\n // The tokenizer reads the raw source (3.6.1): `<` and `>` instead of the\n // `&lt;`/`&gt;` the escaped text had. Up to 3.6.0 an html comment rule ran on\n // that escaped text and never matched; it is gone, so comments stay plain.\n html: [\n ...SHARED_RULES,\n { pattern: /<\\/?[\\w-]+/g, className: 'token-tag' },\n { pattern: /\\/?>/g, className: 'token-tag' },\n { pattern: /\\b[\\w-]+(?==)/g, className: 'token-attr' },\n ],\n css: [\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /[.#][\\w-]+/g, className: 'token-selector' },\n { pattern: /\\b(?:px|rem|em|%|vh|vw|s|ms|deg|fr)\\b/g, className: 'token-unit' },\n { pattern: /[\\w-]+(?=\\s*:)/g, className: 'token-property' },\n ],\n scss: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\$[\\w-]+/g, className: 'token-variable' },\n { pattern: /@(?:use|forward|import|mixin|include|extend|if|else|each|for|while|function|return)\\b/g, className: 'token-keyword' },\n { pattern: /&/g, className: 'token-selector' },\n { pattern: /[.#][\\w-]+/g, className: 'token-selector' },\n ],\n json: [\n ...SHARED_RULES,\n { pattern: /\\b(?:true|false|null)\\b/g, className: 'token-constant' },\n { pattern: /\"(?:[^\"\\\\]|\\\\.)*\"(?=\\s*:)/g, className: 'token-property' },\n ],\n bash: [\n { pattern: /#.*$/gm, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\$[\\w{][\\w}]*/g, className: 'token-variable' },\n { pattern: /\\b(?:if|then|else|elif|fi|for|do|done|while|until|case|esac|function|in|select|time|coproc)\\b/g, className: 'token-keyword' },\n { pattern: /(?:^|\\s)(?:npm|npx|yarn|pnpm|git|cd|ls|mkdir|rm|cp|mv|echo|cat|grep|find|sed|awk|curl|wget|docker|node|python|pip|brew|apt|sudo)\\b/g, className: 'token-builtin' },\n { pattern: /--?[\\w-]+/g, className: 'token-flag' },\n ],\n python: [\n { pattern: /#.*$/gm, className: 'token-comment' },\n { pattern: /\"\"\"[\\s\\S]*?\"\"\"/g, className: 'token-string' },\n { pattern: /'''[\\s\\S]*?'''/g, className: 'token-string' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:def|class|import|from|as|return|if|elif|else|for|while|break|continue|pass|raise|try|except|finally|with|yield|lambda|assert|del|global|nonlocal|async|await|in|not|and|or|is)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:True|False|None)\\b/g, className: 'token-constant' },\n { pattern: /\\b(?:str|int|float|bool|list|dict|tuple|set|bytes|type|object|Exception|ValueError|TypeError|KeyError|IndexError|AttributeError|RuntimeError|StopIteration)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:print|len|range|enumerate|zip|map|filter|sorted|reversed|isinstance|issubclass|hasattr|getattr|setattr|super|property|staticmethod|classmethod|open|input)\\b/g, className: 'token-builtin' },\n { pattern: /\\bself\\b/g, className: 'token-variable' },\n ],\n java: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:abstract|assert|break|case|catch|class|continue|default|do|else|enum|extends|final|finally|for|if|implements|import|instanceof|interface|native|new|package|private|protected|public|return|static|strictfp|super|switch|synchronized|this|throw|throws|transient|try|volatile|while|var|record|sealed|permits|yield)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:true|false|null)\\b/g, className: 'token-constant' },\n { pattern: /\\b(?:void|int|long|short|byte|float|double|char|boolean|String|Integer|Long|Double|Float|Boolean|Character|Object|Class|List|Map|Set|ArrayList|HashMap|HashSet|Optional|Stream|Collectors)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:System|Math|Arrays|Collections|Objects|Thread|Runnable)\\b/g, className: 'token-builtin' },\n ],\n};\n\n/** A run of source text; `className` is one `token-*` class, or `null` for plain text. */\ninterface CodeSegment {\n readonly text: string;\n readonly className: string | null;\n}\n\n/** One rendered line: its segments in order. */\ntype CodeLine = readonly CodeSegment[];\n\n/**\n * What an empty line renders: U+00A0, the glyph the `&nbsp;` of 3.6.0\n * produced, so the line keeps its height and a selection its text.\n */\nconst EMPTY_LINE: CodeLine = [{ text: String.fromCharCode(0xa0), className: null }];\n\n/** Matches of the language's rules on the raw source, sorted, without overlaps. */\nfunction tokenize(code: string, language: string): { start: number; end: number; className: string }[] {\n const rules = LANGUAGE_RULES[language];\n if (!rules) return [];\n\n // Collect all matches with positions\n const tokens: { start: number; end: number; className: string }[] = [];\n\n for (const rule of rules) {\n const regex = new RegExp(rule.pattern.source, rule.pattern.flags);\n let match: RegExpExecArray | null;\n while ((match = regex.exec(code)) !== null) {\n tokens.push({\n start: match.index,\n end: match.index + match[0].length,\n className: rule.className,\n });\n }\n }\n\n // Sort by start position, longer matches first for same position\n tokens.sort((a, b) => a.start - b.start || b.end - a.end);\n\n // Remove overlapping tokens (keep first/longest)\n const filtered: typeof tokens = [];\n let lastEnd = 0;\n for (const token of tokens) {\n if (token.start >= lastEnd) {\n filtered.push(token);\n lastEnd = token.end;\n }\n }\n return filtered;\n}\n\n/**\n * The highlighted source as lines of text segments, rendered by the template\n * as text nodes and `<span class=\"token-…\">` elements: no markup string, so\n * nothing for Trusted Types to block and nothing in the source is parsed.\n *\n * A token that runs across a line break keeps its class on the part before\n * the first break only; the later parts are plain, as the HTML parser closed\n * the open span at each line end up to 3.6.0.\n */\nfunction highlightLines(code: string, language: string): CodeLine[] {\n const lines: CodeSegment[][] = [[]];\n const append = (text: string, className: string | null) => {\n text.split('\\n').forEach((part, i) => {\n if (i > 0) lines.push([]);\n if (!part) return;\n const line = lines[lines.length - 1];\n const cls = i === 0 ? className : null;\n const last = line[line.length - 1];\n if (cls === null && last?.className === null) {\n line[line.length - 1] = { text: last.text + part, className: null };\n } else {\n line.push({ text: part, className: cls });\n }\n });\n };\n\n let cursor = 0;\n for (const token of tokenize(code, language)) {\n append(code.slice(cursor, token.start), null);\n append(code.slice(token.start, token.end), token.className);\n cursor = token.end;\n }\n append(code.slice(cursor), null);\n\n return lines.map(line => (line.length ? line : EMPTY_LINE));\n}\n\n@Component({\n selector: 'lc-code-block',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_CHECK, ICON_CLIPBOARD])],\n templateUrl: './code-block.component.html',\n styleUrls: ['./code-block.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\n/**\n * Code block component for displaying syntax-highlighted source code.\n *\n * Features:\n * - Syntax highlighting for 10+ languages (TypeScript, Python, Java, etc.)\n * - Optional line numbers\n * - Copy-to-clipboard button\n * - Optional header with filename and language label\n * - Dark-mode compatible color scheme\n * - Keyboard: the body is a focusable, named region that scrolls with the\n * arrow keys; `wrap` wraps instead\n *\n * @example\n * ```html\n * <lc-code-block [code]=\"source\" language=\"typescript\" [showLineNumbers]=\"true\" />\n * ```\n */\nexport class CodeBlockComponent {\n /** Code content to display */\n code = input.required<string>();\n\n /** Language label */\n language = input<CodeBlockLanguage>('text');\n\n /** Optional filename to display */\n filename = input<string>();\n\n /** Whether to show line numbers */\n showLineNumbers = input<boolean>(true);\n\n /** Whether to show copy button */\n showCopy = input<boolean>(true);\n\n /** Whether the code block has a header bar */\n showHeader = input<boolean>(true);\n\n /**\n * Accessible name of the scrollable code region (`role=\"region\"`). When it\n * is unset or empty, the `filename` is used, then the `language`. Set a\n * short, localised name that is unique on the page, such as \"Befehl für\n * Claude Code\". Not used when `wrap` is set.\n * @default undefined\n */\n regionLabel = input<string | undefined>(undefined);\n\n /**\n * Wraps long lines (`white-space: pre-wrap`, breaking anywhere if needed)\n * instead of scrolling sideways. A wrapped block doesn't scroll, so its body\n * is neither a tab stop nor a region.\n * @default false\n */\n wrap = input<boolean>(false);\n\n /** Visible label of the copy button. */\n copyLabel = input('Copy');\n\n /** Accessible name of the copy button. */\n copyAriaLabel = input('Copy code');\n\n /** Label of the copy button after copying (also its accessible name). */\n copiedLabel = input('Copied');\n\n /** Announced to screen readers after copying. */\n copiedAnnouncement = input('Copied to clipboard');\n\n protected copied = signal(false);\n\n protected highlightedLines = computed<CodeLine[]>(() =>\n highlightLines(this.code(), this.language())\n );\n\n protected lineNumbers = computed(() => {\n return this.code().split('\\n').map((_, i) => i + 1);\n });\n\n protected headerLabel = computed(() => {\n return this.filename() || this.language();\n });\n\n /** The region's name: `regionLabel`, then `filename`, then `language` (`''` falls through). */\n protected regionName = computed(() => this.regionLabel() || this.headerLabel());\n\n protected codeClasses = computed(() => {\n const classes = ['code-block', `code-block--${this.language()}`];\n if (this.wrap()) classes.push('code-block--wrap');\n return classes.join(' ');\n });\n\n protected async copyToClipboard(): Promise<void> {\n try {\n await navigator.clipboard.writeText(this.code());\n this.copied.set(true);\n setTimeout(() => this.copied.set(false), 2000);\n } catch {\n // Fallback: just ignore if clipboard API not available\n }\n }\n}\n","<div [class]=\"codeClasses()\">\n @if (showHeader()) {\n <div class=\"code-block__header\">\n <span class=\"code-block__language\">{{ headerLabel() }}</span>\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"code-block__copy\"\n [attr.aria-label]=\"copied() ? copiedLabel() : copyAriaLabel()\"\n (click)=\"copyToClipboard()\"\n >\n @if (copied()) {\n <lc-icon name=\"check\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copiedLabel() }}</span>\n } @else {\n <lc-icon name=\"clipboard-document\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copyLabel() }}</span>\n }\n </button>\n <span class=\"code-block__live\" role=\"status\" aria-live=\"polite\">{{ copied() ? copiedAnnouncement() : '' }}</span>\n }\n </div>\n }\n <div\n class=\"code-block__body\"\n [attr.tabindex]=\"wrap() ? null : 0\"\n [attr.role]=\"wrap() ? null : 'region'\"\n [attr.aria-label]=\"wrap() ? null : regionName()\"\n >\n <pre class=\"code-block__pre\"><code class=\"code-block__code\">@for (line of highlightedLines(); track $index; let i = $index) {<span class=\"code-block__line\">@if (showLineNumbers()) {<span class=\"code-block__line-number\">{{ lineNumbers()[i] }}</span>}<span class=\"code-block__line-content\">@for (segment of line; track $index) {@if (segment.className) {<span [attr.class]=\"segment.className\">{{ segment.text }}</span>}@else{{{ segment.text }}}}</span></span>\n}</code></pre>\n </div>\n</div>\n"],"mappings":";;;;AA2BA,MAAM,eAA4B;CAEhC;EAAE,SAAS;EAAsB,WAAW;CAAc;CAC1D;EAAE,SAAS;EAAsB,WAAW;CAAc;CAC1D;EAAE,SAAS;EAAsB,WAAW;CAAc;CAE1D;EAAE,SAAS;EAAsB,WAAW;CAAc;AAC3D;AAED,MAAM,iBAA8C;CAClD,YAAY;EACV;GAAE,SAAS;GAAa,WAAW;EAAe;EAClD;GAAE,SAAS;GAAqB,WAAW;EAAe;EAC1D,GAAG;EACH;GAAE,SAAS;GAAS,WAAW;EAAiB;EAChD;GAAE,SAAS;GAAyR,WAAW;EAAe;EAC9T;GAAE,SAAS;GAA6F,WAAW;EAAY;EAC/H;GAAE,SAAS;GAAmD,WAAW;EAAgB;EACzF;GAAE,SAAS;GAAkE,WAAW;EAAY;CACrG;CACD,YAAY;EACV;GAAE,SAAS;GAAa,WAAW;EAAe;EAClD;GAAE,SAAS;GAAqB,WAAW;EAAe;EAC1D,GAAG;EACH;GAAE,SAAS;GAA2N,WAAW;EAAe;EAChQ;GAAE,SAAS;GAAmD,WAAW;EAAgB;CAC1F;CAID,MAAM;EACJ,GAAG;EACH;GAAE,SAAS;GAAe,WAAW;EAAW;EAChD;GAAE,SAAS;GAAS,WAAW;EAAW;EAC1C;GAAE,SAAS;GAAkB,WAAW;EAAY;CACrD;CACD,KAAK;EACH;GAAE,SAAS;GAAqB,WAAW;EAAe;EAC1D,GAAG;EACH;GAAE,SAAS;GAAe,WAAW;EAAgB;EACrD;GAAE,SAAS;GAA0C,WAAW;EAAY;EAC5E;GAAE,SAAS;GAAmB,WAAW;EAAgB;CAC1D;CACD,MAAM;EACJ;GAAE,SAAS;GAAa,WAAW;EAAe;EAClD;GAAE,SAAS;GAAqB,WAAW;EAAe;EAC1D,GAAG;EACH;GAAE,SAAS;GAAa,WAAW;EAAgB;EACnD;GAAE,SAAS;GAA0F,WAAW;EAAe;EAC/H;GAAE,SAAS;GAAM,WAAW;EAAgB;EAC5C;GAAE,SAAS;GAAe,WAAW;EAAgB;CACtD;CACD,MAAM;EACJ,GAAG;EACH;GAAE,SAAS;GAA4B,WAAW;EAAgB;EAClE;GAAE,SAAS;GAA8B,WAAW;EAAgB;CACrE;CACD,MAAM;EACJ;GAAE,SAAS;GAAU,WAAW;EAAe;EAC/C,GAAG;EACH;GAAE,SAAS;GAAkB,WAAW;EAAgB;EACxD;GAAE,SAAS;GAAkG,WAAW;EAAe;EACvI;GAAE,SAAS;GAAuI,WAAW;EAAe;EAC5K;GAAE,SAAS;GAAc,WAAW;EAAY;CACjD;CACD,QAAQ;EACN;GAAE,SAAS;GAAU,WAAW;EAAe;EAC/C;GAAE,SAAS;GAAmB,WAAW;EAAc;EACvD;GAAE,SAAS;GAAmB,WAAW;EAAc;EACvD,GAAG;EACH;GAAE,SAAS;GAAS,WAAW;EAAiB;EAChD;GAAE,SAAS;GAA0L,WAAW;EAAe;EAC/N;GAAE,SAAS;GAA4B,WAAW;EAAgB;EAClE;GAAE,SAAS;GAAkK,WAAW;EAAY;EACpM;GAAE,SAAS;GAAsK,WAAW;EAAe;EAC3M;GAAE,SAAS;GAAa,WAAW;EAAgB;CACpD;CACD,MAAM;EACJ;GAAE,SAAS;GAAa,WAAW;EAAe;EAClD;GAAE,SAAS;GAAqB,WAAW;EAAe;EAC1D,GAAG;EACH;GAAE,SAAS;GAAS,WAAW;EAAiB;EAChD;GAAE,SAAS;GAAiU,WAAW;EAAe;EACtW;GAAE,SAAS;GAA4B,WAAW;EAAgB;EAClE;GAAE,SAAS;GAAiM,WAAW;EAAY;EACnO;GAAE,SAAS;GAAmE,WAAW;EAAe;CACzG;AACF;AAeD,MAAM,aAAuB,CAAC;CAAE,MAAM,OAAO,aAAa,GAAI;CAAG,WAAW;AAAI,CAAE;AAGlF,SAAS,SAAS,MAAc,UAAgB;CAC9C,MAAM,QAAQ,eAAe;CAC7B,IAAI,CAAC,OAAO,OAAO,CAAA;CAGnB,MAAM,SAA8D,CAAA;CAEpE,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,IAAI,OAAO,KAAK,QAAQ,QAAQ,KAAK,QAAQ,KAAK;EAChE,IAAI;EACJ,QAAQ,QAAQ,MAAM,KAAK,IAAI,OAAO,MACpC,OAAO,KAAK;GACV,OAAO,MAAM;GACb,KAAK,MAAM,QAAQ,MAAM,EAAE,CAAC;GAC5B,WAAW,KAAK;EACjB,CAAA;CAEL;CAGA,OAAO,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG;CAGxD,MAAM,WAA0B,CAAA;CAChC,IAAI,UAAU;CACd,KAAK,MAAM,SAAS,QAClB,IAAI,MAAM,SAAS,SAAS;EAC1B,SAAS,KAAK,KAAK;EACnB,UAAU,MAAM;CAClB;CAEF,OAAO;AACT;AAWA,SAAS,eAAe,MAAc,UAAgB;CACpD,MAAM,QAAyB,CAAC,CAAA,CAAE;CAClC,MAAM,UAAU,MAAc,cAA4B;EACxD,KAAK,MAAM,IAAI,CAAC,CAAC,SAAS,MAAM,MAAK;GACnC,IAAI,IAAI,GAAG,MAAM,KAAK,CAAA,CAAE;GACxB,IAAI,CAAC,MAAM;GACX,MAAM,OAAO,MAAM,MAAM,SAAS;GAClC,MAAM,MAAM,MAAM,IAAI,YAAY;GAClC,MAAM,OAAO,KAAK,KAAK,SAAS;GAChC,IAAI,QAAQ,QAAQ,MAAM,cAAc,MACtC,KAAK,KAAK,SAAS,KAAK;IAAE,MAAM,KAAK,OAAO;IAAM,WAAW;GAAI;QAEjE,KAAK,KAAK;IAAE,MAAM;IAAM,WAAW;GAAG,CAAE;EAE5C,CAAC;CACH;CAEA,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,SAAS,MAAM,QAAQ,GAAG;EAC5C,OAAO,KAAK,MAAM,QAAQ,MAAM,KAAK,GAAG,IAAI;EAC5C,OAAO,KAAK,MAAM,MAAM,OAAO,MAAM,GAAG,GAAG,MAAM,SAAS;EAC1D,SAAS,MAAM;CACjB;CACA,OAAO,KAAK,MAAM,MAAM,GAAG,IAAI;CAE/B,OAAO,MAAM,KAAI,SAAS,KAAK,SAAS,OAAO,UAAW;AAC5D;AA4BA,IAAa,qBAAb,MAAa,mBAAkB;CAE7B,OAAO,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAG/B,WAAW,MAAyB,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAG3C,WAAW,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAG1B,kBAAkB,MAAe,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,+BAAA,CAAA,CAAA;CAGtC,WAAW,MAAe,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAG/B,aAAa,MAAe,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CASjC,cAAc,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAQlD,OAAO,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAG5B,YAAY,MAAM,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAGzB,gBAAgB,MAAM,aAAY,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAGlC,cAAc,MAAM,UAAS,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAG7B,qBAAqB,MAAM,uBAAsB,GAAA,YAAA,CAAA,EAAA,WAAA,qBAAA,CAAA,+BAAA,CAAA,CAAA;CAEvC,SAAS,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CAEtB,mBAAmB,eAC3B,eAAe,KAAK,KAAI,GAAI,KAAK,SAAQ,CAAE,GAC5C,GAAA,YAAA,CAAA,EAAA,WAAA,mBAAA,CAAA,+BAAA,CAAA,CAAA;CAES,cAAc,eAAc;EACpC,OAAO,KAAK,KAAI,CAAE,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC;CACpD,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAEQ,cAAc,eAAc;EACpC,OAAO,KAAK,SAAQ,KAAM,KAAK,SAAQ;CACzC,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAGQ,aAAa,eAAe,KAAK,YAAW,KAAM,KAAK,YAAW,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAErE,cAAc,eAAc;EACpC,MAAM,UAAU,CAAC,cAAc,eAAe,KAAK,SAAQ,GAAI;EAC/D,IAAI,KAAK,KAAI,GAAI,QAAQ,KAAK,kBAAkB;EAChD,OAAO,QAAQ,KAAK,GAAG;CACzB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAEQ,MAAM,kBAAe;EAC7B,IAAI;GACF,MAAM,UAAU,UAAU,UAAU,KAAK,KAAI,CAAE;GAC/C,KAAK,OAAO,IAAI,IAAI;GACpB,iBAAiB,KAAK,OAAO,IAAI,KAAK,GAAG,GAAI;EAC/C,QAAQ,CAER;CACF;CA/EW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAkB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAlB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAkB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,iBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,eAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,oBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,UAAA;EAAA,UCrO/B;EAiCA,QAAA,CAAA,ijHAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MD6KY;GAAa,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;EAAA,CAAA;EAAA,eACR,CAAC,iBAAiB,CAAC,YAAY,cAAc,CAAC,CAAC;EAAC,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAsBpD,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAkB,YAAA,CAAA;EA1B9B,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAe,YACb;GAAI,SACP,CAAC,aAAa;GAAC,eACT,CAAC,iBAAiB,CAAC,YAAY,cAAc,CAAC,CAAC;GAAC,iBAG9C,wBAAwB;GAAM,UAAA;GAAA,QAAA,CAAA,ijHAAA;EAAA,CAAA"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-code-block.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/code-block/code-block.component.ts","../../../../libs/angular-ui-kit/src/lib/code-block/code-block.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n computed,\n signal,\n} from '@angular/core';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHECK, ICON_CLIPBOARD } from '@lifecorner/angular-ui-kit/icons';\n\nexport type CodeBlockLanguage =\n | 'typescript'\n | 'javascript'\n | 'html'\n | 'css'\n | 'scss'\n | 'json'\n | 'bash'\n | 'python'\n | 'java'\n | 'text';\n\ninterface TokenRule {\n pattern: RegExp;\n className: string;\n}\n\nconst SHARED_RULES: TokenRule[] = [\n // Strings (double/single/backtick)\n { pattern: /`(?:[^`\\\\]|\\\\.)*`/g, className: 'token-string' },\n { pattern: /\"(?:[^\"\\\\]|\\\\.)*\"/g, className: 'token-string' },\n { pattern: /'(?:[^'\\\\]|\\\\.)*'/g, className: 'token-string' },\n // Numbers\n { pattern: /\\b\\d+(?:\\.\\d+)?\\b/g, className: 'token-number' },\n];\n\nconst LANGUAGE_RULES: Record<string, TokenRule[]> = {\n typescript: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:import|export|from|const|let|var|function|return|if|else|for|while|class|interface|type|enum|extends|implements|new|this|super|async|await|yield|of|in|as|is|readonly|public|private|protected|static|abstract|override|get|set|constructor|declare|namespace|module|require)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:string|number|boolean|void|null|undefined|never|any|unknown|object|symbol|bigint)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:true|false|null|undefined|NaN|Infinity)\\b/g, className: 'token-constant' },\n { pattern: /(?<=\\b(?:class|interface|type|enum|extends|implements)\\s+)\\w+/g, className: 'token-type' },\n ],\n javascript: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\b(?:import|export|from|const|let|var|function|return|if|else|for|while|class|extends|new|this|super|async|await|yield|of|in|typeof|instanceof|delete|throw|try|catch|finally|switch|case|default|break|continue|do)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:true|false|null|undefined|NaN|Infinity)\\b/g, className: 'token-constant' },\n ],\n // The tokenizer reads the raw source (3.6.1): `<` and `>` instead of the\n // `&lt;`/`&gt;` the escaped text had. Up to 3.6.0 an html comment rule ran on\n // that escaped text and never matched; it is gone, so comments stay plain.\n html: [\n ...SHARED_RULES,\n { pattern: /<\\/?[\\w-]+/g, className: 'token-tag' },\n { pattern: /\\/?>/g, className: 'token-tag' },\n { pattern: /\\b[\\w-]+(?==)/g, className: 'token-attr' },\n ],\n css: [\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /[.#][\\w-]+/g, className: 'token-selector' },\n { pattern: /\\b(?:px|rem|em|%|vh|vw|s|ms|deg|fr)\\b/g, className: 'token-unit' },\n { pattern: /[\\w-]+(?=\\s*:)/g, className: 'token-property' },\n ],\n scss: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\$[\\w-]+/g, className: 'token-variable' },\n { pattern: /@(?:use|forward|import|mixin|include|extend|if|else|each|for|while|function|return)\\b/g, className: 'token-keyword' },\n { pattern: /&/g, className: 'token-selector' },\n { pattern: /[.#][\\w-]+/g, className: 'token-selector' },\n ],\n json: [\n ...SHARED_RULES,\n { pattern: /\\b(?:true|false|null)\\b/g, className: 'token-constant' },\n { pattern: /\"(?:[^\"\\\\]|\\\\.)*\"(?=\\s*:)/g, className: 'token-property' },\n ],\n bash: [\n { pattern: /#.*$/gm, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\$[\\w{][\\w}]*/g, className: 'token-variable' },\n { pattern: /\\b(?:if|then|else|elif|fi|for|do|done|while|until|case|esac|function|in|select|time|coproc)\\b/g, className: 'token-keyword' },\n { pattern: /(?:^|\\s)(?:npm|npx|yarn|pnpm|git|cd|ls|mkdir|rm|cp|mv|echo|cat|grep|find|sed|awk|curl|wget|docker|node|python|pip|brew|apt|sudo)\\b/g, className: 'token-builtin' },\n { pattern: /--?[\\w-]+/g, className: 'token-flag' },\n ],\n python: [\n { pattern: /#.*$/gm, className: 'token-comment' },\n { pattern: /\"\"\"[\\s\\S]*?\"\"\"/g, className: 'token-string' },\n { pattern: /'''[\\s\\S]*?'''/g, className: 'token-string' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:def|class|import|from|as|return|if|elif|else|for|while|break|continue|pass|raise|try|except|finally|with|yield|lambda|assert|del|global|nonlocal|async|await|in|not|and|or|is)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:True|False|None)\\b/g, className: 'token-constant' },\n { pattern: /\\b(?:str|int|float|bool|list|dict|tuple|set|bytes|type|object|Exception|ValueError|TypeError|KeyError|IndexError|AttributeError|RuntimeError|StopIteration)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:print|len|range|enumerate|zip|map|filter|sorted|reversed|isinstance|issubclass|hasattr|getattr|setattr|super|property|staticmethod|classmethod|open|input)\\b/g, className: 'token-builtin' },\n { pattern: /\\bself\\b/g, className: 'token-variable' },\n ],\n java: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:abstract|assert|break|case|catch|class|continue|default|do|else|enum|extends|final|finally|for|if|implements|import|instanceof|interface|native|new|package|private|protected|public|return|static|strictfp|super|switch|synchronized|this|throw|throws|transient|try|volatile|while|var|record|sealed|permits|yield)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:true|false|null)\\b/g, className: 'token-constant' },\n { pattern: /\\b(?:void|int|long|short|byte|float|double|char|boolean|String|Integer|Long|Double|Float|Boolean|Character|Object|Class|List|Map|Set|ArrayList|HashMap|HashSet|Optional|Stream|Collectors)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:System|Math|Arrays|Collections|Objects|Thread|Runnable)\\b/g, className: 'token-builtin' },\n ],\n};\n\n/** A run of source text; `className` is one `token-*` class, or `null` for plain text. */\ninterface CodeSegment {\n readonly text: string;\n readonly className: string | null;\n}\n\n/** One rendered line: its segments in order. */\ntype CodeLine = readonly CodeSegment[];\n\n/**\n * What an empty line renders: U+00A0, the glyph the `&nbsp;` of 3.6.0\n * produced, so the line keeps its height and a selection its text.\n */\nconst EMPTY_LINE: CodeLine = [{ text: String.fromCharCode(0xa0), className: null }];\n\n/** Matches of the language's rules on the raw source, sorted, without overlaps. */\nfunction tokenize(code: string, language: string): { start: number; end: number; className: string }[] {\n const rules = LANGUAGE_RULES[language];\n if (!rules) return [];\n\n // Collect all matches with positions\n const tokens: { start: number; end: number; className: string }[] = [];\n\n for (const rule of rules) {\n const regex = new RegExp(rule.pattern.source, rule.pattern.flags);\n let match: RegExpExecArray | null;\n while ((match = regex.exec(code)) !== null) {\n tokens.push({\n start: match.index,\n end: match.index + match[0].length,\n className: rule.className,\n });\n }\n }\n\n // Sort by start position, longer matches first for same position\n tokens.sort((a, b) => a.start - b.start || b.end - a.end);\n\n // Remove overlapping tokens (keep first/longest)\n const filtered: typeof tokens = [];\n let lastEnd = 0;\n for (const token of tokens) {\n if (token.start >= lastEnd) {\n filtered.push(token);\n lastEnd = token.end;\n }\n }\n return filtered;\n}\n\n/**\n * The highlighted source as lines of text segments, rendered by the template\n * as text nodes and `<span class=\"token-…\">` elements: no markup string, so\n * nothing for Trusted Types to block and nothing in the source is parsed.\n *\n * A token that runs across a line break keeps its class on the part before\n * the first break only; the later parts are plain, as the HTML parser closed\n * the open span at each line end up to 3.6.0.\n */\nfunction highlightLines(code: string, language: string): CodeLine[] {\n const lines: CodeSegment[][] = [[]];\n const append = (text: string, className: string | null) => {\n text.split('\\n').forEach((part, i) => {\n if (i > 0) lines.push([]);\n if (!part) return;\n const line = lines[lines.length - 1];\n const cls = i === 0 ? className : null;\n const last = line[line.length - 1];\n if (cls === null && last?.className === null) {\n line[line.length - 1] = { text: last.text + part, className: null };\n } else {\n line.push({ text: part, className: cls });\n }\n });\n };\n\n let cursor = 0;\n for (const token of tokenize(code, language)) {\n append(code.slice(cursor, token.start), null);\n append(code.slice(token.start, token.end), token.className);\n cursor = token.end;\n }\n append(code.slice(cursor), null);\n\n return lines.map(line => (line.length ? line : EMPTY_LINE));\n}\n\n@Component({\n selector: 'lc-code-block',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_CHECK, ICON_CLIPBOARD])],\n templateUrl: './code-block.component.html',\n styleUrls: ['./code-block.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\n/**\n * Code block component for displaying syntax-highlighted source code.\n *\n * Features:\n * - Syntax highlighting for 10+ languages (TypeScript, Python, Java, etc.)\n * - Optional line numbers\n * - Copy-to-clipboard button\n * - Optional header with filename and language label\n * - Dark-mode compatible color scheme\n * - Keyboard: the body is a focusable, named region that scrolls with the\n * arrow keys; `wrap` wraps instead\n *\n * @example\n * ```html\n * <lc-code-block [code]=\"source\" language=\"typescript\" [showLineNumbers]=\"true\" />\n * ```\n */\nexport class CodeBlockComponent {\n /** Code content to display */\n code = input.required<string>();\n\n /** Language label */\n language = input<CodeBlockLanguage>('text');\n\n /** Optional filename to display */\n filename = input<string>();\n\n /** Whether to show line numbers */\n showLineNumbers = input<boolean>(true);\n\n /** Whether to show copy button */\n showCopy = input<boolean>(true);\n\n /** Whether the code block has a header bar */\n showHeader = input<boolean>(true);\n\n /**\n * Accessible name of the scrollable code region (`role=\"region\"`). When it\n * is unset or empty, the `filename` is used, then the `language`. Set a\n * short, localised name that is unique on the page, such as \"Befehl für\n * Claude Code\". Not used when `wrap` is set.\n * @default undefined\n */\n regionLabel = input<string | undefined>(undefined);\n\n /**\n * Wraps long lines (`white-space: pre-wrap`, breaking anywhere if needed)\n * instead of scrolling sideways. A wrapped block doesn't scroll, so its body\n * is neither a tab stop nor a region.\n * @default false\n */\n wrap = input<boolean>(false);\n\n /** Visible label of the copy button. */\n copyLabel = input('Copy');\n\n /** Accessible name of the copy button. */\n copyAriaLabel = input('Copy code');\n\n /** Label of the copy button after copying (also its accessible name). */\n copiedLabel = input('Copied');\n\n /** Announced to screen readers after copying. */\n copiedAnnouncement = input('Copied to clipboard');\n\n protected copied = signal(false);\n\n protected highlightedLines = computed<CodeLine[]>(() =>\n highlightLines(this.code(), this.language())\n );\n\n protected lineNumbers = computed(() => {\n return this.code().split('\\n').map((_, i) => i + 1);\n });\n\n protected headerLabel = computed(() => {\n return this.filename() || this.language();\n });\n\n /** The region's name: `regionLabel`, then `filename`, then `language` (`''` falls through). */\n protected regionName = computed(() => this.regionLabel() || this.headerLabel());\n\n protected codeClasses = computed(() => {\n const classes = ['code-block', `code-block--${this.language()}`];\n if (this.wrap()) classes.push('code-block--wrap');\n return classes.join(' ');\n });\n\n protected async copyToClipboard(): Promise<void> {\n try {\n await navigator.clipboard.writeText(this.code());\n this.copied.set(true);\n setTimeout(() => this.copied.set(false), 2000);\n } catch {\n // Fallback: just ignore if clipboard API not available\n }\n }\n}\n","<div [class]=\"codeClasses()\">\n @if (showHeader()) {\n <div class=\"code-block__header\">\n <span class=\"code-block__language\">{{ headerLabel() }}</span>\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"code-block__copy\"\n [attr.aria-label]=\"copied() ? copiedLabel() : copyAriaLabel()\"\n (click)=\"copyToClipboard()\"\n >\n @if (copied()) {\n <lc-icon name=\"check\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copiedLabel() }}</span>\n } @else {\n <lc-icon name=\"clipboard-document\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copyLabel() }}</span>\n }\n </button>\n <span class=\"code-block__live\" role=\"status\" aria-live=\"polite\">{{ copied() ? copiedAnnouncement() : '' }}</span>\n }\n </div>\n }\n <div\n class=\"code-block__body\"\n [attr.tabindex]=\"wrap() ? null : 0\"\n [attr.role]=\"wrap() ? null : 'region'\"\n [attr.aria-label]=\"wrap() ? null : regionName()\"\n >\n <pre class=\"code-block__pre\"><code class=\"code-block__code\">@for (line of highlightedLines(); track $index; let i = $index) {<span class=\"code-block__line\">@if (showLineNumbers()) {<span class=\"code-block__line-number\">{{ lineNumbers()[i] }}</span>}<span class=\"code-block__line-content\">@for (segment of line; track $index) {@if (segment.className) {<span [attr.class]=\"segment.className\">{{ segment.text }}</span>}@else{{{ segment.text }}}}</span></span>\n}</code></pre>\n </div>\n</div>\n"],"mappings":"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"}
@@ -408,7 +408,7 @@ var ComboboxComponent = class ComboboxComponent {
408
408
  }],
409
409
  ngImport: i0,
410
410
  template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <div #inputWrapper class=\"lc-combobox__input-wrapper\">\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n tabindex=\"-1\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayMatchWidth]=\"true\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n }\n</div>\n",
411
- styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text-primary);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md, .375rem);background-color:var(--color-surface-2);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:hover{border-color:var(--color-text-secondary)}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{box-shadow:0 0 0 3px color-mix(in srgb,var(--color-error-default) 20%,transparent)}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.6;cursor:not-allowed;background-color:var(--color-surface-muted)}.lc-combobox--disabled .lc-combobox__input-wrapper:hover{border-color:var(--color-border)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text-primary);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-text-tertiary)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;cursor:pointer;color:var(--color-text-tertiary);border-radius:var(--border-radius-sm, .25rem);flex-shrink:0}.lc-combobox__clear:hover:not(:disabled){color:var(--color-text-primary);background-color:var(--color-surface-hover)}.lc-combobox__clear:disabled{cursor:not-allowed}.lc-combobox__dropdown{margin-top:.25rem;max-height:240px;overflow-y:auto;background-color:var(--color-surface-raised);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md, .375rem);box-shadow:var(--elevation-3)}.lc-combobox__group-header{padding:.375rem .75rem;font-size:var(--font-size-xs, .75rem);font-weight:600;color:var(--color-text-tertiary);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem}.lc-combobox__group-header:first-child{margin-top:0}.lc-combobox__option{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;cursor:pointer;transition:background-color .1s}.lc-combobox__option--highlighted{background-color:var(--color-surface-hover)}.lc-combobox__option--disabled{opacity:.5;cursor:not-allowed}.lc-combobox__option--create{color:var(--color-primary-600);font-weight:500;border-top:1px solid var(--color-divider)}.lc-combobox__option-icon{flex-shrink:0;color:var(--color-text-tertiary)}.lc-combobox__option-content{display:flex;flex-direction:column;min-width:0}.lc-combobox__option-label{font-size:var(--font-size-sm, .875rem);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__option-desc{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__empty{padding:.75rem;text-align:center;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .875rem)}.lc-combobox__helper{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary)}.lc-combobox__error{font-size:var(--font-size-xs, .75rem);color:var(--color-text-error)}\n"],
411
+ styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text-primary);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md, .375rem);background-color:var(--color-surface-2);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:hover{border-color:var(--color-text-secondary)}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline:2px solid var(--color-error-default);outline-offset:0;box-shadow:none}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.6;cursor:not-allowed;background-color:var(--color-surface-muted)}.lc-combobox--disabled .lc-combobox__input-wrapper:hover{border-color:var(--color-border)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text-primary);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-text-tertiary)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;cursor:pointer;color:var(--color-text-tertiary);border-radius:var(--border-radius-sm, .25rem);flex-shrink:0}.lc-combobox__clear:hover:not(:disabled){color:var(--color-text-primary);background-color:var(--color-surface-hover)}.lc-combobox__clear:disabled{cursor:not-allowed}.lc-combobox__dropdown{margin-top:.25rem;max-height:240px;overflow-y:auto;background-color:var(--color-surface-raised);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md, .375rem);box-shadow:var(--elevation-3)}.lc-combobox__group-header{padding:.375rem .75rem;font-size:var(--font-size-xs, .75rem);font-weight:600;color:var(--color-text-tertiary);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem}.lc-combobox__group-header:first-child{margin-top:0}.lc-combobox__option{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;cursor:pointer;transition:background-color .1s}.lc-combobox__option--highlighted{background-color:var(--color-surface-hover)}.lc-combobox__option--disabled{opacity:.5;cursor:not-allowed}.lc-combobox__option--create{color:var(--color-primary-600);font-weight:500;border-top:1px solid var(--color-divider)}.lc-combobox__option-icon{flex-shrink:0;color:var(--color-text-tertiary)}.lc-combobox__option-content{display:flex;flex-direction:column;min-width:0}.lc-combobox__option-label{font-size:var(--font-size-sm, .875rem);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__option-desc{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__empty{padding:.75rem;text-align:center;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .875rem)}.lc-combobox__helper{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary)}.lc-combobox__error{font-size:var(--font-size-xs, .75rem);color:var(--color-text-error)}@media(forced-colors:active){.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline-color:Highlight}}\n"],
412
412
  dependencies: [
413
413
  {
414
414
  kind: "ngmodule",
@@ -523,7 +523,7 @@ i0.ɵɵngDeclareClassMetadata({
523
523
  multi: true
524
524
  }],
525
525
  template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <div #inputWrapper class=\"lc-combobox__input-wrapper\">\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n tabindex=\"-1\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayMatchWidth]=\"true\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n }\n</div>\n",
526
- styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text-primary);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md, .375rem);background-color:var(--color-surface-2);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:hover{border-color:var(--color-text-secondary)}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{box-shadow:0 0 0 3px color-mix(in srgb,var(--color-error-default) 20%,transparent)}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.6;cursor:not-allowed;background-color:var(--color-surface-muted)}.lc-combobox--disabled .lc-combobox__input-wrapper:hover{border-color:var(--color-border)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text-primary);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-text-tertiary)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;cursor:pointer;color:var(--color-text-tertiary);border-radius:var(--border-radius-sm, .25rem);flex-shrink:0}.lc-combobox__clear:hover:not(:disabled){color:var(--color-text-primary);background-color:var(--color-surface-hover)}.lc-combobox__clear:disabled{cursor:not-allowed}.lc-combobox__dropdown{margin-top:.25rem;max-height:240px;overflow-y:auto;background-color:var(--color-surface-raised);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md, .375rem);box-shadow:var(--elevation-3)}.lc-combobox__group-header{padding:.375rem .75rem;font-size:var(--font-size-xs, .75rem);font-weight:600;color:var(--color-text-tertiary);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem}.lc-combobox__group-header:first-child{margin-top:0}.lc-combobox__option{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;cursor:pointer;transition:background-color .1s}.lc-combobox__option--highlighted{background-color:var(--color-surface-hover)}.lc-combobox__option--disabled{opacity:.5;cursor:not-allowed}.lc-combobox__option--create{color:var(--color-primary-600);font-weight:500;border-top:1px solid var(--color-divider)}.lc-combobox__option-icon{flex-shrink:0;color:var(--color-text-tertiary)}.lc-combobox__option-content{display:flex;flex-direction:column;min-width:0}.lc-combobox__option-label{font-size:var(--font-size-sm, .875rem);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__option-desc{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__empty{padding:.75rem;text-align:center;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .875rem)}.lc-combobox__helper{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary)}.lc-combobox__error{font-size:var(--font-size-xs, .75rem);color:var(--color-text-error)}\n"]
526
+ styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text-primary);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md, .375rem);background-color:var(--color-surface-2);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:hover{border-color:var(--color-text-secondary)}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline:2px solid var(--color-error-default);outline-offset:0;box-shadow:none}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.6;cursor:not-allowed;background-color:var(--color-surface-muted)}.lc-combobox--disabled .lc-combobox__input-wrapper:hover{border-color:var(--color-border)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text-primary);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-text-tertiary)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;cursor:pointer;color:var(--color-text-tertiary);border-radius:var(--border-radius-sm, .25rem);flex-shrink:0}.lc-combobox__clear:hover:not(:disabled){color:var(--color-text-primary);background-color:var(--color-surface-hover)}.lc-combobox__clear:disabled{cursor:not-allowed}.lc-combobox__dropdown{margin-top:.25rem;max-height:240px;overflow-y:auto;background-color:var(--color-surface-raised);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md, .375rem);box-shadow:var(--elevation-3)}.lc-combobox__group-header{padding:.375rem .75rem;font-size:var(--font-size-xs, .75rem);font-weight:600;color:var(--color-text-tertiary);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem}.lc-combobox__group-header:first-child{margin-top:0}.lc-combobox__option{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;cursor:pointer;transition:background-color .1s}.lc-combobox__option--highlighted{background-color:var(--color-surface-hover)}.lc-combobox__option--disabled{opacity:.5;cursor:not-allowed}.lc-combobox__option--create{color:var(--color-primary-600);font-weight:500;border-top:1px solid var(--color-divider)}.lc-combobox__option-icon{flex-shrink:0;color:var(--color-text-tertiary)}.lc-combobox__option-content{display:flex;flex-direction:column;min-width:0}.lc-combobox__option-label{font-size:var(--font-size-sm, .875rem);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__option-desc{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__empty{padding:.75rem;text-align:center;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .875rem)}.lc-combobox__helper{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary)}.lc-combobox__error{font-size:var(--font-size-xs, .75rem);color:var(--color-text-error)}@media(forced-colors:active){.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline-color:Highlight}}\n"]
527
527
  }]
528
528
  }],
529
529
  ctorParameters: () => [],
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-combobox.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/combobox/combobox.component.ts","../../../../libs/angular-ui-kit/src/lib/combobox/combobox.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n signal,\n computed,\n afterRenderEffect,\n forwardRef,\n ElementRef,\n viewChild,\n HostListener,\n inject,\n untracked,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { OverlayModule } from '@angular/cdk/overlay';\nimport { Observable, Subject, catchError, debounce, defer, of, switchMap, timer } from 'rxjs';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_PLUS, ICON_X_MARK } from '@lifecorner/angular-ui-kit/icons';\nimport { SpinnerComponent } from '@lifecorner/angular-ui-kit/spinner';\nimport { ChipComponent } from '@lifecorner/angular-ui-kit/chip';\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n description?: string;\n icon?: string;\n group?: string;\n disabled?: boolean;\n}\n\nexport type ComboboxValue = ComboboxOption | ComboboxOption[] | null;\n\nexport type ComboboxSize = 'xs' | 'sm' | 'md' | 'lg';\n\n/**\n * Combobox / async autocomplete component.\n *\n * Supports free-text input with sync/async option suggestions,\n * single/multiple selection, create new entries, and keyboard navigation.\n * The suggestion list is rendered in a CDK overlay so it is never clipped\n * by `overflow: hidden` ancestors (cards, modals, table cells).\n *\n * @example\n * ```html\n * <lc-combobox\n * label=\"Assign to user\"\n * [loadOptions]=\"searchUsers\"\n * [(value)]=\"assignee\"\n * allowCreate\n * />\n * ```\n */\n@Component({\n selector: 'lc-combobox',\n standalone: true,\n imports: [OverlayModule, IconComponent, SpinnerComponent, ChipComponent],\n viewProviders: [ɵprovideKitIcons([ICON_X_MARK, ICON_PLUS])],\n templateUrl: './combobox.component.html',\n styleUrls: ['./combobox.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => ComboboxComponent),\n multi: true,\n },\n ],\n})\nexport class ComboboxComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Per-instance ids so label/listbox/options can be linked via ARIA */\n readonly inputId = `lc-combobox-${++ComboboxComponent.nextId}`;\n readonly listboxId = `${this.inputId}-listbox`;\n protected readonly createOptionId = `${this.listboxId}-create`;\n\n private readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputEl');\n private readonly dropdownEl = viewChild<ElementRef<HTMLElement>>('dropdownEl');\n\n private readonly elRef = inject(ElementRef);\n private readonly querySubject = new Subject<string>();\n\n /** Sync options */\n readonly options = input<ComboboxOption[]>([]);\n\n /** Async option loader */\n readonly loadOptions = input<((query: string) => Observable<ComboboxOption[]>)>();\n\n /** Debounce for async queries */\n readonly debounceMs = input<number>(250);\n\n /** Minimum characters before triggering search */\n readonly minChars = input<number>(1);\n\n /** Placeholder text */\n readonly placeholder = input<string>('Search…');\n\n /** Label */\n readonly label = input<string>();\n\n /** Helper text */\n readonly helperText = input<string>();\n\n /** Error message */\n readonly error = input<string>();\n\n /** Disabled state */\n readonly disabled = input<boolean>(false);\n\n /** Allow multiple selections */\n readonly multiple = input<boolean>(false);\n\n /** Allow creating new entries */\n readonly allowCreate = input<boolean>(false);\n\n /** Show loading spinner (controlled) */\n readonly loading = input<boolean>(false);\n\n /** Empty state message */\n readonly emptyMessage = input<string>('No results');\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear');\n\n /** Label before the query in the \"create\" option (`allowCreate`): `Create \"query\"`. */\n readonly createOptionLabel = input('Create');\n\n /** Size */\n readonly size = input<ComboboxSize>('md');\n\n /** Value change event */\n readonly valueChange = output<ComboboxValue>();\n\n /** Query change event */\n readonly queryChange = output<string>();\n\n /** Option selected event */\n readonly optionSelected = output<ComboboxOption>();\n\n /** Created event (allowCreate) */\n readonly created = output<string>();\n\n /** Internal state */\n protected query = signal('');\n protected isOpen = signal(false);\n protected highlightedIndex = signal(-1);\n protected asyncOptions = signal<ComboboxOption[]>([]);\n protected isLoading = signal(false);\n\n /** Disabled via `setDisabledState` (reactive forms) */\n private readonly formDisabled = signal(false);\n\n /** Effective disabled state: `disabled` input OR form control disabled */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n /** Selected values */\n protected selectedSingle = signal<ComboboxOption | null>(null);\n protected selectedMultiple = signal<ComboboxOption[]>([]);\n\n /** Resolved visible options */\n protected visibleOptions = computed(() => {\n const q = this.query().toLowerCase();\n const loader = this.loadOptions();\n\n let opts: ComboboxOption[];\n if (loader) {\n opts = this.asyncOptions();\n } else {\n opts = this.options();\n if (q.length >= this.minChars()) {\n opts = opts.filter(\n (o) =>\n o.label.toLowerCase().includes(q) ||\n o.value.toLowerCase().includes(q) ||\n (o.description && o.description.toLowerCase().includes(q))\n );\n }\n }\n\n // Remove already selected in multiple mode\n if (this.multiple()) {\n const selected = new Set(this.selectedMultiple().map((s) => s.value));\n opts = opts.filter((o) => !selected.has(o.value));\n }\n\n return opts;\n });\n\n /** Grouped options */\n protected groupedOptions = computed(() => {\n const opts = this.visibleOptions();\n const groups = new Map<string, ComboboxOption[]>();\n\n for (const opt of opts) {\n const group = opt.group || '';\n if (!groups.has(group)) groups.set(group, []);\n groups.get(group)!.push(opt);\n }\n\n return Array.from(groups.entries()).map(([label, items]) => ({\n label,\n items,\n }));\n });\n\n /** Flat list for keyboard nav */\n protected flatOptions = computed(() => {\n return this.groupedOptions().flatMap((g) => g.items);\n });\n\n /** Whether to show \"Create\" option */\n protected showCreateOption = computed(() => {\n if (!this.allowCreate()) return false;\n const q = this.query().trim();\n if (!q) return false;\n const exact = this.visibleOptions().some(\n (o) => o.label.toLowerCase() === q.toLowerCase()\n );\n return !exact;\n });\n\n /** id of the highlighted option — announced via aria-activedescendant */\n protected readonly activeDescendantId = computed(() => {\n if (!this.isOpen()) return null;\n const i = this.highlightedIndex();\n if (i < 0) return null;\n if (i === this.flatOptions().length) {\n return this.showCreateOption() ? this.createOptionId : null;\n }\n return i < this.flatOptions().length ? this.optionId(i) : null;\n });\n\n /** ids referenced by aria-describedby (helper / error) */\n protected readonly describedBy = computed(() => {\n if (this.error()) return `${this.inputId}-error`;\n if (this.helperText()) return `${this.inputId}-helper`;\n return null;\n });\n\n /** Container classes */\n protected containerClasses = computed(() => {\n const classes = [`lc-combobox`, `lc-combobox--${this.size()}`];\n if (this.isDisabled()) classes.push('lc-combobox--disabled');\n if (this.error()) classes.push('lc-combobox--error');\n if (this.isOpen()) classes.push('lc-combobox--open');\n return classes.join(' ');\n });\n\n protected optionId(index: number): string {\n return `${this.listboxId}-opt-${index}`;\n }\n\n // ControlValueAccessor\n private onChange: (value: ComboboxValue) => void = () => { /* set by registerOnChange */ };\n private onTouched: () => void = () => { /* set by registerOnTouched */ };\n\n constructor() {\n // Async loading pipe. `debounce` (not `debounceTime`) reads the signal per\n // emission, so `debounceMs` is honoured after inputs are populated.\n this.querySubject\n .pipe(\n debounce(() => timer(this.debounceMs())),\n switchMap((q) => {\n const loader = this.loadOptions();\n if (!loader || q.length < this.minChars()) {\n return of([] as ComboboxOption[]);\n }\n this.isLoading.set(true);\n // `defer` turns a synchronously throwing loader into an error\n // notification; `catchError` keeps the pipe alive for the next query.\n return defer(() => loader(q)).pipe(catchError(() => of([] as ComboboxOption[])));\n }),\n takeUntilDestroyed()\n )\n .subscribe((results) => {\n this.asyncOptions.set(results);\n this.isLoading.set(false);\n });\n\n // Keep the highlighted option visible while navigating with the keyboard\n // (after render, so the option element exists in the overlay pane).\n afterRenderEffect(() => {\n const id = this.activeDescendantId();\n if (!id) return;\n untracked(() => {\n // ids are generated from a counter, so no CSS escaping is needed\n this.dropdownEl()\n ?.nativeElement.querySelector<HTMLElement>(`#${id}`)\n ?.scrollIntoView?.({ block: 'nearest' });\n });\n });\n }\n\n writeValue(value: ComboboxValue): void {\n if (this.multiple()) {\n this.selectedMultiple.set(Array.isArray(value) ? value : []);\n } else {\n const single = value && !Array.isArray(value) ? value : null;\n this.selectedSingle.set(single);\n this.query.set(single ? single.label : '');\n }\n }\n\n registerOnChange(fn: (value: ComboboxValue) => 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 if (isDisabled) this.isOpen.set(false);\n }\n\n /**\n * Clear the current selection and query. Emits `null` (single) or `[]`\n * (multiple) exactly once, like a user-initiated deselect.\n */\n clear(): void {\n this.query.set('');\n this.highlightedIndex.set(-1);\n this.isOpen.set(false);\n const value: ComboboxValue = this.multiple() ? [] : null;\n if (this.multiple()) {\n this.selectedMultiple.set([]);\n } else {\n this.selectedSingle.set(null);\n }\n this.onChange(value);\n this.valueChange.emit(value);\n }\n\n protected onClearClick(): void {\n if (this.isDisabled()) return;\n this.clear();\n this.inputEl()?.nativeElement.focus();\n }\n\n protected onInputChange(event: Event): void {\n const value = (event.target as HTMLInputElement).value;\n this.query.set(value);\n this.queryChange.emit(value);\n this.highlightedIndex.set(-1);\n\n if (this.loadOptions()) {\n this.querySubject.next(value);\n }\n\n if (value.length >= this.minChars()) {\n this.isOpen.set(true);\n }\n }\n\n protected onInputFocus(): void {\n if (this.query().length >= this.minChars() || this.options().length > 0) {\n this.isOpen.set(true);\n }\n }\n\n protected onInputBlur(): void {\n this.onTouched();\n // Delay to allow click on dropdown\n setTimeout(() => this.isOpen.set(false), 200);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n const flat = this.flatOptions();\n const total = flat.length + (this.showCreateOption() ? 1 : 0);\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.isOpen.set(true);\n if (total > 0) this.highlightedIndex.update((i) => (i + 1) % total);\n break;\n case 'ArrowUp':\n event.preventDefault();\n if (total > 0) this.highlightedIndex.update((i) => (i - 1 + total) % total);\n break;\n case 'Enter':\n event.preventDefault();\n if (this.highlightedIndex() >= 0) {\n if (this.highlightedIndex() === flat.length && this.showCreateOption()) {\n this.onCreateNew();\n } else {\n const opt = flat[this.highlightedIndex()];\n if (opt && !opt.disabled) {\n this.selectOption(opt);\n }\n }\n } else if (this.showCreateOption()) {\n this.onCreateNew();\n }\n break;\n case 'Escape':\n if (this.isOpen()) {\n this.isOpen.set(false);\n this.highlightedIndex.set(-1);\n // Escape consumed by the list — an enclosing modal must not close too.\n event.stopPropagation();\n }\n break;\n case 'Tab':\n if (this.highlightedIndex() >= 0 && this.isOpen()) {\n const opt = flat[this.highlightedIndex()];\n if (opt && !opt.disabled) {\n this.selectOption(opt);\n }\n }\n this.isOpen.set(false);\n break;\n case 'Backspace':\n if (this.multiple() && !this.query() && this.selectedMultiple().length > 0) {\n this.removeSelected(this.selectedMultiple()[this.selectedMultiple().length - 1]);\n }\n break;\n }\n }\n\n protected selectOption(option: ComboboxOption): void {\n if (this.multiple()) {\n this.selectedMultiple.update((sel) => [...sel, option]);\n this.query.set('');\n const value = this.selectedMultiple();\n this.onChange(value);\n this.valueChange.emit(value);\n } else {\n this.selectedSingle.set(option);\n this.query.set(option.label);\n this.onChange(option);\n this.valueChange.emit(option);\n }\n this.optionSelected.emit(option);\n this.isOpen.set(false);\n this.highlightedIndex.set(-1);\n }\n\n protected removeSelected(option: ComboboxOption): void {\n if (this.isDisabled()) return;\n this.selectedMultiple.update((sel) =>\n sel.filter((s) => s.value !== option.value)\n );\n const value = this.selectedMultiple();\n this.onChange(value);\n this.valueChange.emit(value);\n this.inputEl()?.nativeElement.focus();\n }\n\n protected onCreateNew(): void {\n const q = this.query().trim();\n if (!q) return;\n const newOption: ComboboxOption = { value: q, label: q };\n this.created.emit(q);\n this.selectOption(newOption);\n }\n\n @HostListener('document:click', ['$event'])\n protected onDocumentClick(event: MouseEvent): void {\n const target = event.target as Node | null;\n // The dropdown lives in the CDK overlay container, outside the host element.\n if (\n !this.elRef.nativeElement.contains(target) &&\n !this.dropdownEl()?.nativeElement.contains(target)\n ) {\n this.isOpen.set(false);\n }\n }\n}\n","<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <div #inputWrapper class=\"lc-combobox__input-wrapper\">\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n tabindex=\"-1\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayMatchWidth]=\"true\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n }\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-combobox.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/combobox/combobox.component.ts","../../../../libs/angular-ui-kit/src/lib/combobox/combobox.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n signal,\n computed,\n afterRenderEffect,\n forwardRef,\n ElementRef,\n viewChild,\n HostListener,\n inject,\n untracked,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { OverlayModule } from '@angular/cdk/overlay';\nimport { Observable, Subject, catchError, debounce, defer, of, switchMap, timer } from 'rxjs';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_PLUS, ICON_X_MARK } from '@lifecorner/angular-ui-kit/icons';\nimport { SpinnerComponent } from '@lifecorner/angular-ui-kit/spinner';\nimport { ChipComponent } from '@lifecorner/angular-ui-kit/chip';\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n description?: string;\n icon?: string;\n group?: string;\n disabled?: boolean;\n}\n\nexport type ComboboxValue = ComboboxOption | ComboboxOption[] | null;\n\nexport type ComboboxSize = 'xs' | 'sm' | 'md' | 'lg';\n\n/**\n * Combobox / async autocomplete component.\n *\n * Supports free-text input with sync/async option suggestions,\n * single/multiple selection, create new entries, and keyboard navigation.\n * The suggestion list is rendered in a CDK overlay so it is never clipped\n * by `overflow: hidden` ancestors (cards, modals, table cells).\n *\n * @example\n * ```html\n * <lc-combobox\n * label=\"Assign to user\"\n * [loadOptions]=\"searchUsers\"\n * [(value)]=\"assignee\"\n * allowCreate\n * />\n * ```\n */\n@Component({\n selector: 'lc-combobox',\n standalone: true,\n imports: [OverlayModule, IconComponent, SpinnerComponent, ChipComponent],\n viewProviders: [ɵprovideKitIcons([ICON_X_MARK, ICON_PLUS])],\n templateUrl: './combobox.component.html',\n styleUrls: ['./combobox.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => ComboboxComponent),\n multi: true,\n },\n ],\n})\nexport class ComboboxComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Per-instance ids so label/listbox/options can be linked via ARIA */\n readonly inputId = `lc-combobox-${++ComboboxComponent.nextId}`;\n readonly listboxId = `${this.inputId}-listbox`;\n protected readonly createOptionId = `${this.listboxId}-create`;\n\n private readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputEl');\n private readonly dropdownEl = viewChild<ElementRef<HTMLElement>>('dropdownEl');\n\n private readonly elRef = inject(ElementRef);\n private readonly querySubject = new Subject<string>();\n\n /** Sync options */\n readonly options = input<ComboboxOption[]>([]);\n\n /** Async option loader */\n readonly loadOptions = input<((query: string) => Observable<ComboboxOption[]>)>();\n\n /** Debounce for async queries */\n readonly debounceMs = input<number>(250);\n\n /** Minimum characters before triggering search */\n readonly minChars = input<number>(1);\n\n /** Placeholder text */\n readonly placeholder = input<string>('Search…');\n\n /** Label */\n readonly label = input<string>();\n\n /** Helper text */\n readonly helperText = input<string>();\n\n /** Error message */\n readonly error = input<string>();\n\n /** Disabled state */\n readonly disabled = input<boolean>(false);\n\n /** Allow multiple selections */\n readonly multiple = input<boolean>(false);\n\n /** Allow creating new entries */\n readonly allowCreate = input<boolean>(false);\n\n /** Show loading spinner (controlled) */\n readonly loading = input<boolean>(false);\n\n /** Empty state message */\n readonly emptyMessage = input<string>('No results');\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear');\n\n /** Label before the query in the \"create\" option (`allowCreate`): `Create \"query\"`. */\n readonly createOptionLabel = input('Create');\n\n /** Size */\n readonly size = input<ComboboxSize>('md');\n\n /** Value change event */\n readonly valueChange = output<ComboboxValue>();\n\n /** Query change event */\n readonly queryChange = output<string>();\n\n /** Option selected event */\n readonly optionSelected = output<ComboboxOption>();\n\n /** Created event (allowCreate) */\n readonly created = output<string>();\n\n /** Internal state */\n protected query = signal('');\n protected isOpen = signal(false);\n protected highlightedIndex = signal(-1);\n protected asyncOptions = signal<ComboboxOption[]>([]);\n protected isLoading = signal(false);\n\n /** Disabled via `setDisabledState` (reactive forms) */\n private readonly formDisabled = signal(false);\n\n /** Effective disabled state: `disabled` input OR form control disabled */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n /** Selected values */\n protected selectedSingle = signal<ComboboxOption | null>(null);\n protected selectedMultiple = signal<ComboboxOption[]>([]);\n\n /** Resolved visible options */\n protected visibleOptions = computed(() => {\n const q = this.query().toLowerCase();\n const loader = this.loadOptions();\n\n let opts: ComboboxOption[];\n if (loader) {\n opts = this.asyncOptions();\n } else {\n opts = this.options();\n if (q.length >= this.minChars()) {\n opts = opts.filter(\n (o) =>\n o.label.toLowerCase().includes(q) ||\n o.value.toLowerCase().includes(q) ||\n (o.description && o.description.toLowerCase().includes(q))\n );\n }\n }\n\n // Remove already selected in multiple mode\n if (this.multiple()) {\n const selected = new Set(this.selectedMultiple().map((s) => s.value));\n opts = opts.filter((o) => !selected.has(o.value));\n }\n\n return opts;\n });\n\n /** Grouped options */\n protected groupedOptions = computed(() => {\n const opts = this.visibleOptions();\n const groups = new Map<string, ComboboxOption[]>();\n\n for (const opt of opts) {\n const group = opt.group || '';\n if (!groups.has(group)) groups.set(group, []);\n groups.get(group)!.push(opt);\n }\n\n return Array.from(groups.entries()).map(([label, items]) => ({\n label,\n items,\n }));\n });\n\n /** Flat list for keyboard nav */\n protected flatOptions = computed(() => {\n return this.groupedOptions().flatMap((g) => g.items);\n });\n\n /** Whether to show \"Create\" option */\n protected showCreateOption = computed(() => {\n if (!this.allowCreate()) return false;\n const q = this.query().trim();\n if (!q) return false;\n const exact = this.visibleOptions().some(\n (o) => o.label.toLowerCase() === q.toLowerCase()\n );\n return !exact;\n });\n\n /** id of the highlighted option — announced via aria-activedescendant */\n protected readonly activeDescendantId = computed(() => {\n if (!this.isOpen()) return null;\n const i = this.highlightedIndex();\n if (i < 0) return null;\n if (i === this.flatOptions().length) {\n return this.showCreateOption() ? this.createOptionId : null;\n }\n return i < this.flatOptions().length ? this.optionId(i) : null;\n });\n\n /** ids referenced by aria-describedby (helper / error) */\n protected readonly describedBy = computed(() => {\n if (this.error()) return `${this.inputId}-error`;\n if (this.helperText()) return `${this.inputId}-helper`;\n return null;\n });\n\n /** Container classes */\n protected containerClasses = computed(() => {\n const classes = [`lc-combobox`, `lc-combobox--${this.size()}`];\n if (this.isDisabled()) classes.push('lc-combobox--disabled');\n if (this.error()) classes.push('lc-combobox--error');\n if (this.isOpen()) classes.push('lc-combobox--open');\n return classes.join(' ');\n });\n\n protected optionId(index: number): string {\n return `${this.listboxId}-opt-${index}`;\n }\n\n // ControlValueAccessor\n private onChange: (value: ComboboxValue) => void = () => { /* set by registerOnChange */ };\n private onTouched: () => void = () => { /* set by registerOnTouched */ };\n\n constructor() {\n // Async loading pipe. `debounce` (not `debounceTime`) reads the signal per\n // emission, so `debounceMs` is honoured after inputs are populated.\n this.querySubject\n .pipe(\n debounce(() => timer(this.debounceMs())),\n switchMap((q) => {\n const loader = this.loadOptions();\n if (!loader || q.length < this.minChars()) {\n return of([] as ComboboxOption[]);\n }\n this.isLoading.set(true);\n // `defer` turns a synchronously throwing loader into an error\n // notification; `catchError` keeps the pipe alive for the next query.\n return defer(() => loader(q)).pipe(catchError(() => of([] as ComboboxOption[])));\n }),\n takeUntilDestroyed()\n )\n .subscribe((results) => {\n this.asyncOptions.set(results);\n this.isLoading.set(false);\n });\n\n // Keep the highlighted option visible while navigating with the keyboard\n // (after render, so the option element exists in the overlay pane).\n afterRenderEffect(() => {\n const id = this.activeDescendantId();\n if (!id) return;\n untracked(() => {\n // ids are generated from a counter, so no CSS escaping is needed\n this.dropdownEl()\n ?.nativeElement.querySelector<HTMLElement>(`#${id}`)\n ?.scrollIntoView?.({ block: 'nearest' });\n });\n });\n }\n\n writeValue(value: ComboboxValue): void {\n if (this.multiple()) {\n this.selectedMultiple.set(Array.isArray(value) ? value : []);\n } else {\n const single = value && !Array.isArray(value) ? value : null;\n this.selectedSingle.set(single);\n this.query.set(single ? single.label : '');\n }\n }\n\n registerOnChange(fn: (value: ComboboxValue) => 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 if (isDisabled) this.isOpen.set(false);\n }\n\n /**\n * Clear the current selection and query. Emits `null` (single) or `[]`\n * (multiple) exactly once, like a user-initiated deselect.\n */\n clear(): void {\n this.query.set('');\n this.highlightedIndex.set(-1);\n this.isOpen.set(false);\n const value: ComboboxValue = this.multiple() ? [] : null;\n if (this.multiple()) {\n this.selectedMultiple.set([]);\n } else {\n this.selectedSingle.set(null);\n }\n this.onChange(value);\n this.valueChange.emit(value);\n }\n\n protected onClearClick(): void {\n if (this.isDisabled()) return;\n this.clear();\n this.inputEl()?.nativeElement.focus();\n }\n\n protected onInputChange(event: Event): void {\n const value = (event.target as HTMLInputElement).value;\n this.query.set(value);\n this.queryChange.emit(value);\n this.highlightedIndex.set(-1);\n\n if (this.loadOptions()) {\n this.querySubject.next(value);\n }\n\n if (value.length >= this.minChars()) {\n this.isOpen.set(true);\n }\n }\n\n protected onInputFocus(): void {\n if (this.query().length >= this.minChars() || this.options().length > 0) {\n this.isOpen.set(true);\n }\n }\n\n protected onInputBlur(): void {\n this.onTouched();\n // Delay to allow click on dropdown\n setTimeout(() => this.isOpen.set(false), 200);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n const flat = this.flatOptions();\n const total = flat.length + (this.showCreateOption() ? 1 : 0);\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.isOpen.set(true);\n if (total > 0) this.highlightedIndex.update((i) => (i + 1) % total);\n break;\n case 'ArrowUp':\n event.preventDefault();\n if (total > 0) this.highlightedIndex.update((i) => (i - 1 + total) % total);\n break;\n case 'Enter':\n event.preventDefault();\n if (this.highlightedIndex() >= 0) {\n if (this.highlightedIndex() === flat.length && this.showCreateOption()) {\n this.onCreateNew();\n } else {\n const opt = flat[this.highlightedIndex()];\n if (opt && !opt.disabled) {\n this.selectOption(opt);\n }\n }\n } else if (this.showCreateOption()) {\n this.onCreateNew();\n }\n break;\n case 'Escape':\n if (this.isOpen()) {\n this.isOpen.set(false);\n this.highlightedIndex.set(-1);\n // Escape consumed by the list — an enclosing modal must not close too.\n event.stopPropagation();\n }\n break;\n case 'Tab':\n if (this.highlightedIndex() >= 0 && this.isOpen()) {\n const opt = flat[this.highlightedIndex()];\n if (opt && !opt.disabled) {\n this.selectOption(opt);\n }\n }\n this.isOpen.set(false);\n break;\n case 'Backspace':\n if (this.multiple() && !this.query() && this.selectedMultiple().length > 0) {\n this.removeSelected(this.selectedMultiple()[this.selectedMultiple().length - 1]);\n }\n break;\n }\n }\n\n protected selectOption(option: ComboboxOption): void {\n if (this.multiple()) {\n this.selectedMultiple.update((sel) => [...sel, option]);\n this.query.set('');\n const value = this.selectedMultiple();\n this.onChange(value);\n this.valueChange.emit(value);\n } else {\n this.selectedSingle.set(option);\n this.query.set(option.label);\n this.onChange(option);\n this.valueChange.emit(option);\n }\n this.optionSelected.emit(option);\n this.isOpen.set(false);\n this.highlightedIndex.set(-1);\n }\n\n protected removeSelected(option: ComboboxOption): void {\n if (this.isDisabled()) return;\n this.selectedMultiple.update((sel) =>\n sel.filter((s) => s.value !== option.value)\n );\n const value = this.selectedMultiple();\n this.onChange(value);\n this.valueChange.emit(value);\n this.inputEl()?.nativeElement.focus();\n }\n\n protected onCreateNew(): void {\n const q = this.query().trim();\n if (!q) return;\n const newOption: ComboboxOption = { value: q, label: q };\n this.created.emit(q);\n this.selectOption(newOption);\n }\n\n @HostListener('document:click', ['$event'])\n protected onDocumentClick(event: MouseEvent): void {\n const target = event.target as Node | null;\n // The dropdown lives in the CDK overlay container, outside the host element.\n if (\n !this.elRef.nativeElement.contains(target) &&\n !this.dropdownEl()?.nativeElement.contains(target)\n ) {\n this.isOpen.set(false);\n }\n }\n}\n","<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <div #inputWrapper class=\"lc-combobox__input-wrapper\">\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n tabindex=\"-1\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayMatchWidth]=\"true\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n }\n</div>\n"],"mappings":"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"}