@taiga-ui/addon-doc
Version:
Taiga UI based library for developing documentation portals for Angular libraries.
1 lines • 138 kB
Source Map (JSON)
{"version":3,"file":"taiga-ui-addon-doc-components.mjs","sources":["../../../projects/addon-doc/components/api/api.component.ts","../../../projects/addon-doc/components/api/api-item-number.directive.ts","../../../projects/addon-doc/components/api/inspect.pipe.ts","../../../projects/addon-doc/components/api/type-reference.pipe.ts","../../../projects/addon-doc/components/api/api-item.component.ts","../../../projects/addon-doc/components/api/api-item.template.html","../../../projects/addon-doc/components/code/index.ts","../../../projects/addon-doc/components/code/index.html","../../../projects/addon-doc/components/copy/index.ts","../../../projects/addon-doc/components/copy/index.html","../../../projects/addon-doc/components/demo/index.ts","../../../projects/addon-doc/components/demo/index.html","../../../projects/addon-doc/components/doc-tab/index.ts","../../../projects/addon-doc/components/doc-tab/index.html","../../../projects/addon-doc/components/example/example.options.ts","../../../projects/addon-doc/components/example/example-get-tabs.pipe.ts","../../../projects/addon-doc/components/example/example.component.ts","../../../projects/addon-doc/components/example/example.template.html","../../../projects/addon-doc/components/navigation/navigation.providers.ts","../../../projects/addon-doc/components/navigation/scroll-into-view.directive.ts","../../../projects/addon-doc/components/navigation/navigation.component.ts","../../../projects/addon-doc/components/navigation/navigation.template.html","../../../projects/addon-doc/components/internal/header/index.ts","../../../projects/addon-doc/components/internal/header/index.html","../../../projects/addon-doc/components/internal/source-code/source-code.component.ts","../../../projects/addon-doc/components/internal/source-code/source-code.template.html","../../../projects/addon-doc/components/main/main.component.ts","../../../projects/addon-doc/components/main/main.template.html","../../../projects/addon-doc/components/toc/index.ts","../../../projects/addon-doc/components/toc/index.html","../../../projects/addon-doc/components/page/page.providers.ts","../../../projects/addon-doc/components/page/page-tab.directive.ts","../../../projects/addon-doc/components/page/page.component.ts","../../../projects/addon-doc/components/page/page.template.html","../../../projects/addon-doc/components/theme-switcher/theme-switcher.component.ts","../../../projects/addon-doc/components/theme-switcher/theme-switcher.template.html","../../../projects/addon-doc/components/taiga-ui-addon-doc-components.ts"],"sourcesContent":["import {ChangeDetectionStrategy, Component, ViewEncapsulation} from '@angular/core';\nimport {tuiHintOptionsProvider} from '@taiga-ui/core/portals/hint';\n\n@Component({\n selector: 'table[tuiDocAPI]',\n template: `\n <thead>\n <tr>\n <ng-content select=\"th\" />\n </tr>\n </thead>\n <tbody><ng-content /></tbody>\n <ng-content select=\"tbody\" />\n `,\n styleUrl: './api.style.less',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [tuiHintOptionsProvider({appearance: 'floating'})],\n})\nexport class TuiDocAPI {}\n","import {Directive, input} from '@angular/core';\n\n@Directive({selector: 'tr[tuiDocAPIItem][type=number]'})\nexport class TuiDocAPINumberItem {\n public readonly min = input<number | null>(null);\n public readonly max = input<number | null>(null);\n}\n","import {inject, Pipe, type PipeTransform, TemplateRef} from '@angular/core';\nimport {WA_IS_E2E} from '@ng-web-apis/platform';\nimport {tuiInspect} from '@taiga-ui/addon-doc/utils';\n\n@Pipe({name: 'tuiInspect'})\nexport class TuiInspectPipe implements PipeTransform {\n private readonly isE2E = inject(WA_IS_E2E);\n\n public transform(value: unknown, depth = 2): string {\n if (this.isE2E && typeof value === 'function') {\n /**\n * @description:\n * When developing in production mode the webpack bundler minify\n * functions in different ways, then due to which the string content\n * of the function may differ from build to build, which can be to\n * various problems when screenshot testing on e2e.\n */\n return 'λ(x) => y';\n }\n\n return value instanceof TemplateRef ? 'TemplateRef' : tuiInspect(value, depth);\n }\n}\n","import {inject, Pipe, type PipeTransform} from '@angular/core';\nimport {\n TUI_DOC_TYPE_REFERENCE_HANDLER,\n TUI_DOC_TYPE_REFERENCE_PARSER,\n} from '@taiga-ui/addon-doc/tokens';\n\n@Pipe({name: 'tuiTypeReference'})\nexport class TuiTypeReferencePipe implements PipeTransform {\n private readonly parser = inject(TUI_DOC_TYPE_REFERENCE_PARSER);\n private readonly linkHandler = inject(TUI_DOC_TYPE_REFERENCE_HANDLER);\n\n public transform(original: string): ReadonlyArray<{\n type: string;\n extracted: string;\n reference: string | null;\n }> {\n return this.parser(original)\n .map(({type, extracted}) => ({\n type,\n extracted,\n reference: this.linkHandler?.(extracted) ?? null,\n }))\n .sort((a, b) => b.reference?.localeCompare(a.reference ?? '') ?? -1);\n }\n}\n","import {Location, NgTemplateOutlet} from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n input,\n model,\n type OnInit,\n} from '@angular/core';\nimport {FormsModule} from '@angular/forms';\nimport {ActivatedRoute, type Params, UrlSerializer} from '@angular/router';\nimport {TUI_DOC_URL_STATE_HANDLER} from '@taiga-ui/addon-doc/tokens';\nimport {tuiCoerceValue, tuiInspect} from '@taiga-ui/addon-doc/utils';\nimport {tuiIsNumber} from '@taiga-ui/cdk/utils/miscellaneous';\nimport {TuiInput} from '@taiga-ui/core/components/input';\nimport {TuiNotificationService} from '@taiga-ui/core/components/notification';\nimport {TuiHint} from '@taiga-ui/core/portals/hint';\nimport {TuiDataListWrapper} from '@taiga-ui/kit/components/data-list-wrapper';\nimport {TuiInputNumber} from '@taiga-ui/kit/components/input-number';\nimport {TuiSelect} from '@taiga-ui/kit/components/select';\nimport {TuiSwitch} from '@taiga-ui/kit/components/switch';\nimport {TuiChevron} from '@taiga-ui/kit/directives/chevron';\n\nimport {TuiDocAPINumberItem} from './api-item-number.directive';\nimport {TuiInspectPipe} from './inspect.pipe';\nimport {TuiTypeReferencePipe} from './type-reference.pipe';\n\nconst SERIALIZED_SUFFIX = '$';\n\n@Component({\n selector: 'tr[tuiDocAPIItem]',\n imports: [\n FormsModule,\n NgTemplateOutlet,\n TuiChevron,\n TuiDataListWrapper,\n TuiHint,\n TuiInput,\n TuiInputNumber,\n TuiInspectPipe,\n TuiSelect,\n TuiSwitch,\n TuiTypeReferencePipe,\n ],\n templateUrl: './api-item.template.html',\n styleUrl: './api-item.style.less',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TuiDocAPIItem<T> implements OnInit {\n private readonly locationRef = inject(Location);\n private readonly activatedRoute = inject(ActivatedRoute);\n private readonly urlSerializer = inject(UrlSerializer);\n private readonly urlStateHandler = inject(TUI_DOC_URL_STATE_HANDLER);\n private readonly alerts = inject(TuiNotificationService);\n\n protected readonly numberItem = inject(TuiDocAPINumberItem, {\n self: true,\n optional: true,\n });\n\n protected readonly isBananaBox = computed(() => this.name().startsWith('[('));\n protected readonly isInput = computed(() => this.name().startsWith('['));\n protected readonly isOutput = computed(() => this.name().startsWith('('));\n public readonly name = input('');\n public readonly type = input('');\n public readonly value = model<T>();\n public readonly items = input([], {transform: (v?: readonly T[]) => v || []});\n\n protected readonly hasCleaner = computed(\n () => this.type().includes('null') || this.type().includes('PolymorpheusContent'),\n );\n\n public ngOnInit(): void {\n this.parseParams(this.activatedRoute.snapshot.queryParams);\n }\n\n public onValueChange(value: T): void {\n this.value.set(value);\n this.setQueryParam(value);\n }\n\n public emitEvent(event: unknown): void {\n console.info('emitEvent', event);\n\n const alert =\n !event || event?.toString() === '[object Object]'\n ? tuiInspect(event, 2)\n : (event as string);\n\n this.alerts.open(alert, {label: this.name()}).subscribe();\n }\n\n private clearBrackets(value: string): string {\n return value.replaceAll(/[()[\\]]/g, '');\n }\n\n private parseParams(params: Params): void {\n const name = this.clearBrackets(this.name());\n const propertyValue: string | undefined = params[name];\n\n const propertyValueWithSuffix: number | string | undefined =\n params[`${name}${SERIALIZED_SUFFIX}`];\n\n if (!propertyValue && !propertyValueWithSuffix) {\n return;\n }\n\n const items = this.items();\n\n let value =\n !!propertyValueWithSuffix && items\n ? items[propertyValueWithSuffix as number]\n : tuiCoerceValue(propertyValue);\n\n if (this.type() === 'string' && tuiIsNumber(value)) {\n value = value.toString();\n }\n\n this.onValueChange(value as T);\n }\n\n private setQueryParam(value: T | boolean | number | string | null): void {\n const tree = this.urlSerializer.parse(this.locationRef.path());\n const isValueAvailableByKey = value instanceof Object;\n const items = this.items();\n\n const computedValue =\n isValueAvailableByKey && items ? items.indexOf(value as T) : value;\n\n const suffix = isValueAvailableByKey ? SERIALIZED_SUFFIX : '';\n const propName = `${this.clearBrackets(this.name())}${suffix}`;\n\n tree.queryParams = {\n ...tree.queryParams,\n [propName]: computedValue,\n };\n\n this.locationRef.go(this.urlStateHandler(tree));\n }\n}\n","<td class=\"t-td\">\n <code\n tuiHintDirection=\"top-end\"\n class=\"t-name\"\n [class.t-name_banana]=\"isBananaBox()\"\n [class.t-name_input]=\"isInput()\"\n [class.t-name_output]=\"isOutput()\"\n [tuiHint]=\"hint\"\n >\n {{ name() }}\n </code>\n <ng-template #hint>\n <ng-content>{{ name() }}</ng-content>\n </ng-template>\n <div class=\"t-description\">\n <ng-container *ngTemplateOutlet=\"hint\" />\n </div>\n</td>\n<td class=\"t-td\">\n <code class=\"t-type\">\n @for (item of type() | tuiTypeReference; track item) {\n @if (item.reference) {\n <a\n rel=\"noreferrer\"\n target=\"_blank\"\n class=\"t-reference\"\n [attr.href]=\"item.reference\"\n >\n {{ item.type }}\n </a>\n } @else {\n {{ item.type }}\n }\n @if (!$last) {\n <span> | </span>\n }\n }\n </code>\n</td>\n<td\n tuiTextfieldSize=\"m\"\n class=\"t-td\"\n>\n @if (items().length) {\n <tui-textfield\n tuiChevron\n class=\"t-input\"\n [content]=\"content\"\n [tuiTextfieldCleaner]=\"hasCleaner()\"\n >\n <input\n tuiSelect\n [ngModel]=\"value() ?? null\"\n (ngModelChange)=\"onValueChange($event)\"\n />\n <tui-data-list-wrapper\n *tuiDropdown\n [itemContent]=\"content\"\n [items]=\"items()\"\n />\n </tui-textfield>\n } @else {\n @if (value() !== undefined) {\n @if (type() === 'boolean') {\n <input\n tuiSwitch\n type=\"checkbox\"\n [id]=\"name()\"\n [ngModel]=\"value()\"\n (ngModelChange)=\"onValueChange($event)\"\n />\n }\n\n @if (type() === 'string') {\n <tui-textfield class=\"t-input\">\n <input\n tuiInput\n [id]=\"name()\"\n [ngModel]=\"value() || ''\"\n (ngModelChange)=\"onValueChange($event)\"\n />\n </tui-textfield>\n }\n\n @if (type() === 'number' || type() === 'bigint' || type() === 'number | bigint') {\n <tui-textfield class=\"t-input\">\n <input\n tuiInputNumber\n [id]=\"name()\"\n [max]=\"numberItem?.max() ?? null\"\n [min]=\"numberItem?.min() ?? null\"\n [ngModel]=\"value()\"\n [step]=\"1\"\n (ngModelChange)=\"onValueChange($event || 0)\"\n />\n </tui-textfield>\n }\n }\n }\n <ng-template\n #content\n let-data\n >\n <code [style.margin]=\"0\">{{ data | tuiInspect }}</code>\n </ng-template>\n</td>\n","import {ClipboardModule} from '@angular/cdk/clipboard';\nimport {isPlatformServer} from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n input,\n type OnChanges,\n PLATFORM_ID,\n} from '@angular/core';\nimport {toSignal} from '@angular/core/rxjs-interop';\nimport {TUI_DOC_EXAMPLE_MARKDOWN_CODE_PROCESSOR} from '@taiga-ui/addon-doc/tokens';\nimport {type TuiRawLoaderContent} from '@taiga-ui/addon-doc/types';\nimport {tuiRawLoad} from '@taiga-ui/addon-doc/utils';\nimport {type TuiHandler} from '@taiga-ui/cdk/types';\nimport {TuiButton} from '@taiga-ui/core/components/button';\nimport {TUI_COMMON_ICONS} from '@taiga-ui/core/tokens';\nimport {TUI_COPY_OPTIONS} from '@taiga-ui/kit/components/copy';\nimport {TUI_COPY_TEXTS} from '@taiga-ui/kit/tokens';\nimport {HighlightAuto} from 'ngx-highlightjs';\nimport {HighlightLineNumbers} from 'ngx-highlightjs/line-numbers';\nimport {BehaviorSubject, map, startWith, Subject, switchMap, timer} from 'rxjs';\n\n@Component({\n selector: 'tui-doc-code',\n imports: [ClipboardModule, HighlightAuto, HighlightLineNumbers, TuiButton],\n templateUrl: './index.html',\n styleUrl: './index.less',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {'[class._has-filename]': 'hasFilename'},\n})\nexport class TuiDocCode implements OnChanges {\n private readonly copy = inject(TUI_COPY_OPTIONS).icon;\n private readonly icons = inject(TUI_COMMON_ICONS);\n private readonly rawLoader$$ = new BehaviorSubject<TuiRawLoaderContent>('');\n private readonly texts = inject(TUI_COPY_TEXTS);\n protected readonly isServer = isPlatformServer(inject(PLATFORM_ID));\n\n protected readonly markdownCodeProcessor: TuiHandler<string, readonly string[]> =\n inject(TUI_DOC_EXAMPLE_MARKDOWN_CODE_PROCESSOR);\n\n protected readonly copy$ = new Subject<void>();\n protected readonly copyText = computed(() => this.texts()[0]);\n\n protected readonly icon = toSignal(\n this.copy$.pipe(\n switchMap(() =>\n timer(2000).pipe(\n map(() => this.copy),\n startWith(this.icons.check),\n ),\n ),\n ),\n {initialValue: this.copy},\n );\n\n protected readonly processor = toSignal(\n this.rawLoader$$.pipe(\n switchMap(tuiRawLoad),\n map((value: string) => this.markdownCodeProcessor(value)),\n ),\n {initialValue: []},\n );\n\n public readonly filename = input('');\n public readonly code = input<TuiRawLoaderContent>('');\n public readonly lineNumbers = input(true);\n\n public get hasFilename(): boolean {\n return !!this.filename();\n }\n\n public ngOnChanges(): void {\n this.rawLoader$$.next(this.code());\n }\n}\n","@if (filename()) {\n <p class=\"t-header\">\n {{ filename() }}\n </p>\n}\n@for (content of processor(); track content) {\n <pre class=\"t-code\">\n @if (lineNumbers()) {\n <code [highlightAuto]=\"content\" lineNumbers>{{isServer ? content : ''}}</code>\n } @else {\n <code [highlightAuto]=\"content\">{{isServer ? content : ''}}</code>\n }\n <div class=\"t-code-actions\">\n <button\n tuiIconButton\n type=\"button\"\n appearance=\"\"\n size=\"s\"\n class=\"t-copy-button\"\n [iconStart]=\"icon()\"\n [cdkCopyToClipboard]=\"content\"\n (click)=\"copy$.next()\"\n >\n {{ copyText() }}\n </button>\n <ng-content />\n </div>\n </pre>\n}\n","import {ChangeDetectionStrategy, Component, inject} from '@angular/core';\nimport {toSignal} from '@angular/core/rxjs-interop';\nimport {TUI_FALSE_HANDLER} from '@taiga-ui/cdk/constants';\nimport {TuiButton} from '@taiga-ui/core/components/button';\nimport {TUI_COPY_TEXTS} from '@taiga-ui/kit/tokens';\nimport {map, startWith, Subject, switchMap, timer} from 'rxjs';\n\nconst COPIED_TIMEOUT = 1500;\n\n@Component({\n selector: 'tui-doc-copy',\n imports: [TuiButton],\n templateUrl: './index.html',\n styleUrl: './index.less',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TuiDocCopy {\n private readonly copy$ = new Subject<void>();\n protected readonly texts = inject(TUI_COPY_TEXTS);\n\n protected readonly copied = toSignal(\n this.copy$.pipe(\n switchMap(() =>\n timer(COPIED_TIMEOUT).pipe(map(TUI_FALSE_HANDLER), startWith(true)),\n ),\n ),\n {initialValue: false},\n );\n\n protected onClick(): void {\n this.copy$.next();\n }\n}\n","<button\n appearance=\"\"\n size=\"s\"\n tuiButton\n type=\"button\"\n class=\"t-copy\"\n (click)=\"onClick()\"\n>\n <span class=\"t-content\">\n <span\n class=\"t-initial\"\n [attr.data-text]=\"copied() ? '' : texts()[0]\"\n >\n @if (!copied()) {\n <ng-content />\n }\n </span>\n {{ copied() ? texts()[1] : '' }}\n </span>\n</button>\n","import {Location, NgTemplateOutlet} from '@angular/common';\nimport {\n type AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n ElementRef,\n inject,\n input,\n Pipe,\n type PipeTransform,\n type Signal,\n signal,\n TemplateRef,\n viewChild,\n} from '@angular/core';\nimport {takeUntilDestroyed, toObservable} from '@angular/core/rxjs-interop';\nimport {\n type AbstractControl,\n FormGroup,\n FormsModule,\n ReactiveFormsModule,\n} from '@angular/forms';\nimport {type Params, UrlSerializer, type UrlTree} from '@angular/router';\nimport {\n TUI_DOC_DEMO_TEXTS,\n TUI_DOC_ICONS,\n TUI_DOC_URL_STATE_HANDLER,\n} from '@taiga-ui/addon-doc/tokens';\nimport {type TuiDemoParams} from '@taiga-ui/addon-doc/types';\nimport {tuiCleanObject, tuiCoerceValueIsTrue} from '@taiga-ui/addon-doc/utils';\nimport {TuiItem} from '@taiga-ui/cdk/directives/item';\nimport {TuiResizable, TuiResizer} from '@taiga-ui/cdk/directives/resizer';\nimport {tuiInjectElement} from '@taiga-ui/cdk/utils/dom';\nimport {tuiClamp} from '@taiga-ui/cdk/utils/math';\nimport {tuiPx} from '@taiga-ui/cdk/utils/miscellaneous';\nimport {TuiButton} from '@taiga-ui/core/components/button';\nimport {TuiExpand} from '@taiga-ui/core/components/expand';\nimport {TuiIcon} from '@taiga-ui/core/components/icon';\nimport {TuiTextfield} from '@taiga-ui/core/components/textfield';\nimport {TUI_DARK_MODE} from '@taiga-ui/core/tokens';\nimport {TuiDataListWrapper} from '@taiga-ui/kit/components/data-list-wrapper';\nimport {TuiSelect} from '@taiga-ui/kit/components/select';\nimport {TuiSwitch} from '@taiga-ui/kit/components/switch';\nimport {TuiChevron} from '@taiga-ui/kit/directives/chevron';\nimport {skip} from 'rxjs';\n\nconst MIN_WIDTH = 160;\n\n@Pipe({name: 'json'})\nexport class TuiJsonPipe implements PipeTransform {\n public transform(value: unknown): string {\n return JSON.stringify(\n value,\n (_, x) => (typeof x === 'bigint' ? `${String(x)}n` : x),\n 2,\n );\n }\n}\n\n@Component({\n selector: 'tui-doc-demo',\n imports: [\n FormsModule,\n NgTemplateOutlet,\n ReactiveFormsModule,\n TuiButton,\n TuiChevron,\n TuiDataListWrapper,\n TuiExpand,\n TuiIcon,\n TuiItem,\n TuiJsonPipe,\n TuiResizable,\n TuiResizer,\n TuiSelect,\n TuiSwitch,\n TuiTextfield,\n ],\n templateUrl: './index.html',\n styleUrl: './index.less',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[attr.tuiTheme]': 'theme()',\n '[class._sticky]': 'sticky()',\n '(document:mouseup.zoneless)': 'onMouseUp()',\n '(window:resize)': 'onResize()',\n },\n})\nexport class TuiDocDemo implements AfterViewInit {\n private readonly resizable: Signal<ElementRef<HTMLElement>> = viewChild.required(\n TuiResizable,\n {read: ElementRef},\n );\n\n private readonly content = viewChild.required<ElementRef<HTMLElement>>('content');\n private readonly el = tuiInjectElement();\n private readonly locationRef = inject(Location);\n private readonly urlSerializer = inject(UrlSerializer);\n private readonly urlStateHandler = inject(TUI_DOC_URL_STATE_HANDLER);\n private readonly darkMode = inject(TUI_DARK_MODE);\n protected readonly template = contentChild(TemplateRef<Record<string, unknown>>);\n protected readonly rendered = signal(false);\n protected readonly theme = computed(() => (this.dark() ? 'dark' : 'light'));\n protected readonly icons = inject(TUI_DOC_ICONS);\n\n protected readonly dark = signal(\n tuiCoerceValueIsTrue(this.params.darkMode ?? this.darkMode()),\n );\n\n protected readonly $ = toObservable(this.darkMode)\n .pipe(skip(1), takeUntilDestroyed())\n .subscribe((mode) => this.onModeChange(mode));\n\n protected form?: FormGroup;\n protected readonly updateOnVariants = ['change', 'blur', 'submit'] as const;\n\n protected updateOn: 'blur' | 'change' | 'submit' =\n this.params.updateOn || this.updateOnVariants[0];\n\n protected opaque = tuiCoerceValueIsTrue(this.params.sandboxOpaque ?? true);\n protected expanded = tuiCoerceValueIsTrue(this.params.sandboxExpanded ?? false);\n protected sandboxWidth = Number.parseInt(this.params.sandboxWidth, 10);\n protected readonly texts = inject(TUI_DOC_DEMO_TEXTS);\n public readonly control = input<AbstractControl | null>(null);\n public readonly sticky = input(true);\n\n public ngAfterViewInit(): void {\n this.createForm();\n this.updateWidth(this.sandboxWidth + this.delta);\n this.rendered.set(true);\n }\n\n protected onResize(): void {\n this.updateWidth();\n this.onMouseUp();\n }\n\n protected onMouseUp(): void {\n this.updateUrl({sandboxWidth: this.sandboxWidth});\n }\n\n protected onModeChange(darkMode: boolean): void {\n this.dark.set(darkMode);\n this.updateUrl({sandboxWidth: this.sandboxWidth, darkMode});\n }\n\n protected toggleDetails(): void {\n this.expanded = !this.expanded;\n this.updateUrl({sandboxExpanded: this.expanded});\n }\n\n protected changeOpaque(opaque: boolean): void {\n this.opaque = opaque;\n this.updateUrl({sandboxOpaque: this.opaque});\n }\n\n protected updateOnChange(updateOn: 'blur' | 'change' | 'submit'): void {\n this.updateOn = updateOn;\n this.updateUrl({updateOn});\n this.createForm();\n }\n\n protected updateWidth(width = Number.NaN): void {\n if (!this.resizable() || !this.content()) {\n return;\n }\n\n const safe = width || this.resizable().nativeElement.clientWidth;\n const total = this.el.clientWidth;\n const clamped = Math.round(tuiClamp(safe, MIN_WIDTH, total)) - this.delta;\n const validated = safe < total ? clamped : Number.NaN;\n\n this.resizable().nativeElement.style.width = validated ? tuiPx(safe) : '';\n this.sandboxWidth = validated;\n }\n\n private get delta(): number {\n return this.resizable() && this.content()\n ? this.resizable().nativeElement.clientWidth -\n this.content().nativeElement.clientWidth\n : 0;\n }\n\n private get params(): Params | TuiDemoParams {\n return this.getUrlTree().queryParams;\n }\n\n private updateUrl(params: TuiDemoParams): void {\n const tree = this.getUrlTree();\n const {queryParams} = tree;\n\n delete queryParams.sandboxWidth;\n\n tree.queryParams = {\n ...queryParams,\n ...tuiCleanObject({...params}),\n };\n\n this.locationRef.go(this.urlStateHandler(tree));\n }\n\n private createForm(): void {\n const control = this.control();\n\n if (control) {\n this.form = new FormGroup({value: control}, {updateOn: this.updateOn});\n }\n }\n\n private getUrlTree(): UrlTree {\n return this.urlSerializer.parse(this.locationRef.path());\n }\n}\n","<div class=\"t-settings\">\n <label tuiLabel>\n <input\n size=\"s\"\n tuiSwitch\n type=\"checkbox\"\n [ngModel]=\"dark()\"\n (ngModelChange)=\"onModeChange($event)\"\n />\n <small>{{ texts()[0] }}</small>\n </label>\n <label tuiLabel>\n <input\n size=\"s\"\n tuiSwitch\n type=\"checkbox\"\n [ngModel]=\"opaque\"\n (ngModelChange)=\"changeOpaque($event)\"\n />\n <small>{{ texts()[1] }}</small>\n </label>\n</div>\n<div\n tuiResizable\n class=\"t-wrapper\"\n [class.t-wrapper_transparent]=\"!opaque\"\n [style.visibility]=\"rendered() ? 'visible' : 'hidden'\"\n>\n <div class=\"t-content\">\n <div\n #content\n id=\"demo-content\"\n >\n @if (form) {\n <form\n id=\"tui-demo-form\"\n [formGroup]=\"form\"\n >\n <ng-container [ngTemplateOutlet]=\"template() || null\" />\n </form>\n }\n <ng-content />\n </div>\n </div>\n <div\n class=\"t-resizer\"\n [tuiResizer]=\"[1, 0]\"\n (tuiSizeChange)=\"updateWidth($event[0])\"\n >\n <tui-icon [icon]=\"icons.resizer\" />\n </div>\n</div>\n@if (form) {\n <tui-expand [expanded]=\"expanded\">\n <ng-template tuiItem>\n <pre class=\"t-value\">Form data: {{ form.value | json }}</pre>\n </ng-template>\n </tui-expand>\n <div class=\"t-footer\">\n <button\n appearance=\"flat-grayscale\"\n automation-id=\"tui-demo-button__toggle-details\"\n size=\"s\"\n tuiButton\n type=\"button\"\n class=\"t-button\"\n [tuiChevron]=\"expanded\"\n (click)=\"toggleDetails()\"\n >\n {{ texts()[2] }}\n </button>\n <tui-textfield\n tuiChevron\n tuiTextfieldAppearance=\"secondary-grayscale\"\n tuiTextfieldSize=\"s\"\n class=\"t-select\"\n [tuiTextfieldCleaner]=\"false\"\n >\n <input\n automation-id=\"tui-demo-select__expand-update-on\"\n tuiSelect\n [ngModel]=\"updateOn\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"updateOnChange($event)\"\n />\n <tui-data-list-wrapper\n *tuiDropdown\n [items]=\"updateOnVariants\"\n />\n </tui-textfield>\n <button\n appearance=\"secondary-grayscale\"\n automation-id=\"tui-demo-button__reset-state\"\n form=\"tui-demo-form\"\n size=\"s\"\n tuiButton\n type=\"reset\"\n >\n Reset\n </button>\n <button\n appearance=\"secondary-grayscale\"\n automation-id=\"tui-demo-button__submit-state\"\n form=\"tui-demo-form\"\n size=\"s\"\n tuiButton\n type=\"submit\"\n >\n Submit\n </button>\n </div>\n}\n","import {ChangeDetectionStrategy, Component, input} from '@angular/core';\n\n@Component({\n selector: 'tui-doc-tab',\n templateUrl: './index.html',\n styleUrl: './index.less',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TuiDocTab {\n public readonly src = input('');\n}\n","<div class=\"t-tab\">\n <img\n alt=\"Documentation tab icon\"\n class=\"t-icon\"\n [src]=\"src()\"\n />\n <ng-content />\n</div>\n","import {TUI_EXAMPLE_PRIMARY_FILE_NAME} from '@taiga-ui/addon-doc/types';\nimport {type TuiBooleanHandler} from '@taiga-ui/cdk/types';\nimport {tuiCreateOptions} from '@taiga-ui/cdk/utils/di';\nimport {type PolymorpheusContent} from '@taiga-ui/polymorpheus';\n\nexport interface TuiDocExampleOptions {\n codeEditorVisibilityHandler: TuiBooleanHandler<Record<string, string>>;\n fullsize: boolean;\n tabTitles: Map<unknown, PolymorpheusContent>;\n}\n\nexport const TUI_DOC_EXAMPLE_DEFAULT_OPTIONS: TuiDocExampleOptions = {\n codeEditorVisibilityHandler: (files) =>\n Boolean(\n files[TUI_EXAMPLE_PRIMARY_FILE_NAME.TS] &&\n files[TUI_EXAMPLE_PRIMARY_FILE_NAME.HTML],\n ),\n tabTitles: new Map(),\n fullsize: true,\n};\n\n/**\n * Default parameters for DocExample component\n */\nexport const [TUI_DOC_EXAMPLE_OPTIONS, tuiDocExampleOptionsProvider] = tuiCreateOptions(\n TUI_DOC_EXAMPLE_DEFAULT_OPTIONS,\n);\n","import {Pipe, type PipeTransform} from '@angular/core';\n\n@Pipe({name: 'tuiDocExampleGetTabs'})\nexport class TuiDocExampleGetTabsPipe implements PipeTransform {\n public transform(content: Record<string, string>, defaultTab: string): string[] {\n return [defaultTab, ...Object.keys(content).filter((tab) => content[tab])];\n }\n}\n","import {Clipboard} from '@angular/cdk/clipboard';\nimport {DOCUMENT, NgComponentOutlet, NgTemplateOutlet} from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n input,\n type OnChanges,\n resource,\n type Signal,\n signal,\n type Type,\n} from '@angular/core';\nimport {toSignal} from '@angular/core/rxjs-interop';\nimport {ActivatedRoute, RouterLink} from '@angular/router';\nimport {WA_LOCATION} from '@ng-web-apis/common';\nimport {\n WaIntersectionObservee,\n WaIntersectionObserverDirective,\n} from '@ng-web-apis/intersection-observer';\nimport {\n TUI_DOC_CODE_ACTIONS,\n TUI_DOC_CODE_EDITOR,\n TUI_DOC_EXAMPLE_CONTENT_PROCESSOR,\n TUI_DOC_ICONS,\n TUI_DOC_PREVIEW_TEXT,\n} from '@taiga-ui/addon-doc/tokens';\nimport {type TuiRawLoaderContent} from '@taiga-ui/addon-doc/types';\nimport {tuiRawLoadRecord, tuiToKebab} from '@taiga-ui/addon-doc/utils';\nimport {TuiMapperPipe} from '@taiga-ui/cdk/pipes/mapper';\nimport {type TuiContext} from '@taiga-ui/cdk/types';\nimport {TuiButton} from '@taiga-ui/core/components/button';\nimport {TuiLink} from '@taiga-ui/core/components/link';\nimport {TuiLoader} from '@taiga-ui/core/components/loader';\nimport {TuiNotificationService} from '@taiga-ui/core/components/notification';\nimport {TuiTitle} from '@taiga-ui/core/components/title';\nimport {TuiFullscreen} from '@taiga-ui/kit/components/fullscreen';\nimport {TuiSegmented} from '@taiga-ui/kit/components/segmented';\nimport {TUI_COPY_TEXTS, TUI_DONE_WORD} from '@taiga-ui/kit/tokens';\nimport {TuiHeader} from '@taiga-ui/layout/components/header';\nimport {type PolymorpheusContent, PolymorpheusOutlet} from '@taiga-ui/polymorpheus';\nimport {BehaviorSubject, map, switchMap} from 'rxjs';\n\nimport {TuiDocCode} from '../code';\nimport {TUI_DOC_EXAMPLE_OPTIONS} from './example.options';\nimport {TuiDocExampleGetTabsPipe} from './example-get-tabs.pipe';\n\n@Component({\n selector: 'tui-doc-example',\n imports: [\n NgComponentOutlet,\n NgTemplateOutlet,\n PolymorpheusOutlet,\n RouterLink,\n TuiButton,\n TuiDocCode,\n TuiDocExampleGetTabsPipe,\n TuiFullscreen,\n TuiHeader,\n TuiLink,\n TuiLoader,\n TuiMapperPipe,\n TuiSegmented,\n TuiTitle,\n ],\n templateUrl: './example.template.html',\n styleUrl: './example.style.less',\n changeDetection: ChangeDetectionStrategy.OnPush,\n hostDirectives: [\n WaIntersectionObserverDirective,\n {\n directive: WaIntersectionObservee,\n outputs: ['waIntersectionObservee'],\n },\n ],\n host: {\n waIntersectionRootMargin: '-40px 0px 1000000% 0px',\n waIntersectionThreshold: '1',\n '[attr.id]': 'resolvedId()',\n '[class._fullsize]': 'fullsize()',\n '(waIntersectionObservee)': 'onIntersection()',\n },\n})\nexport class TuiDocExample implements OnChanges {\n private readonly doc = inject(DOCUMENT);\n private readonly clipboard = inject(Clipboard);\n private readonly alerts = inject(TuiNotificationService);\n private readonly location = inject(WA_LOCATION);\n private readonly copyTexts = inject(TUI_COPY_TEXTS);\n private readonly doneWord = inject(TUI_DONE_WORD);\n private readonly processContent = inject(TUI_DOC_EXAMPLE_CONTENT_PROCESSOR);\n\n private readonly rawLoader$$ = new BehaviorSubject<\n Record<string, TuiRawLoaderContent>\n >({});\n\n protected readonly fullscreenEnabled = inject(DOCUMENT).fullscreenEnabled;\n protected readonly icons = inject(TUI_DOC_ICONS);\n protected readonly options = inject(TUI_DOC_EXAMPLE_OPTIONS);\n protected readonly defaultTab = inject(TUI_DOC_PREVIEW_TEXT);\n protected readonly codeEditor = inject(TUI_DOC_CODE_EDITOR, {optional: true});\n\n protected readonly codeActions =\n inject<ReadonlyArray<PolymorpheusContent<TuiContext<string>>>>(\n TUI_DOC_CODE_ACTIONS,\n );\n\n protected readonly route = inject(ActivatedRoute);\n protected activeItemIndex = 0;\n protected fullscreen = false;\n protected readonly copy = computed(() => this.copyTexts()[0]);\n protected readonly loading = signal(false);\n\n protected readonly resolvedId = computed(\n () => this.id() || tuiToKebab(this.heading()),\n );\n\n protected readonly processor: Signal<Record<string, string>> = toSignal(\n this.rawLoader$$.pipe(\n switchMap(tuiRawLoadRecord),\n map((value) => this.processContent(value)),\n ),\n {initialValue: {}},\n );\n\n protected readonly lazyComponent = resource({\n request: async () => this.component(),\n loader: async ({request}) => {\n if (!request) {\n return null;\n }\n\n const component = await request;\n\n return component && 'default' in component ? component.default : component;\n },\n });\n\n public readonly heading = input('');\n public readonly id = input('');\n public readonly description = input<PolymorpheusContent>();\n public readonly fullsize = input(inject(TUI_DOC_EXAMPLE_OPTIONS).fullsize);\n\n public readonly component =\n input<Promise<Type<unknown> | {default: Type<unknown>}>>();\n\n public readonly content = input<Record<string, TuiRawLoaderContent>>({});\n public readonly preview = input(true);\n\n public ngOnChanges(): void {\n this.rawLoader$$.next(this.content());\n }\n\n protected readonly visible = (files: Record<string, string>): boolean =>\n Boolean(this.codeEditor && this.options.codeEditorVisibilityHandler(files));\n\n protected getTabTitle(fileName: string): PolymorpheusContent {\n return this.options.tabTitles.get(fileName) || fileName;\n }\n\n protected copyExampleLink(target: EventTarget | null): void {\n this.clipboard.copy((target as HTMLAnchorElement | null)?.href ?? '');\n this.alerts\n .open(this.copyTexts()[1], {label: this.doneWord(), appearance: 'positive'})\n .subscribe();\n }\n\n protected edit(files: Record<string, string>): void {\n this.loading.set(true);\n this.codeEditor\n ?.edit(this.location.pathname.slice(1), this.resolvedId() || '', files)\n .finally(() => this.loading.set(false));\n }\n\n protected onIntersection(): void {\n this.doc.dispatchEvent(\n new CustomEvent('tui-example', {detail: this.resolvedId()}),\n );\n }\n}\n","@if (heading()) {\n <header tuiHeader=\"h6\">\n <hgroup tuiTitle>\n <h3>\n <a\n appearance=\"\"\n routerLink=\".\"\n tuiLink\n class=\"t-link\"\n [attr.title]=\"copy()\"\n [fragment]=\"resolvedId()\"\n [iconEnd]=\"icons.link\"\n [relativeTo]=\"route.firstChild ?? route\"\n (click)=\"copyExampleLink($event.currentTarget)\"\n >\n {{ heading() }}\n </a>\n </h3>\n </hgroup>\n </header>\n\n @if (description()) {\n <div tuiDescription>\n <ng-container *polymorpheusOutlet=\"description() as text\">\n {{ text }}\n </ng-container>\n </div>\n }\n}\n<div\n class=\"t-example\"\n [(tuiFullscreen)]=\"fullscreen\"\n>\n @if (processor() | tuiDocExampleGetTabs: defaultTab(); as all) {\n @let tabs = preview() ? all : all.slice(1);\n @if (tabs.length) {\n <div class=\"t-header\">\n <tui-segmented\n class=\"t-tabs\"\n [(activeItemIndex)]=\"activeItemIndex\"\n >\n @for (tab of tabs; track tab) {\n <button type=\"button\">\n <ng-container *polymorpheusOutlet=\"getTabTitle(tab) as text\">\n {{ text }}\n </ng-container>\n </button>\n }\n </tui-segmented>\n @if (processor() | tuiMapper: visible) {\n <tui-loader\n size=\"xs\"\n class=\"t-code-editor\"\n [loading]=\"loading()\"\n [overlay]=\"true\"\n (click)=\"edit(processor())\"\n >\n @if (codeEditor?.content; as content) {\n <ng-container *polymorpheusOutlet=\"content as editContent\">\n {{ editContent }}\n </ng-container>\n } @else {\n <button\n appearance=\"flat\"\n size=\"s\"\n tuiButton\n type=\"button\"\n >\n Edit on {{ codeEditor?.name }}\n </button>\n }\n </tui-loader>\n }\n @if (fullscreenEnabled && preview()) {\n <button\n appearance=\"flat-grayscale\"\n size=\"xs\"\n tuiIconButton\n type=\"button\"\n [iconStart]=\"fullscreen ? icons.shrink : icons.expand\"\n (click)=\"fullscreen = !fullscreen\"\n >\n Fullscreen\n </button>\n }\n </div>\n }\n @for (tab of tabs; track tab) {\n <div class=\"t-content\">\n @if (!$index && preview()) {\n <section\n automation-id=\"tui-doc-example\"\n class=\"t-demo\"\n [style.display]=\"activeItemIndex === $index ? 'block' : 'none'\"\n >\n <ng-container *ngTemplateOutlet=\"content\" />\n <ng-container *ngComponentOutlet=\"lazyComponent.value() ?? null\" />\n </section>\n }\n @let code = processor()[tab] || '';\n <tui-doc-code\n [code]=\"code\"\n [style.display]=\"activeItemIndex === $index && ($index || !preview()) ? 'block' : 'none'\"\n >\n @for (action of codeActions; track action) {\n <ng-container *polymorpheusOutlet=\"action as text; context: {$implicit: code}\">\n {{ text }}\n </ng-container>\n }\n </tui-doc-code>\n </div>\n } @empty {\n <div class=\"t-content\">\n <section class=\"t-demo\">\n <ng-container *ngTemplateOutlet=\"content\" />\n </section>\n </div>\n }\n }\n</div>\n<ng-template #content>\n <ng-content />\n</ng-template>\n","import {InjectionToken, type Provider} from '@angular/core';\nimport {takeUntilDestroyed} from '@angular/core/rxjs-interop';\nimport {ActivatedRoute, type Event, NavigationEnd, Router, Scroll} from '@angular/router';\nimport {TUI_DOC_PAGES, TUI_DOC_TITLE} from '@taiga-ui/addon-doc/tokens';\nimport {type TuiDocRoutePages} from '@taiga-ui/addon-doc/types';\nimport {tuiAutoFocusOptionsProvider} from '@taiga-ui/cdk/directives/auto-focus';\nimport {tuiIsPresent} from '@taiga-ui/cdk/utils/miscellaneous';\nimport {tuiLinkOptionsProvider} from '@taiga-ui/core/components/link';\nimport {tuiScrollbarOptionsProvider} from '@taiga-ui/core/components/scrollbar';\nimport {filter, map, mergeMap, type Observable} from 'rxjs';\n\nfunction labelsProviderFactory(pages: TuiDocRoutePages): readonly string[] {\n return pages\n .map(({section}) => section)\n .filter(tuiIsPresent)\n .filter((item, index, array) => array.indexOf(item) === index);\n}\n\n/**\n * Page title\n */\nexport const NAVIGATION_TITLE = new InjectionToken<Observable<string>>(\n ngDevMode ? 'NAVIGATION_TITLE' : '',\n);\n\n/**\n * Navigation sections labels for search\n */\nexport const NAVIGATION_LABELS = new InjectionToken<readonly string[]>(\n ngDevMode ? 'NAVIGATION_LABELS' : '',\n);\n\n/**\n * Navigation pages\n */\nexport const NAVIGATION_ITEMS = new InjectionToken<readonly TuiDocRoutePages[]>(\n ngDevMode ? 'NAVIGATION_ITEMS' : '',\n);\n\nexport const NAVIGATION_PROVIDERS: Provider[] = [\n tuiAutoFocusOptionsProvider({preventScroll: true}),\n tuiLinkOptionsProvider({appearance: 'action-grayscale'}),\n {\n provide: NAVIGATION_TITLE,\n deps: [Router, ActivatedRoute, TUI_DOC_TITLE],\n useFactory: (\n router: Router,\n activatedRoute: ActivatedRoute,\n titlePrefix: string,\n ): Observable<string> =>\n router.events.pipe(\n filter(\n (event: Event) =>\n event instanceof NavigationEnd ||\n (event instanceof Scroll\n ? event.routerEvent instanceof NavigationEnd\n : false),\n ),\n map(() => activatedRoute.firstChild),\n filter(tuiIsPresent),\n mergeMap(({data}) => data),\n map(({title}) => `${titlePrefix}${title}`),\n takeUntilDestroyed(),\n ),\n },\n {\n provide: NAVIGATION_LABELS,\n deps: [TUI_DOC_PAGES],\n useFactory: labelsProviderFactory,\n },\n {\n provide: NAVIGATION_ITEMS,\n deps: [TUI_DOC_PAGES],\n useFactory: (pages: TuiDocRoutePages): readonly TuiDocRoutePages[] => {\n const labels = labelsProviderFactory(pages);\n\n return [\n ...labels.map((label) => pages.filter(({section}) => section === label)),\n pages.filter((page) => !page.section),\n ];\n },\n },\n tuiScrollbarOptionsProvider({mode: 'hover'}),\n];\n","import {Directive, inject, input, type OnChanges} from '@angular/core';\nimport {takeUntilDestroyed} from '@angular/core/rxjs-interop';\nimport {TUI_DOC_PAGE_LOADED} from '@taiga-ui/addon-doc/tokens';\nimport {tuiZonefreeScheduler} from '@taiga-ui/cdk/observables';\nimport {tuiGetElementObscures, tuiInjectElement} from '@taiga-ui/cdk/utils/dom';\nimport {debounceTime, filter, ReplaySubject, switchMap, take} from 'rxjs';\n\n@Directive({selector: '[tuiDocScrollIntoViewLink]'})\nexport class TuiDocScrollIntoViewLink implements OnChanges {\n private readonly scroll$ = new ReplaySubject<boolean>(1);\n private readonly el = tuiInjectElement();\n\n protected readonly sub = inject(TUI_DOC_PAGE_LOADED)\n .pipe(\n filter(Boolean),\n take(1),\n switchMap(() => this.scroll$),\n debounceTime(750, tuiZonefreeScheduler()),\n filter((shallWe) => shallWe && !!tuiGetElementObscures(this.el)),\n takeUntilDestroyed(),\n )\n .subscribe(() => this.el.scrollIntoView());\n\n public readonly tuiDocScrollIntoViewLink = input(false);\n\n public ngOnChanges(): void {\n this.scroll$.next(this.tuiDocScrollIntoViewLink());\n }\n}\n","import {DOCUMENT, NgTemplateOutlet} from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n inject,\n type Signal,\n signal,\n viewChild,\n} from '@angular/core';\nimport {takeUntilDestroyed, toSignal} from '@angular/core/rxjs-interop';\nimport {FormControl, ReactiveFormsModule} from '@angular/forms';\nimport {Title} from '@angular/platform-browser';\nimport {Router, RouterLink, RouterLinkActive, Scroll} from '@angular/router';\nimport {\n TUI_DOC_ICONS,\n TUI_DOC_PAGE_LOADED,\n TUI_DOC_PAGES_ICONS,\n TUI_DOC_SEARCH_ENABLED,\n TUI_DOC_SEARCH_TEXT,\n} from '@taiga-ui/addon-doc/tokens';\nimport {type TuiDocRoutePage, type TuiDocRoutePages} from '@taiga-ui/addon-doc/types';\nimport {tuiTransliterateKeyboardLayout} from '@taiga-ui/addon-doc/utils';\nimport {TuiAutoFocus} from '@taiga-ui/cdk/directives/auto-focus';\nimport {tuiControlValue, tuiWatch} from '@taiga-ui/cdk/observables';\nimport {TuiDataList} from '@taiga-ui/core/components/data-list';\nimport {TuiExpand} from '@taiga-ui/core/components/expand';\nimport {TuiIcon} from '@taiga-ui/core/components/icon';\nimport {TuiInput, TuiInputDirective} from '@taiga-ui/core/components/input';\nimport {TuiLink} from '@taiga-ui/core/components/link';\nimport {TuiScrollbar} from '@taiga-ui/core/components/scrollbar';\nimport {TUI_COMMON_ICONS} from '@taiga-ui/core/tokens';\nimport {TuiAccordion} from '@taiga-ui/kit/components/accordion';\nimport {TuiDrawer} from '@taiga-ui/kit/components/drawer';\nimport {PolymorpheusOutlet} from '@taiga-ui/polymorpheus';\nimport {combineLatest, filter, fromEvent, map, of, switchMap, take} from 'rxjs';\n\nimport {\n NAVIGATION_ITEMS,\n NAVIGATION_LABELS,\n NAVIGATION_PROVIDERS,\n NAVIGATION_TITLE,\n} from './navigation.providers';\nimport {TuiDocScrollIntoViewLink} from './scroll-into-view.directive';\n\nfunction tuiUniqBy<T extends Record<string, any>>(\n array: readonly T[],\n key: keyof T,\n): readonly T[] {\n return Array.from(\n array\n .reduce(\n (map, item) => (map.has(item[key]) ? map : map.set(item[key], item)),\n new Map<T[keyof T], T>(),\n )\n .values(),\n );\n}\n\n@Component({\n selector: 'tui-doc-navigation',\n imports: [\n NgTemplateOutlet,\n PolymorpheusOutlet,\n ReactiveFormsModule,\n RouterLink,\n RouterLinkActive,\n TuiAccordion,\n TuiAutoFocus,\n TuiDataList,\n TuiDocScrollIntoViewLink,\n TuiExpand,\n TuiIcon,\n TuiInput,\n TuiLink,\n TuiScrollbar,\n ],\n templateUrl: './navigation.template.html',\n styleUrl: './navigation.style.less',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: NAVIGATION_PROVIDERS,\n host: {\n '[class._open]': 'menuOpen',\n '(window:keydown)': 'onFocusSearch($event)',\n },\n})\nexport class TuiDocNavigation {\n private readonly searchInput: Signal<ElementRef<HTMLInputElement> | undefined> =\n viewChild(TuiInputDirective, {read: ElementRef});\n\n private readonly router = inject(Router);\n private readonly doc = inject(DOCUMENT);\n protected readonly open = si